* ohci.c
* - Open Host Controller Interface driver
*/
-#define DEBUG 0
+#define DEBUG 1
#define VERSION VER2(0,1)
#include <usb_host.h>
#include "ohci.h"
#include <modules.h>
#include <drv_pci.h>
+#include <timers.h>
// === CONSTANTS ===
#define MAX_CONTROLLERS 4
#define MAX_TD_PAGES 2
#define MAX_ENDPT_PAGES 2
-#define MAX_PACKET_SIZE 0x1000 // TODO: Check what should be used
+#define MAX_PACKET_SIZE 1023 // TODO: Check what should be used
// === PROTOTYPES ===
int OHCI_Initialise(char **Arguments);
void OHCI_CheckPortUpdate(void *Ptr);
void OHCI_InterruptHandler(int IRQ, void *Ptr);
-tOHCI_Endpoint *OHCI_int_AllocateEndpt(tOHCI_Controller *Controller);
+tOHCI_Endpoint *OHCI_int_AllocateEndpt(tOHCI_Controller *Controller, int Dest);
tOHCI_Endpoint *OHCI_int_GetEndptFromPhys(tPAddr PhysAddr);
tOHCI_GeneralTD *OHCI_int_AllocateGTD(tOHCI_Controller *Controller);
tOHCI_GeneralTD *OHCI_int_GetGTDFromPhys(tPAddr PhysAddr);
ctrlr->PciId = id;
ctrlr->IRQNum = PCI_GetIRQ(id);
ctrlr->ControlSpacePhys = PCI_GetBAR(id, 0); // Offset 0x10
+ PCI_ConfigWrite( id, 4, 2, 0x0006 ); // Enable memory and bus master
if( ctrlr->ControlSpacePhys == 0 || ctrlr->ControlSpacePhys & 1 ) {
ctrlr->ControlSpacePhys = 0;
void OHCI_InitialiseController(tOHCI_Controller *Controller)
{
tOHCI_Controller *cnt = Controller;
- struct sRegisters *iospace;
+ volatile struct sRegisters *iospace;
Log_Debug("OHCI", "Card #%i at 0x%X, IRQ %i",
cnt->ID, cnt->ControlSpacePhys, cnt->IRQNum);
// - Prepare mappings
cnt->ControlSpace = (void*)MM_MapHWPages(cnt->ControlSpacePhys, 1);
IRQ_AddHandler( cnt->IRQNum, OHCI_InterruptHandler, cnt);
+ iospace = cnt->ControlSpace;
+
+ Log_Debug("OHCI", "Card #%i version is 0x%x", cnt->ID, iospace->HcRevision);
+
+ // Check who had control
+ if( iospace->HcControl & (1 << 8) ) // InterruptRouting
+ {
+ LOG("USB was in the hands of SMM, asking for it back");
+ // SMM has control, ask for it back
+ // - Write '1' to OwnershipChangeRequest
+ // - Wait for InterruptRouting to clear
+ // TODO: Timeout
+ while( iospace->HcControl & (1 << 8) ) ;
+ LOG("Obtained USB");
+ }
+ else if( (iospace->HcControl & 0xC0) != 0x00 ) // UsbReset
+ {
+ LOG("USB was in the hands of the BIOS");
+ // BIOS had control, check for Operational
+ if( (iospace->HcControl & 0xC0) != 0x80 ) // UsbOperational
+ {
+ // - If not, set to resume
+ iospace->HcControl &= ~0xC0; // UsbResume
+ iospace->HcControl |= 0x40; // UsbResume
+ // TODO: Wait
+ }
+ }
+ else
+ {
+ // Cold boot, wait a bit
+ // TODO: Wait for reset time
+ }
// Allocate HCCA area
cnt->HCCA = (void*)MM_AllocDMA(1, 32, &cnt->HCCAPhys);
cnt->IntLists = (void*)( (tVAddr)cnt->HCCA + 512 );
LOG("HCCAPhys = %P, HCCA = %p", cnt->HCCAPhys, cnt->HCCA);
- iospace = cnt->ControlSpace;
-
// --- Restart the controller ---
Uint32 fm_interval = iospace->HcFmInterval;
iospace->HcCommandStatus |= (1 << 0);
// - Wait 10 micro-seconds
+ // TODO:
iospace->HcFmInterval = fm_interval;
// (Now in UsbSuspend state)
// - Wait 2ms
+ // TODO:
+ // - Check that things are good?
// --- Initialise Virtual Queues ---
memset(cnt->IntLists, 0, sizeof(*cnt->IntLists));
iospace->HcControlHeadED = MM_GetPhysAddr( (tVAddr)&cnt->IntLists->StopED );
iospace->HcBulkHeadED = MM_GetPhysAddr( (tVAddr)&cnt->IntLists->StopED );
iospace->HcHCCA = cnt->HCCAPhys;
- iospace->HcInterruptEnable = 0xFFFFFFFF; // TODO: Without SOF detect
+ iospace->HcInterruptEnable = 0x7B; // 0111 1011 (RHSC, FNO, UE, RD, WDH, SO)
+ iospace->HcInterruptDisable = 0x4; // Disable SOF interrupts
iospace->HcControl = 0x3C; // All queues on
iospace->HcPeriodicStart = fm_interval / 10 * 9; // 90% of fm_interval
// --- Start
iospace->HcControl |= (2 << 6); // UsbOperational
+
+ LOG("HcRhDescriptorA = 0x%x", iospace->HcRhDescriptorA);
+ LOG("HcRhDescriptorB = 0x%x", iospace->HcRhDescriptorB);
+
+ // --- Tell USB core that this controller is avaliable
+ cnt->nPorts = iospace->HcRhDescriptorA & 0x7F;
+ if( cnt->nPorts > 15 ) {
+ // Oops?
+ Log_Warning("OHCI", "Controller reports %i ports, but spec only allows 15, capping", cnt->nPorts);
+ cnt->nPorts = 15;
+ }
+ cnt->RootHub = USB_RegisterHost(&gOHCI_HostDef, cnt, cnt->nPorts);
}
// --- USB IO Functions ---
tPAddr td_phys;
tOHCI_Endpoint *ep;
- // Sanity check
+ // --- Sanity check
if( Length > MAX_PACKET_SIZE )
return NULL;
- // Check that the packet resides within memory the controller can access
+ ENTER("pController xDest iDataTgl iType pCb pCbData pBuf iLength",
+ Controller, Dest, DataTgl, Type, Cb, CbData, Buf, Length);
+
+ // ---- Check that the packet resides within memory the controller can access
if( MM_GetPhysAddr((tVAddr)Buf) >= (1ULL << 32) || MM_GetPhysAddr((tVAddr)Buf + Length -1) >= (1ULL << 32) )
{
// TODO: Handle destination outside of 32-bit address range
Log_Warning("OHCI", "_int_DoTD - Buffer outside of 32-bit range, TODO: Handle this");
+ LEAVE('n');
return NULL;
}
- // Find Endpoint descriptor
- // TODO:
- // - Allocate one if needed
- // TODO:
+ // --- Find/Allocate Endpoint descriptor
+ ep = OHCI_int_AllocateEndpt(Controller, Dest);
+ if( !ep ) {
+ Log_Warning("OHCI", "_int_DoTD - Unable to find/allocate Endpoint 0x%x... oops", Dest);
+ LEAVE('n');
+ return NULL;
+ }
- // Allocate a TD
+ // --- Allocate a TD
td = OHCI_int_AllocateGTD(Controller);
if( !td ) {
Log_Warning("OHCI", "_int_DoTD - Unable to allocate TD... oops");
+ LEAVE('n');
return NULL;
}
td_phys = MM_GetPhysAddr( (tVAddr)td );
- // Fill the TD
+
+ // --- Fill the TD
td->Flags |= (1 << 18); // Buffer rounding
td->Flags |= (Type & 3) << 19; // Type/Direction
td->Flags |= (2 | DataTgl) << 24; // Data Toggle
td->CbPointer = (tVAddr)Cb;
td->CbArg = (tVAddr)CbData;
- // Add to the end of the Endpoint's list
+ // --- Add to the end of the Endpoint's list
if( ep->TailP )
OHCI_int_GetGTDFromPhys( ep->TailP )->NextTD = td_phys;
else
ep->HeadP = td_phys;
ep->TailP = td_phys;
+ LEAVE('p', td);
return td;
}
void *OHCI_DataIN(void *Ptr, int Dest, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
}
int OHCI_IsTransferComplete(void *Ptr, void *Handle)
{
- return 0;
+ tOHCI_GeneralTD *td = Handle;
+
+ ENTER("pPtr pHandle", Ptr, Handle);
+
+ if( td->CBP != 0 )
+ {
+ LEAVE('i', 0);
+ return 0;
+ }
+ else
+ {
+ Log_Warning("OHCI", "TODO: Implement cleanup in OHCI_IsTransferComplete");
+ LEAVE('i', 1);
+ return 1;
+ }
}
// --- Interrupt polling ---
switch( slot )
{
case 1:
- // Add to all lists
+ // Add to period 1 list
break;
case 2:
- // Add to every second list
+ // Determine best list by current load
break;
case 4:
- // Add to every fourth list
break;
case 8:
- // Add to every eighth list
break;
case 16:
- // Add to first list
break;
case 32:
- // Add to first list
break;
default:
Log_Error("OHCI", "OHCI_StartPoll - `slot` is invalid (%i)", slot);
break;
}
+ Log_Warning("OHCI", "TODO: Implement OHCI_StartPoll");
return NULL;
}
// --- Root hub ---
void OHCI_CheckPortUpdate(void *Ptr)
{
+ tOHCI_Controller *cnt = Ptr;
+ for( int i = 0; i < cnt->nPorts; i ++ )
+ {
+ volatile Uint32 *status_ptr = &cnt->ControlSpace->HcRhPortStatus[i];
+ Uint32 status = *status_ptr;
+ // Connect change?
+ if( status & (1 << 16) )
+ {
+ LOG("Connect status change port %i, *status = 0x%x", i, status);
+ // Connect or disconnect?
+ if( status & (1 << 0) )
+ {
+ // Connect, set things up :)
+
+ // - TODO: Power on?
+ // - Reset port
+ *status_ptr = 1 << 4; // PRS
+ Time_Delay(50);
+ // - Enable port
+ *status_ptr = 1 << 1; // SetPortEnable
+ LOG("Device connected on port %i", i);
+
+ // - Tell stack
+ USB_DeviceConnected(cnt->RootHub, i);
+ }
+ else
+ {
+ // Disconnect
+ USB_DeviceDisconnected(cnt->RootHub, i);
+ }
+ }
+
+ // TODO: Handle other bits?
+ }
}
// --- Interrupt handler
{
tOHCI_Controller *cnt = Ptr;
// TODO: Interrupt handler
- Log_Debug("OHIC", "Interrupt handler on controller %i", cnt->ID);
+ LOG("Interrupt on controller %i - Status = 0x%x",
+ cnt->ID, cnt->ControlSpace->HcInterruptStatus);
+
+ cnt->ControlSpace->HcInterruptStatus = cnt->ControlSpace->HcInterruptStatus;
}
// --- Internal Functions ---
-tOHCI_Endpoint *OHCI_int_AllocateEndpt(tOHCI_Controller *Controller)
+tOHCI_Endpoint *OHCI_int_AllocateEndpt(tOHCI_Controller *Controller, int Dest)
{
+ tOHCI_Endpoint *first_free_ep = NULL;
+ tOHCI_Endpoint *first_freeable_ep = NULL;
int pg, i;
+ int _dest;
+
+ // Convert 8.4 dev:endpt destination into 4.7 endpt:dev
+ _dest = (Dest >> 4) | ((Dest & 0xF) << 7);
+
// TODO: Locking
for( pg = 0; pg < MAX_ENDPT_PAGES; pg ++ )
{
if( !gapOHCI_EndpointPool[pg] ) {
- gapOHCI_EndpointPool[pg] = (void*)MM_AllocDMA(1, 32, NULL);
- memset(gapOHCI_EndpointPool, 0, PAGE_SIZE);
+ tPAddr paddr; // Not used
+ gapOHCI_EndpointPool[pg] = (void*)MM_AllocDMA(1, 32, &paddr);
+ memset(gapOHCI_EndpointPool[pg], 0, PAGE_SIZE);
}
for( i = 0; i < ciEndpoints_per_page; i ++ )
{
- // Check if allocated, and if not take it
- if( gapOHCI_EndpointPool[pg][i].Flags & (1 << 31) )
- continue ;
- gapOHCI_EndpointPool[pg][i].Flags = (1 << 31);
-
- // Set controller ID
- gapOHCI_EndpointPool[pg][i].Flags |= Controller->ID << 27;
+ tOHCI_Endpoint *ep = &gapOHCI_EndpointPool[pg][i];
- return &gapOHCI_EndpointPool[pg][i];
+ // Check if it's allocated
+ if( ep->Flags & (1 << 31) )
+ {
+ if( ep->HeadP == 0 )
+ first_freeable_ep = ep;
+
+ if( ((ep->Flags >> 27) & 0xF) != Controller->ID )
+ continue ;
+ if( (ep->Flags & 0x7FF) != _dest )
+ continue ;
+ return ep;
+ }
+ else
+ {
+ if( !first_free_ep )
+ first_free_ep = ep;
+ }
}
}
+
+ if( first_free_ep ) {
+ first_free_ep->Flags = (1 << 31);
+
+ // Set controller ID
+ first_free_ep->Flags |= Controller->ID << 27;
+ first_free_ep->Flags |= _dest;
+ first_free_ep->Flags |= 1023 << 16; // Max Packet Size
+
+ return first_free_ep;
+ }
+
+ if( first_freeable_ep ) {
+ #if 0
+ first_free_ep->Flags = (1 << 31);
+
+ // Set controller ID
+ first_free_ep->Flags |= Controller->ID << 27;
+ first_free_ep->Flags |= _dest;
+
+ return first_free_ep;
+ #endif
+ Log_Warning("OHCI", "TODO: Implement freeing EPs when no avalable slots");
+ }
+
return NULL;
}
tOHCI_GeneralTD *OHCI_int_AllocateGTD(tOHCI_Controller *Controller)
{
+ int pg, i;
// TODO: Locking
+ for( pg = 0; pg < MAX_TD_PAGES; pg ++ )
+ {
+ if( !gapOHCI_TDPool[pg] ) {
+ tPAddr paddr; // Not used
+ gapOHCI_TDPool[pg] = (void*)MM_AllocDMA(1, 32, &paddr);
+ memset(gapOHCI_TDPool[pg], 0, PAGE_SIZE);
+ }
+
+ for( i = 0; i < ciTDs_per_page; i ++ )
+ {
+ // Check if allocated, and if not take it
+ if( gapOHCI_TDPool[pg][i].Flags & (1 << 31) )
+ continue ;
+ gapOHCI_TDPool[pg][i].Flags = (1 << 31);
+
+ // Set controller ID
+ gapOHCI_TDPool[pg][i].Flags |= Controller->ID << 27;
+
+ return &gapOHCI_TDPool[pg][i];
+ }
+ }
return NULL;
}