X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=KernelLand%2FModules%2FUSB%2FHID%2Fmain.c;h=c2802737dabca2747c2a613f5fc53b60f158e58d;hb=f7ec06bee2b80613d80c314bf864c69209d09829;hp=31375b0b64ad42fe57dfc1259ce622d6c3cc32fa;hpb=48743e39650eb1ef988380e9d95f27fd40d3a9ce;p=tpg%2Facess2.git diff --git a/KernelLand/Modules/USB/HID/main.c b/KernelLand/Modules/USB/HID/main.c index 31375b0b..c2802737 100644 --- a/KernelLand/Modules/USB/HID/main.c +++ b/KernelLand/Modules/USB/HID/main.c @@ -8,29 +8,422 @@ #define DEBUG 0 #define VERSION VER2(0,1) #include +#include #include +#include "hid.h" +#include "hid_reports.h" +#include + +// === TYPES === +typedef struct sHID_Device tHID_Device; + +struct sHID_Device +{ + void *Next; // Used by sub-driver + tUSB_DataCallback DataAvail; + // ... Device-specific data +}; + +// === IMPORTS === +extern tHID_ReportCallbacks gHID_Mouse_ReportCBs; +extern tHID_ReportCallbacks gHID_Kb_ReportCBs; // === PROTOTYPES === - int HID_Initialise(const char **Arguments); -void HID_DeviceConnected(tUSBInterface *Dev); + int HID_Initialise(char **Arguments); +void HID_InterruptCallback(tUSBInterface *Dev, int EndPt, int Length, void *Data); +void HID_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen); +tHID_ReportCallbacks *HID_RootCollection(tUSBInterface *Dev, tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value); +void HID_int_ParseReport(tUSBInterface *Dev, Uint8 *Data, size_t Length, tHID_ReportCallbacks *StartCBs); + +static void _AddItem(struct sHID_IntList *List, Uint32 Value); +static void _AddItems(struct sHID_IntList *List, Uint32 First, Uint32 Last); +static void _FreeList(struct sHID_IntList *List); // === GLOBALS === -MODULE_DEFINE(0, VERSION, USB_HID, HID_Initialise, NULL, "USB_Core", NULL); -tUSBDriver gHID_Driver = { +MODULE_DEFINE(0, VERSION, USB_HID, HID_Initialise, NULL, "USB_Core", "Keyboard", "Mouse", NULL); +tUSBDriver gHID_USBDriver = { .Name = "HID", .Match = {.Class = {0x030000, 0xFF0000}}, .Connected = HID_DeviceConnected, + .MaxEndpoints = 2, + .Endpoints = { + {0x80, HID_InterruptCallback}, + {0, NULL} + } +}; +tHID_ReportCallbacks gHID_RootCallbacks = { + .Collection = HID_RootCollection }; // === CODE === -int HID_Initialise(const char **Arguments) +int HID_Initialise(char **Arguments) { - USB_RegisterDriver( &gHID_Driver ); + USB_RegisterDriver( &gHID_USBDriver ); + return 0; } -void HID_DeviceConnected(tUSBInterface *Dev) +/** + * \brief Callback for when there's new data from the device + * + * Calls the subdriver callback (stored at a fixed offset in the device data structure) + */ +void HID_InterruptCallback(tUSBInterface *Dev, int EndPt, int Length, void *Data) +{ + tHID_Device *info; + + info = USB_GetDeviceDataPtr(Dev); + if(!info) { + Log_Error("USB HID", "Device %p doesn't have a data pointer.", Dev); + return ; + } + + LOG("Data for %p", info->DataAvail); + info->DataAvail(Dev, EndPt, Length, Data); +} + +/** + * \brief Handle a device connection + */ +void HID_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen) { + struct sDescriptor_HID *hid_desc; + size_t ofs = 0; + size_t report_len = 0; + + ENTER("pDev pDescriptors iDescriptorsLen", Dev, Descriptors, DescriptorsLen); + + // --- Locate HID descriptor --- + hid_desc = NULL; + while(ofs + 2 <= DescriptorsLen) + { + hid_desc = (void*)( (char*)Descriptors + ofs ); + ofs += hid_desc->Length; + // Sanity check length + if( ofs > DescriptorsLen ) { + hid_desc = NULL; + break ; + } + // Check for correct type + if( hid_desc->Type == 0x21 ) + break ; + // On to the next one then + } + if( hid_desc == NULL ) { + Log_Warning("USB_HID", "Device doesn't have a HID descritor"); + LEAVE('-'); + return ; + } + // - Sanity check length + if( hid_desc->Length < sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0]) ) + { + // Too small! + Log_Warning("USB_HID", "HID Descriptor undersized (%i < %i)", + hid_desc->Length, + sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0])); + LEAVE('-'); + return ; + } + + // --- Dump descriptor header --- + LOG("hid_desc = {"); + LOG(" .Length = %i", hid_desc->Length); + LOG(" .Type = 0x%x", hid_desc->Type); + LOG(" .Version = 0x%x", hid_desc->Version); + LOG(" .NumDescriptors = %i", hid_desc->NumDescriptors); + LOG("}"); + + // --- Locate report descriptor length --- + for( int i = 0; i < hid_desc->NumDescriptors; i ++ ) + { + if( hid_desc->Descriptors[i].DescType == 0x22 ) { + report_len = LittleEndian16( hid_desc->Descriptors[i].DescLen ); + break ; + } + } + if( report_len == 0 ) { + Log_Warning("USB_HID", "No report descriptor"); + LEAVE('-'); + return ; + } + + // --- Read and parse report descriptor --- + // NOTE: Also does sub-driver selection and initialisation + Uint8 report_data[report_len]; + USB_ReadDescriptor(Dev, 0x1022, 0, report_len, report_data); + HID_int_ParseReport(Dev, report_data, report_len, &gHID_RootCallbacks); + + // --- Start polling --- + // Only if the device was initialised + if( USB_GetDeviceDataPtr(Dev) ) + { + // Poll Endpoint .+1 (interupt) + USB_StartPollingEndpoint(Dev, 1); + } + else + { + Log_Log("USB_HID", "Device not intitialised"); + } + + LEAVE('-'); +} + +/** + * \brief Handle a Collection item in a report + * + * When an application collection is found in the root of the tree, the approriate + * sub-handler is invoked (mouse, keyboard, gamepad, ...) + */ +tHID_ReportCallbacks *HID_RootCollection( + tUSBInterface *Dev, + tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value + ) +{ + Uint32 usage; + + // Check for "Application" collection + if( Value != 1 ) return NULL; + + // Check usages + if( Local->Usages.nItems == 0 ) return NULL; + usage = Local->Usages.Items[0]; + switch(usage >> 16) + { + case 0x0001: // General Desktop + switch(usage & 0xFFFF) + { + case 0x0001: // Pointer + LOG("Desktop->Pointer"); + break; + case 0x0002: // Mouse + LOG("Desktop->Mouse"); + return &gHID_Mouse_ReportCBs; + case 0x0004: // Joystick + case 0x0005: // Game Pad + LOG("Desktop->Gamepad"); + break; + case 0x0006: // Keyboard + LOG("Desktop->Keyboard"); + return &gHID_Kb_ReportCBs; + } + break; + case 0x0007: // Keyboard / Keypad + LOG("Keyboard"); + return &gHID_Kb_ReportCBs; + } + return NULL; +} + +void HID_int_ParseReport(tUSBInterface *Dev, Uint8 *Data, size_t Length, tHID_ReportCallbacks *StartCBs) +{ + const int max_cb_level = 8; + int cb_level = 0; + tHID_ReportCallbacks *cur_cbs; + tHID_ReportCallbacks *cb_stack[max_cb_level]; + tHID_ReportGlobalState global_state; + tHID_ReportLocalState local_state; + + ENTER("pData iLength pStartCBs", Data, Length, StartCBs); + + // Initialise callback stack + cb_stack[0] = StartCBs; + cur_cbs = StartCBs; + + // Clear state + memset(&global_state, 0, sizeof(global_state)); + memset(&local_state, 0, sizeof(local_state)); + + // Iterate though the report data + for( int ofs = 0; ofs < Length; ) + { + Uint8 byte; + Uint32 val; + + // --- Get value and length --- + byte = Data[ofs]; + switch(byte & 3) + { + case 0: + val = 0; + ofs += 1; + break; + case 1: + if( ofs + 2 > Length ) { LEAVE('-'); return; } + val = Data[ofs+1]; + ofs += 2; + break; + case 2: + if( ofs + 3 > Length ) { LEAVE('-'); return; } + val = Data[ofs + 1] | (Data[ofs + 1]<<8); + ofs += 3; + break; + case 3: + if( ofs + 5 > Length ) { LEAVE('-'); return; } + val = Data[ofs + 1] | (Data[ofs + 2] << 8) | (Data[ofs + 3] << 16) | (Data[ofs + 4] << 24); + ofs += 5; + break; + } + + // --- Process the item --- + LOG("Type = 0x%x, len = %i, val = 0x%x", byte & 0xFC, byte & 3, val); + switch(byte & 0xFC) + { + // Main items + // - Input + case 0x80: + LOG("Input 0x%x", val); + if( cur_cbs && cur_cbs->Input ) + cur_cbs->Input( Dev, &global_state, &local_state, val ); + goto _clear_local; + // - Output + case 0x90: + LOG("Output 0x%x", val); + if( cur_cbs && cur_cbs->Output ) + cur_cbs->Output( Dev, &global_state, &local_state, val ); + goto _clear_local; + // - Collection + case 0xA0: + LOG("Collection 0x%x", val); + + tHID_ReportCallbacks *next_cbs = NULL; + if( cur_cbs && cur_cbs->Collection ) + next_cbs = cur_cbs->Collection( Dev, &global_state, &local_state, val ); + cb_level ++; + if( cb_level < max_cb_level ) + cb_stack[cb_level] = next_cbs; + else + next_cbs = NULL; + cur_cbs = next_cbs; + goto _clear_local; + // - Feature + case 0xB0: + LOG("Feature 0x%x", val); + if( cur_cbs && cur_cbs->Feature ) + cur_cbs->Feature( Dev, &global_state, &local_state, val ); + goto _clear_local; + // - End collection + case 0xC0: + if( cur_cbs && cur_cbs->EndCollection ) + cur_cbs->EndCollection(Dev); + if( cb_level == 0 ) { + Log_Warning("USB_HID", "Inbalance in HID collections"); + LEAVE('-'); + return ; + } + cb_level --; + if( cb_level < max_cb_level ) + cur_cbs = cb_stack[cb_level]; + goto _clear_local; + // -- Helper to clear the local state + _clear_local: + _FreeList( &local_state.Strings ); + _FreeList( &local_state.Usages ); + _FreeList( &local_state.Designators ); + memset( &local_state, 0, sizeof(local_state) ); + break; + + // Global Items + case 0x04: global_state.UsagePage = val<<16; break; // - Usage Page + case 0x14: global_state.LogMin = val; break; // - Logical Min + case 0x24: global_state.LogMax = val; break; // - Logical Max + case 0x34: global_state.PhysMin = val; break; // - Physical Min + case 0x44: global_state.PhysMax = val; break; // - Physical Max + case 0x54: global_state.UnitExp = val; break; // - Unit Exp + case 0x64: global_state.Unit = val; break; // - Unit + case 0x74: global_state.ReportSize = val; break; // - Report Size + case 0x84: global_state.ReportID = val; break; // - Report ID (TODO: Flag when used) + case 0x94: global_state.ReportCount = val; break; // - Report Count + case 0xA4: LOG("TODO: Implement Global Push"); break; // - Push + case 0xB4: LOG("TODO: Implement Global Pop"); break; // - Pop + + // Local Items + // - Usages + case 0x08: // Single + if( (byte & 3) != 3 ) val |= global_state.UsagePage; + LOG("Usage %x", val); + _AddItem(&local_state.Usages, val); + break; + case 0x18: // Range start + if( (byte & 3) != 3 ) val |= global_state.UsagePage; + LOG("Usage start %x", val); + local_state.UsageMin = val; + break; + case 0x28: // Range end (apply) + if( (byte & 3) != 3 ) val |= global_state.UsagePage; + LOG("Usage end %x (from %x)", val, local_state.UsageMin); + _AddItems(&local_state.Usages, local_state.UsageMin, val); + break; + // - Designators (Index into Physical report) + case 0x38: + _AddItem(&local_state.Designators, val); + break; + case 0x48: + local_state.DesignatorMin = val; + break; + case 0x58: + _AddItems(&local_state.Designators, local_state.DesignatorMin, val); + break; + // - Reserved/hole + case 0x68: + break; + // - Strings + case 0x78: + _AddItem(&local_state.Strings, val); + break; + case 0x88: + local_state.StringMin = val; + break; + case 0x98: + _AddItems(&local_state.Strings, local_state.StringMin, val); + break; + // - Delimiter + case 0xA8: + break; + + // Long Item + case 0xFC: + LOG("Long Item"); + break; + + // Reserved + default: + LOG("0x%x RESVD", byte & 0xFC); + break; + } + } + + LEAVE('-'); +} + +// -------------------------------------------------------------------- +// List helpers +// -------------------------------------------------------------------- +static void _AddItem(struct sHID_IntList *List, Uint32 Value) +{ + if( List->Space == List->nItems ) + { + List->Space += 10; + List->Items = realloc( List->Items, List->Space * sizeof(List->Items[0]) ); + } + +// LOG("Added %x to %p", Value, List); + List->Items[ List->nItems ] = Value; + List->nItems ++; +} + +static void _AddItems(struct sHID_IntList *List, Uint32 First, Uint32 Last) +{ + while( First <= Last ) + { + _AddItem(List, First); + First ++; + } +} + +static void _FreeList(struct sHID_IntList *List) +{ + if( List->Items ) + free(List->Items); }