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

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