Modules/USB_HID - Fixed report parsing bug that caused mouse not not detect buttons...
[tpg/acess2.git] / KernelLand / Modules / USB / HID / main.c
1 /*
2  * Acess2 USB Stack HID Driver
3  * - By John Hodge (thePowersGang)
4  *
5  * main.c
6  * - Driver Core
7  */
8 #define DEBUG   1
9 #define VERSION VER2(0,1)
10 #include <acess.h>
11 #include <modules.h>
12 #include <usb_core.h>
13 #include "hid.h"
14 #include "hid_reports.h"
15 #include <fs_devfs.h>
16
17 // === TYPES ===
18 typedef struct sHID_Device      tHID_Device;
19
20 struct sHID_Device
21 {
22         void    *Next;  // Used by sub-driver
23         tUSB_DataCallback       DataAvail;
24         // ... Device-specific data
25 };
26
27 // === IMPORTS ===
28 extern tHID_ReportCallbacks     gHID_Mouse_ReportCBs;
29 extern tDevFS_Driver    gHID_Mouse_DevFS;
30
31 // === PROTOTYPES ===
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);
37
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);
41
42 // === GLOBALS ===
43 MODULE_DEFINE(0, VERSION, USB_HID, HID_Initialise, NULL, "USB_Core", NULL);
44 tUSBDriver      gHID_USBDriver = {
45         .Name = "HID",
46         .Match = {.Class = {0x030000, 0xFF0000}},
47         .Connected = HID_DeviceConnected,
48         .MaxEndpoints = 2,
49         .Endpoints = {
50                 {0x80, HID_InterruptCallback},
51                 {0, NULL}
52         }
53 };
54 tHID_ReportCallbacks    gHID_RootCallbacks = {
55         .Collection = HID_RootCollection
56 };
57
58 // === CODE ===
59 int HID_Initialise(char **Arguments)
60 {
61         USB_RegisterDriver( &gHID_USBDriver );
62         
63         DevFS_AddDevice( &gHID_Mouse_DevFS );
64         
65         return 0;
66 }
67
68 /**
69  * \brief Callback for when there's new data from the device
70  * 
71  * Calls the subdriver callback (stored at a fixed offset in the device data structure)
72  */
73 void HID_InterruptCallback(tUSBInterface *Dev, int EndPt, int Length, void *Data)
74 {
75         tHID_Device     *info;
76         
77         info = USB_GetDeviceDataPtr(Dev);
78         if(!info) {
79                 Log_Error("USB HID", "Device %p doesn't have a data pointer.", Dev);
80                 return ;
81         }
82         
83         info->DataAvail(Dev, EndPt, Length, Data);
84 }
85
86 /**
87  * \brief Handle a device connection
88  */
89 void HID_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen)
90 {
91         struct sDescriptor_HID  *hid_desc;
92         size_t  ofs = 0;
93         size_t  report_len = 0;
94         
95         ENTER("pDev pDescriptors iDescriptorsLen", Dev, Descriptors, DescriptorsLen);
96
97         // --- Locate HID descriptor ---
98         hid_desc = NULL;
99         while(ofs + 2 <= DescriptorsLen)
100         {
101                 hid_desc = (void*)( (char*)Descriptors + ofs );
102                 ofs += hid_desc->Length;
103                 // Sanity check length
104                 if( ofs > DescriptorsLen ) {
105                         hid_desc = NULL;
106                         break ;
107                 }
108                 // Check for correct type
109                 if( hid_desc->Type == 0x21 )
110                         break ;
111                 // On to the next one then
112         }
113         if( hid_desc == NULL ) {
114                 Log_Warning("USB_HID", "Device doesn't have a HID descritor");
115                 LEAVE('-');
116                 return ;
117         }
118         // - Sanity check length
119         if( hid_desc->Length < sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0]) )
120         {
121                 // Too small!
122                 Log_Warning("USB_HID", "HID Descriptor undersized (%i < %i)",
123                         hid_desc->Length,
124                         sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0]));
125                 LEAVE('-');
126                 return ;
127         }
128
129         // --- Dump descriptor header ---
130         LOG("hid_desc = {");
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);
135         LOG("}");
136
137         // --- Locate report descriptor length ---
138         for( int i = 0; i < hid_desc->NumDescriptors; i ++ )
139         {
140                 if( hid_desc->Descriptors[i].DescType == 0x22 ) {
141                         report_len = LittleEndian16( hid_desc->Descriptors[i].DescLen );
142                         break ;
143                 }
144         }
145         if( report_len == 0 ) {
146                 Log_Warning("USB_HID", "No report descriptor");
147                 LEAVE('-');
148                 return ;
149         }
150         
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);
156         
157         // --- Start polling ---
158         // Only if the device was initialised
159         if( USB_GetDeviceDataPtr(Dev) )
160         {
161                 // Poll Endpoint .+1 (interupt)
162                 USB_StartPollingEndpoint(Dev, 1);
163         }
164         else
165         {
166                 Log_Log("USB_HID", "Device not intitialised");
167         }
168         
169         
170         LEAVE('-');
171 }
172
173 /**
174  * \brief Handle a Collection item in a report
175  * 
176  * When an application collection is found in the root of the tree, the approriate
177  * sub-handler is invoked (mouse, keyboard, gamepad, ...)
178  */
179 tHID_ReportCallbacks *HID_RootCollection(
180         tUSBInterface *Dev,
181         tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value
182         )
183 {
184         Uint32  usage;
185         
186         // Check for "Application" collection
187         if( Value != 1 )        return NULL;
188         
189         // Check usages
190         if( Local->Usages.nItems == 0 ) return NULL;
191         usage = Local->Usages.Items[0];
192         switch(usage >> 16)
193         {
194         case 0x0001:    // General Desktop
195                 switch(usage & 0xFFFF)
196                 {
197                 case 0x0001:    // Pointer
198                         LOG("Desktop->Pointer");
199                         break;
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");
206                         break;
207                 case 0x0006:    // Keyboard
208                         LOG("Desktop->Keyboard");
209                         break;
210                 }
211                 break;
212         case 0x0007:    // Keyboard / Keypad
213                 LOG("Keyboard");
214                 break;
215         }
216         return NULL;
217 }
218
219 void HID_int_ParseReport(tUSBInterface *Dev, Uint8 *Data, size_t Length, tHID_ReportCallbacks *StartCBs)
220 {
221         const int       max_cb_level = 8;
222          int    cb_level = 0;
223         tHID_ReportCallbacks    *cur_cbs;
224         tHID_ReportCallbacks    *cb_stack[max_cb_level];
225         tHID_ReportGlobalState  global_state;
226         tHID_ReportLocalState   local_state;
227         
228         ENTER("pData iLength pStartCBs", Data, Length, StartCBs);
229
230         // Initialise callback stack
231         cb_stack[0] = StartCBs;
232         cur_cbs = StartCBs;
233
234         // Clear state
235         memset(&global_state, 0, sizeof(global_state));
236         memset(&local_state, 0, sizeof(local_state));
237
238         // Iterate though the report data
239         for( int ofs = 0; ofs < Length; )
240         {
241                 Uint8   byte;
242                 Uint32  val;
243                 
244                 // --- Get value and length ---
245                 byte = Data[ofs];
246                 switch(byte & 3)
247                 {
248                 case 0:
249                         val = 0;
250                         ofs += 1;
251                         break;
252                 case 1:
253                         if( ofs + 2 > Length ) { LEAVE('-'); return; }
254                         val = Data[ofs+1];
255                         ofs += 2;
256                         break;
257                 case 2:
258                         if( ofs + 3 > Length ) { LEAVE('-'); return; }
259                         val = Data[ofs + 1] | (Data[ofs + 1]<<8);
260                         ofs += 3;
261                         break;
262                 case 3:
263                         if( ofs + 5 > Length ) { LEAVE('-'); return; }
264                         val = Data[ofs + 1] | (Data[ofs + 2] << 8) | (Data[ofs + 3] << 16) | (Data[ofs + 4] << 24);
265                         ofs += 5;
266                         break;
267                 }
268         
269                 // --- Process the item ---
270                 LOG("Type = 0x%x, len = %i, val = 0x%x", byte & 0xFC, byte & 3, val);
271                 switch(byte & 0xFC)
272                 {
273                 // Main items
274                 // - Input
275                 case 0x80:
276                         LOG("Input 0x%x", val);
277                         if( cur_cbs && cur_cbs->Input )
278                                 cur_cbs->Input( Dev, &global_state, &local_state, val );
279                         goto _clear_local;
280                 // - Output
281                 case 0x90:
282                         LOG("Output 0x%x", val);
283                         if( cur_cbs && cur_cbs->Output )
284                                 cur_cbs->Output( Dev, &global_state, &local_state, val );
285                         goto _clear_local;
286                 // - Collection
287                 case 0xA0:
288                         LOG("Collection 0x%x", val);
289                         
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 );
293                         cb_level ++;
294                         if( cb_level < max_cb_level )
295                                 cb_stack[cb_level] = next_cbs;
296                         else
297                                 next_cbs = NULL;
298                         cur_cbs = next_cbs;
299                         goto _clear_local;
300                 // - Feature
301                 case 0xB0:
302                         LOG("Feature 0x%x", val);
303                         if( cur_cbs && cur_cbs->Feature )
304                                 cur_cbs->Feature( Dev, &global_state, &local_state, val );
305                         goto _clear_local;
306                 // - End collection
307                 case 0xC0:
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");
312                                 LEAVE('-');
313                                 return ;
314                         }
315                         cb_level --;
316                         if( cb_level < max_cb_level )
317                                 cur_cbs = cb_stack[cb_level];
318                         goto _clear_local;
319                 // -- Helper to clear the local state
320                 _clear_local:
321                         _FreeList( &local_state.Strings );
322                         _FreeList( &local_state.Usages );
323                         _FreeList( &local_state.Designators );
324                         memset( &local_state, 0, sizeof(local_state) );
325                         break;
326                 
327                 // Global Items
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
340                 
341                 // Local Items
342                 // - Usages
343                 case 0x08:      // Single
344                         if( (byte & 3) != 3 )   val |= global_state.UsagePage;
345                         LOG("Usage %x", val);
346                         _AddItem(&local_state.Usages, val);
347                         break;
348                 case 0x18:      // Range start
349                         if( (byte & 3) != 3 )   val |= global_state.UsagePage;
350                         LOG("Usage start %x", val);
351                         local_state.UsageMin = val;
352                         break;
353                 case 0x28:      // Range end (apply)
354                         if( (byte & 3) != 3 )   val |= global_state.UsagePage;
355                         LOG("Usage end %x (from %x)", val, local_state.UsageMin);
356                         _AddItems(&local_state.Usages, local_state.UsageMin, val);
357                         break;
358                 // - Designators (Index into Physical report)
359                 case 0x38:
360                         _AddItem(&local_state.Designators, val);
361                         break;
362                 case 0x48:
363                         local_state.DesignatorMin = val;
364                         break;
365                 case 0x58:
366                         _AddItems(&local_state.Designators, local_state.DesignatorMin, val);
367                         break;
368                 // - Reserved/hole
369                 case 0x68:
370                         break;
371                 // - Strings
372                 case 0x78:
373                         _AddItem(&local_state.Strings, val);
374                         break;
375                 case 0x88:
376                         local_state.StringMin = val;
377                         break;
378                 case 0x98:
379                         _AddItems(&local_state.Strings, local_state.StringMin, val);
380                         break;
381                 // - Delimiter
382                 case 0xA8:
383                         break;
384                 
385                 // Long Item
386                 case 0xFC:
387                         LOG("Long Item");
388                         break;
389                 
390                 // Reserved
391                 default:
392                         LOG("0x%x RESVD", byte & 0xFC);
393                         break;
394                 }
395         }
396
397         LEAVE('-');
398 }
399
400 // --------------------------------------------------------------------
401 // List helpers
402 // --------------------------------------------------------------------
403 static void _AddItem(struct sHID_IntList *List, Uint32 Value)
404 {
405         if( List->Space == List->nItems )
406         {
407                 List->Space += 10;
408                 List->Items = realloc( List->Items, List->Space * sizeof(List->Items[0]) );
409         }
410         
411 //      LOG("Added %x to %p", Value, List);
412         List->Items[ List->nItems ] = Value;
413         List->nItems ++;
414 }
415
416 static void _AddItems(struct sHID_IntList *List, Uint32 First, Uint32 Last)
417 {
418         while( First <= Last )
419         {
420                 _AddItem(List, First);
421                 First ++;
422         }
423 }
424
425 static void _FreeList(struct sHID_IntList *List)
426 {
427         if( List->Items )
428                 free(List->Items);
429 }
430

UCC git Repository :: git.ucc.asn.au