2 * Acess2 USB Stack HID Driver
3 * - By John Hodge (thePowersGang)
9 #define VERSION VER2(0,1)
14 #include "hid_reports.h"
18 typedef struct sHID_Device tHID_Device;
22 void *Next; // Used by sub-driver
23 tUSB_DataCallback DataAvail;
24 // ... Device-specific data
28 extern tHID_ReportCallbacks gHID_Mouse_ReportCBs;
29 extern tHID_ReportCallbacks gHID_Kb_ReportCBs;
32 int HID_Initialise(char **Arguments);
33 void HID_InterruptCallback(tUSBInterface *Dev, int EndPt, int Length, void *Data);
34 void HID_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen);
35 tHID_ReportCallbacks *HID_RootCollection(tUSBInterface *Dev, tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value);
36 void HID_int_ParseReport(tUSBInterface *Dev, Uint8 *Data, size_t Length, tHID_ReportCallbacks *StartCBs);
38 static void _AddItem(struct sHID_IntList *List, Uint32 Value);
39 static void _AddItems(struct sHID_IntList *List, Uint32 First, Uint32 Last);
40 static void _FreeList(struct sHID_IntList *List);
43 MODULE_DEFINE(0, VERSION, USB_HID, HID_Initialise, NULL, "USB_Core", "Keyboard", "Mouse", NULL);
44 tUSBDriver gHID_USBDriver = {
46 .Match = {.Class = {0x030000, 0xFF0000}},
47 .Connected = HID_DeviceConnected,
50 {0x80, HID_InterruptCallback},
54 tHID_ReportCallbacks gHID_RootCallbacks = {
55 .Collection = HID_RootCollection
59 int HID_Initialise(char **Arguments)
61 USB_RegisterDriver( &gHID_USBDriver );
67 * \brief Callback for when there's new data from the device
69 * Calls the subdriver callback (stored at a fixed offset in the device data structure)
71 void HID_InterruptCallback(tUSBInterface *Dev, int EndPt, int Length, void *Data)
75 info = USB_GetDeviceDataPtr(Dev);
77 Log_Error("USB HID", "Device %p doesn't have a data pointer.", Dev);
81 LOG("Data for %p", info->DataAvail);
82 info->DataAvail(Dev, EndPt, Length, Data);
86 * \brief Handle a device connection
88 void HID_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen)
90 struct sDescriptor_HID *hid_desc;
92 size_t report_len = 0;
94 ENTER("pDev pDescriptors iDescriptorsLen", Dev, Descriptors, DescriptorsLen);
96 // --- Locate HID descriptor ---
98 while(ofs + 2 <= DescriptorsLen)
100 hid_desc = (void*)( (char*)Descriptors + ofs );
101 ofs += hid_desc->Length;
102 // Sanity check length
103 if( ofs > DescriptorsLen ) {
107 // Check for correct type
108 if( hid_desc->Type == 0x21 )
110 // On to the next one then
112 if( hid_desc == NULL ) {
113 Log_Warning("USB_HID", "Device doesn't have a HID descritor");
117 // - Sanity check length
118 if( hid_desc->Length < sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0]) )
121 Log_Warning("USB_HID", "HID Descriptor undersized (%i < %i)",
123 sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0]));
128 // --- Dump descriptor header ---
130 LOG(" .Length = %i", hid_desc->Length);
131 LOG(" .Type = 0x%x", hid_desc->Type);
132 LOG(" .Version = 0x%x", hid_desc->Version);
133 LOG(" .NumDescriptors = %i", hid_desc->NumDescriptors);
136 // --- Locate report descriptor length ---
137 for( int i = 0; i < hid_desc->NumDescriptors; i ++ )
139 if( hid_desc->Descriptors[i].DescType == 0x22 ) {
140 report_len = LittleEndian16( hid_desc->Descriptors[i].DescLen );
144 if( report_len == 0 ) {
145 Log_Warning("USB_HID", "No report descriptor");
150 // --- Read and parse report descriptor ---
151 // NOTE: Also does sub-driver selection and initialisation
152 Uint8 *report_data = alloca(report_len);
153 USB_ReadDescriptor(Dev, 0x1022, 0, report_len, report_data);
154 HID_int_ParseReport(Dev, report_data, report_len, &gHID_RootCallbacks);
156 // --- Start polling ---
157 // Only if the device was initialised
158 if( USB_GetDeviceDataPtr(Dev) )
160 // Poll Endpoint .+1 (interupt)
161 USB_StartPollingEndpoint(Dev, 1);
165 Log_Log("USB_HID", "Device not intitialised");
173 * \brief Handle a Collection item in a report
175 * When an application collection is found in the root of the tree, the approriate
176 * sub-handler is invoked (mouse, keyboard, gamepad, ...)
178 tHID_ReportCallbacks *HID_RootCollection(
180 tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value
185 // Check for "Application" collection
186 if( Value != 1 ) return NULL;
189 if( Local->Usages.nItems == 0 ) return NULL;
190 usage = Local->Usages.Items[0];
193 case 0x0001: // General Desktop
194 switch(usage & 0xFFFF)
196 case 0x0001: // Pointer
197 LOG("Desktop->Pointer");
199 case 0x0002: // Mouse
200 LOG("Desktop->Mouse");
201 return &gHID_Mouse_ReportCBs;
202 case 0x0004: // Joystick
203 case 0x0005: // Game Pad
204 LOG("Desktop->Gamepad");
206 case 0x0006: // Keyboard
207 LOG("Desktop->Keyboard");
208 return &gHID_Kb_ReportCBs;
211 case 0x0007: // Keyboard / Keypad
213 return &gHID_Kb_ReportCBs;
218 void HID_int_ParseReport(tUSBInterface *Dev, Uint8 *Data, size_t Length, tHID_ReportCallbacks *StartCBs)
220 const int max_cb_level = 8;
222 tHID_ReportCallbacks *cur_cbs;
223 tHID_ReportCallbacks *cb_stack[max_cb_level];
224 tHID_ReportGlobalState global_state;
225 tHID_ReportLocalState local_state;
227 ENTER("pData iLength pStartCBs", Data, Length, StartCBs);
229 // Initialise callback stack
230 cb_stack[0] = StartCBs;
234 memset(&global_state, 0, sizeof(global_state));
235 memset(&local_state, 0, sizeof(local_state));
237 // Iterate though the report data
238 for( int ofs = 0; ofs < Length; )
243 // --- Get value and length ---
252 if( ofs + 2 > Length ) { LEAVE('-'); return; }
257 if( ofs + 3 > Length ) { LEAVE('-'); return; }
258 val = Data[ofs + 1] | (Data[ofs + 1]<<8);
262 if( ofs + 5 > Length ) { LEAVE('-'); return; }
263 val = Data[ofs + 1] | (Data[ofs + 2] << 8) | (Data[ofs + 3] << 16) | (Data[ofs + 4] << 24);
268 // --- Process the item ---
269 LOG("Type = 0x%x, len = %i, val = 0x%x", byte & 0xFC, byte & 3, val);
275 LOG("Input 0x%x", val);
276 if( cur_cbs && cur_cbs->Input )
277 cur_cbs->Input( Dev, &global_state, &local_state, val );
281 LOG("Output 0x%x", val);
282 if( cur_cbs && cur_cbs->Output )
283 cur_cbs->Output( Dev, &global_state, &local_state, val );
287 LOG("Collection 0x%x", val);
289 tHID_ReportCallbacks *next_cbs = NULL;
290 if( cur_cbs && cur_cbs->Collection )
291 next_cbs = cur_cbs->Collection( Dev, &global_state, &local_state, val );
293 if( cb_level < max_cb_level )
294 cb_stack[cb_level] = next_cbs;
301 LOG("Feature 0x%x", val);
302 if( cur_cbs && cur_cbs->Feature )
303 cur_cbs->Feature( Dev, &global_state, &local_state, val );
307 if( cur_cbs && cur_cbs->EndCollection )
308 cur_cbs->EndCollection(Dev);
309 if( cb_level == 0 ) {
310 Log_Warning("USB_HID", "Inbalance in HID collections");
315 if( cb_level < max_cb_level )
316 cur_cbs = cb_stack[cb_level];
318 // -- Helper to clear the local state
320 _FreeList( &local_state.Strings );
321 _FreeList( &local_state.Usages );
322 _FreeList( &local_state.Designators );
323 memset( &local_state, 0, sizeof(local_state) );
327 case 0x04: global_state.UsagePage = val<<16; break; // - Usage Page
328 case 0x14: global_state.LogMin = val; break; // - Logical Min
329 case 0x24: global_state.LogMax = val; break; // - Logical Max
330 case 0x34: global_state.PhysMin = val; break; // - Physical Min
331 case 0x44: global_state.PhysMax = val; break; // - Physical Max
332 case 0x54: global_state.UnitExp = val; break; // - Unit Exp
333 case 0x64: global_state.Unit = val; break; // - Unit
334 case 0x74: global_state.ReportSize = val; break; // - Report Size
335 case 0x84: global_state.ReportID = val; break; // - Report ID (TODO: Flag when used)
336 case 0x94: global_state.ReportCount = val; break; // - Report Count
337 case 0xA4: LOG("TODO: Implement Global Push"); break; // - Push
338 case 0xB4: LOG("TODO: Implement Global Pop"); break; // - Pop
343 if( (byte & 3) != 3 ) val |= global_state.UsagePage;
344 LOG("Usage %x", val);
345 _AddItem(&local_state.Usages, val);
347 case 0x18: // Range start
348 if( (byte & 3) != 3 ) val |= global_state.UsagePage;
349 LOG("Usage start %x", val);
350 local_state.UsageMin = val;
352 case 0x28: // Range end (apply)
353 if( (byte & 3) != 3 ) val |= global_state.UsagePage;
354 LOG("Usage end %x (from %x)", val, local_state.UsageMin);
355 _AddItems(&local_state.Usages, local_state.UsageMin, val);
357 // - Designators (Index into Physical report)
359 _AddItem(&local_state.Designators, val);
362 local_state.DesignatorMin = val;
365 _AddItems(&local_state.Designators, local_state.DesignatorMin, val);
372 _AddItem(&local_state.Strings, val);
375 local_state.StringMin = val;
378 _AddItems(&local_state.Strings, local_state.StringMin, val);
391 LOG("0x%x RESVD", byte & 0xFC);
399 // --------------------------------------------------------------------
401 // --------------------------------------------------------------------
402 static void _AddItem(struct sHID_IntList *List, Uint32 Value)
404 if( List->Space == List->nItems )
407 List->Items = realloc( List->Items, List->Space * sizeof(List->Items[0]) );
410 // LOG("Added %x to %p", Value, List);
411 List->Items[ List->nItems ] = Value;
415 static void _AddItems(struct sHID_IntList *List, Uint32 First, Uint32 Last)
417 while( First <= Last )
419 _AddItem(List, First);
424 static void _FreeList(struct sHID_IntList *List)