2 * Acess2 USB Stack HID Driver
3 * - By John Hodge (thePowersGang)
9 #define VERSION VER2(0,1)
14 #include "hid_reports.h"
17 int HID_Initialise(char **Arguments);
18 void HID_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen);
19 tHID_ReportCallbacks *HID_RootCollection(tUSBInterface *Dev, tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value);
20 void HID_int_ParseReport(tUSBInterface *Dev, Uint8 *Data, size_t Length, tHID_ReportCallbacks *StartCBs);
22 static void _AddItem(struct sHID_IntList *List, Uint32 Value);
23 static void _AddItems(struct sHID_IntList *List, Uint32 First, Uint32 Last);
24 static void _FreeList(struct sHID_IntList *List);
27 MODULE_DEFINE(0, VERSION, USB_HID, HID_Initialise, NULL, "USB_Core", NULL);
28 tUSBDriver gHID_USBDriver = {
30 .Match = {.Class = {0x030000, 0xFF0000}},
31 .Connected = HID_DeviceConnected,
33 tHID_ReportCallbacks gHID_RootCallbacks = {
34 .Collection = HID_RootCollection
38 int HID_Initialise(char **Arguments)
40 USB_RegisterDriver( &gHID_USBDriver );
44 void HID_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen)
46 struct sDescriptor_HID *hid_desc;
48 size_t report_len = 0;
50 ENTER("pDev pDescriptors iDescriptorsLen", Dev, Descriptors, DescriptorsLen);
52 // Locate HID descriptor
54 while(ofs + 2 <= DescriptorsLen)
56 hid_desc = (void*)( (char*)Descriptors + ofs );
57 ofs += hid_desc->Length;
58 // Sanity check length
59 if( ofs > DescriptorsLen ) {
63 // Check for correct type
64 if( hid_desc->Type == 0x21 )
66 // On to the next one then
68 if( hid_desc == NULL ) {
69 Log_Warning("USB_HID", "Device doesn't have a HID descritor");
75 // Dump descriptor header
77 LOG(" .Length = %i", hid_desc->Length);
78 LOG(" .Type = 0x%x", hid_desc->Type);
79 LOG(" .Version = 0x%x", hid_desc->Version);
80 LOG(" .NumDescriptors = %i", hid_desc->NumDescriptors);
83 if( hid_desc->Length < sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0]) )
86 Log_Warning("USB_HID", "HID Descriptor undersized (%i < %i)",
88 sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0]));
93 // Locate report descriptor
94 for( int i = 0; i < hid_desc->NumDescriptors; i ++ )
96 if( hid_desc->Descriptors[i].DescType == 0x22 ) {
97 report_len = LittleEndian16( hid_desc->Descriptors[i].DescLen );
101 if( report_len == 0 ) {
102 Log_Warning("USB_HID", "No report descriptor");
107 // Read and parse report descriptor
108 Uint8 *report_data = alloca(report_len);
109 USB_ReadDescriptor(Dev, 0x1022, 0, report_len, report_data);
110 HID_int_ParseReport(Dev, report_data, report_len, &gHID_RootCallbacks);
116 * \brief Handle a Collection item in a report
118 * When an application collection is found in the root of the tree, the approriate
119 * sub-handler is invoked (mouse, keyboard, gamepad, ...)
121 tHID_ReportCallbacks *HID_RootCollection(
123 tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value
128 // Check for "Application" collection
129 if( Value != 1 ) return NULL;
132 if( Local->Usages.nItems == 0 ) return NULL;
133 usage = Local->Usages.Items[0];
136 case 0x0001: // General Desktop
137 switch(usage & 0xFFFF)
139 case 0x0001: // Pointer
140 LOG("Desktop->Pointer");
142 case 0x0002: // Mouse
143 LOG("Desktop->Mouse");
145 case 0x0004: // Joystick
146 case 0x0005: // Game Pad
147 LOG("Desktop->Gamepad");
149 case 0x0006: // Keyboard
150 LOG("Desktop->Keyboard");
154 case 0x0007: // Keyboard / Keypad
161 void HID_int_ParseReport(tUSBInterface *Dev, Uint8 *Data, size_t Length, tHID_ReportCallbacks *StartCBs)
163 const int max_cb_level = 8;
165 tHID_ReportCallbacks *cur_cbs;
166 tHID_ReportCallbacks *cb_stack[max_cb_level];
167 tHID_ReportGlobalState global_state;
168 tHID_ReportLocalState local_state;
170 ENTER("pData iLength pStartCBs", Data, Length, StartCBs);
172 cb_stack[0] = StartCBs;
175 memset(&global_state, 0, sizeof(global_state));
176 memset(&local_state, 0, sizeof(local_state));
178 for( int ofs = 0; ofs < Length; )
184 // Get value (and increase offset)
196 val = Data[ofs + 1] | (Data[ofs + 1]<<8);
200 val = Data[ofs + 1] | (Data[ofs + 2] << 8) | (Data[ofs + 3] << 16) | (Data[ofs + 4] << 24);
205 LOG("Type = 0x%x, len = %i, val = 0x%x", byte & 0xFC, byte & 3, val);
211 LOG("Input 0x%x", val);
212 if( cur_cbs && cur_cbs->Input )
213 cur_cbs->Input( Dev, &global_state, &local_state, val );
217 LOG("Output 0x%x", val);
218 if( cur_cbs && cur_cbs->Output )
219 cur_cbs->Output( Dev, &global_state, &local_state, val );
223 LOG("Collection 0x%x", val);
225 tHID_ReportCallbacks *next_cbs = NULL;
226 if( cur_cbs && cur_cbs->Collection )
227 next_cbs = cur_cbs->Collection( Dev, &global_state, &local_state, val );
229 if( cb_level < max_cb_level )
230 cb_stack[cb_level] = next_cbs;
237 LOG("Feature 0x%x", val);
238 if( cur_cbs && cur_cbs->Feature )
239 cur_cbs->Feature( Dev, &global_state, &local_state, val );
243 if( cur_cbs && cur_cbs->EndCollection )
244 cur_cbs->EndCollection(Dev);
245 if( cb_level == 0 ) {
246 Log_Warning("USB_HID", "Inbalance in HID collections");
251 if( cb_level < max_cb_level )
252 cur_cbs = cb_stack[cb_level];
254 // -- Helper to clear the local state
256 _FreeList( &local_state.Strings );
257 _FreeList( &local_state.Usages );
258 _FreeList( &local_state.Designators );
259 memset( &local_state, 0, sizeof(local_state) );
263 case 0x04: global_state.UsagePage = val<<16; break; // - Usage Page
264 case 0x14: global_state.LogMin = val; break; // - Logical Min
265 case 0x24: global_state.LogMax = val; break; // - Logical Max
266 case 0x34: global_state.PhysMin = val; break; // - Physical Min
267 case 0x44: global_state.PhysMax = val; break; // - Physical Max
268 case 0x54: global_state.UnitExp = val; break; // - Unit Exp
269 case 0x64: global_state.Unit = val; break; // - Unit
270 case 0x74: global_state.ReportSize = val; break; // - Report Size
271 case 0x84: global_state.ReportID = val; break; // - Report ID (TODO: Flag when used)
272 case 0x94: global_state.ReportCount = val; break; // - Report Count
273 case 0xA4: LOG("TODO: Implement Global Push"); break; // - Push
274 case 0xB4: LOG("TODO: Implement Global Pop"); break; // - Pop
279 if( (byte & 3) != 3 ) val |= global_state.UsagePage;
280 _AddItem(&local_state.Usages, val);
282 case 0x18: // Range start
283 if( (byte & 3) != 3 ) val |= global_state.UsagePage;
284 local_state.UsageMin = val;
286 case 0x28: // Range end (apply)
287 if( (byte & 3) != 3 ) val |= global_state.UsagePage;
288 _AddItems(&local_state.Usages, local_state.UsageMin, val);
290 // - Designators (Index into Physical report)
292 _AddItem(&local_state.Designators, val);
295 local_state.DesignatorMin = val;
298 _AddItems(&local_state.Designators, local_state.DesignatorMin, val);
305 _AddItem(&local_state.Strings, val);
308 local_state.StringMin = val;
311 _AddItems(&local_state.Strings, local_state.StringMin, val);
324 LOG("0x%x RESVD", byte & 0xFC);
332 static void _AddItem(struct sHID_IntList *List, Uint32 Value)
334 if( List->Space == List->nItems )
337 List->Items = realloc( List->Items, List->Space * sizeof(List->Items[0]) );
340 List->Items[ List->nItems ] = Value;
344 static void _AddItems(struct sHID_IntList *List, Uint32 First, Uint32 Last)
346 while( First <= Last )
348 _AddItem(List, First);
353 static void _FreeList(struct sHID_IntList *List)