xref: /netbsd-src/sys/dev/usb/usbdi_util.c (revision 6cf6fe02a981b55727c49c3d37b0d8191a98c0ee)
1 /*	$NetBSD: usbdi_util.c,v 1.63 2014/09/12 16:40:38 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.63 2014/09/12 16:40:38 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/malloc.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 #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(usbd_device_handle dev, int type, int index, int len, void *desc)
66 {
67 	usb_device_request_t req;
68 
69 	DPRINTFN(3,("usbd_get_desc: type=%d, index=%d, len=%d\n",
70 		    type, index, len));
71 
72 	req.bmRequestType = UT_READ_DEVICE;
73 	req.bRequest = UR_GET_DESCRIPTOR;
74 	USETW2(req.wValue, type, index);
75 	USETW(req.wIndex, 0);
76 	USETW(req.wLength, len);
77 	return (usbd_do_request(dev, &req, desc));
78 }
79 
80 usbd_status
81 usbd_get_config_desc(usbd_device_handle dev, int confidx,
82 		     usb_config_descriptor_t *d)
83 {
84 	usbd_status err;
85 
86 	DPRINTFN(3,("usbd_get_config_desc: confidx=%d\n", confidx));
87 	err = usbd_get_desc(dev, UDESC_CONFIG, confidx,
88 			    USB_CONFIG_DESCRIPTOR_SIZE, d);
89 	if (err)
90 		return (err);
91 	if (d->bDescriptorType != UDESC_CONFIG) {
92 		DPRINTFN(-1,("usbd_get_config_desc: confidx=%d, bad desc "
93 			     "len=%d type=%d\n",
94 			     confidx, d->bLength, d->bDescriptorType));
95 		return (USBD_INVAL);
96 	}
97 	return (USBD_NORMAL_COMPLETION);
98 }
99 
100 usbd_status
101 usbd_get_config_desc_full(usbd_device_handle dev, int conf, void *d, int size)
102 {
103 	DPRINTFN(3,("usbd_get_config_desc_full: conf=%d\n", conf));
104 	return (usbd_get_desc(dev, UDESC_CONFIG, conf, size, d));
105 }
106 
107 usbd_status
108 usbd_get_device_desc(usbd_device_handle dev, usb_device_descriptor_t *d)
109 {
110 	DPRINTFN(3,("usbd_get_device_desc:\n"));
111 	return (usbd_get_desc(dev, UDESC_DEVICE,
112 			     0, USB_DEVICE_DESCRIPTOR_SIZE, d));
113 }
114 
115 usbd_status
116 usbd_get_device_status(usbd_device_handle dev, usb_status_t *st)
117 {
118 	usb_device_request_t req;
119 
120 	req.bmRequestType = UT_READ_DEVICE;
121 	req.bRequest = UR_GET_STATUS;
122 	USETW(req.wValue, 0);
123 	USETW(req.wIndex, 0);
124 	USETW(req.wLength, sizeof(usb_status_t));
125 	return (usbd_do_request(dev, &req, st));
126 }
127 
128 usbd_status
129 usbd_get_hub_status(usbd_device_handle dev, usb_hub_status_t *st)
130 {
131 	usb_device_request_t req;
132 
133 	req.bmRequestType = UT_READ_CLASS_DEVICE;
134 	req.bRequest = UR_GET_STATUS;
135 	USETW(req.wValue, 0);
136 	USETW(req.wIndex, 0);
137 	USETW(req.wLength, sizeof(usb_hub_status_t));
138 	return (usbd_do_request(dev, &req, st));
139 }
140 
141 usbd_status
142 usbd_set_address(usbd_device_handle dev, int addr)
143 {
144 	usb_device_request_t req;
145 
146 	req.bmRequestType = UT_WRITE_DEVICE;
147 	req.bRequest = UR_SET_ADDRESS;
148 	USETW(req.wValue, addr);
149 	USETW(req.wIndex, 0);
150 	USETW(req.wLength, 0);
151 	return usbd_do_request(dev, &req, 0);
152 }
153 
154 usbd_status
155 usbd_get_port_status(usbd_device_handle dev, int port, usb_port_status_t *ps)
156 {
157 	usb_device_request_t req;
158 
159 	req.bmRequestType = UT_READ_CLASS_OTHER;
160 	req.bRequest = UR_GET_STATUS;
161 	USETW(req.wValue, 0);
162 	USETW(req.wIndex, port);
163 	USETW(req.wLength, sizeof *ps);
164 	return (usbd_do_request(dev, &req, ps));
165 }
166 
167 usbd_status
168 usbd_clear_hub_feature(usbd_device_handle dev, int sel)
169 {
170 	usb_device_request_t req;
171 
172 	req.bmRequestType = UT_WRITE_CLASS_DEVICE;
173 	req.bRequest = UR_CLEAR_FEATURE;
174 	USETW(req.wValue, sel);
175 	USETW(req.wIndex, 0);
176 	USETW(req.wLength, 0);
177 	return (usbd_do_request(dev, &req, 0));
178 }
179 
180 usbd_status
181 usbd_set_hub_feature(usbd_device_handle dev, int sel)
182 {
183 	usb_device_request_t req;
184 
185 	req.bmRequestType = UT_WRITE_CLASS_DEVICE;
186 	req.bRequest = UR_SET_FEATURE;
187 	USETW(req.wValue, sel);
188 	USETW(req.wIndex, 0);
189 	USETW(req.wLength, 0);
190 	return (usbd_do_request(dev, &req, 0));
191 }
192 
193 usbd_status
194 usbd_clear_port_feature(usbd_device_handle dev, int port, int sel)
195 {
196 	usb_device_request_t req;
197 
198 	req.bmRequestType = UT_WRITE_CLASS_OTHER;
199 	req.bRequest = UR_CLEAR_FEATURE;
200 	USETW(req.wValue, sel);
201 	USETW(req.wIndex, port);
202 	USETW(req.wLength, 0);
203 	return (usbd_do_request(dev, &req, 0));
204 }
205 
206 usbd_status
207 usbd_set_port_feature(usbd_device_handle dev, int port, int sel)
208 {
209 	usb_device_request_t req;
210 
211 	req.bmRequestType = UT_WRITE_CLASS_OTHER;
212 	req.bRequest = UR_SET_FEATURE;
213 	USETW(req.wValue, sel);
214 	USETW(req.wIndex, port);
215 	USETW(req.wLength, 0);
216 	return (usbd_do_request(dev, &req, 0));
217 }
218 
219 usbd_status
220 usbd_get_protocol(usbd_interface_handle iface, u_int8_t *report)
221 {
222 	usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface);
223 	usbd_device_handle dev;
224 	usb_device_request_t req;
225 
226 	DPRINTFN(4, ("usbd_get_protocol: iface=%p, endpt=%d\n",
227 		     iface, id->bInterfaceNumber));
228 	if (id == NULL)
229 		return (USBD_IOERROR);
230 	usbd_interface2device_handle(iface, &dev);
231 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
232 	req.bRequest = UR_GET_PROTOCOL;
233 	USETW(req.wValue, 0);
234 	USETW(req.wIndex, id->bInterfaceNumber);
235 	USETW(req.wLength, 1);
236 	return (usbd_do_request(dev, &req, report));
237 }
238 
239 usbd_status
240 usbd_set_protocol(usbd_interface_handle iface, int report)
241 {
242 	usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface);
243 	usbd_device_handle dev;
244 	usb_device_request_t req;
245 
246 	DPRINTFN(4, ("usbd_set_protocol: iface=%p, report=%d, endpt=%d\n",
247 		     iface, report, id->bInterfaceNumber));
248 	if (id == NULL)
249 		return (USBD_IOERROR);
250 	usbd_interface2device_handle(iface, &dev);
251 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
252 	req.bRequest = UR_SET_PROTOCOL;
253 	USETW(req.wValue, report);
254 	USETW(req.wIndex, id->bInterfaceNumber);
255 	USETW(req.wLength, 0);
256 	return (usbd_do_request(dev, &req, 0));
257 }
258 
259 usbd_status
260 usbd_set_report(usbd_interface_handle iface, int type, int id, void *data,
261 		int len)
262 {
263 	usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
264 	usbd_device_handle dev;
265 	usb_device_request_t req;
266 
267 	DPRINTFN(4, ("usbd_set_report: len=%d\n", len));
268 	if (ifd == NULL)
269 		return (USBD_IOERROR);
270 	usbd_interface2device_handle(iface, &dev);
271 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
272 	req.bRequest = UR_SET_REPORT;
273 	USETW2(req.wValue, type, id);
274 	USETW(req.wIndex, ifd->bInterfaceNumber);
275 	USETW(req.wLength, len);
276 	return (usbd_do_request(dev, &req, data));
277 }
278 
279 usbd_status
280 usbd_get_report(usbd_interface_handle iface, int type, int id, void *data,
281 		int len)
282 {
283 	usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
284 	usbd_device_handle dev;
285 	usb_device_request_t req;
286 
287 	DPRINTFN(4, ("usbd_get_report: len=%d\n", len));
288 	if (ifd == NULL)
289 		return (USBD_IOERROR);
290 	usbd_interface2device_handle(iface, &dev);
291 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
292 	req.bRequest = UR_GET_REPORT;
293 	USETW2(req.wValue, type, id);
294 	USETW(req.wIndex, ifd->bInterfaceNumber);
295 	USETW(req.wLength, len);
296 	return (usbd_do_request(dev, &req, data));
297 }
298 
299 usbd_status
300 usbd_set_idle(usbd_interface_handle iface, int duration, int id)
301 {
302 	usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
303 	usbd_device_handle dev;
304 	usb_device_request_t req;
305 
306 	DPRINTFN(4, ("usbd_set_idle: %d %d\n", duration, id));
307 	if (ifd == NULL)
308 		return (USBD_IOERROR);
309 	usbd_interface2device_handle(iface, &dev);
310 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
311 	req.bRequest = UR_SET_IDLE;
312 	USETW2(req.wValue, duration, id);
313 	USETW(req.wIndex, ifd->bInterfaceNumber);
314 	USETW(req.wLength, 0);
315 	return (usbd_do_request(dev, &req, 0));
316 }
317 
318 usbd_status
319 usbd_get_report_descriptor(usbd_device_handle dev, int ifcno,
320 			   int size, void *d)
321 {
322 	usb_device_request_t req;
323 
324 	req.bmRequestType = UT_READ_INTERFACE;
325 	req.bRequest = UR_GET_DESCRIPTOR;
326 	USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */
327 	USETW(req.wIndex, ifcno);
328 	USETW(req.wLength, size);
329 	return (usbd_do_request(dev, &req, d));
330 }
331 
332 usb_hid_descriptor_t *
333 usbd_get_hid_descriptor(usbd_interface_handle ifc)
334 {
335 	usb_interface_descriptor_t *idesc = usbd_get_interface_descriptor(ifc);
336 	usbd_device_handle dev;
337 	usb_config_descriptor_t *cdesc;
338 	usb_hid_descriptor_t *hd;
339 	char *p, *end;
340 
341 	if (idesc == NULL)
342 		return (NULL);
343 	usbd_interface2device_handle(ifc, &dev);
344 	cdesc = usbd_get_config_descriptor(dev);
345 
346 	p = (char *)idesc + idesc->bLength;
347 	end = (char *)cdesc + UGETW(cdesc->wTotalLength);
348 
349 	for (; p < end; p += hd->bLength) {
350 		hd = (usb_hid_descriptor_t *)p;
351 		if (p + hd->bLength <= end && hd->bDescriptorType == UDESC_HID)
352 			return (hd);
353 		if (hd->bDescriptorType == UDESC_INTERFACE)
354 			break;
355 	}
356 	return (NULL);
357 }
358 
359 usbd_status
360 usbd_read_report_desc(usbd_interface_handle ifc, void **descp, int *sizep,
361 		       struct malloc_type * mem)
362 {
363 	usb_interface_descriptor_t *id;
364 	usb_hid_descriptor_t *hid;
365 	usbd_device_handle dev;
366 	usbd_status err;
367 
368 	usbd_interface2device_handle(ifc, &dev);
369 	id = usbd_get_interface_descriptor(ifc);
370 	if (id == NULL)
371 		return (USBD_INVAL);
372 	hid = usbd_get_hid_descriptor(ifc);
373 	if (hid == NULL)
374 		return (USBD_IOERROR);
375 	*sizep = UGETW(hid->descrs[0].wDescriptorLength);
376 	*descp = malloc(*sizep, mem, M_NOWAIT);
377 	if (*descp == NULL)
378 		return (USBD_NOMEM);
379 	err = usbd_get_report_descriptor(dev, id->bInterfaceNumber,
380 					 *sizep, *descp);
381 	if (err) {
382 		free(*descp, mem);
383 		*descp = NULL;
384 		return (err);
385 	}
386 	return (USBD_NORMAL_COMPLETION);
387 }
388 
389 usbd_status
390 usbd_get_config(usbd_device_handle dev, u_int8_t *conf)
391 {
392 	usb_device_request_t req;
393 
394 	req.bmRequestType = UT_READ_DEVICE;
395 	req.bRequest = UR_GET_CONFIG;
396 	USETW(req.wValue, 0);
397 	USETW(req.wIndex, 0);
398 	USETW(req.wLength, 1);
399 	return (usbd_do_request(dev, &req, conf));
400 }
401 
402 usbd_status
403 usbd_bulk_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
404 		   u_int16_t flags, u_int32_t timeout, void *buf,
405 		   u_int32_t *size, const char *lbl)
406 {
407 	usbd_status err;
408 
409 	USBHIST_FUNC(); USBHIST_CALLED(usbdebug);
410 
411 	usbd_setup_xfer(xfer, pipe, 0, buf, *size, flags, timeout, NULL);
412 	DPRINTFN(1, ("usbd_bulk_transfer: start transfer %d bytes\n", *size));
413 	err = usbd_sync_transfer_sig(xfer);
414 
415 	usbd_get_xfer_status(xfer, NULL, NULL, size, NULL);
416 	DPRINTFN(1,("usbd_bulk_transfer: transferred %d\n", *size));
417 	if (err) {
418 		DPRINTF(("usbd_bulk_transfer: error=%d\n", err));
419 		usbd_clear_endpoint_stall(pipe);
420 	}
421 	USBHIST_LOG(usbdebug, "<- done err %d", xfer, err, 0, 0);
422 
423 	return (err);
424 }
425 
426 usbd_status
427 usbd_intr_transfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe,
428 		   u_int16_t flags, u_int32_t timeout, void *buf,
429 		   u_int32_t *size, const char *lbl)
430 {
431 	usbd_status err;
432 
433 	USBHIST_FUNC(); USBHIST_CALLED(usbdebug);
434 
435 	usbd_setup_xfer(xfer, pipe, 0, buf, *size, flags, timeout, NULL);
436 
437 	DPRINTFN(1, ("usbd_intr_transfer: start transfer %d bytes\n", *size));
438 	err = usbd_sync_transfer_sig(xfer);
439 
440 	usbd_get_xfer_status(xfer, NULL, NULL, size, NULL);
441 
442 	DPRINTFN(1,("usbd_intr_transfer: transferred %d\n", *size));
443 	if (err) {
444 		DPRINTF(("usbd_intr_transfer: error=%d\n", err));
445 		usbd_clear_endpoint_stall(pipe);
446 	}
447 	USBHIST_LOG(usbdebug, "<- done err %d", xfer, err, 0, 0);
448 
449 	return (err);
450 }
451 
452 void
453 usb_detach_wait(device_t dv, kcondvar_t *cv, kmutex_t *lock)
454 {
455 	DPRINTF(("usb_detach_wait: waiting for %s\n", device_xname(dv)));
456 	if (cv_timedwait(cv, lock, hz * 60))	// dv, PZERO, "usbdet", hz * 60
457 		printf("usb_detach_wait: %s didn't detach\n",
458 		        device_xname(dv));
459 	DPRINTF(("usb_detach_wait: %s done\n", device_xname(dv)));
460 }
461 
462 void
463 usb_detach_broadcast(device_t dv, kcondvar_t *cv)
464 {
465 	DPRINTF(("usb_detach_broadcast: for %s\n", device_xname(dv)));
466 	cv_broadcast(cv);
467 }
468 
469 void
470 usb_detach_waitold(device_t dv)
471 {
472 	DPRINTF(("usb_detach_waitold: waiting for %s\n", device_xname(dv)));
473 	if (tsleep(dv, PZERO, "usbdet", hz * 60)) /* XXXSMP ok */
474 		printf("usb_detach_waitold: %s didn't detach\n",
475 		        device_xname(dv));
476 	DPRINTF(("usb_detach_waitold: %s done\n", device_xname(dv)));
477 }
478 
479 void
480 usb_detach_wakeupold(device_t dv)
481 {
482 	DPRINTF(("usb_detach_wakeupold: for %s\n", device_xname(dv)));
483 	wakeup(dv); /* XXXSMP ok */
484 }
485 
486 const usb_cdc_descriptor_t *
487 usb_find_desc(usbd_device_handle dev, int type, int subtype)
488 {
489 	usbd_desc_iter_t iter;
490 	const usb_cdc_descriptor_t *desc;
491 
492 	usb_desc_iter_init(dev, &iter);
493 	for (;;) {
494 		desc = (const usb_cdc_descriptor_t *)usb_desc_iter_next(&iter);
495 		if (!desc || (desc->bDescriptorType == type &&
496 			      (subtype == USBD_CDCSUBTYPE_ANY ||
497 			       subtype == desc->bDescriptorSubtype)))
498 			break;
499 	}
500 	return desc;
501 }
502 
503 /* same as usb_find_desc(), but searches only in the specified interface. */
504 const usb_cdc_descriptor_t *
505 usb_find_desc_if(usbd_device_handle dev, int type, int subtype,
506 		 usb_interface_descriptor_t *id)
507 {
508 	usbd_desc_iter_t iter;
509 	const usb_cdc_descriptor_t *desc;
510 
511 	if (id == NULL)
512 		return usb_find_desc(dev, type, subtype);
513 
514 	usb_desc_iter_init(dev, &iter);
515 
516 	iter.cur = (void *)id;		/* start from the interface desc */
517 	usb_desc_iter_next(&iter);	/* and skip it */
518 
519 	while ((desc = (const usb_cdc_descriptor_t *)usb_desc_iter_next(&iter))
520 	       != NULL) {
521 		if (desc->bDescriptorType == UDESC_INTERFACE) {
522 			/* we ran into the next interface --- not found */
523 			return NULL;
524 		}
525 		if (desc->bDescriptorType == type &&
526 		    (subtype == USBD_CDCSUBTYPE_ANY ||
527 		     subtype == desc->bDescriptorSubtype))
528 			break;
529 	}
530 	return desc;
531 }
532