07f41c4987a198a9c9fdeb001518319b9ab66b1c
[tpg/acess2.git] / mouse.c
1 /*
2  * Acess2 USB Stack HID Driver
3  * - By John Hodge (thePowersGang)
4  *
5  * mouse.c
6  * - USB Mouse driver
7  */
8 #define DEBUG   1
9 #include <acess.h>
10 #include "hid_reports.h"
11 #include <fs_devfs.h>
12 #include <api_drv_joystick.h>
13
14 // === CONSTANTS ===
15 #define MAX_AXIES       3       // X, Y, Scroll
16 #define MAX_BUTTONS     5       // Left, Right, Middle, ...
17 #define FILE_SIZE       (sizeof(tJoystick_FileHeader) + MAX_AXIES*sizeof(tJoystick_Axis) + MAX_BUTTONS)
18
19 // === TYPES ===
20 typedef struct sHID_Mouse       tHID_Mouse;
21
22 struct sHID_Mouse
23 {
24         tHID_Mouse      *Next;
25         tUSB_DataCallback       DataAvail;
26
27         // VFS Node
28         tVFS_Node       Node;
29
30         // Joystick Spec data   
31         Uint8   FileData[ FILE_SIZE ];
32         tJoystick_FileHeader    *FileHeader;
33         tJoystick_Axis  *Axies;
34         Uint8   *Buttons;
35
36         // Limits for axis positions
37         Uint16  AxisLimits[MAX_AXIES];
38         
39         // - Report parsing
40          int    nMappings;
41         struct {
42                 Uint8   Dest;   // 0x00-0x7F = Buttons, 0x80-0xFE = Axies, 0xFF = Ignore
43                 Uint8   BitSize;
44         } *Mappings;
45         
46         // - Initialisation only data
47          int    CollectionDepth;
48 };
49
50 // === PROTOTYES ===
51 char    *HID_Mouse_Root_ReadDir(tVFS_Node *Node, int Pos);
52 tVFS_Node       *HID_Mouse_Root_FindDir(tVFS_Node *Node, const char *Name);
53 size_t  HID_Mouse_Dev_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer);
54  int    HID_Mouse_Dev_IOCtl(tVFS_Node *Node, int ID, void *Data);
55 void    HID_Mouse_Dev_Reference(tVFS_Node *Node);
56 void    HID_Mouse_Dev_Close(tVFS_Node *Node);
57
58 void    HID_Mouse_DataAvail(tUSBInterface *Dev, int EndPt, int Length, void *Data);
59
60 tHID_ReportCallbacks    *HID_Mouse_Report_Collection(tUSBInterface *Dev, tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value);
61 void    HID_Mouse_Report_EndCollection(tUSBInterface *Dev);
62 void    HID_Mouse_Report_Input(tUSBInterface *Dev, tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local, Uint32 Value);
63
64 // === GLOBALS ===
65 tVFS_NodeType   gHID_Mouse_RootNodeType = {
66         .TypeName = "HID Mouse Root",
67         .ReadDir = HID_Mouse_Root_ReadDir,
68         .FindDir = HID_Mouse_Root_FindDir
69 };
70 tVFS_NodeType   gHID_Mouse_DevNodeType = {
71         .TypeName = "HID Mouse Dev",
72         .Read = HID_Mouse_Dev_Read,
73         .IOCtl = HID_Mouse_Dev_IOCtl,
74         .Reference = HID_Mouse_Dev_Reference,
75         .Close = HID_Mouse_Dev_Close,
76 };
77 tDevFS_Driver   gHID_Mouse_DevFS = {
78         .Name = "USBMouse",
79         .RootNode = {
80                 .Type = &gHID_Mouse_RootNodeType,
81                 .Flags = VFS_FFLAG_DIRECTORY
82         }
83 };
84 tHID_ReportCallbacks    gHID_Mouse_ReportCBs = {
85         .Collection = HID_Mouse_Report_Collection,
86         .EndCollection = HID_Mouse_Report_EndCollection,
87         .Input = HID_Mouse_Report_Input,
88 };
89 tMutex  glHID_MouseListLock;
90 tHID_Mouse      *gpHID_FirstMouse;
91 tHID_Mouse      *gpHID_LastMouse = (tHID_Mouse*)&gpHID_FirstMouse;
92
93 // === CODE ===
94 // ----------------------------------------------------------------------------
95 // VFS Interface
96 // ----------------------------------------------------------------------------
97 char *HID_Mouse_Root_ReadDir(tVFS_Node *Node, int Pos)
98 {
99         char    data[3];
100         if(Pos < 0 || Pos >= Node->Size)        return NULL;
101         
102         snprintf(data, 3, "%i", Pos);
103         return strdup(data);
104 }
105
106 tVFS_Node *HID_Mouse_Root_FindDir(tVFS_Node *Node, const char *Name)
107 {
108          int    ID;
109          int    ofs;
110         tHID_Mouse      *mouse;
111         
112         if( Name[0] == '\0' )
113                 return NULL;
114         
115         ofs = ParseInt(Name, &ID);
116         if( ofs == 0 || Name[ofs] != '\0' )
117                 return NULL;
118         
119         // Scan list, locate item
120         Mutex_Acquire(&glHID_MouseListLock);
121         for( mouse = gpHID_FirstMouse; mouse && ID --; mouse = mouse->Next ) ;
122         mouse->Node.ReferenceCount ++;  
123         Mutex_Release(&glHID_MouseListLock);
124
125         return &mouse->Node;
126 }
127
128 size_t HID_Mouse_Dev_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer)
129 {
130         tHID_Mouse      *info = Node->ImplPtr;
131         
132         if( Offset > FILE_SIZE )        return 0;
133         
134         if( Length > FILE_SIZE )        Length = FILE_SIZE;
135         if( Offset + Length > FILE_SIZE )       Length = FILE_SIZE - Offset;
136
137         memcpy( Buffer, info->FileData + Offset, Length );
138
139         VFS_MarkAvaliable( &info->Node, 0 );
140         
141         return Length;
142 }
143
144 static const char *csaDevIOCtls[] = {DRV_IOCTLNAMES, DRV_JOY_IOCTLNAMES, NULL};
145 int HID_Mouse_Dev_IOCtl(tVFS_Node *Node, int ID, void *Data)
146 {
147         tJoystick_NumValue      *numval = Data;
148         tHID_Mouse      *info = Node->ImplPtr;
149         switch(ID)
150         {
151         BASE_IOCTLS(DRV_TYPE_JOYSTICK, "USBMouse", 0x050, csaDevIOCtls);
152         
153         case JOY_IOCTL_GETSETAXISLIMIT:
154                 if( !numval || !CheckMem(numval, sizeof(*numval)) )
155                         return -1;
156                 if(numval->Num < 0 || numval->Num >= MAX_AXIES)
157                         return 0;
158                 if(numval->Value != -1)
159                         info->AxisLimits[numval->Num] = numval->Value;
160                 return info->AxisLimits[numval->Num];
161
162         case JOY_IOCTL_GETSETAXISPOSITION:
163                 if( !numval || !CheckMem(numval, sizeof(*numval)) )
164                         return -1;
165                 if(numval->Num < 0 || numval->Num >= MAX_AXIES)
166                         return 0;
167                 if(numval->Value != -1)
168                         info->Axies[numval->Num].CursorPos = numval->Value;
169                 return info->Axies[numval->Num].CursorPos;
170         }
171         return -1;
172 }
173 void HID_Mouse_Dev_Reference(tVFS_Node *Node)
174 {
175         Node->ReferenceCount ++;
176 }
177 void HID_Mouse_Dev_Close(tVFS_Node *Node)
178 {
179         Node->ReferenceCount --;
180 }
181
182 // ----------------------------------------------------------------------------
183 // Data input / Update
184 // ----------------------------------------------------------------------------
185 /**
186  * \brief Read a set amounts of bits from a stream
187  * \param Data  Base of data
188  * \param Offset        Bit offset
189  * \param Length        Number of bits to read
190  * \return Sign-extended value
191  */
192 Sint32 _ReadBits(void *Data, int Offset, int Length)
193 {
194          int    dest_ofs = 0;
195          int    rem = Length;
196         Uint32  rv = 0;
197         Uint8   *bytes = (Uint8*)Data + Offset / 8;
198
199         // Sanity please        
200         if( Length > 32 )       return 0;
201
202         // Leading byte
203         if( Offset & 7 )
204         {
205                 if( Length < 8 - (Offset & 7) )
206                 {
207                         rv = (*bytes >> Offset) & ((1 << Length)-1);
208                         goto _ext;
209                 }
210                 
211                 rv = (*bytes >> Offset);
212                 
213                 dest_ofs = Offset & 7;
214                 rem = Length - (Offset & 7);
215                 bytes ++;
216         }
217
218         // Body bytes
219         while( rem >= 8 )
220         {
221                 rv |= *bytes << dest_ofs;
222                 dest_ofs += 8;
223                 rem -= 8;
224                 bytes ++;
225         }
226         
227         if( rem )
228         {
229                 rv |= (*bytes & ((1 << rem)-1)) << dest_ofs;
230         }
231         
232         // Do sign extension
233 _ext:
234         if( rv & (1 << (Length-1)) )
235                 rv |= 0xFFFFFFFF << Length;
236         return rv;
237 }
238
239 /**
240  * \brief Handle an update from the device
241  */
242 void HID_Mouse_DataAvail(tUSBInterface *Dev, int EndPt, int Length, void *Data)
243 {
244         tHID_Mouse      *info;
245          int    ofs;
246         
247         info = USB_GetDeviceDataPtr(Dev);
248         if( !info )     return ;
249
250         ofs = 0;
251         for( int i = 0; i < info->nMappings; i ++ )
252         {
253                 Sint32  value;
254                 Uint8   dest = info->Mappings[i].Dest;
255
256                 if( ofs + info->Mappings[i].BitSize > Length * 8 )
257                         return ;
258
259                 value = _ReadBits(Data, ofs, info->Mappings[i].BitSize);
260                 LOG("%i+%i: value = %i", ofs, info->Mappings[i].BitSize, value);
261                 ofs += info->Mappings[i].BitSize;
262                 
263                 if( dest == 0xFF )      continue ;
264                 
265                 if( dest & 0x80 )
266                 {
267                         // Axis
268                         info->Axies[ dest & 0x7F ].CurValue = value;
269                 }
270                 else
271                 {
272                         // Button
273                         info->Buttons[ dest & 0x7F ] = (value == 0) ? 0 : 0xFF;
274                 }
275         }
276         
277         // Update axis positions
278         for( int i = 0; i < MAX_AXIES; i ++ )
279         {
280                  int    newpos;
281                 
282                 // TODO: Scaling
283                 newpos = info->Axies[i].CursorPos + info->Axies[i].CurValue;
284                 
285                 if(newpos < 0)  newpos = 0;
286                 if(newpos > info->AxisLimits[i])        newpos = info->AxisLimits[i];
287                 
288                 info->Axies[i].CursorPos = newpos;
289         }
290 //      Log_Debug("USBMouse", "New Pos (%i,%i,%i)",
291 //              info->Axies[0].CursorPos, info->Axies[1].CursorPos, info->Axies[2].CursorPos
292 //              );
293         VFS_MarkAvaliable( &info->Node, 1 );
294 }
295
296 // ----------------------------------------------------------------------------
297 // Device initialisation
298 // ----------------------------------------------------------------------------
299 /**
300  * \brief Handle the opening of a collection
301  */
302 tHID_ReportCallbacks *HID_Mouse_Report_Collection(
303         tUSBInterface *Dev,
304         tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local,
305         Uint32 Value
306         )
307 {
308         tHID_Mouse      *info;
309
310         info = USB_GetDeviceDataPtr(Dev);
311         if( !info )
312         {
313                 // New device!
314                 info = calloc( sizeof(tHID_Mouse), 1 );
315                 info->DataAvail = HID_Mouse_DataAvail;
316                 info->Node.ImplPtr = info;
317                 info->Node.Type = &gHID_Mouse_DevNodeType;
318                 
319                 info->FileHeader = (void*)info->FileData;
320                 info->Axies = (void*)(info->FileHeader + 1);
321                 info->Buttons = (void*)(info->Axies + MAX_AXIES);
322                 info->FileHeader->NAxies = MAX_AXIES;
323                 info->FileHeader->NButtons = MAX_BUTTONS;
324
325                 for( int i = 0; i < MAX_AXIES; i ++ ) {
326                         info->Axies[i].MinValue = -10;
327                         info->Axies[i].MaxValue = 10;
328                 }
329                 
330         
331                 LOG("Initialised new mouse at %p", info);
332                 
333                 USB_SetDeviceDataPtr(Dev, info);
334         }
335         else
336         {
337                 info->CollectionDepth ++;
338         }
339         
340         return &gHID_Mouse_ReportCBs;
341 }
342
343 /**
344  * \brief Handle the end of a collection
345  */
346 void HID_Mouse_Report_EndCollection(tUSBInterface *Dev)
347 {
348         tHID_Mouse      *info;
349
350         info = USB_GetDeviceDataPtr(Dev);       
351         if(!info) {
352                 Log_Error("HID", "HID is not initialised when _AddInput is called");
353                 return ;
354         }
355
356         if( info->CollectionDepth == 0 )
357         {
358                 // Perform final initialisation steps
359                 Mutex_Acquire(&glHID_MouseListLock);
360                 gpHID_LastMouse->Next = info;
361                 gpHID_LastMouse = info;
362                 gHID_Mouse_DevFS.RootNode.Size ++;
363                 Mutex_Release(&glHID_MouseListLock);
364         }
365         else
366         {
367                 info->CollectionDepth --;
368         }
369 }
370
371 /**
372  * \brief Add a new input mapping
373  */
374 void HID_int_AddInput(tUSBInterface *Dev, Uint32 Usage, Uint8 Size, Uint32 Min, Uint32 Max)
375 {
376         Uint8   tag;
377         tHID_Mouse      *info;
378
379         info = USB_GetDeviceDataPtr(Dev);
380         if(!info) {
381                 Log_Error("HID", "HID is not initialised when _AddInput is called");
382                 return ;
383         }
384
385         // --- Get the destination for the field ---
386         switch(Usage)
387         {
388         case 0x00010030:        tag = 0x80;     break;  // Generic Desktop - X
389         case 0x00010031:        tag = 0x81;     break;  // Generic Desktop - Y
390         case 0x00010038:        tag = 0x82;     break;  // Generic Desktop - Wheel
391         case 0x00090001:        tag = 0x00;     break;  // Button 1
392         case 0x00090002:        tag = 0x01;     break;  // Button 2
393         case 0x00090003:        tag = 0x02;     break;  // Button 3
394         case 0x00090004:        tag = 0x03;     break;  // Button 4
395         case 0x00090005:        tag = 0x04;     break;  // Button 5
396         default:        tag = 0xFF;     break;
397         }
398         
399         // --- Add to list of mappings ---
400         info->nMappings ++;
401         info->Mappings = realloc(info->Mappings, info->nMappings * sizeof(info->Mappings[0]));
402         // TODO: NULL check
403         info->Mappings[ info->nMappings - 1].Dest = tag;
404         info->Mappings[ info->nMappings - 1].BitSize = Size;
405         
406         // --- Update Min/Max for Axies ---
407         // TODO: DPI too
408         if( tag != 0xFF && (tag & 0x80) )
409         {
410                 info->Axies[ tag & 0x7F ].MinValue = Min;
411                 info->Axies[ tag & 0x7F ].MaxValue = Max;
412         }
413 }
414
415 /**
416  * \brief Handle an input item in a report
417  */
418 void HID_Mouse_Report_Input(
419         tUSBInterface *Dev,
420         tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local,
421         Uint32 Value
422         )
423 {
424         Uint32  usage = 0;
425         for( int i = 0; i < Global->ReportCount; i ++ )
426         {
427                 // - Update usage
428                 if( i < Local->Usages.nItems )
429                         usage = Local->Usages.Items[i];
430                 // - Add to list
431                 HID_int_AddInput(Dev, usage, Global->ReportSize, Global->LogMin, Global->LogMax);
432         }
433 }
434

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