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