diff options
| author | skrll <skrll@NetBSD.org> | 2005-05-08 06:19:10 +0000 |
|---|---|---|
| committer | skrll <skrll@NetBSD.org> | 2005-05-08 06:19:10 +0000 |
| commit | f2f62a17261e91d0569f53d3987f573f8937347f (patch) | |
| tree | 3ac4073b27edcf27a4292c7b8ebb000a5939dfd7 /sys/dev | |
| parent | f398966b60b3412825dcbd930eaafc16553be772 (diff) | |
Add support for optional interrupt output pipes as described in the
HID specicification version 1.11.
Reviewed by Lennart. Thanks.
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/usb/uhidev.c | 172 | ||||
| -rw-r--r-- | sys/dev/usb/uhidev.h | 11 |
2 files changed, 140 insertions, 43 deletions
diff --git a/sys/dev/usb/uhidev.c b/sys/dev/usb/uhidev.c index e665a219bab..084f8f18ae9 100644 --- a/sys/dev/usb/uhidev.c +++ b/sys/dev/usb/uhidev.c @@ -1,4 +1,4 @@ -/* $NetBSD: uhidev.c,v 1.24 2005/02/27 00:27:51 perry Exp $ */ +/* $NetBSD: uhidev.c,v 1.25 2005/05/08 06:19:10 skrll Exp $ */ /* * Copyright (c) 2001 The NetBSD Foundation, Inc. @@ -42,7 +42,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: uhidev.c,v 1.24 2005/02/27 00:27:51 perry Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uhidev.c,v 1.25 2005/05/08 06:19:10 skrll Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -112,6 +112,7 @@ USB_ATTACH(uhidev) struct uhidev *dev; int size, nrepid, repid, repsz; int repsizes[256]; + int i; void *desc; const void *descptr; usbd_status err; @@ -136,32 +137,48 @@ USB_ATTACH(uhidev) (void)usbd_set_protocol(iface, 1); #endif - ed = usbd_interface2endpoint_descriptor(iface, 0); - if (ed == NULL) { - printf("%s: could not read endpoint descriptor\n", - USBDEVNAME(sc->sc_dev)); - sc->sc_dying = 1; - USB_ATTACH_ERROR_RETURN; + sc->sc_iep_addr = sc->sc_oep_addr = -1; + for (i = 0; i < id->bNumEndpoints; i++) { + ed = usbd_interface2endpoint_descriptor(iface, i); + if (ed == NULL) { + printf("%s: could not read endpoint descriptor\n", + USBDEVNAME(sc->sc_dev)); + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; + } + + DPRINTFN(10,("uhidev_attach: bLength=%d bDescriptorType=%d " + "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" + " bInterval=%d\n", + ed->bLength, ed->bDescriptorType, + ed->bEndpointAddress & UE_ADDR, + UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out", + ed->bmAttributes & UE_XFERTYPE, + UGETW(ed->wMaxPacketSize), ed->bInterval)); + + if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && + (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) { + sc->sc_iep_addr = ed->bEndpointAddress; + } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && + (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) { + sc->sc_oep_addr = ed->bEndpointAddress; + } else { + printf("%s: unexpected endpoint\n", USBDEVNAME(sc->sc_dev)); + sc->sc_dying = 1; + USB_ATTACH_ERROR_RETURN; + } } - DPRINTFN(10,("uhidev_attach: bLength=%d bDescriptorType=%d " - "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" - " bInterval=%d\n", - ed->bLength, ed->bDescriptorType, - ed->bEndpointAddress & UE_ADDR, - UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out", - ed->bmAttributes & UE_XFERTYPE, - UGETW(ed->wMaxPacketSize), ed->bInterval)); - - if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN || - (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { - printf("%s: unexpected endpoint\n", USBDEVNAME(sc->sc_dev)); + /* + * Check that we found an input interrupt endpoint. The output interrupt + * endpoint is optional + */ + if (sc->sc_iep_addr == -1) { + printf("%s: no input interrupt endpoint\n", USBDEVNAME(sc->sc_dev)); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } - sc->sc_ep_addr = ed->bEndpointAddress; - /* XXX need to extend this */ descptr = NULL; if (uaa->vendor == USB_VENDOR_WACOM) { @@ -362,8 +379,8 @@ USB_DETACH(uhidev) DPRINTF(("uhidev_detach: sc=%p flags=%d\n", sc, flags)); sc->sc_dying = 1; - if (sc->sc_intrpipe != NULL) - usbd_abort_pipe(sc->sc_intrpipe); + if (sc->sc_ipipe != NULL) + usbd_abort_pipe(sc->sc_ipipe); if (sc->sc_repdesc != NULL) free(sc->sc_repdesc, M_USBDEV); @@ -414,7 +431,7 @@ uhidev_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("%s: interrupt status=%d\n", USBDEVNAME(sc->sc_dev), status)); - usbd_clear_endpoint_stall_async(sc->sc_intrpipe); + usbd_clear_endpoint_stall_async(sc->sc_ipipe); return; } @@ -455,6 +472,7 @@ uhidev_open(struct uhidev *scd) { struct uhidev_softc *sc = scd->sc_parent; usbd_status err; + int error; DPRINTF(("uhidev_open: open pipe, state=%d refcnt=%d\n", scd->sc_state, sc->sc_refcnt)); @@ -470,22 +488,62 @@ uhidev_open(struct uhidev *scd) sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK); - /* Set up interrupt pipe. */ + /* Set up input interrupt pipe. */ DPRINTF(("uhidev_open: isize=%d, ep=0x%02x\n", sc->sc_isize, - sc->sc_ep_addr)); - err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, - USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, sc->sc_ibuf, + sc->sc_iep_addr)); + + err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_iep_addr, + USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_ibuf, sc->sc_isize, uhidev_intr, USBD_DEFAULT_INTERVAL); - if (err) { + if (err != USBD_NORMAL_COMPLETION) { DPRINTF(("uhidopen: usbd_open_pipe_intr failed, " - "error=%d\n",err)); - free(sc->sc_ibuf, M_USBDEV); - scd->sc_state &= ~UHIDEV_OPEN; - sc->sc_refcnt = 0; - sc->sc_intrpipe = NULL; - return (EIO); + "error=%d\n", err)); + error = EIO; + goto out1; + } + + /* + * Set up output interrupt pipe if an output interrupt endpoint + * exists. + */ + if (sc->sc_oep_addr != -1) { + DPRINTF(("uhidev_open: oep=0x%02x\n", sc->sc_oep_addr)); + + err = usbd_open_pipe(sc->sc_iface, sc->sc_oep_addr, + 0, &sc->sc_opipe); + + if (err != USBD_NORMAL_COMPLETION) { + DPRINTF(("uhidev_open: usbd_open_pipe failed, " + "error=%d\n", err)); + error = EIO; + goto out2; + } + DPRINTF(("uhidev_open: sc->sc_opipe=%p\n", sc->sc_opipe)); + + sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev); + if (sc->sc_oxfer == NULL) { + DPRINTF(("uhidev_open: couldn't allocate an xfer\n")); + error = ENOMEM; + goto out3; + } } + return (0); +out3: + /* Abort output pipe */ + usbd_close_pipe(sc->sc_opipe); +out2: + /* Abort input pipe */ + usbd_close_pipe(sc->sc_ipipe); +out1: + DPRINTF(("uhidev_open: failed in someway")); + free(sc->sc_ibuf, M_USBDEV); + scd->sc_state &= ~UHIDEV_OPEN; + sc->sc_refcnt = 0; + sc->sc_ipipe = NULL; + sc->sc_opipe = NULL; + sc->sc_oxfer = NULL; + return error; } void @@ -500,11 +558,20 @@ uhidev_close(struct uhidev *scd) return; DPRINTF(("uhidev_close: close pipe\n")); + if (sc->sc_oxfer != NULL) + usbd_free_xfer(sc->sc_oxfer); + /* Disable interrupts. */ - if (sc->sc_intrpipe != NULL) { - usbd_abort_pipe(sc->sc_intrpipe); - usbd_close_pipe(sc->sc_intrpipe); - sc->sc_intrpipe = NULL; + if (sc->sc_opipe != NULL) { + usbd_abort_pipe(sc->sc_opipe); + usbd_close_pipe(sc->sc_opipe); + sc->sc_opipe = NULL; + } + + if (sc->sc_ipipe != NULL) { + usbd_abort_pipe(sc->sc_ipipe); + usbd_close_pipe(sc->sc_ipipe); + sc->sc_ipipe = NULL; } if (sc->sc_ibuf != NULL) { @@ -557,3 +624,28 @@ uhidev_get_report(struct uhidev *scd, int type, void *data, int len) return usbd_get_report(scd->sc_parent->sc_iface, type, scd->sc_report_id, data, len); } + +usbd_status +uhidev_write(struct uhidev_softc *sc, void *data, int len) +{ + + DPRINTF(("uhidev_write: data=%p, len=%d\n", data, len)); + + if (sc->sc_opipe == NULL) + return USBD_INVAL; + +#ifdef UHIDEV_DEBUG + if (uhidevdebug > 50) { + + u_int32_t i; + u_int8_t *d = data; + + DPRINTF(("uhidev_write: data =")); + for (i = 0; i < len; i++) + DPRINTF((" %02x", d[i])); + DPRINTF(("\n")); + } +#endif + return usbd_intr_transfer(sc->sc_oxfer, sc->sc_opipe, 0, + USBD_NO_TIMEOUT, data, &len, "uhidevwi"); +} diff --git a/sys/dev/usb/uhidev.h b/sys/dev/usb/uhidev.h index 5894993dc47..3d16dbfe67f 100644 --- a/sys/dev/usb/uhidev.h +++ b/sys/dev/usb/uhidev.h @@ -1,4 +1,4 @@ -/* $NetBSD: uhidev.h,v 1.4 2004/09/13 12:55:49 drochner Exp $ */ +/* $NetBSD: uhidev.h,v 1.5 2005/05/08 06:19:10 skrll Exp $ */ /* * Copyright (c) 2001 The NetBSD Foundation, Inc. @@ -46,12 +46,16 @@ struct uhidev_softc { USBBASEDEVICE sc_dev; /* base device */ usbd_device_handle sc_udev; usbd_interface_handle sc_iface; /* interface */ - usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ - int sc_ep_addr; + usbd_pipe_handle sc_ipipe; /* input interrupt pipe */ + int sc_iep_addr; u_char *sc_ibuf; u_int sc_isize; + usbd_pipe_handle sc_opipe; /* output interrupt pipe */ + usbd_xfer_handle sc_oxfer; /* write request */ + int sc_oep_addr; + void *sc_repdesc; int sc_repdesc_size; @@ -89,3 +93,4 @@ void uhidev_close(struct uhidev *); usbd_status uhidev_set_report(struct uhidev *scd, int type, void *data,int len); void uhidev_set_report_async(struct uhidev *scd, int type, void *data, int len); usbd_status uhidev_get_report(struct uhidev *scd, int type, void *data,int len); +usbd_status uhidev_write(struct uhidev_softc *, void *, int); |
