* hub.c
* - Basic hub driver
*/
+#define DEBUG 1
#include <usb_hub.h>
#define MAX_PORTS 32 // Not actually a max, but used for DeviceRemovable
// === PROTOTYPES ===
void Hub_Connected(tUSBInterface *Dev);
void Hub_Disconnected(tUSBInterface *Dev);
-void Hub_PortStatusChange(tUSBInterface *Dev, int Length, void *Data);
+void Hub_PortStatusChange(tUSBInterface *Dev, int Endpoint, int Length, void *Data);
void Hub_int_HandleChange(tUSBInterface *Dev, int Port);
// === GLOBALS ===
struct sHubDescriptor hub_desc;
struct sHubInfo *info;
- // Read hub descriptor
- USB_ReadDescriptor(Dev, 0x29, 0, sizeof(hub_desc), &hub_desc);
+ // Read hub descriptor (Class descriptor 0x29)
+ USB_ReadDescriptor(Dev, 0x129, 0, sizeof(hub_desc), &hub_desc);
LOG("%i Ports", hub_desc.NbrPorts);
LOG("Takes %i ms for power to stabilise", hub_desc.PwrOn2PwrGood*2);
free(info);
}
-void Hub_PortStatusChange(tUSBInterface *Dev, int Length, void *Data)
+void Hub_PortStatusChange(tUSBInterface *Dev, int Endpoint, int Length, void *Data)
{
Uint8 *status = Data;
struct sHubInfo *info = USB_GetDeviceDataPtr(Dev);
typedef struct sUSBInterface tUSBInterface;
typedef struct sUSBDriver tUSBDriver;
+typedef void (*tUSB_DataCallback)(tUSBInterface *Dev, int EndPt, int Length, void *Data);
+
/**
*/
struct sUSBDriver
// 23:16 - Interface Class
// 15:8 - Interface Sub Class
// 7:0 - Interface Protocol
- Uint32 ClassMask;
Uint32 ClassCode;
+ Uint32 ClassMask;
} Class;
struct {
Uint16 VendorID;
// NOTE: Top bit indicates the direction (1=Input)
Uint8 Attributes;
// Data availiable Callback
- void (*DataAvail)(tUSBInterface *Dev, int Length, void *Data);
+ tUSB_DataCallback DataAvail;
} Endpoints[];
};
// TODO: Async
extern void USB_SendData(tUSBInterface *Dev, int Endpoint, int Length, void *Data);
extern void USB_RecvData(tUSBInterface *Dev, int Endpoint, int Length, void *Data);
+extern void USB_RecvDataA(tUSBInterface *Dev, int Endpoint, int Length, void *DataBuf, tUSB_DataCallback Callback);
#endif
typedef struct sUSBHostDef tUSBHostDef;
-typedef void (*tUSBHostCb)(void *DataPtr);
+typedef void (*tUSBHostCb)(void *DataPtr, void *Data, int Length);
-typedef void *(*tUSBHostOp)(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb bIOC, void *Data, size_t Length);
+typedef void *(*tUSBHostOp)(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb CB, void *CbData, void *Data, size_t Length);
/**
* \brief Defines a USB Host Controller type
tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts);
// === GLOBALS ===
-tUSBDriver *gUSB_InterfaceDrivers = &gUSBHub_Driver;
+tUSBDriver *gpUSB_InterfaceDrivers = &gUSBHub_Driver;
// === CODE ===
tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts)
host->RootHubDev.Host = host;
host->RootHubDev.Address = 0;
- host->RootHubIf.Next = NULL;
+// host->RootHubIf.Next = NULL;
host->RootHubIf.Dev = &host->RootHubDev;
host->RootHubIf.Driver = NULL;
host->RootHubIf.Data = NULL;
return &host->RootHub;
}
+// --- Drivers ---
void USB_RegisterDriver(tUSBDriver *Driver)
{
Log_Warning("USB", "TODO: Implement USB_RegisterDriver");
}
+tUSBDriver *USB_int_FindDriverByClass(Uint32 ClassCode)
+{
+ ENTER("xClassCode", ClassCode);
+ for( tUSBDriver *ret = gpUSB_InterfaceDrivers; ret; ret = ret->Next )
+ {
+ LOG(" 0x%x & 0x%x == 0x%x?", ClassCode, ret->Match.Class.ClassMask, ret->Match.Class.ClassCode);
+ if( (ClassCode & ret->Match.Class.ClassMask) == ret->Match.Class.ClassCode )
+ {
+ LOG("Found '%s'", ret->Name);
+ LEAVE('p', ret);
+ return ret;
+ }
+ }
+ LEAVE('n');
+ return NULL;
+}
+
// --- Hub Registration ---
// NOTE: Doesn't do much nowdays
tUSBHub *USB_RegisterHub(tUSBInterface *Device, int PortCount)
struct sUSBEndpoint
{
- tUSBEndpoint *Next; // In the segmented list
+ tUSBEndpoint *Next; // (usb_poll.c) Clock list
tUSBInterface *Interface;
- int EndpointNum;
+ int EndpointIdx; // Interface endpoint index
+ int EndpointNum; // Device endpoint num
int PollingPeriod; // In 1ms intervals
- int PollingAtoms; // *INTERNAL* usb_poll.c
int MaxPacketSize; // In bytes
-
Uint8 Type; // Same as sUSBDriver.Endpoints.Type
+
+ int PollingAtoms; // (usb_poll.c) Period in clock list
+ void *InputData;
};
/**
*/
struct sUSBInterface
{
- tUSBInterface *Next;
+// tUSBInterface *Next;
tUSBDevice *Dev;
tUSBDriver *Driver;
tUSBHub RootHub;
};
-extern void USB_NewDevice(tUSBHub *Hub);
+extern tUSBDriver *USB_int_FindDriverByClass(Uint32 ClassCode);
#endif
// === CODE ===
void USB_DeviceConnected(tUSBHub *Hub, int Port)
{
- tUSBDevice *dev;
+ tUSBDevice tmpdev;
+ tUSBDevice *dev = &tmpdev;
if( Port >= Hub->nPorts ) return ;
if( Hub->Devices[Port] ) return ;
// Device should be in 'Default' state
// Create structure
- dev = malloc(sizeof(tUSBDevice));
dev->ParentHub = Hub;
dev->Host = Hub->Interface->Dev->Host;
dev->Address = 0;
free(tmp);
}
}
+
+ // TODO: Support alternate configurations
// 3. Get configurations
for( int i = 0; i < 1; i ++ )
struct sDescriptor_Configuration desc;
void *full_buf;
char *cur_ptr;
-
+
USB_int_ReadDescriptor(dev, 0, 2, i, sizeof(desc), &desc);
LOG("Configuration Descriptor %i = {", i);
LOG(" .Length = %i", desc.Length);
free(tmp);
}
+ // TODO: Split here and allow some method of selection
+
+ // Allocate device now that we have the configuration
+ dev = malloc(sizeof(tUSBDevice) + desc.NumInterfaces * sizeof(void*));
+ memcpy(dev, &tmpdev, sizeof(tUSBDevice));
+ dev->nInterfaces = desc.NumInterfaces;
+
+ // Allocate a temp buffer for config info
cur_ptr = full_buf = malloc( LittleEndian16(desc.TotalLength) );
USB_int_ReadDescriptor(dev, 0, 2, i, desc.TotalLength, full_buf);
for( int j = 0; j < desc.NumInterfaces; j ++ )
{
struct sDescriptor_Interface *iface;
+ tUSBInterface *dev_if;
iface = (void*)cur_ptr;
// TODO: Sanity check with remaining space
cur_ptr += sizeof(*iface);
-
+
LOG("Interface %i/%i = {", i, j);
LOG(" .InterfaceNum = %i", iface->InterfaceNum);
LOG(" .NumEndpoints = %i", iface->NumEndpoints);
}
LOG("}");
+ dev_if = malloc(sizeof(tUSBInterface) + iface->NumEndpoints*sizeof(dev_if->Endpoints[0]));
+ dev_if->Dev = dev;
+ dev_if->Driver = NULL;
+ dev_if->Data = NULL;
+ dev_if->nEndpoints = iface->NumEndpoints;
+ dev->Interfaces[j] = dev_if;
+
+ // Copy interface data
for( int k = 0; k < iface->NumEndpoints; k ++ )
{
struct sDescriptor_Endpoint *endpt;
LOG(" .MaxPacketSize = %i", LittleEndian16(endpt->MaxPacketSize));
LOG(" .PollingInterval = %i", endpt->PollingInterval);
LOG("}");
+
+ dev_if->Endpoints[k].Next = NULL;
+ dev_if->Endpoints[k].Interface = dev_if;
+ dev_if->Endpoints[k].EndpointIdx = k;
+ dev_if->Endpoints[k].EndpointNum = endpt->Address & 0x7F;
+ dev_if->Endpoints[k].PollingPeriod = endpt->PollingInterval;
+ dev_if->Endpoints[k].MaxPacketSize = LittleEndian16(endpt->MaxPacketSize);
+ dev_if->Endpoints[k].Type = endpt->Attributes | (endpt->Address & 0x80);
+ dev_if->Endpoints[k].PollingAtoms = 0;
+ dev_if->Endpoints[k].InputData = NULL;
+ }
+
+ // Initialise driver
+ dev_if->Driver = USB_int_FindDriverByClass(
+ ((int)iface->InterfaceClass << 16)
+ |((int)iface->InterfaceSubClass << 8)
+ |((int)iface->InterfaceProtocol << 0)
+ );
+ if(!dev_if->Driver) {
+ Log_Notice("USB", "No driver for Class %02x:%02x:%02x",
+ iface->InterfaceClass, iface->InterfaceSubClass, iface->InterfaceProtocol
+ );
+ }
+ else {
+ dev_if->Driver->Connected( dev_if );
}
}
#include <usb_core.h>
#include "usb.h"
#include "usb_lowlevel.h"
+#include <semaphore.h>
+
+typedef struct sAsyncOp tAsyncOp;
+
+struct sAsyncOp
+{
+ tAsyncOp *Next;
+ tUSBEndpoint *Endpt;
+ int Length;
+ void *Data;
+};
// === PROTOTYPES ===
void USB_ReadDescriptor(tUSBInterface *Iface, int Type, int Index, int Length, void *Data);
void USB_Request(tUSBInterface *Iface, int Endpoint, int Type, int Req, int Value, int Index, int Len, void *Data);
// === GLOBALS ===
+tSemaphore glUSB_AsyncQueue;
// === CODE ===
void USB_ReadDescriptor(tUSBInterface *Iface, int Type, int Index, int Length, void *Data)
USB_int_Request(Iface->Dev->Host, Iface->Dev->Address, endpt, Type, Req, Value, Index, Len, Data);
}
+
+void USB_SendData(tUSBInterface *Dev, int Endpoint, int Length, void *Data)
+{
+ Log_Warning("USB", "TODO: Implement USB_SendData");
+}
+
+void USB_RecvData(tUSBInterface *Dev, int Endpoint, int Length, void *Data)
+{
+ Log_Warning("USB", "TODO: Implement USB_RecvData");
+}
+
+void USB_RecvDataA(tUSBInterface *Dev, int Endpoint, int Length, void *DataBuf, tUSB_DataCallback Callback)
+{
+ Log_Warning("USB", "TODO: Implement USB_RecvDataA");
+}
+
+void USB_AsyncThread(void *Unused)
+{
+ for(;;)
+ {
+ Semaphore_Wait(&glUSB_AsyncQueue, 1);
+
+ }
+}
+
req.Index = LittleEndian16( Indx );
req.Length = LittleEndian16( Len );
- hdl = Host->HostDef->SendSETUP(Host->Ptr, Addr, EndPt, 0, NULL, &req, sizeof(req));
+ hdl = Host->HostDef->SendSETUP(Host->Ptr, Addr, EndPt, 0, NULL, NULL, &req, sizeof(req));
// TODO: Data toggle?
// TODO: Multi-packet transfers
{
void *hdl2;
- hdl = Host->HostDef->SendIN(Host->Ptr, Addr, EndPt, 0, NULL, Data, Len);
+ hdl = Host->HostDef->SendIN(Host->Ptr, Addr, EndPt, 0, NULL, NULL, Data, Len);
- hdl2 = Host->HostDef->SendOUT(Host->Ptr, Addr, EndPt, 0, NULL, NULL, 0);
+ hdl2 = Host->HostDef->SendOUT(Host->Ptr, Addr, EndPt, 0, NULL, NULL, NULL, 0);
while( Host->HostDef->IsOpComplete(Host->Ptr, hdl2) == 0 )
Time_Delay(1);
}
void *hdl2;
if( Len > 0 )
- hdl = Host->HostDef->SendOUT(Host->Ptr, Addr, EndPt, 0, NULL, Data, Len);
+ hdl = Host->HostDef->SendOUT(Host->Ptr, Addr, EndPt, 0, NULL, NULL, Data, Len);
else
hdl = NULL;
// Status phase (DataToggle=1)
- hdl2 = Host->HostDef->SendIN(Host->Ptr, Addr, EndPt, 1, NULL, NULL, 0);
+ hdl2 = Host->HostDef->SendIN(Host->Ptr, Addr, EndPt, 1, NULL, NULL, NULL, 0);
while( Host->HostDef->IsOpComplete(Host->Ptr, hdl2) == 0 )
Time_Delay(1);
}
void *final;
req.ReqType = 0x80;
+ switch( Type & 0xF00 )
+ {
+ case 0x000: req.ReqType |= (0 << 5); break; // Standard
+ case 0x100: req.ReqType |= (1 << 5); break; // Class
+ case 0x200: req.ReqType |= (2 << 5); break; // Vendor
+ }
+
req.Request = 6; // GET_DESCRIPTOR
req.Value = LittleEndian16( ((Type & 0xFF) << 8) | (Index & 0xFF) );
req.Index = LittleEndian16( 0 ); // TODO: Language ID
Dev->Host->HostDef->SendSETUP(
Dev->Host->Ptr, Dev->Address, Endpoint,
- 0, NULL,
+ 0, NULL, NULL,
&req, sizeof(req)
);
{
Dev->Host->HostDef->SendIN(
Dev->Host->Ptr, Dev->Address, Endpoint,
- bToggle, NULL,
+ bToggle, NULL, NULL,
Dest, ciMaxPacketSize
);
bToggle = !bToggle;
final = Dev->Host->HostDef->SendIN(
Dev->Host->Ptr, Dev->Address, Endpoint,
- bToggle, INVLPTR,
+ bToggle, INVLPTR, NULL,
Dest, Length
);
// TODO: Check that this endpoint isn't already on the queue
+ endpt->InputData = malloc(endpt->MaxPacketSize);
+
// Determine polling period in atoms
endpt->PollingAtoms = (endpt->PollingPeriod + POLL_ATOM-1) / POLL_ATOM;
if(endpt->PollingAtoms > POLL_SLOTS) endpt->PollingAtoms = POLL_SLOTS;
// TODO: Check the endpoint
// TODO: Async checking?
// - Send the read request on all of them then wait for the first to complete
+ USB_RecvDataA(
+ ep->Interface, ep->EndpointIdx,
+ ep->MaxPacketSize, ep->InputData,
+ ep->Interface->Driver->Endpoints[ep->EndpointIdx].DataAvail
+ );
+
// Call callback
// Reschedule
void UHCI_Cleanup();
tUHCI_TD *UHCI_int_AllocateTD(tUHCI_Controller *Cont);
void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD);
-void *UHCI_int_SendTransaction(tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, tUSBHostCb Cb, void *Data, size_t Length);
-void *UHCI_DataIN(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *Data, size_t Length);
-void *UHCI_DataOUT(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *Data, size_t Length);
-void *UHCI_SendSetup(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *Data, size_t Length);
+void *UHCI_int_SendTransaction(tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
+void *UHCI_DataIN(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
+void *UHCI_DataOUT(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
+void *UHCI_SendSetup(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length);
int UHCI_IsTransferComplete(void *Ptr, void *Handle);
int UHCI_Int_InitHost(tUHCI_Controller *Host);
void UHCI_CheckPortUpdate(void *Ptr);
return NULL;
}
+tUHCI_TD *UHCI_int_GetTDFromPhys(tPAddr PAddr)
+{
+ // TODO: Fix this to work with a non-contiguous pool
+ static tPAddr td_pool_base;
+ if(!td_pool_base) td_pool_base = MM_GetPhysAddr( (tVAddr)gaUHCI_TDPool );
+ return gaUHCI_TDPool + (PAddr - td_pool_base) / sizeof(gaUHCI_TDPool[0]);
+}
+
void UHCI_int_AppendTD(tUHCI_Controller *Cont, tUHCI_TD *TD)
{
int next_frame = (inw(Cont->IOBase + FRNUM) + 2) & (1024-1);
- tPAddr td_pool_base = MM_GetPhysAddr( (tVAddr)gaUHCI_TDPool );
tUHCI_TD *prev_td;
Uint32 link;
// Find the end of the list
link = Cont->FrameList[next_frame];
do {
- // TODO: Fix this to work with a non-contiguous pool
- prev_td = gaUHCI_TDPool + (link - td_pool_base) / sizeof(gaUHCI_TDPool[0]);
+ prev_td = UHCI_int_GetTDFromPhys(link);
link = prev_td->Link;
} while( !(link & 1) );
* \param Addr Function Address * 16 + Endpoint
* \param bTgl Data toggle value
*/
-void *UHCI_int_SendTransaction(tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl, tUSBHostCb Cb, void *Data, size_t Length)
+void *UHCI_int_SendTransaction(
+ tUHCI_Controller *Cont, int Addr, Uint8 Type, int bTgl,
+ tUSBHostCb Cb, void *CbData, void *Data, size_t Length)
{
tUHCI_TD *td;
td = UHCI_int_AllocateTD(Cont);
if( !td ) {
- //
+ // TODO: Wait for one to free?
Log_Error("UHCI", "No avaliable TDs, transaction dropped");
return NULL;
}
);
// TODO: Need to enable IOC to copy the data back
// td->BufferPointer =
+ td->_info.bCopyData = 1;
return NULL;
}
else {
td->BufferPointer = MM_GetPhysAddr( (tVAddr)Data );
+ td->_info.bCopyData = 0;
}
// Interrupt on completion
if( Cb ) {
td->Control |= (1 << 24);
td->_info.Callback = Cb; // NOTE: if ERRPTR then the TD is kept allocated until checked
- if( Cb != INVLPTR )
- {
- Log_Warning("UHCI", "TODO: Support IOC... somehow");
- }
+ td->_info.CallbackPtr = CbData;
}
- td->_info.DestPtr = Data;
+ td->_info.DataPtr = Data;
UHCI_int_AppendTD(Cont, td);
return td;
}
-void *UHCI_DataIN(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *Data, size_t Length)
+void *UHCI_DataIN(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
{
- return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0x69, DataTgl, Cb, Data, Length);
+ return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0x69, DataTgl, Cb, CbData, Buf, Length);
}
-void *UHCI_DataOUT(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *Data, size_t Length)
+void *UHCI_DataOUT(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
{
- return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0xE1, DataTgl, Cb, Data, Length);
+ return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0xE1, DataTgl, Cb, CbData, Buf, Length);
}
-void *UHCI_SendSetup(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *Data, size_t Length)
+void *UHCI_SendSetup(void *Ptr, int Fcn, int Endpt, int DataTgl, tUSBHostCb Cb, void *CbData, void *Buf, size_t Length)
{
- return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0x2D, DataTgl, Cb, Data, Length);
+ return UHCI_int_SendTransaction(Ptr, Fcn*16+Endpt, 0x2D, DataTgl, Cb, CbData, Buf, Length);
}
int UHCI_IsTransferComplete(void *Ptr, void *Handle)
{
tUHCI_TD *td = Handle;
- return !(td->Control & (1 << 23));
+ int ret;
+ ret = !(td->Control & (1 << 23));
+ if(ret) {
+ td->_info.Callback = NULL;
+ td->Link = 1;
+ }
+ return ret;
}
// === INTERNAL FUNCTIONS ===
Uint16 status = inw(Host->IOBase + USBSTS);
Log_Debug("UHCI", "UHIC Interrupt, status = 0x%x", status);
+ // Interrupt-on-completion
if( status & 1 )
{
- // Interrupt-on-completion
+ tPAddr link;
+ int frame = inw(Host->IOBase + FRNUM) - 1;
+ if(frame < 0) frame += 1024;
+
+ link = Host->FrameList[frame];
+ Host->FrameList[frame] = 1;
+ while( !(link & 1) )
+ {
+ tUHCI_TD *td = UHCI_int_GetTDFromPhys(link);
+ int byte_count = (td->Control&0x7FF)+1;
+ // Handle non-page aligned destination
+ // TODO: This will break if the destination is not in global memory
+ if(td->_info.bCopyData)
+ {
+ void *ptr = (void*)MM_MapTemp(td->BufferPointer);
+ memcpy(td->_info.DataPtr, ptr, byte_count);
+ MM_FreeTemp((tVAddr)ptr);
+ }
+ // Callback
+ if(td->_info.Callback && td->_info.Callback != INVLPTR)
+ {
+ td->_info.Callback(td->_info.CallbackPtr, td->_info.DataPtr, byte_count);
+ td->_info.Callback = NULL;
+ }
+ link = td->Link;
+ if( td->_info.Callback != INVLPTR )
+ td->Link = 1;
+ }
+
+// Host->LastCleanedFrame = frame;
}
outw(Host->IOBase + USBSTS, status);
*/
Uint32 BufferPointer;
- union
+ struct
{
- /**
- * \brief Avaliable for use by software
- */
- Uint32 Avaliable[4];
-
- struct
- {
- void *Callback;
- void *DestPtr;
- } _info;
- };
-};
+ tUSBHostCb Callback;
+ void *CallbackPtr;
+ void *DataPtr;
+ int bCopyData;
+ } _info;
+} __attribute__((aligned(16)));
struct sUHCI_QH
{