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