1 /* $NetBSD: uhidev.c,v 1.45 2009/11/12 19:58:27 dyoung Exp $ */
4 * Copyright (c) 2001 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (lennart@augustsson.net) at
9 * Carlstedt Research & Technology.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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.
34 * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: uhidev.c,v 1.45 2009/11/12 19:58:27 dyoung Exp $");
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/malloc.h>
44 #include <sys/signalvar.h>
45 #include <sys/device.h>
46 #include <sys/ioctl.h>
49 #include <dev/usb/usb.h>
50 #include <dev/usb/usbhid.h>
52 #include <dev/usb/usbdevs.h>
53 #include <dev/usb/usbdi.h>
54 #include <dev/usb/usbdi_util.h>
55 #include <dev/usb/hid.h>
56 #include <dev/usb/usb_quirks.h>
58 #include <dev/usb/uhidev.h>
60 /* Report descriptor for broken Wacom Graphire */
61 #include <dev/usb/ugraphire_rdesc.h>
66 #define DPRINTF(x) if (uhidevdebug) logprintf x
67 #define DPRINTFN(n,x) if (uhidevdebug>(n)) logprintf x
74 Static
void uhidev_intr(usbd_xfer_handle
, usbd_private_handle
, usbd_status
);
76 Static
int uhidev_maxrepid(void *, int);
77 Static
int uhidevprint(void *, const char *);
79 int uhidev_match(device_t
, cfdata_t
, void *);
80 void uhidev_attach(device_t
, device_t
, void *);
81 void uhidev_childdet(device_t
, device_t
);
82 int uhidev_detach(device_t
, int);
83 int uhidev_activate(device_t
, enum devact
);
84 extern struct cfdriver uhidev_cd
;
85 CFATTACH_DECL2_NEW(uhidev
, sizeof(struct uhidev_softc
), uhidev_match
,
86 uhidev_attach
, uhidev_detach
, uhidev_activate
, NULL
, uhidev_childdet
);
90 USB_IFMATCH_START(uhidev
, uaa
);
92 if (uaa
->class != UICLASS_HID
)
94 if (usbd_get_quirks(uaa
->device
)->uq_flags
& UQ_HID_IGNORE
)
96 return (UMATCH_IFACECLASS_GENERIC
);
101 USB_IFATTACH_START(uhidev
, sc
, uaa
);
102 usbd_interface_handle iface
= uaa
->iface
;
103 usb_interface_descriptor_t
*id
;
104 usb_endpoint_descriptor_t
*ed
;
105 struct uhidev_attach_arg uha
;
108 int maxinpktsize
, size
, nrepid
, repid
, repsz
;
115 int locs
[UHIDBUSCF_NLOCS
];
118 sc
->sc_udev
= uaa
->device
;
119 sc
->sc_iface
= iface
;
124 id
= usbd_get_interface_descriptor(iface
);
126 devinfop
= usbd_devinfo_alloc(uaa
->device
, 0);
127 aprint_normal_dev(self
, "%s, iclass %d/%d\n",
128 devinfop
, id
->bInterfaceClass
, id
->bInterfaceSubClass
);
129 usbd_devinfo_free(devinfop
);
131 if (!pmf_device_register(self
, NULL
, NULL
))
132 aprint_error_dev(self
, "couldn't establish power handler\n");
134 (void)usbd_set_idle(iface
, 0, 0);
137 qflags
= usbd_get_quirks(sc
->sc_udev
)->uq_flags
;
138 if ((qflags
& UQ_NO_SET_PROTO
) == 0 &&
139 id
->bInterfaceSubClass
!= UISUBCLASS_BOOT
)
140 (void)usbd_set_protocol(iface
, 1);
144 sc
->sc_iep_addr
= sc
->sc_oep_addr
= -1;
145 for (i
= 0; i
< id
->bNumEndpoints
; i
++) {
146 ed
= usbd_interface2endpoint_descriptor(iface
, i
);
148 aprint_error_dev(self
,
149 "could not read endpoint descriptor\n");
151 USB_ATTACH_ERROR_RETURN
;
154 DPRINTFN(10,("uhidev_attach: bLength=%d bDescriptorType=%d "
155 "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
157 ed
->bLength
, ed
->bDescriptorType
,
158 ed
->bEndpointAddress
& UE_ADDR
,
159 UE_GET_DIR(ed
->bEndpointAddress
)==UE_DIR_IN
? "in" : "out",
160 ed
->bmAttributes
& UE_XFERTYPE
,
161 UGETW(ed
->wMaxPacketSize
), ed
->bInterval
));
163 if (UE_GET_DIR(ed
->bEndpointAddress
) == UE_DIR_IN
&&
164 (ed
->bmAttributes
& UE_XFERTYPE
) == UE_INTERRUPT
) {
165 maxinpktsize
= UGETW(ed
->wMaxPacketSize
);
166 sc
->sc_iep_addr
= ed
->bEndpointAddress
;
167 } else if (UE_GET_DIR(ed
->bEndpointAddress
) == UE_DIR_OUT
&&
168 (ed
->bmAttributes
& UE_XFERTYPE
) == UE_INTERRUPT
) {
169 sc
->sc_oep_addr
= ed
->bEndpointAddress
;
171 aprint_verbose_dev(self
, "endpoint %d: ignored\n", i
);
176 * Check that we found an input interrupt endpoint. The output interrupt
177 * endpoint is optional
179 if (sc
->sc_iep_addr
== -1) {
180 aprint_error_dev(self
, "no input interrupt endpoint\n");
182 USB_ATTACH_ERROR_RETURN
;
185 /* XXX need to extend this */
187 if (uaa
->vendor
== USB_VENDOR_WACOM
) {
188 static uByte reportbuf
[] = {2, 2, 2};
190 /* The report descriptor for the Wacom Graphire is broken. */
191 switch (uaa
->product
) {
192 case USB_PRODUCT_WACOM_GRAPHIRE
:
193 size
= sizeof uhid_graphire_report_descr
;
194 descptr
= uhid_graphire_report_descr
;
197 case USB_PRODUCT_WACOM_GRAPHIRE3_4X5
:
198 case USB_PRODUCT_WACOM_GRAPHIRE3_6X8
:
199 case USB_PRODUCT_WACOM_GRAPHIRE4_4X5
: /* The 6x8 too? */
201 * The Graphire3 needs 0x0202 to be written to
202 * feature report ID 2 before it'll start
203 * returning digitizer data.
205 usbd_set_report(uaa
->iface
, UHID_FEATURE_REPORT
, 2,
206 &reportbuf
, sizeof reportbuf
);
208 size
= sizeof uhid_graphire3_4x5_report_descr
;
209 descptr
= uhid_graphire3_4x5_report_descr
;
212 /* Keep descriptor */
218 desc
= malloc(size
, M_USBDEV
, M_NOWAIT
);
222 err
= USBD_NORMAL_COMPLETION
;
223 memcpy(desc
, descptr
, size
);
227 err
= usbd_read_report_desc(uaa
->iface
, &desc
, &size
,
231 aprint_error_dev(self
, "no report descriptor\n");
233 USB_ATTACH_ERROR_RETURN
;
236 if (uaa
->vendor
== USB_VENDOR_HOSIDEN
&&
237 uaa
->product
== USB_PRODUCT_HOSIDEN_PPP
) {
238 static uByte reportbuf
[] = { 1 };
240 * This device was sold by Konami with its ParaParaParadise
241 * game for PlayStation2. It needs to be "turned on"
242 * before it will send any reports.
245 usbd_set_report(uaa
->iface
, UHID_FEATURE_REPORT
, 0,
246 &reportbuf
, sizeof reportbuf
);
249 sc
->sc_repdesc
= desc
;
250 sc
->sc_repdesc_size
= size
;
253 nrepid
= uhidev_maxrepid(desc
, size
);
255 USB_ATTACH_SUCCESS_RETURN
;
257 aprint_normal_dev(self
, "%d report ids\n", nrepid
);
259 repsizes
= malloc(nrepid
* sizeof(*repsizes
), M_TEMP
, M_NOWAIT
);
260 if (repsizes
== NULL
)
262 sc
->sc_subdevs
= malloc(nrepid
* sizeof(device_t
),
263 M_USBDEV
, M_NOWAIT
| M_ZERO
);
264 if (sc
->sc_subdevs
== NULL
) {
265 free(repsizes
, M_TEMP
);
267 aprint_error_dev(self
, "no memory\n");
268 USB_ATTACH_ERROR_RETURN
;
271 /* Just request max packet size for the interrupt pipe */
272 sc
->sc_isize
= maxinpktsize
;
273 sc
->sc_nrepid
= nrepid
;
275 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH
, sc
->sc_udev
,
278 for (repid
= 0; repid
< nrepid
; repid
++) {
279 repsz
= hid_report_size(desc
, size
, hid_input
, repid
);
280 DPRINTF(("uhidev_match: repid=%d, repsz=%d\n", repid
, repsz
));
281 repsizes
[repid
] = repsz
;
284 DPRINTF(("uhidev_attach: isize=%d\n", sc
->sc_isize
));
287 for (repid
= 0; repid
< nrepid
; repid
++) {
288 DPRINTF(("uhidev_match: try repid=%d\n", repid
));
289 if (hid_report_size(desc
, size
, hid_input
, repid
) == 0 &&
290 hid_report_size(desc
, size
, hid_output
, repid
) == 0 &&
291 hid_report_size(desc
, size
, hid_feature
, repid
) == 0) {
292 ; /* already NULL in sc->sc_subdevs[repid] */
294 uha
.reportid
= repid
;
295 locs
[UHIDBUSCF_REPORTID
] = repid
;
297 dev
= config_found_sm_loc(self
,
298 "uhidbus", locs
, &uha
,
299 uhidevprint
, config_stdsubmatch
);
300 sc
->sc_subdevs
[repid
] = dev
;
302 csc
= device_private(dev
);
303 csc
->sc_in_rep_size
= repsizes
[repid
];
305 DPRINTF(("uhidev_match: repid=%d dev=%p\n",
307 if (csc
->sc_intr
== NULL
) {
308 free(repsizes
, M_TEMP
);
309 aprint_error_dev(self
,
310 "sc_intr == NULL\n");
311 USB_ATTACH_ERROR_RETURN
;
315 rnd_attach_source(&csc
->rnd_source
,
322 free(repsizes
, M_TEMP
);
324 USB_ATTACH_SUCCESS_RETURN
;
328 uhidev_maxrepid(void *buf
, int len
)
336 for (d
= hid_start_parse(buf
, len
, hid_none
); hid_get_item(d
, &h
); )
337 if (h
.report_ID
> maxid
)
344 uhidevprint(void *aux
, const char *pnp
)
346 struct uhidev_attach_arg
*uha
= aux
;
349 aprint_normal("uhid at %s", pnp
);
350 if (uha
->reportid
!= 0)
351 aprint_normal(" reportid %d", uha
->reportid
);
356 uhidev_activate(device_t self
, enum devact act
)
358 struct uhidev_softc
*sc
= device_private(self
);
361 case DVACT_DEACTIVATE
:
370 uhidev_childdet(device_t self
, device_t child
)
373 struct uhidev_softc
*sc
= device_private(self
);
375 for (i
= 0; i
< sc
->sc_nrepid
; i
++) {
376 if (sc
->sc_subdevs
[i
] == child
)
379 KASSERT(i
< sc
->sc_nrepid
);
380 sc
->sc_subdevs
[i
] = NULL
;
385 USB_DETACH_START(uhidev
, sc
);
391 DPRINTF(("uhidev_detach: sc=%p flags=%d\n", sc
, flags
));
394 if (sc
->sc_ipipe
!= NULL
)
395 usbd_abort_pipe(sc
->sc_ipipe
);
397 if (sc
->sc_repdesc
!= NULL
)
398 free(sc
->sc_repdesc
, M_USBDEV
);
401 for (i
= 0; i
< sc
->sc_nrepid
; i
++) {
402 if (sc
->sc_subdevs
[i
] != NULL
) {
404 csc
= device_private(sc
->sc_subdevs
[i
]);
405 rnd_detach_source(&csc
->rnd_source
);
407 rv
|= config_detach(sc
->sc_subdevs
[i
], flags
);
411 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH
, sc
->sc_udev
,
414 pmf_device_deregister(self
);
420 uhidev_intr(usbd_xfer_handle xfer
, usbd_private_handle addr
, usbd_status status
)
422 struct uhidev_softc
*sc
= addr
;
429 usbd_get_xfer_status(xfer
, NULL
, NULL
, &cc
, NULL
);
432 if (uhidevdebug
> 5) {
435 DPRINTF(("uhidev_intr: status=%d cc=%d\n", status
, cc
));
436 DPRINTF(("uhidev_intr: data ="));
437 for (i
= 0; i
< cc
; i
++)
438 DPRINTF((" %02x", sc
->sc_ibuf
[i
]));
443 if (status
== USBD_CANCELLED
)
446 if (status
!= USBD_NORMAL_COMPLETION
) {
447 DPRINTF(("%s: interrupt status=%d\n", USBDEVNAME(sc
->sc_dev
),
449 usbd_clear_endpoint_stall_async(sc
->sc_ipipe
);
454 if (sc
->sc_nrepid
!= 1)
458 if (rep
>= sc
->sc_nrepid
) {
459 printf("uhidev_intr: bad repid %d\n", rep
);
462 cdev
= sc
->sc_subdevs
[rep
];
465 scd
= device_private(cdev
);
466 DPRINTFN(5,("uhidev_intr: rep=%d, scd=%p state=0x%x\n",
467 rep
, scd
, scd
? scd
->sc_state
: 0));
468 if (!(scd
->sc_state
& UHIDEV_OPEN
))
471 if (scd
->sc_in_rep_size
!= cc
) {
472 DPRINTF(("%s: expected %d bytes, got %d\n",
473 USBDEVNAME(sc
->sc_dev
), scd
->sc_in_rep_size
, cc
));
477 DPRINTF(("%s: 0-length input ignored\n",
478 USBDEVNAME(sc
->sc_dev
)));
482 rnd_add_uint32(&scd
->rnd_source
, (uintptr_t)(sc
->sc_ibuf
));
484 scd
->sc_intr(scd
, p
, cc
);
488 uhidev_get_report_desc(struct uhidev_softc
*sc
, void **desc
, int *size
)
490 *desc
= sc
->sc_repdesc
;
491 *size
= sc
->sc_repdesc_size
;
495 uhidev_open(struct uhidev
*scd
)
497 struct uhidev_softc
*sc
= scd
->sc_parent
;
501 DPRINTF(("uhidev_open: open pipe, state=%d refcnt=%d\n",
502 scd
->sc_state
, sc
->sc_refcnt
));
504 if (scd
->sc_state
& UHIDEV_OPEN
)
506 scd
->sc_state
|= UHIDEV_OPEN
;
510 if (sc
->sc_isize
== 0)
513 sc
->sc_ibuf
= malloc(sc
->sc_isize
, M_USBDEV
, M_WAITOK
);
515 /* Set up input interrupt pipe. */
516 DPRINTF(("uhidev_open: isize=%d, ep=0x%02x\n", sc
->sc_isize
,
519 err
= usbd_open_pipe_intr(sc
->sc_iface
, sc
->sc_iep_addr
,
520 USBD_SHORT_XFER_OK
, &sc
->sc_ipipe
, sc
, sc
->sc_ibuf
,
521 sc
->sc_isize
, uhidev_intr
, USBD_DEFAULT_INTERVAL
);
522 if (err
!= USBD_NORMAL_COMPLETION
) {
523 DPRINTF(("uhidopen: usbd_open_pipe_intr failed, "
530 * Set up output interrupt pipe if an output interrupt endpoint
533 if (sc
->sc_oep_addr
!= -1) {
534 DPRINTF(("uhidev_open: oep=0x%02x\n", sc
->sc_oep_addr
));
536 err
= usbd_open_pipe(sc
->sc_iface
, sc
->sc_oep_addr
,
539 if (err
!= USBD_NORMAL_COMPLETION
) {
540 DPRINTF(("uhidev_open: usbd_open_pipe failed, "
545 DPRINTF(("uhidev_open: sc->sc_opipe=%p\n", sc
->sc_opipe
));
547 sc
->sc_oxfer
= usbd_alloc_xfer(sc
->sc_udev
);
548 if (sc
->sc_oxfer
== NULL
) {
549 DPRINTF(("uhidev_open: couldn't allocate an xfer\n"));
557 /* Abort output pipe */
558 usbd_close_pipe(sc
->sc_opipe
);
560 /* Abort input pipe */
561 usbd_close_pipe(sc
->sc_ipipe
);
563 DPRINTF(("uhidev_open: failed in someway"));
564 free(sc
->sc_ibuf
, M_USBDEV
);
565 scd
->sc_state
&= ~UHIDEV_OPEN
;
574 uhidev_close(struct uhidev
*scd
)
576 struct uhidev_softc
*sc
= scd
->sc_parent
;
578 if (!(scd
->sc_state
& UHIDEV_OPEN
))
580 scd
->sc_state
&= ~UHIDEV_OPEN
;
583 DPRINTF(("uhidev_close: close pipe\n"));
585 if (sc
->sc_oxfer
!= NULL
)
586 usbd_free_xfer(sc
->sc_oxfer
);
588 /* Disable interrupts. */
589 if (sc
->sc_opipe
!= NULL
) {
590 usbd_abort_pipe(sc
->sc_opipe
);
591 usbd_close_pipe(sc
->sc_opipe
);
595 if (sc
->sc_ipipe
!= NULL
) {
596 usbd_abort_pipe(sc
->sc_ipipe
);
597 usbd_close_pipe(sc
->sc_ipipe
);
601 if (sc
->sc_ibuf
!= NULL
) {
602 free(sc
->sc_ibuf
, M_USBDEV
);
608 uhidev_set_report(struct uhidev
*scd
, int type
, void *data
, int len
)
613 if (scd
->sc_report_id
== 0)
614 return usbd_set_report(scd
->sc_parent
->sc_iface
, type
,
615 scd
->sc_report_id
, data
, len
);
617 buf
= malloc(len
+ 1, M_TEMP
, M_WAITOK
);
618 buf
[0] = scd
->sc_report_id
;
619 memcpy(buf
+1, data
, len
);
621 retstat
= usbd_set_report(scd
->sc_parent
->sc_iface
, type
,
622 scd
->sc_report_id
, buf
, len
+ 1);
630 uhidev_set_report_async(struct uhidev
*scd
, int type
, void *data
, int len
)
634 if (scd
->sc_report_id
) {
635 buf
[0] = scd
->sc_report_id
;
636 memcpy(buf
+1, data
, len
);
641 usbd_set_report_async(scd
->sc_parent
->sc_iface
, type
,
642 scd
->sc_report_id
, data
, len
);
646 uhidev_get_report(struct uhidev
*scd
, int type
, void *data
, int len
)
648 return usbd_get_report(scd
->sc_parent
->sc_iface
, type
,
649 scd
->sc_report_id
, data
, len
);
653 uhidev_write(struct uhidev_softc
*sc
, void *data
, int len
)
656 DPRINTF(("uhidev_write: data=%p, len=%d\n", data
, len
));
658 if (sc
->sc_opipe
== NULL
)
662 if (uhidevdebug
> 50) {
667 DPRINTF(("uhidev_write: data ="));
668 for (i
= 0; i
< len
; i
++)
669 DPRINTF((" %02x", d
[i
]));
673 return usbd_intr_transfer(sc
->sc_oxfer
, sc
->sc_opipe
, 0,
674 USBD_NO_TIMEOUT
, data
, &len
, "uhidevwi");