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

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