Modules/USB - Made HIDMouse a folder
[tpg/acess2.git] / KernelLand / Modules / USB / HID / 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         return Length;
140 }
141
142 static const char *csaDevIOCtls[] = {DRV_IOCTLNAMES, DRV_JOY_IOCTLNAMES, NULL};
143 int HID_Mouse_Dev_IOCtl(tVFS_Node *Node, int ID, void *Data)
144 {
145         switch(ID)
146         {
147         BASE_IOCTLS(DRV_TYPE_JOYSTICK, "USBMouse", 0x050, csaDevIOCtls);
148         }
149         return -1;
150 }
151 void HID_Mouse_Dev_Reference(tVFS_Node *Node)
152 {
153         Node->ReferenceCount ++;
154 }
155 void HID_Mouse_Dev_Close(tVFS_Node *Node)
156 {
157         Node->ReferenceCount --;
158 }
159
160 // ----------------------------------------------------------------------------
161 // Data input / Update
162 // ----------------------------------------------------------------------------
163 /**
164  * \brief Read a set amounts of bits from a stream
165  * \param Data  Base of data
166  * \param Offset        Bit offset
167  * \param Length        Number of bits to read
168  * \return Sign-extended value
169  */
170 Sint32 _ReadBits(void *Data, int Offset, int Length)
171 {
172          int    dest_ofs = 0;
173         Uint32  rv = 0;
174         Uint8   *bytes = (Uint8*)Data + Offset / 8;
175
176         // Sanity please        
177         if( Length > 32 )       return 0;
178
179         // Leading byte
180         if( Offset & 7 )
181         {
182                 if( Length < 8 - (Offset & 7) )
183                 {
184                         rv = (*bytes >> Offset) & ((1 << Length)-1);
185                         goto _ext;
186                 }
187                 
188                 rv = (*bytes >> Offset);
189                 
190                 dest_ofs = Offset & 7;
191                 Length -= Offset & 7;
192                 bytes ++;
193         }
194
195         // Body bytes
196         while( Length >= 8 )
197         {
198                 rv |= *bytes << dest_ofs;
199                 dest_ofs += 8;
200                 Length -= 8;
201                 bytes ++;
202         }
203         
204         if( Length )
205         {
206                 rv |= (*bytes & ((1 << Length)-1)) << dest_ofs;
207                 
208         }
209         
210         // Do sign extension
211 _ext:
212         if( rv & (1 << (Length-1)) )
213                 rv |= 0xFFFFFFFF << Length;
214         return rv;
215 }
216
217 /**
218  * \brief Handle an update from the device
219  */
220 void HID_Mouse_DataAvail(tUSBInterface *Dev, int EndPt, int Length, void *Data)
221 {
222         tHID_Mouse      *info;
223          int    ofs;
224         
225         info = USB_GetDeviceDataPtr(Dev);
226         if( !info )     return ;
227
228         Log_Debug("USBMouse", "info = %p", info);       
229         
230         ofs = 0;
231         for( int i = 0; i < info->nMappings; i ++ )
232         {
233                 Sint32  value;
234                 Uint8   dest = info->Mappings[i].Dest;
235
236                 if( ofs + info->Mappings[i].BitSize > Length * 8 )
237                         return ;
238
239                 value = _ReadBits(Data, ofs, info->Mappings[i].BitSize);
240                 ofs += info->Mappings[i].BitSize;
241                 
242                 if( dest == 0xFF )      continue ;
243                 
244                 if( dest & 0x80 )
245                 {
246                         // Axis
247                         info->Axies[ dest & 0x7F ].CurValue = value;
248                 }
249                 else
250                 {
251                         // Button
252                         info->Buttons[ dest & 0x7F ] = (value == 0) ? 0 : 0xFF;
253                 }
254         }
255         
256         // Update axis positions
257         for( int i = 0; i < MAX_AXIES; i ++ )
258         {
259                  int    newpos;
260                 
261                 // TODO: Scaling
262                 newpos = info->Axies[i].CursorPos + info->Axies[i].CurValue;
263                 
264                 if(newpos < 0)  newpos = 0;
265                 if(newpos > info->AxisLimits[i])        newpos = info->AxisLimits[i];
266                 
267                 info->Axies[i].CursorPos = newpos;
268         }
269         Log_Debug("USBMouse", "New Pos (%i,%i,%i)",
270                 info->Axies[0].CursorPos, info->Axies[1].CursorPos, info->Axies[2].CursorPos
271                 );
272 }
273
274 // ----------------------------------------------------------------------------
275 // Device initialisation
276 // ----------------------------------------------------------------------------
277 /**
278  * \brief Handle the opening of a collection
279  */
280 tHID_ReportCallbacks *HID_Mouse_Report_Collection(
281         tUSBInterface *Dev,
282         tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local,
283         Uint32 Value
284         )
285 {
286         tHID_Mouse      *info;
287
288         info = USB_GetDeviceDataPtr(Dev);
289         if( !info )
290         {
291                 // New device!
292                 info = calloc( sizeof(tHID_Mouse), 1 );
293                 info->DataAvail = HID_Mouse_DataAvail;
294                 info->Node.ImplPtr = info;
295                 info->Node.Type = &gHID_Mouse_DevNodeType;
296                 
297                 info->FileHeader = (void*)info->FileData;
298                 info->Axies = (void*)(info->FileHeader + 1);
299                 info->Buttons = (void*)(info->Axies + MAX_AXIES);
300         
301                 LOG("Initialised new mouse at %p", info);
302                 
303                 USB_SetDeviceDataPtr(Dev, info);
304         }
305         else
306         {
307                 info->CollectionDepth ++;
308         }
309         
310         return &gHID_Mouse_ReportCBs;
311 }
312
313 /**
314  * \brief Handle the end of a collection
315  */
316 void HID_Mouse_Report_EndCollection(tUSBInterface *Dev)
317 {
318         tHID_Mouse      *info;
319
320         info = USB_GetDeviceDataPtr(Dev);       
321         if(!info) {
322                 Log_Error("HID", "HID is not initialised when _AddInput is called");
323                 return ;
324         }
325
326         if( info->CollectionDepth == 0 )
327         {
328                 // Perform final initialisation steps
329                 Mutex_Acquire(&glHID_MouseListLock);
330                 gpHID_LastMouse->Next = info;
331                 gpHID_LastMouse = info;
332                 gHID_Mouse_DevFS.RootNode.Size ++;
333                 Mutex_Release(&glHID_MouseListLock);
334         }
335         else
336         {
337                 info->CollectionDepth --;
338         }
339 }
340
341 /**
342  * \brief Add a new input mapping
343  */
344 void HID_int_AddInput(tUSBInterface *Dev, Uint32 Usage, Uint8 Size, Uint32 Min, Uint32 Max)
345 {
346         Uint8   tag;
347         tHID_Mouse      *info;
348
349         info = USB_GetDeviceDataPtr(Dev);
350         if(!info) {
351                 Log_Error("HID", "HID is not initialised when _AddInput is called");
352                 return ;
353         }
354
355         // --- Get the destination for the field ---
356         switch(Usage)
357         {
358         case 0x00010030:        tag = 0x80;     break;  // Generic Desktop - X
359         case 0x00010031:        tag = 0x81;     break;  // Generic Desktop - Y
360         case 0x00010038:        tag = 0x82;     break;  // Generic Desktop - Wheel
361         case 0x00090001:        tag = 0x00;     break;  // Button 1
362         case 0x00090002:        tag = 0x01;     break;  // Button 2
363         case 0x00090003:        tag = 0x02;     break;  // Button 3
364         case 0x00090004:        tag = 0x03;     break;  // Button 4
365         case 0x00090005:        tag = 0x04;     break;  // Button 5
366         default:        tag = 0xFF;     break;
367         }
368         
369         // --- Add to list of mappings ---
370         info->nMappings ++;
371         info->Mappings = realloc(info->Mappings, info->nMappings * sizeof(info->Mappings[0]));
372         // TODO: NULL check
373         info->Mappings[ info->nMappings - 1].Dest = tag;
374         info->Mappings[ info->nMappings - 1].BitSize = Size;
375         
376         // --- Update Min/Max for Axies ---
377         // TODO: DPI too
378         if( tag != 0xFF && (tag & 0x80) )
379         {
380                 info->Axies[ tag & 0x7F ].MinValue = Min;
381                 info->Axies[ tag & 0x7F ].MaxValue = Max;
382         }
383 }
384
385 /**
386  * \brief Handle an input item in a report
387  */
388 void HID_Mouse_Report_Input(
389         tUSBInterface *Dev,
390         tHID_ReportGlobalState *Global, tHID_ReportLocalState *Local,
391         Uint32 Value
392         )
393 {
394         Uint32  usage = 0;
395         for( int i = 0; i < Global->ReportCount; i ++ )
396         {
397                 // - Update usage
398                 if( i < Local->Usages.nItems )
399                         usage = Local->Usages.Items[i];
400                 // - Add to list
401                 HID_int_AddInput(Dev, usage, Global->ReportSize, Global->LogMin, Global->LogMax);
402         }
403 }
404

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