revert between 56095 -> 55830 in arch
[AROS.git] / workbench / devs / USB / classes / HID / hidclass.c
blob4a41d0a579eb94f4026864d4c97ea5d3b1dd0ad4
1 /*
2 Copyright (C) 2006 by Michal Schulz
3 $Id$
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this program; if not, write to the
17 Free Software Foundation, Inc.,
18 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #define DEBUG 0
23 #include <aros/debug.h>
24 #include <aros/libcall.h>
25 #include <aros/asmcall.h>
27 #include <dos/dos.h>
28 #include <dos/dosextens.h>
30 #include <usb/usb.h>
31 #include <usb/usb_core.h>
32 #include <usb/hid.h>
33 #include "hid.h"
35 #include <proto/oop.h>
36 #include <proto/dos.h>
38 static usb_interface_descriptor_t *find_idesc(usb_config_descriptor_t *cd, int ifaceidx, int altidx)
40 char *p = (char *)cd;
41 char *end = p + AROS_LE2WORD(cd->wTotalLength);
42 usb_interface_descriptor_t *d;
43 int curidx, lastidx, curaidx = 0;
45 for (curidx = lastidx = -1; p < end; ) {
46 d = (usb_interface_descriptor_t *)p;
48 if (d->bLength == 0) /* bad descriptor */
49 break;
50 p += d->bLength;
51 if (p <= end && d->bDescriptorType == UDESC_INTERFACE) {
52 if (d->bInterfaceNumber != lastidx) {
53 lastidx = d->bInterfaceNumber;
54 curidx++;
55 curaidx = 0;
56 } else
57 curaidx++;
58 if (ifaceidx == curidx && altidx == curaidx)
59 return (d);
62 return (NULL);
65 static AROS_INTH1(HidInterrupt, HidData *, hid)
67 AROS_INTFUNC_INIT
69 uint8_t reportid = 0;
71 /* Invalidate the cache. Report has been sent through DMA */
72 CacheClearE(hid->buffer, hid->buflen, CACRF_InvalidateD);
74 if (hid->nreport != 1)
76 reportid = hid->buffer[0];
78 /* And let the class handle it */
79 HIDD_USBHID_ParseReport(hid->o, reportid, &hid->buffer[1], hid->buflen);
81 else
82 HIDD_USBHID_ParseReport(hid->o, 0, hid->buffer, hid->buflen);
84 return 0;
86 AROS_INTFUNC_EXIT
89 BOOL METHOD(HID, Hidd_USBHID, GetReportDescriptor)
91 USBDevice_Request req;
92 intptr_t ifnr;
94 OOP_GetAttr(o, aHidd_USBDevice_InterfaceNumber, &ifnr);
96 req.bmRequestType = UT_READ_INTERFACE;
97 req.bRequest = UR_GET_DESCRIPTOR;
98 req.wValue = AROS_WORD2LE(UDESC_REPORT << 8 | 0);
99 req.wIndex = AROS_WORD2LE(ifnr);
100 req.wLength = AROS_WORD2LE(msg->length);
102 return HIDD_USBDevice_ControlMessage(o, NULL, &req, msg->buffer, msg->length);
105 BOOL METHOD(HID, Hidd_USBHID, SetIdle)
107 USBDevice_Request req;
108 intptr_t ifnr;
110 D(bug("[HID] HID::SetIdle(%d, %d)\n", msg->duration, msg->id));
112 OOP_GetAttr(o, aHidd_USBDevice_InterfaceNumber, &ifnr);
114 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
115 req.bRequest = UR_SET_IDLE;
116 req.wValue = AROS_WORD2LE(msg->duration << 8| msg->id);
117 req.wIndex = AROS_WORD2LE(ifnr);
118 req.wLength = AROS_WORD2LE(0);
120 return HIDD_USBDevice_ControlMessage(o, NULL, &req, NULL, 0);
123 BOOL METHOD(HID, Hidd_USBHID, SetReport)
125 USBDevice_Request req;
126 intptr_t ifnr;
129 uint8_t *b = msg->report;
130 int i;
132 bug("[HID] HID::SetReport(%d, %d, \"", msg->type, msg->id);
133 for (i=0; i < msg->length; i++)
134 bug("%02x", b[i]);
135 bug("\")\n");
138 OOP_GetAttr(o, aHidd_USBDevice_InterfaceNumber, &ifnr);
140 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
141 req.bRequest = UR_SET_REPORT;
142 req.wValue = AROS_WORD2LE(msg->type << 8| msg->id);
143 req.wIndex = AROS_WORD2LE(ifnr);
144 req.wLength = AROS_WORD2LE(msg->length);
146 return HIDD_USBDevice_ControlMessage(o, NULL, &req, msg->report, msg->length);
149 BOOL METHOD(HID, Hidd_USBHID, SetProtocol)
151 USBDevice_Request req;
152 intptr_t ifnr;
154 D(bug("[HID] HID::SetProtocol(%s)\n", msg->protocol ? "Report":"Boot"));
156 OOP_GetAttr(o, aHidd_USBDevice_InterfaceNumber, &ifnr);
158 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
159 req.bRequest = UR_SET_PROTOCOL;
160 req.wValue = AROS_WORD2LE(msg->protocol);
161 req.wIndex = AROS_WORD2LE(ifnr);
162 req.wLength = AROS_WORD2LE(0);
164 return HIDD_USBDevice_ControlMessage(o, NULL, &req, NULL, 0);
167 void METHOD(HID, Hidd_USBHID, ParseReport)
169 HidData *hid = OOP_INST_DATA(cl, o);
170 int i;
171 uint8_t *data = hid->buffer;
172 int len = hid->buflen;
174 if (hid->nreport != 1)
176 data++;
177 len--;
180 D(bug("[HID] Unknown report id %d: ", msg->id));
182 for (i=0; i < len; i++)
183 D(bug("%02x ", data[i]));
185 D(bug("\n"));
188 usb_hid_descriptor_t *METHOD(HID, Hidd_USBHID, GetHidDescriptor)
190 HidData *hid = OOP_INST_DATA(cl, o);
191 usb_hid_descriptor_t *hdesc = NULL, *hd;
192 char *p, *end;
193 intptr_t iface;
195 OOP_GetAttr(o, aHidd_USBDevice_Interface, &iface);
197 if (hid->cdesc)
199 usb_interface_descriptor_t *idesc = find_idesc(hid->cdesc, iface, 0);
200 D(bug("[HID] cdesc=%p, idesc=%p\n", hid->cdesc, idesc));
202 if (!idesc)
203 return NULL;
205 p = (char *)idesc + idesc->bLength;
206 end = (char *)hid->cdesc + AROS_LE2WORD(hid->cdesc->wTotalLength);
208 for (; p < end; p += hd->bLength)
210 hd = (usb_hid_descriptor_t *)p;
211 D(bug("[HID] p=%p bLength=%d bDescriptorType=%x\n", p, hd->bLength, hd->bDescriptorType));
213 if (p + hd->bLength <= end && hd->bDescriptorType == UDESC_HID)
215 hdesc = hd;
216 break;
218 if (hd->bDescriptorType == UDESC_INTERFACE)
219 break;
222 D(bug("[HID] hdesc=%p\n", hdesc));
223 return hdesc;
226 OOP_Object *METHOD(HID, Root, New)
228 D(bug("[HID] HID::New()\n"));
230 BASE(cl->UserData)->LibNode.lib_OpenCnt++;
232 o = (OOP_Object *)OOP_DoSuperMethod(cl, o, (OOP_Msg) msg);
233 if (o)
235 HidData *hid = OOP_INST_DATA(cl, o);
236 usb_config_descriptor_t cdesc;
237 intptr_t iface;
239 OOP_GetAttr(o, aHidd_USBDevice_Interface, &iface);
241 hid->sd = SD(cl);
242 hid->o = o;
244 D(bug("[HID::New()] HidData=%p Configuring the device...\n", hid));
246 /* Configure the HID device */
247 HIDD_USBDevice_Configure(o, 0);
249 D(bug("[HID::New()] Getting device and config descriptors...\n"));
250 HIDD_USBDevice_GetDeviceDescriptor(o, &hid->ddesc);
251 HIDD_USBDevice_GetConfigDescriptor(o, 0, &cdesc);
253 HIDD_USBHID_SetIdle(o, 0, 0);
255 if (AROS_LE2WORD(cdesc.wTotalLength))
256 hid->cdesc = AllocVecPooled(SD(cl)->MemPool, AROS_LE2WORD(cdesc.wTotalLength));
258 if (hid->cdesc)
260 uint16_t repid;
262 D(bug("[HID::New()] Getting config descriptor of size %d...\n",AROS_LE2WORD(cdesc.wTotalLength)));
264 HIDD_USBDevice_GetDescriptor(o, UDESC_CONFIG, 0, AROS_LE2WORD(cdesc.wTotalLength), hid->cdesc);
265 hid->hd = HIDD_USBHID_GetHidDescriptor(o);
266 if (hid->hd) {
267 D(bug("[HID::New()] Hid descriptor @ %p\n", hid->hd));
268 D(bug("[HID::New()] Number of Report descriptors: %d\n", hid->hd->bNumDescriptors));
269 hid->reportLength = AROS_LE2WORD(hid->hd->descrs[0].wDescriptorLength);
270 hid->report = AllocVecPooled(SD(cl)->MemPool, hid->reportLength);
272 D(bug("[HID::New()] Getting report descriptor 1 of size %d\n", hid->reportLength));
274 HIDD_USBHID_GetReportDescriptor(o, hid->reportLength, hid->report);
276 hid->nreport = hid_maxrepid(hid->report, hid->reportLength) + 1;
277 hid->buflen = 0;
279 for (repid = 0; repid < hid->nreport; repid++)
281 uint16_t repsize = hid_report_size(hid->report, hid->reportLength, hid_input, repid);
282 D(bug("[HID::New()] Report %d: size %d\n", repid, repsize));
283 if (repsize > hid->buflen)
284 hid->buflen = repsize;
287 /* If there is more than one report ID, make a space for report ID */
288 if (hid->nreport != 1)
289 hid->buflen++;
291 hid->buffer = AllocVecPooled(SD(cl)->MemPool, hid->buflen);
293 D(bug("[HID::New()] Length of input report is %d\n", hid->buflen));
295 usb_endpoint_descriptor_t *ep = HIDD_USBDevice_GetEndpoint(o, iface, 0);
297 D(bug("[HID::New()] Endpoint descriptor %p addr %02x\n", ep, ep->bEndpointAddress));
299 if (ep)
301 if ((ep->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT)
303 bug("[HID::New()] Wrong endpoint type\n");
304 // TODO: unconfigure, error, coercemethod
307 OOP_Object *drv = NULL;
308 OOP_GetAttr(o, aHidd_USBDevice_Bus, (IPTR *)&drv);
310 if (drv)
312 hid->interrupt.is_Data = hid;
313 hid->interrupt.is_Code = (VOID_FUNC)HidInterrupt;
314 hid->intr_pipe = HIDD_USBDevice_CreatePipe(o, PIPE_Interrupt, ep->bEndpointAddress, ep->bInterval, AROS_LE2WORD(ep->wMaxPacketSize), 0);
315 if (hid->intr_pipe) {
316 HIDD_USBDrv_AddInterrupt(drv, hid->intr_pipe, hid->buffer, hid->buflen, &hid->interrupt);
317 D(bug("[HID] HID::New() = %p\n", o));
318 return o;
322 if (hid->report)
323 FreeVecPooled(SD(cl)->MemPool, hid->report);
324 if (hid->buffer)
325 FreeVecPooled(SD(cl)->MemPool, hid->buffer);
327 FreeVecPooled(SD(cl)->MemPool, hid->cdesc);
331 D(bug("[HID] HID::New() = NULL\n"));
333 BASE(cl->UserData)->LibNode.lib_OpenCnt--;
335 return NULL;
338 void METHOD(HID, Root, Dispose)
340 HidData *hid = OOP_INST_DATA(cl, o);
341 OOP_Object *drv = NULL;
342 struct Library *base = &BASE(cl->UserData)->LibNode;
344 OOP_GetAttr(o, aHidd_USBDevice_Bus, (intptr_t *)&drv);
346 HIDD_USBDrv_RemInterrupt(drv, hid->intr_pipe, &hid->interrupt);
347 HIDD_USBDevice_DeletePipe(o, hid->intr_pipe);
349 if (hid->report)
350 FreeVecPooled(SD(cl)->MemPool, hid->report);
351 if (hid->cdesc)
352 FreeVecPooled(SD(cl)->MemPool, hid->cdesc);
353 if (hid->buffer)
354 FreeVecPooled(SD(cl)->MemPool, hid->buffer);
356 OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
358 base->lib_OpenCnt--;
362 * The MatchCLID library call gets the device descriptor and full config
363 * descriptor (including the interface and endpoint descriptors). It will
364 * return CLID_Hidd_USBHID if the requested interface conforms to HID.
366 AROS_LH3(void *, MatchCLID,
367 AROS_LHA(usb_device_descriptor_t *, dev, A0),
368 AROS_LHA(usb_config_descriptor_t *, cfg, A1),
369 AROS_LHA(int, i, D0),
370 LIBBASETYPEPTR, LIBBASE, 5, HID)
372 AROS_LIBFUNC_INIT
374 void *clid = NULL;
376 D(bug("[HID] MatchCLID(%p, %p)\n", dev, cfg));
378 if (dev->bDeviceClass == UDCLASS_IN_INTERFACE)
380 usb_interface_descriptor_t *iface = find_idesc(cfg, i, 0);
382 D(bug("[HID] UDCLASS_IN_INTERFACE OK. Checking interface %d\n", i));
383 D(bug("[HID] iface %d @ %p protocol %d\n", i, iface, iface->bInterfaceProtocol));
385 if (iface->bInterfaceClass == UICLASS_HID)
387 D(bug("[HID] HID Class found. Handling it.\n"));
389 switch (iface->bInterfaceProtocol)
391 case 1:
392 D(bug("[HID] Keyboard protocol\n"));
393 clid = CLID_Hidd_USBKeyboard;
394 break;
396 case 2:
397 D(bug("[HID] Mouse protocol\n"));
398 clid = CLID_Hidd_USBMouse;
399 break;
401 default:
402 D(bug("[HID] Protocol unknown, using default class\n"));
403 clid = CLID_Hidd_USBHID;
404 break;
409 return clid;
411 AROS_LIBFUNC_EXIT