usbdi_util.c revision 1.13 1 /* $NetBSD: usbdi_util.c,v 1.13 1999/01/08 11:58:26 augustss Exp $ */
2
3 /*
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (augustss (at) carlstedt.se) at
9 * Carlstedt Research & Technology.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/malloc.h>
44 #include <sys/proc.h>
45 #if defined(__FreeBSD__)
46 #include <sys/bus.h>
47 #endif
48
49 #include <dev/usb/usb.h>
50 #include <dev/usb/usbhid.h>
51
52 #include <dev/usb/usbdi.h>
53 #include <dev/usb/usbdi_util.h>
54
55 #ifdef USB_DEBUG
56 #define DPRINTF(x) if (usbdebug) printf x
57 #define DPRINTFN(n,x) if (usbdebug>(n)) printf x
58 extern int usbdebug;
59 #else
60 #define DPRINTF(x)
61 #define DPRINTFN(n,x)
62 #endif
63
64 usbd_status
65 usbd_get_desc(dev, type, index, len, desc)
66 usbd_device_handle dev;
67 int type, index;
68 int len;
69 void *desc;
70 {
71 usb_device_request_t req;
72
73 req.bmRequestType = UT_READ_DEVICE;
74 req.bRequest = UR_GET_DESCRIPTOR;
75 USETW2(req.wValue, type, index);
76 USETW(req.wIndex, 0);
77 USETW(req.wLength, len);
78 return (usbd_do_request(dev, &req, desc));
79 }
80
81 usbd_status
82 usbd_get_config_desc(dev, conf, d)
83 usbd_device_handle dev;
84 int conf;
85 usb_config_descriptor_t *d;
86 {
87 usbd_status r;
88
89 DPRINTFN(3,("usbd_get_config_desc: conf=%d\n", conf));
90 r = usbd_get_desc(dev, UDESC_CONFIG, conf,
91 USB_CONFIG_DESCRIPTOR_SIZE, d);
92 if (r != USBD_NORMAL_COMPLETION)
93 return (r);
94 if (d->bDescriptorType != UDESC_CONFIG) {
95 DPRINTFN(-1,("usbd_get_config_desc: conf %d, bad desc %d\n",
96 conf, d->bDescriptorType));
97 return (USBD_INVAL);
98 }
99 return (USBD_NORMAL_COMPLETION);
100 }
101
102 usbd_status
103 usbd_get_config_desc_full(dev, conf, d, size)
104 usbd_device_handle dev;
105 int conf;
106 void *d;
107 int size;
108 {
109 DPRINTFN(3,("usbd_get_config_desc_full: conf=%d\n", conf));
110 return (usbd_get_desc(dev, UDESC_CONFIG, conf, size, d));
111 }
112
113 usbd_status
114 usbd_get_device_desc(dev, d)
115 usbd_device_handle dev;
116 usb_device_descriptor_t *d;
117 {
118 DPRINTFN(3,("usbd_get_device_desc:\n"));
119 return (usbd_get_desc(dev, UDESC_DEVICE,
120 0, USB_DEVICE_DESCRIPTOR_SIZE, d));
121 }
122
123 usbd_status
124 usbd_get_device_status(dev, st)
125 usbd_device_handle dev;
126 usb_status_t *st;
127 {
128 usb_device_request_t req;
129
130 req.bmRequestType = UT_READ_DEVICE;
131 req.bRequest = UR_GET_STATUS;
132 USETW(req.wValue, 0);
133 USETW(req.wIndex, 0);
134 USETW(req.wLength, sizeof(usb_status_t));
135 return (usbd_do_request(dev, &req, st));
136 }
137
138 usbd_status
139 usbd_get_hub_status(dev, st)
140 usbd_device_handle dev;
141 usb_hub_status_t *st;
142 {
143 usb_device_request_t req;
144
145 req.bmRequestType = UT_READ_CLASS_DEVICE;
146 req.bRequest = UR_GET_STATUS;
147 USETW(req.wValue, 0);
148 USETW(req.wIndex, 0);
149 USETW(req.wLength, sizeof(usb_hub_status_t));
150 return (usbd_do_request(dev, &req, st));
151 }
152
153 usbd_status
154 usbd_set_address(dev, addr)
155 usbd_device_handle dev;
156 int addr;
157 {
158 usb_device_request_t req;
159
160 req.bmRequestType = UT_WRITE_DEVICE;
161 req.bRequest = UR_SET_ADDRESS;
162 USETW(req.wValue, addr);
163 USETW(req.wIndex, 0);
164 USETW(req.wLength, 0);
165 return usbd_do_request(dev, &req, 0);
166 }
167
168 usbd_status
169 usbd_get_port_status(dev, port, ps)
170 usbd_device_handle dev;
171 int port;
172 usb_port_status_t *ps;
173 {
174 usb_device_request_t req;
175
176 req.bmRequestType = UT_READ_CLASS_OTHER;
177 req.bRequest = UR_GET_STATUS;
178 USETW(req.wValue, 0);
179 USETW(req.wIndex, port);
180 USETW(req.wLength, sizeof *ps);
181 return (usbd_do_request(dev, &req, ps));
182 }
183
184 usbd_status
185 usbd_clear_port_feature(dev, port, sel)
186 usbd_device_handle dev;
187 int port, sel;
188 {
189 usb_device_request_t req;
190
191 req.bmRequestType = UT_WRITE_CLASS_OTHER;
192 req.bRequest = UR_CLEAR_FEATURE;
193 USETW(req.wValue, sel);
194 USETW(req.wIndex, port);
195 USETW(req.wLength, 0);
196 return (usbd_do_request(dev, &req, 0));
197 }
198
199 usbd_status
200 usbd_set_port_feature(dev, port, sel)
201 usbd_device_handle dev;
202 int port, sel;
203 {
204 usb_device_request_t req;
205
206 req.bmRequestType = UT_WRITE_CLASS_OTHER;
207 req.bRequest = UR_SET_FEATURE;
208 USETW(req.wValue, sel);
209 USETW(req.wIndex, port);
210 USETW(req.wLength, 0);
211 return (usbd_do_request(dev, &req, 0));
212 }
213
214
215 usbd_status
216 usbd_set_protocol(iface, report)
217 usbd_interface_handle iface;
218 int report;
219 {
220 usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface);
221 usbd_device_handle dev;
222 usb_device_request_t req;
223 usbd_status r;
224
225 DPRINTFN(4, ("usbd_set_protocol: iface=%p, report=%d, endpt=%d\n",
226 iface, report, id->bInterfaceNumber));
227 if (!id)
228 return (USBD_IOERROR);
229 r = usbd_interface2device_handle(iface, &dev);
230 if (r != USBD_NORMAL_COMPLETION)
231 return (r);
232 if (!id)
233 return (USBD_INVAL);
234 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
235 req.bRequest = UR_SET_PROTOCOL;
236 USETW(req.wValue, report);
237 USETW(req.wIndex, id->bInterfaceNumber);
238 USETW(req.wLength, 0);
239 return (usbd_do_request(dev, &req, 0));
240 }
241
242 usbd_status
243 usbd_set_report(iface, type, id, data, len)
244 usbd_interface_handle iface;
245 int type;
246 int id;
247 void *data;
248 int len;
249 {
250 usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
251 usbd_device_handle dev;
252 usb_device_request_t req;
253 usbd_status r;
254
255 DPRINTFN(4, ("usbd_set_report: len=%d\n", len));
256 if (!ifd)
257 return (USBD_IOERROR);
258 r = usbd_interface2device_handle(iface, &dev);
259 if (r != USBD_NORMAL_COMPLETION)
260 return (r);
261 if (!ifd)
262 return (USBD_INVAL);
263 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
264 req.bRequest = UR_SET_REPORT;
265 USETW2(req.wValue, type, id);
266 USETW(req.wIndex, ifd->bInterfaceNumber);
267 USETW(req.wLength, len);
268 return (usbd_do_request(dev, &req, data));
269 }
270
271 usbd_status
272 usbd_set_report_async(iface, type, id, data, len)
273 usbd_interface_handle iface;
274 int type;
275 int id;
276 void *data;
277 int len;
278 {
279 usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
280 usbd_device_handle dev;
281 usb_device_request_t req;
282 usbd_status r;
283
284 DPRINTFN(4, ("usbd_set_report_async: len=%d\n", len));
285 if (!ifd)
286 return (USBD_IOERROR);
287 r = usbd_interface2device_handle(iface, &dev);
288 if (r != USBD_NORMAL_COMPLETION)
289 return (r);
290 if (!ifd)
291 return (USBD_INVAL);
292 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
293 req.bRequest = UR_SET_REPORT;
294 USETW2(req.wValue, type, id);
295 USETW(req.wIndex, ifd->bInterfaceNumber);
296 USETW(req.wLength, len);
297 return (usbd_do_request_async(dev, &req, data));
298 }
299
300 usbd_status
301 usbd_get_report(iface, type, id, data, len)
302 usbd_interface_handle iface;
303 int type;
304 int id;
305 void *data;
306 int len;
307 {
308 usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
309 usbd_device_handle dev;
310 usb_device_request_t req;
311 usbd_status r;
312
313 DPRINTFN(4, ("usbd_set_report: len=%d\n", len));
314 if (!id)
315 return (USBD_IOERROR);
316 r = usbd_interface2device_handle(iface, &dev);
317 if (r != USBD_NORMAL_COMPLETION)
318 return (r);
319 if (!ifd)
320 return (USBD_INVAL);
321 req.bmRequestType = UT_READ_CLASS_INTERFACE;
322 req.bRequest = UR_GET_REPORT;
323 USETW2(req.wValue, type, id);
324 USETW(req.wIndex, ifd->bInterfaceNumber);
325 USETW(req.wLength, len);
326 return (usbd_do_request(dev, &req, data));
327 }
328
329 usbd_status
330 usbd_set_idle(iface, duration, id)
331 usbd_interface_handle iface;
332 int duration;
333 int id;
334 {
335 usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
336 usbd_device_handle dev;
337 usb_device_request_t req;
338 usbd_status r;
339
340 DPRINTFN(4, ("usbd_set_idle: %d %d\n", duration, id));
341 if (!ifd)
342 return (USBD_IOERROR);
343 r = usbd_interface2device_handle(iface, &dev);
344 if (r != USBD_NORMAL_COMPLETION)
345 return (r);
346 if (!ifd)
347 return (USBD_INVAL);
348 req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
349 req.bRequest = UR_SET_IDLE;
350 USETW2(req.wValue, duration, id);
351 USETW(req.wIndex, ifd->bInterfaceNumber);
352 USETW(req.wLength, 0);
353 return (usbd_do_request(dev, &req, 0));
354 }
355
356 usbd_status
357 usbd_get_report_descriptor(dev, ifcno, repid, size, d)
358 usbd_device_handle dev;
359 int ifcno;
360 int repid;
361 int size;
362 void *d;
363 {
364 usb_device_request_t req;
365
366 req.bmRequestType = UT_READ_INTERFACE;
367 req.bRequest = UR_GET_DESCRIPTOR;
368 USETW2(req.wValue, UDESC_REPORT, repid);
369 USETW(req.wIndex, ifcno);
370 USETW(req.wLength, size);
371 return (usbd_do_request(dev, &req, d));
372 }
373
374 usb_hid_descriptor_t *
375 usbd_get_hid_descriptor(ifc)
376 usbd_interface_handle ifc;
377 {
378 usb_interface_descriptor_t *idesc = usbd_get_interface_descriptor(ifc);
379 usbd_device_handle dev;
380 usb_config_descriptor_t *cdesc;
381 usb_hid_descriptor_t *hd;
382 char *p, *end;
383 usbd_status r;
384
385 if (!idesc)
386 return (0);
387 r = usbd_interface2device_handle(ifc, &dev);
388 if (r != USBD_NORMAL_COMPLETION)
389 return (0);
390 cdesc = usbd_get_config_descriptor(dev);
391
392 p = (char *)idesc + idesc->bLength;
393 end = (char *)cdesc + UGETW(cdesc->wTotalLength);
394
395 for (; p < end; p += hd->bLength) {
396 hd = (usb_hid_descriptor_t *)p;
397 if (p + hd->bLength <= end && hd->bDescriptorType == UDESC_HID)
398 return (hd);
399 if (hd->bDescriptorType == UDESC_INTERFACE)
400 break;
401 }
402 return (0);
403 }
404
405 usbd_status
406 usbd_alloc_report_desc(ifc, descp, sizep, mem)
407 usbd_interface_handle ifc;
408 void **descp;
409 int *sizep;
410 #if defined(__NetBSD__)
411 int mem;
412 #elif defined(__FreeBSD__)
413 struct malloc_type *mem;
414 #endif
415
416 {
417 usb_interface_descriptor_t *id;
418 usb_hid_descriptor_t *hid;
419 usbd_device_handle dev;
420 usbd_status r;
421
422 r = usbd_interface2device_handle(ifc, &dev);
423 if (r != USBD_NORMAL_COMPLETION)
424 return (r);
425 id = usbd_get_interface_descriptor(ifc);
426 if (!id)
427 return (USBD_INVAL);
428 hid = usbd_get_hid_descriptor(ifc);
429 if (!hid)
430 return (USBD_IOERROR);
431 *sizep = UGETW(hid->descrs[0].wDescriptorLength);
432 *descp = malloc(*sizep, mem, M_NOWAIT);
433 if (!*descp)
434 return (USBD_NOMEM);
435 /* XXX should not use 0 Report ID */
436 r = usbd_get_report_descriptor(dev, id->bInterfaceNumber, 0,
437 *sizep, *descp);
438 if (r != USBD_NORMAL_COMPLETION) {
439 free(*descp, mem);
440 return (r);
441 }
442 return (USBD_NORMAL_COMPLETION);
443 }
444
445 usbd_status
446 usbd_get_config(dev, conf)
447 usbd_device_handle dev;
448 u_int8_t *conf;
449 {
450 usb_device_request_t req;
451
452 req.bmRequestType = UT_READ_DEVICE;
453 req.bRequest = UR_GET_CONFIG;
454 USETW(req.wValue, 0);
455 USETW(req.wIndex, 0);
456 USETW(req.wLength, 1);
457 return (usbd_do_request(dev, &req, conf));
458 }
459
460 static void usbd_bulk_transfer_cb __P((usbd_request_handle reqh,
461 usbd_private_handle priv, usbd_status status));
462 static void
463 usbd_bulk_transfer_cb(reqh, priv, status)
464 usbd_request_handle reqh;
465 usbd_private_handle priv;
466 usbd_status status;
467 {
468 wakeup(reqh);
469 }
470
471 usbd_status
472 usbd_bulk_transfer(reqh, pipe, flags, buf, size, lbl)
473 usbd_request_handle reqh;
474 usbd_pipe_handle pipe;
475 u_int16_t flags;
476 void *buf;
477 u_int32_t *size;
478 char *lbl;
479 {
480 usbd_private_handle priv;
481 void *buffer;
482 usbd_status r;
483 int s, error;
484
485 r = usbd_setup_request(reqh, pipe, 0, buf, *size,
486 flags, USBD_NO_TIMEOUT, usbd_bulk_transfer_cb);
487 if (r != USBD_NORMAL_COMPLETION)
488 return (r);
489 DPRINTFN(1, ("usbd_bulk_transfer: start transfer %d bytes\n", *size));
490 s = splusb(); /* don't want callback until tsleep() */
491 r = usbd_transfer(reqh);
492 if (r != USBD_IN_PROGRESS) {
493 splx(s);
494 return (r);
495 }
496 error = tsleep((caddr_t)reqh, PZERO | PCATCH, lbl, 0);
497 splx(s);
498 if (error) {
499 usbd_abort_pipe(pipe);
500 return (USBD_INTERRUPTED);
501 }
502 usbd_get_request_status(reqh, &priv, &buffer, size, &r);
503 DPRINTFN(1,("usbd_bulk_transfer: transferred %d\n", *size));
504 if (r != USBD_NORMAL_COMPLETION) {
505 DPRINTF(("usbd_bulk_transfer: error=%d\n", r));
506 usbd_clear_endpoint_stall(pipe);
507 }
508 return (r);
509 }
510
511