Merge branch 'master' of ted.mutabah.net:acess2
[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   0
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 tHID_ReportCallbacks     gHID_Kb_ReportCBs;
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", "Keyboard", "Mouse", 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         return 0;
64 }
65
66 /**
67  * \brief Callback for when there's new data from the device
68  * 
69  * Calls the subdriver callback (stored at a fixed offset in the device data structure)
70  */
71 void HID_InterruptCallback(tUSBInterface *Dev, int EndPt, int Length, void *Data)
72 {
73         tHID_Device     *info;
74         
75         info = USB_GetDeviceDataPtr(Dev);
76         if(!info) {
77                 Log_Error("USB HID", "Device %p doesn't have a data pointer.", Dev);
78                 return ;
79         }
80
81         LOG("Data for %p", info->DataAvail);    
82         info->DataAvail(Dev, EndPt, Length, Data);
83 }
84
85 /**
86  * \brief Handle a device connection
87  */
88 void HID_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen)
89 {
90         struct sDescriptor_HID  *hid_desc;
91         size_t  ofs = 0;
92         size_t  report_len = 0;
93         
94         ENTER("pDev pDescriptors iDescriptorsLen", Dev, Descriptors, DescriptorsLen);
95
96         // --- Locate HID descriptor ---
97         hid_desc = NULL;
98         while(ofs + 2 <= DescriptorsLen)
99         {
100                 hid_desc = (void*)( (char*)Descriptors + ofs );
101                 ofs += hid_desc->Length;
102                 // Sanity check length
103                 if( ofs > DescriptorsLen ) {
104                         hid_desc = NULL;
105                         break ;
106                 }
107                 // Check for correct type
108                 if( hid_desc->Type == 0x21 )
109                         break ;
110                 // On to the next one then
111         }
112         if( hid_desc == NULL ) {
113                 Log_Warning("USB_HID", "Device doesn't have a HID descritor");
114                 LEAVE('-');
115                 return ;
116         }
117         // - Sanity check length
118         if( hid_desc->Length < sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0]) )
119         {
120                 // Too small!
121                 Log_Warning("USB_HID", "HID Descriptor undersized (%i < %i)",
122                         hid_desc->Length,
123                         sizeof(*hid_desc) + hid_desc->NumDescriptors * sizeof(hid_desc->Descriptors[0]));
124                 LEAVE('-');
125                 return ;
126         }
127
128         // --- Dump descriptor header ---
129         LOG("hid_desc = {");
130         LOG("  .Length  = %i", hid_desc->Length);
131         LOG("  .Type    = 0x%x", hid_desc->Type);
132         LOG("  .Version = 0x%x", hid_desc->Version);
133         LOG("  .NumDescriptors = %i", hid_desc->NumDescriptors);
134         LOG("}");
135
136         // --- Locate report descriptor length ---
137         for( int i = 0; i < hid_desc->NumDescriptors; i ++ )
138         {
139                 if( hid_desc->Descriptors[i].DescType == 0x22 ) {
140                         report_len = LittleEndian16( hid_desc->Descriptors[i].DescLen );
141                         break ;
142                 }
143         }
144         if( report_len == 0 ) {
145                 Log_Warning("USB_HID", "No report descriptor");
146                 LEAVE('-');
147                 return ;
148         }
149         
150         // --- Read and parse report descriptor ---
151         // NOTE: Also does sub-driver selection and initialisation
152         Uint8   *report_data = alloca(report_len);
153         USB_ReadDescriptor(Dev, 0x1022, 0, report_len, report_data);
154         HID_int_ParseReport(Dev, report_data, report_len, &gHID_RootCallbacks);
155         
156         // --- Start polling ---
157         // Only if the device was initialised
158         if( USB_GetDeviceDataPtr(Dev) )
159         {
160                 // Poll Endpoint .+1 (interupt)
161                 USB_StartPollingEndpoint(Dev, 1);
162         }
163         else
164         {
165                 Log_Log("USB_HID", "Device not intitialised");
166         }
167         
168         
169         LEAVE('-');
170 }
171
172 /**
173  * \brief Handle a Collection item in a report
174  * 
175  * When an application collection is found in the root of the tree, the approriate
176  * sub-handler is invoked (mouse, keyboard, gamepad, ...)
177  */
178 tHID_ReportCallbacks *HID_RootCollection(
179         tUSBInterface *Dev,
180         tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value
181         )
182 {
183         Uint32  usage;
184         
185         // Check for "Application" collection
186         if( Value != 1 )        return NULL;
187         
188         // Check usages
189         if( Local->Usages.nItems == 0 ) return NULL;
190         usage = Local->Usages.Items[0];
191         switch(usage >> 16)
192         {
193         case 0x0001:    // General Desktop
194                 switch(usage & 0xFFFF)
195                 {
196                 case 0x0001:    // Pointer
197                         LOG("Desktop->Pointer");
198                         break;
199                 case 0x0002:    // Mouse
200                         LOG("Desktop->Mouse");
201                         return &gHID_Mouse_ReportCBs;
202                 case 0x0004:    // Joystick
203                 case 0x0005:    // Game Pad
204                         LOG("Desktop->Gamepad");
205                         break;
206                 case 0x0006:    // Keyboard
207                         LOG("Desktop->Keyboard");
208                         return &gHID_Kb_ReportCBs;
209                 }
210                 break;
211         case 0x0007:    // Keyboard / Keypad
212                 LOG("Keyboard");
213                 return &gHID_Kb_ReportCBs;
214         }
215         return NULL;
216 }
217
218 void HID_int_ParseReport(tUSBInterface *Dev, Uint8 *Data, size_t Length, tHID_ReportCallbacks *StartCBs)
219 {
220         const int       max_cb_level = 8;
221          int    cb_level = 0;
222         tHID_ReportCallbacks    *cur_cbs;
223         tHID_ReportCallbacks    *cb_stack[max_cb_level];
224         tHID_ReportGlobalState  global_state;
225         tHID_ReportLocalState   local_state;
226         
227         ENTER("pData iLength pStartCBs", Data, Length, StartCBs);
228
229         // Initialise callback stack
230         cb_stack[0] = StartCBs;
231         cur_cbs = StartCBs;
232
233         // Clear state
234         memset(&global_state, 0, sizeof(global_state));
235         memset(&local_state, 0, sizeof(local_state));
236
237         // Iterate though the report data
238         for( int ofs = 0; ofs < Length; )
239         {
240                 Uint8   byte;
241                 Uint32  val;
242                 
243                 // --- Get value and length ---
244                 byte = Data[ofs];
245                 switch(byte & 3)
246                 {
247                 case 0:
248                         val = 0;
249                         ofs += 1;
250                         break;
251                 case 1:
252                         if( ofs + 2 > Length ) { LEAVE('-'); return; }
253                         val = Data[ofs+1];
254                         ofs += 2;
255                         break;
256                 case 2:
257                         if( ofs + 3 > Length ) { LEAVE('-'); return; }
258                         val = Data[ofs + 1] | (Data[ofs + 1]<<8);
259                         ofs += 3;
260                         break;
261                 case 3:
262                         if( ofs + 5 > Length ) { LEAVE('-'); return; }
263                         val = Data[ofs + 1] | (Data[ofs + 2] << 8) | (Data[ofs + 3] << 16) | (Data[ofs + 4] << 24);
264                         ofs += 5;
265                         break;
266                 }
267         
268                 // --- Process the item ---
269                 LOG("Type = 0x%x, len = %i, val = 0x%x", byte & 0xFC, byte & 3, val);
270                 switch(byte & 0xFC)
271                 {
272                 // Main items
273                 // - Input
274                 case 0x80:
275                         LOG("Input 0x%x", val);
276                         if( cur_cbs && cur_cbs->Input )
277                                 cur_cbs->Input( Dev, &global_state, &local_state, val );
278                         goto _clear_local;
279                 // - Output
280                 case 0x90:
281                         LOG("Output 0x%x", val);
282                         if( cur_cbs && cur_cbs->Output )
283                                 cur_cbs->Output( Dev, &global_state, &local_state, val );
284                         goto _clear_local;
285                 // - Collection
286                 case 0xA0:
287                         LOG("Collection 0x%x", val);
288                         
289                         tHID_ReportCallbacks    *next_cbs = NULL;
290                         if( cur_cbs && cur_cbs->Collection )
291                                 next_cbs = cur_cbs->Collection( Dev, &global_state, &local_state, val );
292                         cb_level ++;
293                         if( cb_level < max_cb_level )
294                                 cb_stack[cb_level] = next_cbs;
295                         else
296                                 next_cbs = NULL;
297                         cur_cbs = next_cbs;
298                         goto _clear_local;
299                 // - Feature
300                 case 0xB0:
301                         LOG("Feature 0x%x", val);
302                         if( cur_cbs && cur_cbs->Feature )
303                                 cur_cbs->Feature( Dev, &global_state, &local_state, val );
304                         goto _clear_local;
305                 // - End collection
306                 case 0xC0:
307                         if( cur_cbs && cur_cbs->EndCollection )
308                                 cur_cbs->EndCollection(Dev);
309                         if( cb_level == 0 ) {
310                                 Log_Warning("USB_HID", "Inbalance in HID collections");
311                                 LEAVE('-');
312                                 return ;
313                         }
314                         cb_level --;
315                         if( cb_level < max_cb_level )
316                                 cur_cbs = cb_stack[cb_level];
317                         goto _clear_local;
318                 // -- Helper to clear the local state
319                 _clear_local:
320                         _FreeList( &local_state.Strings );
321                         _FreeList( &local_state.Usages );
322                         _FreeList( &local_state.Designators );
323                         memset( &local_state, 0, sizeof(local_state) );
324                         break;
325                 
326                 // Global Items
327                 case 0x04:      global_state.UsagePage = val<<16;       break;  // - Usage Page
328                 case 0x14:      global_state.LogMin = val;      break;  // - Logical Min
329                 case 0x24:      global_state.LogMax = val;      break;  // - Logical Max
330                 case 0x34:      global_state.PhysMin = val;     break;  // - Physical Min
331                 case 0x44:      global_state.PhysMax = val;     break;  // - Physical Max
332                 case 0x54:      global_state.UnitExp = val;     break;  // - Unit Exp
333                 case 0x64:      global_state.Unit = val;        break;  // - Unit
334                 case 0x74:      global_state.ReportSize = val;  break;  // - Report Size
335                 case 0x84:      global_state.ReportID = val;    break;  // - Report ID (TODO: Flag when used)
336                 case 0x94:      global_state.ReportCount = val; break;  // - Report Count
337                 case 0xA4:      LOG("TODO: Implement Global Push");     break;  // - Push
338                 case 0xB4:      LOG("TODO: Implement Global Pop");      break;  // - Pop
339                 
340                 // Local Items
341                 // - Usages
342                 case 0x08:      // Single
343                         if( (byte & 3) != 3 )   val |= global_state.UsagePage;
344                         LOG("Usage %x", val);
345                         _AddItem(&local_state.Usages, val);
346                         break;
347                 case 0x18:      // Range start
348                         if( (byte & 3) != 3 )   val |= global_state.UsagePage;
349                         LOG("Usage start %x", val);
350                         local_state.UsageMin = val;
351                         break;
352                 case 0x28:      // Range end (apply)
353                         if( (byte & 3) != 3 )   val |= global_state.UsagePage;
354                         LOG("Usage end %x (from %x)", val, local_state.UsageMin);
355                         _AddItems(&local_state.Usages, local_state.UsageMin, val);
356                         break;
357                 // - Designators (Index into Physical report)
358                 case 0x38:
359                         _AddItem(&local_state.Designators, val);
360                         break;
361                 case 0x48:
362                         local_state.DesignatorMin = val;
363                         break;
364                 case 0x58:
365                         _AddItems(&local_state.Designators, local_state.DesignatorMin, val);
366                         break;
367                 // - Reserved/hole
368                 case 0x68:
369                         break;
370                 // - Strings
371                 case 0x78:
372                         _AddItem(&local_state.Strings, val);
373                         break;
374                 case 0x88:
375                         local_state.StringMin = val;
376                         break;
377                 case 0x98:
378                         _AddItems(&local_state.Strings, local_state.StringMin, val);
379                         break;
380                 // - Delimiter
381                 case 0xA8:
382                         break;
383                 
384                 // Long Item
385                 case 0xFC:
386                         LOG("Long Item");
387                         break;
388                 
389                 // Reserved
390                 default:
391                         LOG("0x%x RESVD", byte & 0xFC);
392                         break;
393                 }
394         }
395
396         LEAVE('-');
397 }
398
399 // --------------------------------------------------------------------
400 // List helpers
401 // --------------------------------------------------------------------
402 static void _AddItem(struct sHID_IntList *List, Uint32 Value)
403 {
404         if( List->Space == List->nItems )
405         {
406                 List->Space += 10;
407                 List->Items = realloc( List->Items, List->Space * sizeof(List->Items[0]) );
408         }
409         
410 //      LOG("Added %x to %p", Value, List);
411         List->Items[ List->nItems ] = Value;
412         List->nItems ++;
413 }
414
415 static void _AddItems(struct sHID_IntList *List, Uint32 First, Uint32 Last)
416 {
417         while( First <= Last )
418         {
419                 _AddItem(List, First);
420                 First ++;
421         }
422 }
423
424 static void _FreeList(struct sHID_IntList *List)
425 {
426         if( List->Items )
427                 free(List->Items);
428 }
429

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