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 tDevFS_Driver gHID_Mouse_DevFS;
30 extern tHID_ReportCallbacks gHID_Kb_ReportCBs;
33 int HID_Initialise(char **Arguments);
34 void HID_InterruptCallback(tUSBInterface *Dev, int EndPt, int Length, void *Data);
35 void HID_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen);
36 tHID_ReportCallbacks *HID_RootCollection(tUSBInterface *Dev, tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value);
37 void HID_int_ParseReport(tUSBInterface *Dev, Uint8 *Data, size_t Length, tHID_ReportCallbacks *StartCBs);
39 static void _AddItem(struct sHID_IntList *List, Uint32 Value);
40 static void _AddItems(struct sHID_IntList *List, Uint32 First, Uint32 Last);
41 static void _FreeList(struct sHID_IntList *List);
44 MODULE_DEFINE(0, VERSION, USB_HID, HID_Initialise, NULL, "USB_Core", NULL);
45 tUSBDriver gHID_USBDriver = {
47 .Match = {.Class = {0x030000, 0xFF0000}},
48 .Connected = HID_DeviceConnected,
51 {0x80, HID_InterruptCallback},
55 tHID_ReportCallbacks gHID_RootCallbacks = {
56 .Collection = HID_RootCollection
60 int HID_Initialise(char **Arguments)
62 USB_RegisterDriver( &gHID_USBDriver );
64 DevFS_AddDevice( &gHID_Mouse_DevFS );
70 * \brief Callback for when there's new data from the device
72 * Calls the subdriver callback (stored at a fixed offset in the device data structure)
74 void HID_InterruptCallback(tUSBInterface *Dev, int EndPt, int Length, void *Data)
78 info = USB_GetDeviceDataPtr(Dev);
80 Log_Error("USB HID", "Device %p doesn't have a data pointer.", Dev);
84 LOG("Data for %p", info->DataAvail);
85 info->DataAvail(Dev, EndPt, Length, Data);
89 * \brief Handle a device connection
91 void HID_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen)
93 struct sDescriptor_HID *hid_desc;
95 size_t report_len = 0;
97 ENTER("pDev pDescriptors iDescriptorsLen", Dev, Descriptors, DescriptorsLen);
99 // --- Locate HID descriptor ---
101 while(ofs + 2 <= DescriptorsLen)
103 hid_desc = (void*)( (char*)Descriptors + ofs );
104 ofs += hid_desc->Length;
105 // Sanity check length
106 if( ofs > DescriptorsLen ) {
110 // Check for correct type
111 if( hid_desc->Type == 0x21 )
113 // On to the next one then
115 if( hid_desc == NULL ) {
116 Log_Warning("USB_HID", "Device doesn't have a HID descritor");
120 // - Sanity check length
121 if( hid_desc->Length < sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0]) )
124 Log_Warning("USB_HID", "HID Descriptor undersized (%i < %i)",
126 sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0]));
131 // --- Dump descriptor header ---
133 LOG(" .Length = %i", hid_desc->Length);
134 LOG(" .Type = 0x%x", hid_desc->Type);
135 LOG(" .Version = 0x%x", hid_desc->Version);
136 LOG(" .NumDescriptors = %i", hid_desc->NumDescriptors);
139 // --- Locate report descriptor length ---
140 for( int i = 0; i < hid_desc->NumDescriptors; i ++ )
142 if( hid_desc->Descriptors[i].DescType == 0x22 ) {
143 report_len = LittleEndian16( hid_desc->Descriptors[i].DescLen );
147 if( report_len == 0 ) {
148 Log_Warning("USB_HID", "No report descriptor");
153 // --- Read and parse report descriptor ---
154 // NOTE: Also does sub-driver selection and initialisation
155 Uint8 *report_data = alloca(report_len);
156 USB_ReadDescriptor(Dev, 0x1022, 0, report_len, report_data);
157 HID_int_ParseReport(Dev, report_data, report_len, &gHID_RootCallbacks);
159 // --- Start polling ---
160 // Only if the device was initialised
161 if( USB_GetDeviceDataPtr(Dev) )
163 // Poll Endpoint .+1 (interupt)
164 USB_StartPollingEndpoint(Dev, 1);
168 Log_Log("USB_HID", "Device not intitialised");
176 * \brief Handle a Collection item in a report
178 * When an application collection is found in the root of the tree, the approriate
179 * sub-handler is invoked (mouse, keyboard, gamepad, ...)
181 tHID_ReportCallbacks *HID_RootCollection(
183 tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value
188 // Check for "Application" collection
189 if( Value != 1 ) return NULL;
192 if( Local->Usages.nItems == 0 ) return NULL;
193 usage = Local->Usages.Items[0];
196 case 0x0001: // General Desktop
197 switch(usage & 0xFFFF)
199 case 0x0001: // Pointer
200 LOG("Desktop->Pointer");
202 case 0x0002: // Mouse
203 LOG("Desktop->Mouse");
204 return &gHID_Mouse_ReportCBs;
205 case 0x0004: // Joystick
206 case 0x0005: // Game Pad
207 LOG("Desktop->Gamepad");
209 case 0x0006: // Keyboard
210 LOG("Desktop->Keyboard");
211 return &gHID_Kb_ReportCBs;
214 case 0x0007: // Keyboard / Keypad
216 return &gHID_Kb_ReportCBs;
221 void HID_int_ParseReport(tUSBInterface *Dev, Uint8 *Data, size_t Length, tHID_ReportCallbacks *StartCBs)
223 const int max_cb_level = 8;
225 tHID_ReportCallbacks *cur_cbs;
226 tHID_ReportCallbacks *cb_stack[max_cb_level];
227 tHID_ReportGlobalState global_state;
228 tHID_ReportLocalState local_state;
230 ENTER("pData iLength pStartCBs", Data, Length, StartCBs);
232 // Initialise callback stack
233 cb_stack[0] = StartCBs;
237 memset(&global_state, 0, sizeof(global_state));
238 memset(&local_state, 0, sizeof(local_state));
240 // Iterate though the report data
241 for( int ofs = 0; ofs < Length; )
246 // --- Get value and length ---
255 if( ofs + 2 > Length ) { LEAVE('-'); return; }
260 if( ofs + 3 > Length ) { LEAVE('-'); return; }
261 val = Data[ofs + 1] | (Data[ofs + 1]<<8);
265 if( ofs + 5 > Length ) { LEAVE('-'); return; }
266 val = Data[ofs + 1] | (Data[ofs + 2] << 8) | (Data[ofs + 3] << 16) | (Data[ofs + 4] << 24);
271 // --- Process the item ---
272 LOG("Type = 0x%x, len = %i, val = 0x%x", byte & 0xFC, byte & 3, val);
278 LOG("Input 0x%x", val);
279 if( cur_cbs && cur_cbs->Input )
280 cur_cbs->Input( Dev, &global_state, &local_state, val );
284 LOG("Output 0x%x", val);
285 if( cur_cbs && cur_cbs->Output )
286 cur_cbs->Output( Dev, &global_state, &local_state, val );
290 LOG("Collection 0x%x", val);
292 tHID_ReportCallbacks *next_cbs = NULL;
293 if( cur_cbs && cur_cbs->Collection )
294 next_cbs = cur_cbs->Collection( Dev, &global_state, &local_state, val );
296 if( cb_level < max_cb_level )
297 cb_stack[cb_level] = next_cbs;
304 LOG("Feature 0x%x", val);
305 if( cur_cbs && cur_cbs->Feature )
306 cur_cbs->Feature( Dev, &global_state, &local_state, val );
310 if( cur_cbs && cur_cbs->EndCollection )
311 cur_cbs->EndCollection(Dev);
312 if( cb_level == 0 ) {
313 Log_Warning("USB_HID", "Inbalance in HID collections");
318 if( cb_level < max_cb_level )
319 cur_cbs = cb_stack[cb_level];
321 // -- Helper to clear the local state
323 _FreeList( &local_state.Strings );
324 _FreeList( &local_state.Usages );
325 _FreeList( &local_state.Designators );
326 memset( &local_state, 0, sizeof(local_state) );
330 case 0x04: global_state.UsagePage = val<<16; break; // - Usage Page
331 case 0x14: global_state.LogMin = val; break; // - Logical Min
332 case 0x24: global_state.LogMax = val; break; // - Logical Max
333 case 0x34: global_state.PhysMin = val; break; // - Physical Min
334 case 0x44: global_state.PhysMax = val; break; // - Physical Max
335 case 0x54: global_state.UnitExp = val; break; // - Unit Exp
336 case 0x64: global_state.Unit = val; break; // - Unit
337 case 0x74: global_state.ReportSize = val; break; // - Report Size
338 case 0x84: global_state.ReportID = val; break; // - Report ID (TODO: Flag when used)
339 case 0x94: global_state.ReportCount = val; break; // - Report Count
340 case 0xA4: LOG("TODO: Implement Global Push"); break; // - Push
341 case 0xB4: LOG("TODO: Implement Global Pop"); break; // - Pop
346 if( (byte & 3) != 3 ) val |= global_state.UsagePage;
347 LOG("Usage %x", val);
348 _AddItem(&local_state.Usages, val);
350 case 0x18: // Range start
351 if( (byte & 3) != 3 ) val |= global_state.UsagePage;
352 LOG("Usage start %x", val);
353 local_state.UsageMin = val;
355 case 0x28: // Range end (apply)
356 if( (byte & 3) != 3 ) val |= global_state.UsagePage;
357 LOG("Usage end %x (from %x)", val, local_state.UsageMin);
358 _AddItems(&local_state.Usages, local_state.UsageMin, val);
360 // - Designators (Index into Physical report)
362 _AddItem(&local_state.Designators, val);
365 local_state.DesignatorMin = val;
368 _AddItems(&local_state.Designators, local_state.DesignatorMin, val);
375 _AddItem(&local_state.Strings, val);
378 local_state.StringMin = val;
381 _AddItems(&local_state.Strings, local_state.StringMin, val);
394 LOG("0x%x RESVD", byte & 0xFC);
402 // --------------------------------------------------------------------
404 // --------------------------------------------------------------------
405 static void _AddItem(struct sHID_IntList *List, Uint32 Value)
407 if( List->Space == List->nItems )
410 List->Items = realloc( List->Items, List->Space * sizeof(List->Items[0]) );
413 // LOG("Added %x to %p", Value, List);
414 List->Items[ List->nItems ] = Value;
418 static void _AddItems(struct sHID_IntList *List, Uint32 First, Uint32 Last)
420 while( First <= Last )
422 _AddItem(List, First);
427 static void _FreeList(struct sHID_IntList *List)