xref: /netbsd-src/sys/dev/usb/usbdi_util.c (revision 7330f729ccf0bd976a06f95fad452fe774fc7fd1)
1 /*	$NetBSD: usbdi_util.c,v 1.75 2019/08/21 10:48:37 mrg Exp $	*/
2 
3 /*
4  * Copyright (c) 1998, 2012 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 and Matthew R. Green (mrg@eterna.com.au).
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.75 2019/08/21 10:48:37 mrg Exp $");
35 
36 #ifdef _KERNEL_OPT
37 #include "opt_usb.h"
38 #endif
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/kmem.h>
44 #include <sys/proc.h>
45 #include <sys/device.h>
46 #include <sys/bus.h>
47 
48 #include <dev/usb/usb.h>
49 #include <dev/usb/usbhid.h>
50 #include <dev/usb/usbdi.h>
51 #include <dev/usb/usbdivar.h>
52 #include <dev/usb/usbdi_util.h>
53 #include <dev/usb/usb_quirks.h>
54 #include <dev/usb/usbhist.h>
55 
56 #define	DPRINTF(FMT,A,B,C,D)	USBHIST_LOGN(usbdebug,1,FMT,A,B,C,D)
57 #define	DPRINTFN(N,FMT,A,B,C,D)	USBHIST_LOGN(usbdebug,N,FMT,A,B,C,D)
58 
59 usbd_status
60 usbd_get_desc(struct usbd_device *dev, int type, int index, int len, void *desc)
61 {
62 	usb_device_request_t req;
63 	usbd_status err;
64 
65 	USBHIST_FUNC();
66 	USBHIST_CALLARGS(usbdebug, "type=%jd, index=%jd, len=%jd",
67 	    type, index, len, 0);
68 
69 	/*
70 	 * Provide hard-coded configuration descriptors
71 	 * for devices that may corrupt it. This cannot
72 	 * be done for device descriptors which are used
73 	 * to identify the device.
74 	 */
75 	if (type != UDESC_DEVICE &&
76 	    dev->ud_quirks->uq_flags & UQ_DESC_CORRUPT) {
77 		err = usbd_get_desc_fake(dev, type, index, len, desc);
78 		goto out;
79 	}
80 
81 	req.bmRequestType = UT_READ_DEVICE;
82 	req.bRequest = UR_GET_DESCRIPTOR;
83 	USETW2(req.wValue, type, index);
84 	USETW(req.wIndex, 0);
85 	USETW(req.wLength, len);
86 	err = usbd_do_request(dev, &req, desc);
87 
88 out:
89 	return err;
90 }
91 
92 usbd_status
93 usbd_get_config_desc(struct usbd_device *dev, int confidx,
94 		     usb_config_descriptor_t *d)
95 {
96 	USBHIST_FUNC();
97 	USBHIST_CALLARGS(usbdebug, "confidx=%jd", confidx, 0, 0, 0);
98 	usbd_status err;
99 
100 	err = usbd_get_desc(dev, UDESC_CONFIG, confidx,
101 			    USB_CONFIG_DESCRIPTOR_SIZE, d);
102 	if (err)
103 		return err;
104 	if (d->bDescriptorType != UDESC_CONFIG) {
105 		DPRINTFN(1, "confidx=%jd, bad desc len=%d type=%d",
106 		    confidx, d->bLength, d->bDescriptorType, 0);
107 		return USBD_INVAL;
108 	}
109 	return USBD_NORMAL_COMPLETION;
110 }
111 
112 usbd_status
113 usbd_get_config_desc_full(struct usbd_device *dev, int conf, void *d, int size)
114 {
115 	USBHIST_FUNC(); USBHIST_CALLARGS(usbdebug, "conf=%jd", conf, 0, 0, 0);
116 
117 	return usbd_get_desc(dev, UDESC_CONFIG, conf, size, d);
118 }
119 
120 usbd_status
121 usbd_get_bos_desc(struct usbd_device *dev, int confidx,
122 		     usb_bos_descriptor_t *d)
123 {
124 	USBHIST_FUNC();
125 	USBHIST_CALLARGS(usbdebug, "confidx=%jd", confidx, 0, 0, 0);
126 	usbd_status err;
127 
128 	err = usbd_get_desc(dev, UDESC_BOS, confidx,
129 			    USB_BOS_DESCRIPTOR_SIZE, d);
130 	if (err)
131 		return err;
132 	if (d->bDescriptorType != UDESC_BOS) {
133 		DPRINTFN(1, "confidx=%jd, bad desc len=%d type=%d",
134 		    confidx, d->bLength, d->bDescriptorType, 0);
135 		return USBD_INVAL;
136 	}
137 	return USBD_NORMAL_COMPLETION;
138 }
139 
140 usbd_status
141 usbd_get_bos_desc_full(struct usbd_device *dev, int conf, void *d, int size)
142 {
143 	USBHIST_FUNC(); USBHIST_CALLARGS(usbdebug, "conf=%jd", conf, 0, 0, 0);
144 
145 	return usbd_get_desc(dev, UDESC_BOS, conf, size, d);
146 }
147 
148 usbd_status
149 usbd_get_device_desc(struct usbd_device *dev, usb_device_descriptor_t *d)
150 {
151 	USBHIST_FUNC(); USBHIST_CALLED(usbdebug);
152 
153 	return usbd_get_desc(dev, UDESC_DEVICE,
154 			     0, USB_DEVICE_DESCRIPTOR_SIZE, d);
155 }
156 
157 usbd_status
158 usbd_get_device_status(struct usbd_device *dev, usb_status_t *st)
159 {
160 	USBHIST_FUNC(); USBHIST_CALLED(usbdebug);
161 	usb_device_request_t req;
162 
163 	req.bmRequestType = UT_READ_DEVICE;
164 	req.bRequest = UR_GET_STATUS;
165 	USETW(req.wValue, 0);
166 	USETW(req.wIndex, 0);
167 	USETW(req.wLength, sizeof(usb_status_t));
168 	return usbd_do_request(dev, &req, st);
169 }
170 
171 usbd_status
172 usbd_get_hub_status(struct usbd_device *dev, usb_hub_status_t *st)
173 {
174 	USBHIST_FUNC();
175 	USBHIST_CALLARGS(usbdebug, "dev %#jx", (uintptr_t)dev, 0, 0, 0);
176 	usb_device_request_t req;
177 
178 	req.bmRequestType = UT_READ_CLASS_DEVICE;
179 	req.bRequest = UR_GET_STATUS;
180 	USETW(req.wValue, 0);
181 	USETW(req.wIndex, 0);
182 	USETW(req.wLength, sizeof(usb_hub_status_t));
183 	return usbd_do_request(dev, &req, st);
184 }
185 
186 usbd_status
187 usbd_set_address(struct usbd_device *dev, int addr)
188 {
189 	USBHIST_FUNC();
190 	USBHIST_CALLARGS(usbdebug, "dev %#jx addr %jd",
191 	    (uintptr_t)dev, addr, 0, 0);
192 	usb_device_request_t req;
193 
194 	req.bmRequestType = UT_WRITE_DEVICE;
195 	req.bRequest = UR_SET_ADDRESS;
196 	USETW(req.wValue, addr);
197 	USETW(req.wIndex, 0);
198 	USETW(req.wLength, 0);
199 	return usbd_do_request(dev, &req, 0);
200 }
201 
202 usbd_status
203 usbd_get_port_status(struct usbd_device *dev, int port, usb_port_status_t *ps)
204 {
205 	USBHIST_FUNC();
206 	USBHIST_CALLARGS(usbdebug, "dev %#jx port %jd",
207 	    (uintptr_t)dev, port, 0, 0);
208 	usb_device_request_t req;
209 
210 	req.bmRequestType = UT_READ_CLASS_OTHER;
211 	req.bRequest = UR_GET_STATUS;
212 	USETW(req.wValue, 0);
213 	USETW(req.wIndex, port);
214 	USETW(req.wLength, sizeof(*ps));
215 	return usbd_do_request(dev, &req, ps);
216 }
217 
218 /* USB 3.1 10.16.2.6, 10.16.2.6.3 */
219 usbd_status
220 usbd_get_port_status_ext(struct usbd_device *dev, int port,
221     usb_port_status_ext_t *pse)
222 {
223 	USBHIST_FUNC();
224 	USBHIST_CALLARGS(usbdebug, "dev %#jx port %jd",
225 	    (uintptr_t)dev, port, 0, 0);
226 	usb_device_request_t req;
227 
228 	req.bmRequestType = UT_READ_CLASS_OTHER;
229 	req.bRequest = UR_GET_STATUS;
230 	USETW2(req.wValue, 0, UR_PST_EXT_PORT_STATUS);
231 	USETW(req.wIndex, port);
232 	USETW(req.wLength, sizeof(*pse));
233 	return usbd_do_request(dev, &req, pse);
234 }
235 
236 usbd_status
237 usbd_clear_hub_feature(struct usbd_device *dev, int sel)
238 {
239 	USBHIST_FUNC();
240 	USBHIST_CALLARGS(usbdebug, "dev %#jx sel %jd",
241 	    (uintptr_t)dev, sel, 0, 0);
242 	usb_device_request_t req;
243 
244 	req.bmRequestType = UT_WRITE_CLASS_DEVICE;
245 	req.bRequest = UR_CLEAR_FEATURE;
246 	USETW(req.wValue, sel);
247 	USETW(req.wIndex, 0);
248 	USETW(req.wLength, 0);
249 	return usbd_do_request(dev, &req, 0);
250 }
251 
252 usbd_status
253 usbd_set_hub_feature(struct usbd_device *dev, int sel)
254 {
255 	USBHIST_FUNC();
256 	USBHIST_CALLARGS(usbdebug,
257 	    "dev %#jx sel %jd", (uintptr_t)dev, sel, 0, 0);
258 	usb_device_request_t req;
259 
260 	req.bmRequestType = UT_WRITE_CLASS_DEVICE;
261 	req.bRequest = UR_SET_FEATURE;
262 	USETW(req.wValue, sel);
263 	USETW(req.wIndex, 0);
264 	USETW(req.wLength, 0);
265 	return usbd_do_request(dev, &req, 0);
266 }
267 
268 usbd_status
269 usbd_clear_port_feature(struct usbd_device *dev, int port, int sel)
270 {
271 	USBHIST_FUNC();
272 	USBHIST_CALLARGS(usbdebug, "dev %#jx port %jd sel %jd",
273 	    (uintptr_t)dev, port, sel, 0);
274 	usb_device_request_t req;
275 
276 	req.bmRequestType = UT_WRITE_CLASS_OTHER;
277 	req.bRequest = UR_CLEAR_FEATURE;
278 	USETW(req.wValue, sel);
279 	USETW(req.wIndex, port);
280 	USETW(req.wLength, 0);
281 	return usbd_do_request(dev, &req, 0);
282 }
283 
284 usbd_status
285 usbd_set_port_feature(struct usbd_device *dev, int port, int sel)
286 {
287 	USBHIST_FUNC();
288 	USBHIST_CALLARGS(usbdebug, "dev %#jx port %jd sel %.d",
289 	    (uintptr_t)dev, sel, 0, 0);
290 	usb_device_request_t req;
291 
292 	req.bmRequestType = UT_WRITE_CLASS_OTHER;
293 	req.bRequest = UR_SET_FEATURE;
294 	USETW(req.wValue, sel);
295 	USETW(req.wIndex, port);
296 	USETW(req.wLength, 0);
297 	return usbd_do_request(dev, &req, 0);
298 }
299 
300 usbd_status
301 usbd_set_port_u1_timeout(struct usbd_device *dev, int port, int timeout)
302 {
303 	USBHIST_FUNC();
304 	USBHIST_CALLARGS(usbdebug, "dev %#jx port %jd timeout %.d",
305 	    (uintptr_t)dev, port, timeout, 0);
306 	usb_device_request_t req;
307 
308 	req.bmRequestType = UT_WRITE_CLASS_OTHER;
309 	req.bRequest = UR_SET_FEATURE;
310 	USETW(req.wValue, UHF_PORT_U1_TIMEOUT);
311 	USETW2(req.wIndex, timeout, port);
312 	USETW(req.wLength, 0);
313 	return usbd_do_request(dev, &req, 0);
314 }
315 
316 usbd_status
317 usbd_set_port_u2_timeout(struct usbd_device *dev, int port, int timeout)
318 {
319 	USBHIST_FUNC();
320 	USBHIST_CALLARGS(usbdebug, "dev %#jx port %jd timeout %jd",
321 	    (uintptr_t)dev, port, timeout, 0);
322 	usb_device_request_t req;
323 
324 	req.bmRequestType = UT_WRITE_CLASS_OTHER;
325 	req.bRequest = UR_SET_FEATURE;
326 	USETW(req.wValue, UHF_PORT_U2_TIMEOUT);
327 	USETW2(req.wIndex, timeout, port);
328 	USETW(req.wLength, 0);
329 	return usbd_do_request(dev, &req, 0);
330 }
331 
332 usbd_status
333 usbd_get_protocol(struct usbd_interface *iface, uint8_t *report)
334 {
335 	usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface);
336 	struct usbd_device *dev;
337 	usb_device_request_t req;
338 
339 	USBHIST_FUNC();
340 	USBHIST_CALLARGS(usbdebug, "iface=%#jx, endpt=%jd",
341 	    (uintptr_t)iface, id->bInterfaceNumber, 0, 0);
342 
343 	if (id == NULL)
344 		return USBD_IOERROR;
345 
346 	usbd_interface2device_handle(iface, &dev);
347 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
348 	req.bRequest = UR_GET_PROTOCOL;
349 	USETW(req.wValue, 0);
350 	USETW(req.wIndex, id->bInterfaceNumber);
351 	USETW(req.wLength, 1);
352 	return usbd_do_request(dev, &req, report);
353 }
354 
355 usbd_status
356 usbd_set_protocol(struct usbd_interface *iface, int report)
357 {
358 	usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface);
359 	struct usbd_device *dev;
360 	usb_device_request_t req;
361 
362 	USBHIST_FUNC();
363 	USBHIST_CALLARGS(usbdebug, "iface=%#jx, report=%jd, endpt=%jd",
364 	    (uintptr_t)iface, report, id->bInterfaceNumber, 0);
365 
366 	if (id == NULL)
367 		return USBD_IOERROR;
368 
369 	usbd_interface2device_handle(iface, &dev);
370 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
371 	req.bRequest = UR_SET_PROTOCOL;
372 	USETW(req.wValue, report);
373 	USETW(req.wIndex, id->bInterfaceNumber);
374 	USETW(req.wLength, 0);
375 	return usbd_do_request(dev, &req, 0);
376 }
377 
378 usbd_status
379 usbd_set_report(struct usbd_interface *iface, int type, int id, void *data,
380 		int len)
381 {
382 	usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
383 	struct usbd_device *dev;
384 	usb_device_request_t req;
385 
386 	USBHIST_FUNC();
387 	USBHIST_CALLARGS(usbdebug, "len=%jd", len, 0, 0, 0);
388 
389 	if (ifd == NULL)
390 		return USBD_IOERROR;
391 	usbd_interface2device_handle(iface, &dev);
392 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
393 	req.bRequest = UR_SET_REPORT;
394 	USETW2(req.wValue, type, id);
395 	USETW(req.wIndex, ifd->bInterfaceNumber);
396 	USETW(req.wLength, len);
397 	return usbd_do_request(dev, &req, data);
398 }
399 
400 usbd_status
401 usbd_get_report(struct usbd_interface *iface, int type, int id, void *data,
402 		int len)
403 {
404 	usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
405 	struct usbd_device *dev;
406 	usb_device_request_t req;
407 
408 	USBHIST_FUNC(); USBHIST_CALLARGS(usbdebug, "len=%jd", len, 0, 0, 0);
409 
410 	if (ifd == NULL)
411 		return USBD_IOERROR;
412 	usbd_interface2device_handle(iface, &dev);
413 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
414 	req.bRequest = UR_GET_REPORT;
415 	USETW2(req.wValue, type, id);
416 	USETW(req.wIndex, ifd->bInterfaceNumber);
417 	USETW(req.wLength, len);
418 	return usbd_do_request(dev, &req, data);
419 }
420 
421 usbd_status
422 usbd_set_idle(struct usbd_interface *iface, int duration, int id)
423 {
424 	usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
425 	struct usbd_device *dev;
426 	usb_device_request_t req;
427 
428 	USBHIST_FUNC();
429 	USBHIST_CALLARGS(usbdebug, "duration %jd id %jd", duration, id, 0, 0);
430 
431 	if (ifd == NULL)
432 		return USBD_IOERROR;
433 	usbd_interface2device_handle(iface, &dev);
434 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
435 	req.bRequest = UR_SET_IDLE;
436 	USETW2(req.wValue, duration, id);
437 	USETW(req.wIndex, ifd->bInterfaceNumber);
438 	USETW(req.wLength, 0);
439 	return usbd_do_request(dev, &req, 0);
440 }
441 
442 usbd_status
443 usbd_get_report_descriptor(struct usbd_device *dev, int ifcno,
444 			   int size, void *d)
445 {
446 	USBHIST_FUNC();
447 	USBHIST_CALLARGS(usbdebug, "dev %#jx ifcno %jd size %jd",
448 	    (uintptr_t)dev, ifcno, size, 0);
449 	usb_device_request_t req;
450 
451 	req.bmRequestType = UT_READ_INTERFACE;
452 	req.bRequest = UR_GET_DESCRIPTOR;
453 	USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */
454 	USETW(req.wIndex, ifcno);
455 	USETW(req.wLength, size);
456 	return usbd_do_request(dev, &req, d);
457 }
458 
459 usb_hid_descriptor_t *
460 usbd_get_hid_descriptor(struct usbd_interface *ifc)
461 {
462 	usb_interface_descriptor_t *idesc = usbd_get_interface_descriptor(ifc);
463 	struct usbd_device *dev;
464 	usb_config_descriptor_t *cdesc;
465 	usb_hid_descriptor_t *hd;
466 	char *p, *end;
467 
468 	if (idesc == NULL)
469 		return NULL;
470 	usbd_interface2device_handle(ifc, &dev);
471 	cdesc = usbd_get_config_descriptor(dev);
472 
473 	p = (char *)idesc + idesc->bLength;
474 	end = (char *)cdesc + UGETW(cdesc->wTotalLength);
475 
476 	for (; p < end; p += hd->bLength) {
477 		hd = (usb_hid_descriptor_t *)p;
478 		if (p + hd->bLength <= end &&
479 		    hd->bLength >= USB_HID_DESCRIPTOR_SIZE(0) &&
480 		    hd->bDescriptorType == UDESC_HID)
481 			return hd;
482 		if (hd->bDescriptorType == UDESC_INTERFACE)
483 			break;
484 	}
485 	return NULL;
486 }
487 
488 usbd_status
489 usbd_read_report_desc(struct usbd_interface *ifc, void **descp, int *sizep)
490 {
491 	usb_interface_descriptor_t *id;
492 	usb_hid_descriptor_t *hid;
493 	struct usbd_device *dev;
494 	usbd_status err;
495 
496 	usbd_interface2device_handle(ifc, &dev);
497 	id = usbd_get_interface_descriptor(ifc);
498 	if (id == NULL)
499 		return USBD_INVAL;
500 	hid = usbd_get_hid_descriptor(ifc);
501 	if (hid == NULL)
502 		return USBD_IOERROR;
503 	*sizep = UGETW(hid->descrs[0].wDescriptorLength);
504 	if (*sizep == 0)
505 		return USBD_INVAL;
506 	*descp = kmem_alloc(*sizep, KM_SLEEP);
507 	err = usbd_get_report_descriptor(dev, id->bInterfaceNumber,
508 					 *sizep, *descp);
509 	if (err) {
510 		kmem_free(*descp, *sizep);
511 		*descp = NULL;
512 		return err;
513 	}
514 	return USBD_NORMAL_COMPLETION;
515 }
516 
517 usbd_status
518 usbd_get_config(struct usbd_device *dev, uint8_t *conf)
519 {
520 	USBHIST_FUNC();
521 	USBHIST_CALLARGS(usbdebug, "dev %#jx", (uintptr_t)dev, 0, 0, 0);
522 	usb_device_request_t req;
523 
524 	req.bmRequestType = UT_READ_DEVICE;
525 	req.bRequest = UR_GET_CONFIG;
526 	USETW(req.wValue, 0);
527 	USETW(req.wIndex, 0);
528 	USETW(req.wLength, 1);
529 	return usbd_do_request(dev, &req, conf);
530 }
531 
532 usbd_status
533 usbd_bulk_transfer(struct usbd_xfer *xfer, struct usbd_pipe *pipe,
534     uint16_t flags, uint32_t timeout, void *buf, uint32_t *size)
535 {
536 	usbd_status err;
537 
538 	USBHIST_FUNC();
539 	USBHIST_CALLARGS(usbdebug, "start transfer %jd bytes", *size, 0, 0, 0);
540 
541 	usbd_setup_xfer(xfer, 0, buf, *size, flags, timeout, NULL);
542 	err = usbd_sync_transfer_sig(xfer);
543 
544 	usbd_get_xfer_status(xfer, NULL, NULL, size, NULL);
545 	DPRINTFN(1, "transferred %jd", *size, 0, 0, 0);
546 	if (err) {
547 		usbd_clear_endpoint_stall(pipe);
548 	}
549 	USBHIST_LOG(usbdebug, "<- done xfer %#jx err %d", (uintptr_t)xfer,
550 	    err, 0, 0);
551 
552 	return err;
553 }
554 
555 usbd_status
556 usbd_intr_transfer(struct usbd_xfer *xfer, struct usbd_pipe *pipe,
557     uint16_t flags, uint32_t timeout, void *buf, uint32_t *size)
558 {
559 	usbd_status err;
560 
561 	USBHIST_FUNC();
562 	USBHIST_CALLARGS(usbdebug, "start transfer %jd bytes", *size, 0, 0, 0);
563 
564 	usbd_setup_xfer(xfer, 0, buf, *size, flags, timeout, NULL);
565 
566 	err = usbd_sync_transfer_sig(xfer);
567 
568 	usbd_get_xfer_status(xfer, NULL, NULL, size, NULL);
569 
570 	DPRINTFN(1, "transferred %jd", *size, 0, 0, 0);
571 	if (err) {
572 		usbd_clear_endpoint_stall(pipe);
573 	}
574 	USBHIST_LOG(usbdebug, "<- done xfer %#jx err %jd", (uintptr_t)xfer,
575 	    err, 0, 0);
576 
577 	return err;
578 }
579 
580 void
581 usb_detach_waitold(device_t dv)
582 {
583 	USBHIST_FUNC();
584 	USBHIST_CALLARGS(usbdebug, "waiting for dv %#jx",
585 	    (uintptr_t)dv, 0, 0, 0);
586 
587 	if (tsleep(dv, PZERO, "usbdet", hz * 60)) /* XXXSMP ok */
588 		aprint_error_dev(dv, "usb_detach_waitold: didn't detach\n");
589 	DPRINTFN(1, "done", 0, 0, 0, 0);
590 }
591 
592 void
593 usb_detach_wakeupold(device_t dv)
594 {
595 	USBHIST_FUNC();
596 	USBHIST_CALLARGS(usbdebug, "for dv %#jx", (uintptr_t)dv, 0, 0, 0);
597 
598 	wakeup(dv); /* XXXSMP ok */
599 }
600 
601 const usb_cdc_descriptor_t *
602 usb_find_desc(struct usbd_device *dev, int type, int subtype)
603 {
604 	usbd_desc_iter_t iter;
605 	const usb_cdc_descriptor_t *desc;
606 
607 	usb_desc_iter_init(dev, &iter);
608 	for (;;) {
609 		desc = (const usb_cdc_descriptor_t *)usb_desc_iter_next(&iter);
610 		if (!desc || (desc->bDescriptorType == type &&
611 			      (subtype == USBD_CDCSUBTYPE_ANY ||
612 			       subtype == desc->bDescriptorSubtype)))
613 			break;
614 	}
615 	return desc;
616 }
617 
618 /* same as usb_find_desc(), but searches only in the specified interface. */
619 const usb_cdc_descriptor_t *
620 usb_find_desc_if(struct usbd_device *dev, int type, int subtype,
621 		 usb_interface_descriptor_t *id)
622 {
623 	usbd_desc_iter_t iter;
624 	const usb_cdc_descriptor_t *desc;
625 
626 	if (id == NULL)
627 		return usb_find_desc(dev, type, subtype);
628 
629 	usb_desc_iter_init(dev, &iter);
630 
631 	iter.cur = (void *)id;		/* start from the interface desc */
632 	usb_desc_iter_next(&iter);	/* and skip it */
633 
634 	while ((desc = (const usb_cdc_descriptor_t *)usb_desc_iter_next(&iter))
635 	       != NULL) {
636 		if (desc->bDescriptorType == UDESC_INTERFACE) {
637 			/* we ran into the next interface --- not found */
638 			return NULL;
639 		}
640 		if (desc->bDescriptorType == type &&
641 		    (subtype == USBD_CDCSUBTYPE_ANY ||
642 		     subtype == desc->bDescriptorSubtype))
643 			break;
644 	}
645 	return desc;
646 }
647