xref: /netbsd-src/sys/dev/usb/usbdi_util.c (revision dc306354b0b29af51801a7632f1e95265a68cd81)
1 /*	$NetBSD: usbdi_util.c,v 1.13 1999/01/08 11:58:26 augustss 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 (augustss@carlstedt.se) 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  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *        This product includes software developed by the NetBSD
22  *        Foundation, Inc. and its contributors.
23  * 4. Neither the name of The NetBSD Foundation nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
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 #if defined(__FreeBSD__)
46 #include <sys/bus.h>
47 #endif
48 
49 #include <dev/usb/usb.h>
50 #include <dev/usb/usbhid.h>
51 
52 #include <dev/usb/usbdi.h>
53 #include <dev/usb/usbdi_util.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(dev, type, index, len, desc)
66 	usbd_device_handle dev;
67 	int type, index;
68 	int len;
69 	void *desc;
70 {
71 	usb_device_request_t req;
72 
73 	req.bmRequestType = UT_READ_DEVICE;
74 	req.bRequest = UR_GET_DESCRIPTOR;
75 	USETW2(req.wValue, type, index);
76 	USETW(req.wIndex, 0);
77 	USETW(req.wLength, len);
78 	return (usbd_do_request(dev, &req, desc));
79 }
80 
81 usbd_status
82 usbd_get_config_desc(dev, conf, d)
83 	usbd_device_handle dev;
84 	int conf;
85 	usb_config_descriptor_t *d;
86 {
87 	usbd_status r;
88 
89 	DPRINTFN(3,("usbd_get_config_desc: conf=%d\n", conf));
90 	r = usbd_get_desc(dev, UDESC_CONFIG, conf,
91 			  USB_CONFIG_DESCRIPTOR_SIZE, d);
92 	if (r != USBD_NORMAL_COMPLETION)
93 		return (r);
94 	if (d->bDescriptorType != UDESC_CONFIG) {
95 		DPRINTFN(-1,("usbd_get_config_desc: conf %d, bad desc %d\n",
96 			     conf, d->bDescriptorType));
97 		return (USBD_INVAL);
98 	}
99 	return (USBD_NORMAL_COMPLETION);
100 }
101 
102 usbd_status
103 usbd_get_config_desc_full(dev, conf, d, size)
104 	usbd_device_handle dev;
105 	int conf;
106 	void *d;
107 	int size;
108 {
109 	DPRINTFN(3,("usbd_get_config_desc_full: conf=%d\n", conf));
110 	return (usbd_get_desc(dev, UDESC_CONFIG, conf, size, d));
111 }
112 
113 usbd_status
114 usbd_get_device_desc(dev, d)
115 	usbd_device_handle dev;
116 	usb_device_descriptor_t *d;
117 {
118 	DPRINTFN(3,("usbd_get_device_desc:\n"));
119 	return (usbd_get_desc(dev, UDESC_DEVICE,
120 			     0, USB_DEVICE_DESCRIPTOR_SIZE, d));
121 }
122 
123 usbd_status
124 usbd_get_device_status(dev, st)
125 	usbd_device_handle dev;
126 	usb_status_t *st;
127 {
128 	usb_device_request_t req;
129 
130 	req.bmRequestType = UT_READ_DEVICE;
131 	req.bRequest = UR_GET_STATUS;
132 	USETW(req.wValue, 0);
133 	USETW(req.wIndex, 0);
134 	USETW(req.wLength, sizeof(usb_status_t));
135 	return (usbd_do_request(dev, &req, st));
136 }
137 
138 usbd_status
139 usbd_get_hub_status(dev, st)
140 	usbd_device_handle dev;
141 	usb_hub_status_t *st;
142 {
143 	usb_device_request_t req;
144 
145 	req.bmRequestType = UT_READ_CLASS_DEVICE;
146 	req.bRequest = UR_GET_STATUS;
147 	USETW(req.wValue, 0);
148 	USETW(req.wIndex, 0);
149 	USETW(req.wLength, sizeof(usb_hub_status_t));
150 	return (usbd_do_request(dev, &req, st));
151 }
152 
153 usbd_status
154 usbd_set_address(dev, addr)
155 	usbd_device_handle dev;
156 	int addr;
157 {
158 	usb_device_request_t req;
159 
160 	req.bmRequestType = UT_WRITE_DEVICE;
161 	req.bRequest = UR_SET_ADDRESS;
162 	USETW(req.wValue, addr);
163 	USETW(req.wIndex, 0);
164 	USETW(req.wLength, 0);
165 	return usbd_do_request(dev, &req, 0);
166 }
167 
168 usbd_status
169 usbd_get_port_status(dev, port, ps)
170 	usbd_device_handle dev;
171 	int port;
172 	usb_port_status_t *ps;
173 {
174 	usb_device_request_t req;
175 
176 	req.bmRequestType = UT_READ_CLASS_OTHER;
177 	req.bRequest = UR_GET_STATUS;
178 	USETW(req.wValue, 0);
179 	USETW(req.wIndex, port);
180 	USETW(req.wLength, sizeof *ps);
181 	return (usbd_do_request(dev, &req, ps));
182 }
183 
184 usbd_status
185 usbd_clear_port_feature(dev, port, sel)
186 	usbd_device_handle dev;
187 	int port, 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(dev, port, sel)
201 	usbd_device_handle dev;
202 	int port, sel;
203 {
204 	usb_device_request_t req;
205 
206 	req.bmRequestType = UT_WRITE_CLASS_OTHER;
207 	req.bRequest = UR_SET_FEATURE;
208 	USETW(req.wValue, sel);
209 	USETW(req.wIndex, port);
210 	USETW(req.wLength, 0);
211 	return (usbd_do_request(dev, &req, 0));
212 }
213 
214 
215 usbd_status
216 usbd_set_protocol(iface, report)
217 	usbd_interface_handle iface;
218 	int report;
219 {
220 	usb_interface_descriptor_t *id = usbd_get_interface_descriptor(iface);
221 	usbd_device_handle dev;
222 	usb_device_request_t req;
223 	usbd_status r;
224 
225 	DPRINTFN(4, ("usbd_set_protocol: iface=%p, report=%d, endpt=%d\n",
226 		     iface, report, id->bInterfaceNumber));
227 	if (!id)
228 		return (USBD_IOERROR);
229 	r = usbd_interface2device_handle(iface, &dev);
230 	if (r != USBD_NORMAL_COMPLETION)
231 		return (r);
232 	if (!id)
233 		return (USBD_INVAL);
234 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
235 	req.bRequest = UR_SET_PROTOCOL;
236 	USETW(req.wValue, report);
237 	USETW(req.wIndex, id->bInterfaceNumber);
238 	USETW(req.wLength, 0);
239 	return (usbd_do_request(dev, &req, 0));
240 }
241 
242 usbd_status
243 usbd_set_report(iface, type, id, data, len)
244 	usbd_interface_handle iface;
245 	int type;
246 	int id;
247 	void *data;
248 	int len;
249 {
250 	usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
251 	usbd_device_handle dev;
252 	usb_device_request_t req;
253 	usbd_status r;
254 
255 	DPRINTFN(4, ("usbd_set_report: len=%d\n", len));
256 	if (!ifd)
257 		return (USBD_IOERROR);
258 	r = usbd_interface2device_handle(iface, &dev);
259 	if (r != USBD_NORMAL_COMPLETION)
260 		return (r);
261 	if (!ifd)
262 		return (USBD_INVAL);
263 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
264 	req.bRequest = UR_SET_REPORT;
265 	USETW2(req.wValue, type, id);
266 	USETW(req.wIndex, ifd->bInterfaceNumber);
267 	USETW(req.wLength, len);
268 	return (usbd_do_request(dev, &req, data));
269 }
270 
271 usbd_status
272 usbd_set_report_async(iface, type, id, data, len)
273 	usbd_interface_handle iface;
274 	int type;
275 	int id;
276 	void *data;
277 	int len;
278 {
279 	usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
280 	usbd_device_handle dev;
281 	usb_device_request_t req;
282 	usbd_status r;
283 
284 	DPRINTFN(4, ("usbd_set_report_async: len=%d\n", len));
285 	if (!ifd)
286 		return (USBD_IOERROR);
287 	r = usbd_interface2device_handle(iface, &dev);
288 	if (r != USBD_NORMAL_COMPLETION)
289 		return (r);
290 	if (!ifd)
291 		return (USBD_INVAL);
292 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
293 	req.bRequest = UR_SET_REPORT;
294 	USETW2(req.wValue, type, id);
295 	USETW(req.wIndex, ifd->bInterfaceNumber);
296 	USETW(req.wLength, len);
297 	return (usbd_do_request_async(dev, &req, data));
298 }
299 
300 usbd_status
301 usbd_get_report(iface, type, id, data, len)
302 	usbd_interface_handle iface;
303 	int type;
304 	int id;
305 	void *data;
306 	int len;
307 {
308 	usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
309 	usbd_device_handle dev;
310 	usb_device_request_t req;
311 	usbd_status r;
312 
313 	DPRINTFN(4, ("usbd_set_report: len=%d\n", len));
314 	if (!id)
315 		return (USBD_IOERROR);
316 	r = usbd_interface2device_handle(iface, &dev);
317 	if (r != USBD_NORMAL_COMPLETION)
318 		return (r);
319 	if (!ifd)
320 		return (USBD_INVAL);
321 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
322 	req.bRequest = UR_GET_REPORT;
323 	USETW2(req.wValue, type, id);
324 	USETW(req.wIndex, ifd->bInterfaceNumber);
325 	USETW(req.wLength, len);
326 	return (usbd_do_request(dev, &req, data));
327 }
328 
329 usbd_status
330 usbd_set_idle(iface, duration, id)
331 	usbd_interface_handle iface;
332 	int duration;
333 	int id;
334 {
335 	usb_interface_descriptor_t *ifd = usbd_get_interface_descriptor(iface);
336 	usbd_device_handle dev;
337 	usb_device_request_t req;
338 	usbd_status r;
339 
340 	DPRINTFN(4, ("usbd_set_idle: %d %d\n", duration, id));
341 	if (!ifd)
342 		return (USBD_IOERROR);
343 	r = usbd_interface2device_handle(iface, &dev);
344 	if (r != USBD_NORMAL_COMPLETION)
345 		return (r);
346 	if (!ifd)
347 		return (USBD_INVAL);
348 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
349 	req.bRequest = UR_SET_IDLE;
350 	USETW2(req.wValue, duration, id);
351 	USETW(req.wIndex, ifd->bInterfaceNumber);
352 	USETW(req.wLength, 0);
353 	return (usbd_do_request(dev, &req, 0));
354 }
355 
356 usbd_status
357 usbd_get_report_descriptor(dev, ifcno, repid, size, d)
358 	usbd_device_handle dev;
359 	int ifcno;
360 	int repid;
361 	int size;
362 	void *d;
363 {
364 	usb_device_request_t req;
365 
366 	req.bmRequestType = UT_READ_INTERFACE;
367 	req.bRequest = UR_GET_DESCRIPTOR;
368 	USETW2(req.wValue, UDESC_REPORT, repid);
369 	USETW(req.wIndex, ifcno);
370 	USETW(req.wLength, size);
371 	return (usbd_do_request(dev, &req, d));
372 }
373 
374 usb_hid_descriptor_t *
375 usbd_get_hid_descriptor(ifc)
376 	usbd_interface_handle ifc;
377 {
378 	usb_interface_descriptor_t *idesc = usbd_get_interface_descriptor(ifc);
379 	usbd_device_handle dev;
380 	usb_config_descriptor_t *cdesc;
381 	usb_hid_descriptor_t *hd;
382 	char *p, *end;
383 	usbd_status r;
384 
385 	if (!idesc)
386 		return (0);
387 	r = usbd_interface2device_handle(ifc, &dev);
388 	if (r != USBD_NORMAL_COMPLETION)
389 		return (0);
390 	cdesc = usbd_get_config_descriptor(dev);
391 
392 	p = (char *)idesc + idesc->bLength;
393 	end = (char *)cdesc + UGETW(cdesc->wTotalLength);
394 
395 	for (; p < end; p += hd->bLength) {
396 		hd = (usb_hid_descriptor_t *)p;
397 		if (p + hd->bLength <= end && hd->bDescriptorType == UDESC_HID)
398 			return (hd);
399 		if (hd->bDescriptorType == UDESC_INTERFACE)
400 			break;
401 	}
402 	return (0);
403 }
404 
405 usbd_status
406 usbd_alloc_report_desc(ifc, descp, sizep, mem)
407 	usbd_interface_handle ifc;
408 	void **descp;
409 	int *sizep;
410 #if defined(__NetBSD__)
411 	int mem;
412 #elif defined(__FreeBSD__)
413 	struct malloc_type *mem;
414 #endif
415 
416 {
417 	usb_interface_descriptor_t *id;
418 	usb_hid_descriptor_t *hid;
419 	usbd_device_handle dev;
420 	usbd_status r;
421 
422 	r = usbd_interface2device_handle(ifc, &dev);
423 	if (r != USBD_NORMAL_COMPLETION)
424 		return (r);
425 	id = usbd_get_interface_descriptor(ifc);
426 	if (!id)
427 		return (USBD_INVAL);
428 	hid = usbd_get_hid_descriptor(ifc);
429 	if (!hid)
430 		return (USBD_IOERROR);
431 	*sizep = UGETW(hid->descrs[0].wDescriptorLength);
432 	*descp = malloc(*sizep, mem, M_NOWAIT);
433 	if (!*descp)
434 		return (USBD_NOMEM);
435 	/* XXX should not use 0 Report ID */
436 	r = usbd_get_report_descriptor(dev, id->bInterfaceNumber, 0,
437 				       *sizep, *descp);
438 	if (r != USBD_NORMAL_COMPLETION) {
439 		free(*descp, mem);
440 		return (r);
441 	}
442 	return (USBD_NORMAL_COMPLETION);
443 }
444 
445 usbd_status
446 usbd_get_config(dev, conf)
447 	usbd_device_handle dev;
448 	u_int8_t *conf;
449 {
450 	usb_device_request_t req;
451 
452 	req.bmRequestType = UT_READ_DEVICE;
453 	req.bRequest = UR_GET_CONFIG;
454 	USETW(req.wValue, 0);
455 	USETW(req.wIndex, 0);
456 	USETW(req.wLength, 1);
457 	return (usbd_do_request(dev, &req, conf));
458 }
459 
460 static void usbd_bulk_transfer_cb __P((usbd_request_handle reqh,
461 		usbd_private_handle priv, usbd_status status));
462 static void
463 usbd_bulk_transfer_cb(reqh, priv, status)
464 	usbd_request_handle reqh;
465 	usbd_private_handle priv;
466 	usbd_status status;
467 {
468 	wakeup(reqh);
469 }
470 
471 usbd_status
472 usbd_bulk_transfer(reqh, pipe, flags, buf, size, lbl)
473 	usbd_request_handle reqh;
474 	usbd_pipe_handle pipe;
475 	u_int16_t flags;
476 	void *buf;
477 	u_int32_t *size;
478 	char *lbl;
479 {
480 	usbd_private_handle priv;
481 	void *buffer;
482 	usbd_status r;
483 	int s, error;
484 
485 	r = usbd_setup_request(reqh, pipe, 0, buf, *size,
486 			       flags, USBD_NO_TIMEOUT, usbd_bulk_transfer_cb);
487 	if (r != USBD_NORMAL_COMPLETION)
488 		return (r);
489 	DPRINTFN(1, ("usbd_bulk_transfer: start transfer %d bytes\n", *size));
490 	s = splusb();		/* don't want callback until tsleep() */
491 	r = usbd_transfer(reqh);
492 	if (r != USBD_IN_PROGRESS) {
493 		splx(s);
494 		return (r);
495 	}
496 	error = tsleep((caddr_t)reqh, PZERO | PCATCH, lbl, 0);
497 	splx(s);
498 	if (error) {
499 		usbd_abort_pipe(pipe);
500 		return (USBD_INTERRUPTED);
501 	}
502 	usbd_get_request_status(reqh, &priv, &buffer, size, &r);
503 	DPRINTFN(1,("usbd_bulk_transfer: transferred %d\n", *size));
504 	if (r != USBD_NORMAL_COMPLETION) {
505 		DPRINTF(("usbd_bulk_transfer: error=%d\n", r));
506 		usbd_clear_endpoint_stall(pipe);
507 	}
508 	return (r);
509 }
510 
511