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

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