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;
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", 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 );
63 DevFS_AddDevice( &gHID_Mouse_DevFS );
69 * \brief Callback for when there's new data from the device
71 * Calls the subdriver callback (stored at a fixed offset in the device data structure)
73 void HID_InterruptCallback(tUSBInterface *Dev, int EndPt, int Length, void *Data)
77 info = USB_GetDeviceDataPtr(Dev);
79 Log_Error("USB HID", "Device %p doesn't have a data pointer.", Dev);
83 info->DataAvail(Dev, EndPt, Length, Data);
87 * \brief Handle a device connection
89 void HID_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen)
91 struct sDescriptor_HID *hid_desc;
93 size_t report_len = 0;
95 ENTER("pDev pDescriptors iDescriptorsLen", Dev, Descriptors, DescriptorsLen);
97 // --- Locate HID descriptor ---
99 while(ofs + 2 <= DescriptorsLen)
101 hid_desc = (void*)( (char*)Descriptors + ofs );
102 ofs += hid_desc->Length;
103 // Sanity check length
104 if( ofs > DescriptorsLen ) {
108 // Check for correct type
109 if( hid_desc->Type == 0x21 )
111 // On to the next one then
113 if( hid_desc == NULL ) {
114 Log_Warning("USB_HID", "Device doesn't have a HID descritor");
118 // - Sanity check length
119 if( hid_desc->Length < sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0]) )
122 Log_Warning("USB_HID", "HID Descriptor undersized (%i < %i)",
124 sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0]));
129 // --- Dump descriptor header ---
131 LOG(" .Length = %i", hid_desc->Length);
132 LOG(" .Type = 0x%x", hid_desc->Type);
133 LOG(" .Version = 0x%x", hid_desc->Version);
134 LOG(" .NumDescriptors = %i", hid_desc->NumDescriptors);
137 // --- Locate report descriptor length ---
138 for( int i = 0; i < hid_desc->NumDescriptors; i ++ )
140 if( hid_desc->Descriptors[i].DescType == 0x22 ) {
141 report_len = LittleEndian16( hid_desc->Descriptors[i].DescLen );
145 if( report_len == 0 ) {
146 Log_Warning("USB_HID", "No report descriptor");
151 // --- Read and parse report descriptor ---
152 // NOTE: Also does sub-driver selection and initialisation
153 Uint8 *report_data = alloca(report_len);
154 USB_ReadDescriptor(Dev, 0x1022, 0, report_len, report_data);
155 HID_int_ParseReport(Dev, report_data, report_len, &gHID_RootCallbacks);
157 // --- Start polling ---
158 // Only if the device was initialised
159 if( USB_GetDeviceDataPtr(Dev) )
161 // Poll Endpoint .+1 (interupt)
162 USB_StartPollingEndpoint(Dev, 1);
166 Log_Log("USB_HID", "Device not intitialised");
174 * \brief Handle a Collection item in a report
176 * When an application collection is found in the root of the tree, the approriate
177 * sub-handler is invoked (mouse, keyboard, gamepad, ...)
179 tHID_ReportCallbacks *HID_RootCollection(
181 tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value
186 // Check for "Application" collection
187 if( Value != 1 ) return NULL;
190 if( Local->Usages.nItems == 0 ) return NULL;
191 usage = Local->Usages.Items[0];
194 case 0x0001: // General Desktop
195 switch(usage & 0xFFFF)
197 case 0x0001: // Pointer
198 LOG("Desktop->Pointer");
200 case 0x0002: // Mouse
201 LOG("Desktop->Mouse");
202 return &gHID_Mouse_ReportCBs;
203 case 0x0004: // Joystick
204 case 0x0005: // Game Pad
205 LOG("Desktop->Gamepad");
207 case 0x0006: // Keyboard
208 LOG("Desktop->Keyboard");
212 case 0x0007: // Keyboard / Keypad
219 void HID_int_ParseReport(tUSBInterface *Dev, Uint8 *Data, size_t Length, tHID_ReportCallbacks *StartCBs)
221 const int max_cb_level = 8;
223 tHID_ReportCallbacks *cur_cbs;
224 tHID_ReportCallbacks *cb_stack[max_cb_level];
225 tHID_ReportGlobalState global_state;
226 tHID_ReportLocalState local_state;
228 ENTER("pData iLength pStartCBs", Data, Length, StartCBs);
230 // Initialise callback stack
231 cb_stack[0] = StartCBs;
235 memset(&global_state, 0, sizeof(global_state));
236 memset(&local_state, 0, sizeof(local_state));
238 // Iterate though the report data
239 for( int ofs = 0; ofs < Length; )
244 // --- Get value and length ---
253 if( ofs + 2 > Length ) { LEAVE('-'); return; }
258 if( ofs + 3 > Length ) { LEAVE('-'); return; }
259 val = Data[ofs + 1] | (Data[ofs + 1]<<8);
263 if( ofs + 5 > Length ) { LEAVE('-'); return; }
264 val = Data[ofs + 1] | (Data[ofs + 2] << 8) | (Data[ofs + 3] << 16) | (Data[ofs + 4] << 24);
269 // --- Process the item ---
270 LOG("Type = 0x%x, len = %i, val = 0x%x", byte & 0xFC, byte & 3, val);
276 LOG("Input 0x%x", val);
277 if( cur_cbs && cur_cbs->Input )
278 cur_cbs->Input( Dev, &global_state, &local_state, val );
282 LOG("Output 0x%x", val);
283 if( cur_cbs && cur_cbs->Output )
284 cur_cbs->Output( Dev, &global_state, &local_state, val );
288 LOG("Collection 0x%x", val);
290 tHID_ReportCallbacks *next_cbs = NULL;
291 if( cur_cbs && cur_cbs->Collection )
292 next_cbs = cur_cbs->Collection( Dev, &global_state, &local_state, val );
294 if( cb_level < max_cb_level )
295 cb_stack[cb_level] = next_cbs;
302 LOG("Feature 0x%x", val);
303 if( cur_cbs && cur_cbs->Feature )
304 cur_cbs->Feature( Dev, &global_state, &local_state, val );
308 if( cur_cbs && cur_cbs->EndCollection )
309 cur_cbs->EndCollection(Dev);
310 if( cb_level == 0 ) {
311 Log_Warning("USB_HID", "Inbalance in HID collections");
316 if( cb_level < max_cb_level )
317 cur_cbs = cb_stack[cb_level];
319 // -- Helper to clear the local state
321 _FreeList( &local_state.Strings );
322 _FreeList( &local_state.Usages );
323 _FreeList( &local_state.Designators );
324 memset( &local_state, 0, sizeof(local_state) );
328 case 0x04: global_state.UsagePage = val<<16; break; // - Usage Page
329 case 0x14: global_state.LogMin = val; break; // - Logical Min
330 case 0x24: global_state.LogMax = val; break; // - Logical Max
331 case 0x34: global_state.PhysMin = val; break; // - Physical Min
332 case 0x44: global_state.PhysMax = val; break; // - Physical Max
333 case 0x54: global_state.UnitExp = val; break; // - Unit Exp
334 case 0x64: global_state.Unit = val; break; // - Unit
335 case 0x74: global_state.ReportSize = val; break; // - Report Size
336 case 0x84: global_state.ReportID = val; break; // - Report ID (TODO: Flag when used)
337 case 0x94: global_state.ReportCount = val; break; // - Report Count
338 case 0xA4: LOG("TODO: Implement Global Push"); break; // - Push
339 case 0xB4: LOG("TODO: Implement Global Pop"); break; // - Pop
344 if( (byte & 3) != 3 ) val |= global_state.UsagePage;
345 _AddItem(&local_state.Usages, val);
347 case 0x18: // Range start
348 if( (byte & 3) != 3 ) val |= global_state.UsagePage;
349 local_state.UsageMin = val;
351 case 0x28: // Range end (apply)
352 if( (byte & 3) != 3 ) val |= global_state.UsagePage;
353 _AddItems(&local_state.Usages, local_state.UsageMin, val);
355 // - Designators (Index into Physical report)
357 _AddItem(&local_state.Designators, val);
360 local_state.DesignatorMin = val;
363 _AddItems(&local_state.Designators, local_state.DesignatorMin, val);
370 _AddItem(&local_state.Strings, val);
373 local_state.StringMin = val;
376 _AddItems(&local_state.Strings, local_state.StringMin, val);
389 LOG("0x%x RESVD", byte & 0xFC);
397 // --------------------------------------------------------------------
399 // --------------------------------------------------------------------
400 static void _AddItem(struct sHID_IntList *List, Uint32 Value)
402 if( List->Space == List->nItems )
405 List->Items = realloc( List->Items, List->Space * sizeof(List->Items[0]) );
408 List->Items[ List->nItems ] = Value;
412 static void _AddItems(struct sHID_IntList *List, Uint32 First, Uint32 Last)
414 while( First <= Last )
416 _AddItem(List, First);
421 static void _FreeList(struct sHID_IntList *List)