xref: /netbsd-src/sys/dev/usb/usbdi_util.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: usbdi_util.c,v 1.53 2009/11/12 08:28:31 uebayasi 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 (lennart@augustsson.net) 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  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: usbdi_util.c,v 1.53 2009/11/12 08:28:31 uebayasi Exp $");
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/proc.h>
41 #include <sys/device.h>
42 
43 #include <dev/usb/usb.h>
44 #include <dev/usb/usbhid.h>
45 
46 #include <dev/usb/usbdi.h>
47 #include <dev/usb/usbdi_util.h>
48 
49 #ifdef USB_DEBUG
50 #define DPRINTF(x)	if (usbdebug) logprintf x
51 #define DPRINTFN(n,x)	if (usbdebug>(n)) logprintf x
52 extern int usbdebug;
53 #else
54 #define DPRINTF(x)
55 #define DPRINTFN(n,x)
56 #endif
57 
58 usbd_status
59 usbd_get_desc(usbd_device_handle dev, int type, int index, int len, void *desc)
60 {
61 	usb_device_request_t req;
62 
63 	DPRINTFN(3,("usbd_get_desc: type=%d, index=%d, len=%d\n",
64 		    type, index, len));
65 
66 	req.bmRequestType = UT_READ_DEVICE;
67 	req.bRequest = UR_GET_DESCRIPTOR;
68 	USETW2(req.wValue, type, index);
69 	USETW(req.wIndex, 0);
70 	USETW(req.wLength, len);
71 	return (usbd_do_request(dev, &req, desc));
72 }
73 
74 usbd_status
75 usbd_get_config_desc(usbd_device_handle dev, int confidx,
76 		     usb_config_descriptor_t *d)
77 {
78 	usbd_status err;
79 
80 	DPRINTFN(3,("usbd_get_config_desc: confidx=%d\n", confidx));
81 	err = usbd_get_desc(dev, UDESC_CONFIG, confidx,
82 			    USB_CONFIG_DESCRIPTOR_SIZE, d);
83 	if (err)
84 		return (err);
85 	if (d->bDescriptorType != UDESC_CONFIG) {
86 		DPRINTFN(-1,("usbd_get_config_desc: confidx=%d, bad desc "
87 			     "len=%d type=%d\n",
88 			     confidx, d->bLength, d->bDescriptorType));
89 		return (USBD_INVAL);
90 	}
91 	return (USBD_NORMAL_COMPLETION);
92 }
93 
94 usbd_status
95 usbd_get_config_desc_full(usbd_device_handle dev, int conf, void *d, int size)
96 {
97 	DPRINTFN(3,("usbd_get_config_desc_full: conf=%d\n", conf));
98 	return (usbd_get_desc(dev, UDESC_CONFIG, conf, size, d));
99 }
100 
101 usbd_status
102 usbd_get_device_desc(usbd_device_handle dev, usb_device_descriptor_t *d)
103 {
104 	DPRINTFN(3,("usbd_get_device_desc:\n"));
105 	return (usbd_get_desc(dev, UDESC_DEVICE,
106 			     0, USB_DEVICE_DESCRIPTOR_SIZE, d));
107 }
108 
109 usbd_status
110 usbd_get_device_status(usbd_device_handle dev, usb_status_t *st)
111 {
112 	usb_device_request_t req;
113 
114 	req.bmRequestType = UT_READ_DEVICE;
115 	req.bRequest = UR_GET_STATUS;
116 	USETW(req.wValue, 0);
117 	USETW(req.wIndex, 0);
118 	USETW(req.wLength, sizeof(usb_status_t));
119 	return (usbd_do_request(dev, &req, st));
120 }
121 
122 usbd_status
123 usbd_get_hub_status(usbd_device_handle dev, usb_hub_status_t *st)
124 {
125 	usb_device_request_t req;
126 
127 	req.bmRequestType = UT_READ_CLASS_DEVICE;
128 	req.bRequest = UR_GET_STATUS;
129 	USETW(req.wValue, 0);
130 	USETW(req.wIndex, 0);
131 	USETW(req.wLength, sizeof(usb_hub_status_t));
132 	return (usbd_do_request(dev, &req, st));
133 }
134 
135 usbd_status
136 usbd_set_address(usbd_device_handle dev, int addr)
137 {
138 	usb_device_request_t req;
139 
140 	req.bmRequestType = UT_WRITE_DEVICE;
141 	req.bRequest = UR_SET_ADDRESS;
142 	USETW(req.wValue, addr);
143 	USETW(req.wIndex, 0);
144 	USETW(req.wLength, 0);
145 	return usbd_do_request(dev, &req, 0);
146 }
147 
148 usbd_status
149 usbd_get_port_status(usbd_device_handle dev, int port, usb_port_status_t *ps)
150 {
151 	usb_device_request_t req;
152 
153 	req.bmRequestType = UT_READ_CLASS_OTHER;
154 	req.bRequest = UR_GET_STATUS;
155 	USETW(req.wValue, 0);
156 	USETW(req.wIndex, port);
157 	USETW(req.wLength, sizeof *ps);
158 	return (usbd_do_request(dev, &req, ps));
159 }
160 
161 usbd_status
162 usbd_clear_hub_feature(usbd_device_handle dev, int sel)
163 {
164 	usb_device_request_t req;
165 
166 	req.bmRequestType = UT_WRITE_CLASS_DEVICE;
167 	req.bRequest = UR_CLEAR_FEATURE;
168 	USETW(req.wValue, sel);
169 	USETW(req.wIndex, 0);
170 	USETW(req.wLength, 0);
171 	return (usbd_do_request(dev, &req, 0));
172 }
173 
174 usbd_status
175 usbd_set_hub_feature(usbd_device_handle dev, int sel)
176 {
177 	usb_device_request_t req;
178 
179 	req.bmRequestType = UT_WRITE_CLASS_DEVICE;
180 	req.bRequest = UR_SET_FEATURE;
181 	USETW(req.wValue, sel);
182 	USETW(req.wIndex, 0);
183 	USETW(req.wLength, 0);
184 	return (usbd_do_request(dev, &req, 0));
185 }
186 
187 usbd_status
188 usbd_clear_port_feature(usbd_device_handle dev, int port, int sel)
189 {
190 	usb_device_request_t req;
191 
192 	req.bmRequestType = UT_WRITE_CLASS_OTHER;
193 	req.bRequest = UR_CLEAR_FEATURE;
194 	USETW(req.wValue, sel);
195 	USETW(req.wIndex, port);
196 	USETW(req.wLength, 0);
197 	return (usbd_do_request(dev, &req, 0));
198 }
199 
200 usbd_status
201 usbd_set_port_feature(usbd_device_handle dev, int port, int sel)
202 {
203 	usb_device_request_t req;
204 
205 	req.bmRequestType = UT_WRITE_CLASS_OTHER;
206 	req.bRequest = UR_SET_FEATURE;
207 	USETW(req.wValue, sel);
208 	USETW(req.wIndex, port);
209 	USETW(req.wLength, 0);
210 	return (usbd_do_request(dev, &req, 0));
211 }
212 
213 usbd_status
214 usbd_get_protocol(usbd_interface_handle iface, u_int8_t *report)
215 {
216 	usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface);
217 	usbd_device_handle dev;
218 	usb_device_request_t req;
219 
220 	DPRINTFN(4, ("usbd_get_protocol: iface=%p, endpt=%d\n",
221 		     iface, id->bInterfaceNumber));
222 	if (id == NULL)
223 		return (USBD_IOERROR);
224 	usbd_interface2device_handle(iface, &dev);
225 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
226 	req.bRequest = UR_GET_PROTOCOL;
227 	USETW(req.wValue, 0);
228 	USETW(req.wIndex, id->bInterfaceNumber);
229 	USETW(req.wLength, 1);
230 	return (usbd_do_request(dev, &req, report));
231 }
232 
233 usbd_status
234 usbd_set_protocol(usbd_interface_handle iface, int report)
235 {
236 	usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface);
237 	usbd_device_handle dev;
238 	usb_device_request_t req;
239 
240 	DPRINTFN(4, ("usbd_set_protocol: iface=%p, report=%d, endpt=%d\n",
241 		     iface, report, id->bInterfaceNumber));
242 	if (id == NULL)
243 		return (USBD_IOERROR);
244 	usbd_interface2device_handle(iface, &dev);
245 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
246 	req.bRequest = UR_SET_PROTOCOL;
247 	USETW(req.wValue, report);
248 	USETW(req.wIndex, id->bInterfaceNumber);
249 	USETW(req.wLength, 0);
250 	return (usbd_do_request(dev, &req, 0));
251 }
252 
253 usbd_status
254 usbd_set_report(usbd_interface_handle iface, int type, int id, void *data,
255 		int len)
256 {
257 	usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
258 	usbd_device_handle dev;
259 	usb_device_request_t req;
260 
261 	DPRINTFN(4, ("usbd_set_report: len=%d\n", len));
262 	if (ifd == NULL)
263 		return (USBD_IOERROR);
264 	usbd_interface2device_handle(iface, &dev);
265 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
266 	req.bRequest = UR_SET_REPORT;
267 	USETW2(req.wValue, type, id);
268 	USETW(req.wIndex, ifd->bInterfaceNumber);
269 	USETW(req.wLength, len);
270 	return (usbd_do_request(dev, &req, data));
271 }
272 
273 usbd_status
274 usbd_set_report_async(usbd_interface_handle iface, int type, int id, void *data,
275 		      int len)
276 {
277 	usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
278 	usbd_device_handle dev;
279 	usb_device_request_t req;
280 
281 	DPRINTFN(4, ("usbd_set_report_async: len=%d\n", len));
282 	if (ifd == NULL)
283 		return (USBD_IOERROR);
284 	usbd_interface2device_handle(iface, &dev);
285 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
286 	req.bRequest = UR_SET_REPORT;
287 	USETW2(req.wValue, type, id);
288 	USETW(req.wIndex, ifd->bInterfaceNumber);
289 	USETW(req.wLength, len);
290 	return (usbd_do_request_async(dev, &req, data));
291 }
292 
293 usbd_status
294 usbd_get_report(usbd_interface_handle iface, int type, int id, void *data,
295 		int len)
296 {
297 	usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
298 	usbd_device_handle dev;
299 	usb_device_request_t req;
300 
301 	DPRINTFN(4, ("usbd_get_report: len=%d\n", len));
302 	if (ifd == NULL)
303 		return (USBD_IOERROR);
304 	usbd_interface2device_handle(iface, &dev);
305 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
306 	req.bRequest = UR_GET_REPORT;
307 	USETW2(req.wValue, type, id);
308 	USETW(req.wIndex, ifd->bInterfaceNumber);
309 	USETW(req.wLength, len);
310 	return (usbd_do_request(dev, &req, data));
311 }
312 
313 usbd_status
314 usbd_set_idle(usbd_interface_handle iface, int duration, int id)
315 {
316 	usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
317 	usbd_device_handle dev;
318 	usb_device_request_t req;
319 
320 	DPRINTFN(4, ("usbd_set_idle: %d %d\n", duration, id));
321 	if (ifd == NULL)
322 		return (USBD_IOERROR);
323 	usbd_interface2device_handle(iface, &dev);
324 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
325 	req.bRequest = UR_SET_IDLE;
326 	USETW2(req.wValue, duration, id);
327 	USETW(req.wIndex, ifd->bInterfaceNumber);
328 	USETW(req.wLength, 0);
329 	return (usbd_do_request(dev, &req, 0));
330 }
331 
332 usbd_status
333 usbd_get_report_descriptor(usbd_device_handle dev, int ifcno,
334 			   int size, void *d)
335 {
336 	usb_device_request_t req;
337 
338 	req.bmRequestType = UT_READ_INTERFACE;
339 	req.bRequest = UR_GET_DESCRIPTOR;
340 	USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */
341 	USETW(req.wIndex, ifcno);
342 	USETW(req.wLength, size);
343 	return (usbd_do_request(dev, &req, d));
344 }
345 
346 usb_hid_descriptor_t *
347 usbd_get_hid_descriptor(usbd_interface_handle ifc)
348 {
349 	usb_interface_descriptor_t *idesc = usbd_get_interface_descriptor(ifc);
350 	usbd_device_handle dev;
351 	usb_config_descriptor_t *cdesc;
352 	usb_hid_descriptor_t *hd;
353 	char *p, *end;
354 
355 	if (idesc == NULL)
356 		return (NULL);
357 	usbd_interface2device_handle(ifc, &dev);
358 	cdesc = usbd_get_config_descriptor(dev);
359 
360 	p = (char *)idesc + idesc->bLength;
361 	end = (char *)cdesc + UGETW(cdesc->wTotalLength);
362 
363 	for (; p < end; p += hd->bLength) {
364 		hd = (usb_hid_descriptor_t *)p;
365 		if (p + hd->bLength <= end && hd->bDescriptorType == UDESC_HID)
366 			return (hd);
367 		if (hd->bDescriptorType == UDESC_INTERFACE)
368 			break;
369 	}
370 	return (NULL);
371 }
372 
373 usbd_status
374 usbd_read_report_desc(usbd_interface_handle ifc, void **descp, int *sizep,
375 		       usb_malloc_type mem)
376 {
377 	usb_interface_descriptor_t *id;
378 	usb_hid_descriptor_t *hid;
379 	usbd_device_handle dev;
380 	usbd_status err;
381 
382 	usbd_interface2device_handle(ifc, &dev);
383 	id = usbd_get_interface_descriptor(ifc);
384 	if (id == NULL)
385 		return (USBD_INVAL);
386 	hid = usbd_get_hid_descriptor(ifc);
387 	if (hid == NULL)
388 		return (USBD_IOERROR);
389 	*sizep = UGETW(hid->descrs[0].wDescriptorLength);
390 	*descp = malloc(*sizep, mem, M_NOWAIT);
391 	if (*descp == NULL)
392 		return (USBD_NOMEM);
393 	err = usbd_get_report_descriptor(dev, id->bInterfaceNumber,
394 					 *sizep, *descp);
395 	if (err) {
396 		free(*descp, mem);
397 		*descp = NULL;
398 		return (err);
399 	}
400 	return (USBD_NORMAL_COMPLETION);
401 }
402 
403 usbd_status
404 usbd_get_config(usbd_device_handle dev, u_int8_t *conf)
405 {
406 	usb_device_request_t req;
407 
408 	req.bmRequestType = UT_READ_DEVICE;
409 	req.bRequest = UR_GET_CONFIG;
410 	USETW(req.wValue, 0);
411 	USETW(req.wIndex, 0);
412 	USETW(req.wLength, 1);
413 	return (usbd_do_request(dev, &req, conf));
414 }
415 
416 Static void usbd_bulk_transfer_cb(usbd_xfer_handle xfer,
417 				  usbd_private_handle priv, usbd_status status);
418 Static void
419 usbd_bulk_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
420 		      usbd_status status)
421 {
422 	wakeup(xfer);
423 }
424 
425 usbd_status
426 usbd_bulk_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
427 		   u_int16_t flags, u_int32_t timeout, void *buf,
428 		   u_int32_t *size, const char *lbl)
429 {
430 	usbd_status err;
431 	int s, error;
432 
433 	usbd_setup_xfer(xfer, pipe, 0, buf, *size,
434 			flags, timeout, usbd_bulk_transfer_cb);
435 	DPRINTFN(1, ("usbd_bulk_transfer: start transfer %d bytes\n", *size));
436 	s = splusb();		/* don't want callback until tsleep() */
437 	err = usbd_transfer(xfer);
438 	if (err != USBD_IN_PROGRESS) {
439 		splx(s);
440 		return (err);
441 	}
442 	error = tsleep(xfer, PZERO | PCATCH, lbl, 0);
443 	splx(s);
444 	if (error) {
445 		DPRINTF(("usbd_bulk_transfer: tsleep=%d\n", error));
446 		usbd_abort_pipe(pipe);
447 		return (USBD_INTERRUPTED);
448 	}
449 	usbd_get_xfer_status(xfer, NULL, NULL, size, &err);
450 	DPRINTFN(1,("usbd_bulk_transfer: transferred %d\n", *size));
451 	if (err) {
452 		DPRINTF(("usbd_bulk_transfer: error=%d\n", err));
453 		usbd_clear_endpoint_stall(pipe);
454 	}
455 	return (err);
456 }
457 
458 Static void usbd_intr_transfer_cb(usbd_xfer_handle xfer,
459 				  usbd_private_handle priv, usbd_status status);
460 Static void
461 usbd_intr_transfer_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
462 		      usbd_status status)
463 {
464 	wakeup(xfer);
465 }
466 
467 usbd_status
468 usbd_intr_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
469 		   u_int16_t flags, u_int32_t timeout, void *buf,
470 		   u_int32_t *size, const char *lbl)
471 {
472 	usbd_status err;
473 	int s, error;
474 
475 	usbd_setup_xfer(xfer, pipe, 0, buf, *size,
476 			flags, timeout, usbd_intr_transfer_cb);
477 	DPRINTFN(1, ("usbd_intr_transfer: start transfer %d bytes\n", *size));
478 	s = splusb();		/* don't want callback until tsleep() */
479 	err = usbd_transfer(xfer);
480 	if (err != USBD_IN_PROGRESS) {
481 		splx(s);
482 		return (err);
483 	}
484 	error = tsleep(xfer, PZERO | PCATCH, lbl, 0);
485 	splx(s);
486 	if (error) {
487 		DPRINTF(("usbd_intr_transfer: tsleep=%d\n", error));
488 		usbd_abort_pipe(pipe);
489 		return (USBD_INTERRUPTED);
490 	}
491 	usbd_get_xfer_status(xfer, NULL, NULL, size, &err);
492 	DPRINTFN(1,("usbd_intr_transfer: transferred %d\n", *size));
493 	if (err) {
494 		DPRINTF(("usbd_intr_transfer: error=%d\n", err));
495 		usbd_clear_endpoint_stall(pipe);
496 	}
497 	return (err);
498 }
499 
500 void
501 usb_detach_wait(device_ptr_t dv)
502 {
503 	DPRINTF(("usb_detach_wait: waiting for %s\n", USBDEVPTRNAME(dv)));
504 	if (tsleep(dv, PZERO, "usbdet", hz * 60))
505 		printf("usb_detach_wait: %s didn't detach\n",
506 		        USBDEVPTRNAME(dv));
507 	DPRINTF(("usb_detach_wait: %s done\n", USBDEVPTRNAME(dv)));
508 }
509 
510 void
511 usb_detach_wakeup(device_ptr_t dv)
512 {
513 	DPRINTF(("usb_detach_wakeup: for %s\n", USBDEVPTRNAME(dv)));
514 	wakeup(dv);
515 }
516 
517 const usb_cdc_descriptor_t *
518 usb_find_desc(usbd_device_handle dev, int type, int subtype)
519 {
520 	usbd_desc_iter_t iter;
521 	const usb_cdc_descriptor_t *desc;
522 
523 	usb_desc_iter_init(dev, &iter);
524 	for (;;) {
525 		desc = (const usb_cdc_descriptor_t *)usb_desc_iter_next(&iter);
526 		if (!desc || (desc->bDescriptorType == type &&
527 			      (subtype == USBD_CDCSUBTYPE_ANY ||
528 			       subtype == desc->bDescriptorSubtype)))
529 			break;
530 	}
531 	return desc;
532 }
533 
534 /* same as usb_find_desc(), but searches only in the specified interface. */
535 const usb_cdc_descriptor_t *
536 usb_find_desc_if(usbd_device_handle dev, int type, int subtype,
537 		 usb_interface_descriptor_t *id)
538 {
539 	usbd_desc_iter_t iter;
540 	const usb_cdc_descriptor_t *desc;
541 
542 	usb_desc_iter_init(dev, &iter);
543 
544 	iter.cur = (void *)id;		/* start from the interface desc */
545 	usb_desc_iter_next(&iter);	/* and skip it */
546 
547 	while ((desc = (const usb_cdc_descriptor_t *)usb_desc_iter_next(&iter))
548 	       != NULL) {
549 		if (desc->bDescriptorType == UDESC_INTERFACE) {
550 			/* we ran into the next interface --- not found */
551 			return NULL;
552 		}
553 		if (desc->bDescriptorType == type &&
554 		    (subtype == USBD_CDCSUBTYPE_ANY ||
555 		     subtype == desc->bDescriptorSubtype))
556 			break;
557 	}
558 	return desc;
559 }
560