2 * Acess2 USB Stack HID Driver
3 * - By John Hodge (thePowersGang)
9 #define VERSION VER2(0,1)
14 #include "hid_reports.h"
17 typedef struct sHID_Device tHID_Device;
21 void *Next; // Used by sub-driver
22 tUSB_DataCallback DataAvail;
23 // ... Device-specific data
27 int HID_Initialise(char **Arguments);
28 void HID_InterruptCallback(tUSBInterface *Dev, int EndPt, int Length, void *Data);
29 void HID_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen);
30 tHID_ReportCallbacks *HID_RootCollection(tUSBInterface *Dev, tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value);
31 void HID_int_ParseReport(tUSBInterface *Dev, Uint8 *Data, size_t Length, tHID_ReportCallbacks *StartCBs);
33 static void _AddItem(struct sHID_IntList *List, Uint32 Value);
34 static void _AddItems(struct sHID_IntList *List, Uint32 First, Uint32 Last);
35 static void _FreeList(struct sHID_IntList *List);
38 MODULE_DEFINE(0, VERSION, USB_HID, HID_Initialise, NULL, "USB_Core", NULL);
39 tUSBDriver gHID_USBDriver = {
41 .Match = {.Class = {0x030000, 0xFF0000}},
42 .Connected = HID_DeviceConnected,
45 {0x80, HID_InterruptCallback},
49 tHID_ReportCallbacks gHID_RootCallbacks = {
50 .Collection = HID_RootCollection
54 int HID_Initialise(char **Arguments)
56 USB_RegisterDriver( &gHID_USBDriver );
61 * \brief Callback for when there's new data from the device
63 * Calls the subdriver callback (stored at a fixed offset in the device data structure)
65 void HID_InterruptCallback(tUSBInterface *Dev, int EndPt, int Length, void *Data)
69 info = USB_GetDeviceDataPtr(Dev);
71 Log_Error("USB HID", "Device %p doesn't have a data pointer.", Dev);
75 info->DataAvail(Dev, EndPt, Length, Data);
79 * \brief Handle a device connection
81 void HID_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen)
83 struct sDescriptor_HID *hid_desc;
85 size_t report_len = 0;
87 ENTER("pDev pDescriptors iDescriptorsLen", Dev, Descriptors, DescriptorsLen);
89 // --- Locate HID descriptor ---
91 while(ofs + 2 <= DescriptorsLen)
93 hid_desc = (void*)( (char*)Descriptors + ofs );
94 ofs += hid_desc->Length;
95 // Sanity check length
96 if( ofs > DescriptorsLen ) {
100 // Check for correct type
101 if( hid_desc->Type == 0x21 )
103 // On to the next one then
105 if( hid_desc == NULL ) {
106 Log_Warning("USB_HID", "Device doesn't have a HID descritor");
110 // - Sanity check length
111 if( hid_desc->Length < sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0]) )
114 Log_Warning("USB_HID", "HID Descriptor undersized (%i < %i)",
116 sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0]));
121 // --- Dump descriptor header ---
123 LOG(" .Length = %i", hid_desc->Length);
124 LOG(" .Type = 0x%x", hid_desc->Type);
125 LOG(" .Version = 0x%x", hid_desc->Version);
126 LOG(" .NumDescriptors = %i", hid_desc->NumDescriptors);
129 // --- Locate report descriptor length ---
130 for( int i = 0; i < hid_desc->NumDescriptors; i ++ )
132 if( hid_desc->Descriptors[i].DescType == 0x22 ) {
133 report_len = LittleEndian16( hid_desc->Descriptors[i].DescLen );
137 if( report_len == 0 ) {
138 Log_Warning("USB_HID", "No report descriptor");
143 // --- Read and parse report descriptor ---
144 // NOTE: Also does sub-driver selection and initialisation
145 Uint8 *report_data = alloca(report_len);
146 USB_ReadDescriptor(Dev, 0x1022, 0, report_len, report_data);
147 HID_int_ParseReport(Dev, report_data, report_len, &gHID_RootCallbacks);
153 * \brief Handle a Collection item in a report
155 * When an application collection is found in the root of the tree, the approriate
156 * sub-handler is invoked (mouse, keyboard, gamepad, ...)
158 tHID_ReportCallbacks *HID_RootCollection(
160 tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value
165 // Check for "Application" collection
166 if( Value != 1 ) return NULL;
169 if( Local->Usages.nItems == 0 ) return NULL;
170 usage = Local->Usages.Items[0];
173 case 0x0001: // General Desktop
174 switch(usage & 0xFFFF)
176 case 0x0001: // Pointer
177 LOG("Desktop->Pointer");
179 case 0x0002: // Mouse
180 LOG("Desktop->Mouse");
182 case 0x0004: // Joystick
183 case 0x0005: // Game Pad
184 LOG("Desktop->Gamepad");
186 case 0x0006: // Keyboard
187 LOG("Desktop->Keyboard");
191 case 0x0007: // Keyboard / Keypad
198 void HID_int_ParseReport(tUSBInterface *Dev, Uint8 *Data, size_t Length, tHID_ReportCallbacks *StartCBs)
200 const int max_cb_level = 8;
202 tHID_ReportCallbacks *cur_cbs;
203 tHID_ReportCallbacks *cb_stack[max_cb_level];
204 tHID_ReportGlobalState global_state;
205 tHID_ReportLocalState local_state;
207 ENTER("pData iLength pStartCBs", Data, Length, StartCBs);
209 // Initialise callback stack
210 cb_stack[0] = StartCBs;
214 memset(&global_state, 0, sizeof(global_state));
215 memset(&local_state, 0, sizeof(local_state));
217 // Iterate though the report data
218 for( int ofs = 0; ofs < Length; )
223 // --- Get value and length ---
232 if( ofs + 2 > Length ) { LEAVE('-'); return; }
237 if( ofs + 3 > Length ) { LEAVE('-'); return; }
238 val = Data[ofs + 1] | (Data[ofs + 1]<<8);
242 if( ofs + 5 > Length ) { LEAVE('-'); return; }
243 val = Data[ofs + 1] | (Data[ofs + 2] << 8) | (Data[ofs + 3] << 16) | (Data[ofs + 4] << 24);
248 // --- Process the item ---
249 LOG("Type = 0x%x, len = %i, val = 0x%x", byte & 0xFC, byte & 3, val);
255 LOG("Input 0x%x", val);
256 if( cur_cbs && cur_cbs->Input )
257 cur_cbs->Input( Dev, &global_state, &local_state, val );
261 LOG("Output 0x%x", val);
262 if( cur_cbs && cur_cbs->Output )
263 cur_cbs->Output( Dev, &global_state, &local_state, val );
267 LOG("Collection 0x%x", val);
269 tHID_ReportCallbacks *next_cbs = NULL;
270 if( cur_cbs && cur_cbs->Collection )
271 next_cbs = cur_cbs->Collection( Dev, &global_state, &local_state, val );
273 if( cb_level < max_cb_level )
274 cb_stack[cb_level] = next_cbs;
281 LOG("Feature 0x%x", val);
282 if( cur_cbs && cur_cbs->Feature )
283 cur_cbs->Feature( Dev, &global_state, &local_state, val );
287 if( cur_cbs && cur_cbs->EndCollection )
288 cur_cbs->EndCollection(Dev);
289 if( cb_level == 0 ) {
290 Log_Warning("USB_HID", "Inbalance in HID collections");
295 if( cb_level < max_cb_level )
296 cur_cbs = cb_stack[cb_level];
298 // -- Helper to clear the local state
300 _FreeList( &local_state.Strings );
301 _FreeList( &local_state.Usages );
302 _FreeList( &local_state.Designators );
303 memset( &local_state, 0, sizeof(local_state) );
307 case 0x04: global_state.UsagePage = val<<16; break; // - Usage Page
308 case 0x14: global_state.LogMin = val; break; // - Logical Min
309 case 0x24: global_state.LogMax = val; break; // - Logical Max
310 case 0x34: global_state.PhysMin = val; break; // - Physical Min
311 case 0x44: global_state.PhysMax = val; break; // - Physical Max
312 case 0x54: global_state.UnitExp = val; break; // - Unit Exp
313 case 0x64: global_state.Unit = val; break; // - Unit
314 case 0x74: global_state.ReportSize = val; break; // - Report Size
315 case 0x84: global_state.ReportID = val; break; // - Report ID (TODO: Flag when used)
316 case 0x94: global_state.ReportCount = val; break; // - Report Count
317 case 0xA4: LOG("TODO: Implement Global Push"); break; // - Push
318 case 0xB4: LOG("TODO: Implement Global Pop"); break; // - Pop
323 if( (byte & 3) != 3 ) val |= global_state.UsagePage;
324 _AddItem(&local_state.Usages, val);
326 case 0x18: // Range start
327 if( (byte & 3) != 3 ) val |= global_state.UsagePage;
328 local_state.UsageMin = val;
330 case 0x28: // Range end (apply)
331 if( (byte & 3) != 3 ) val |= global_state.UsagePage;
332 _AddItems(&local_state.Usages, local_state.UsageMin, val);
334 // - Designators (Index into Physical report)
336 _AddItem(&local_state.Designators, val);
339 local_state.DesignatorMin = val;
342 _AddItems(&local_state.Designators, local_state.DesignatorMin, val);
349 _AddItem(&local_state.Strings, val);
352 local_state.StringMin = val;
355 _AddItems(&local_state.Strings, local_state.StringMin, val);
368 LOG("0x%x RESVD", byte & 0xFC);
376 // --------------------------------------------------------------------
378 // --------------------------------------------------------------------
379 static void _AddItem(struct sHID_IntList *List, Uint32 Value)
381 if( List->Space == List->nItems )
384 List->Items = realloc( List->Items, List->Space * sizeof(List->Items[0]) );
387 List->Items[ List->nItems ] = Value;
391 static void _AddItems(struct sHID_IntList *List, Uint32 First, Uint32 Last)
393 while( First <= Last )
395 _AddItem(List, First);
400 static void _FreeList(struct sHID_IntList *List)