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