Index: stable/7/sys/dev/usb/ugen.c =================================================================== --- stable/7/sys/dev/usb/ugen.c (revision 190250) +++ stable/7/sys/dev/usb/ugen.c (revision 190251) @@ -1,1586 +1,1590 @@ /* $NetBSD: ugen.c,v 1.79 2006/03/01 12:38:13 yamt Exp $ */ /* Also already merged from NetBSD: * $NetBSD: ugen.c,v 1.61 2002/09/23 05:51:20 simonb Exp $ * $NetBSD: ugen.c,v 1.64 2003/06/28 14:21:46 darrenr Exp $ * $NetBSD: ugen.c,v 1.65 2003/06/29 22:30:56 fvdl Exp $ * $NetBSD: ugen.c,v 1.68 2004/06/23 02:30:52 mycroft Exp $ */ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USB_DEBUG #define DPRINTF(x) if (ugendebug) printf x #define DPRINTFN(n,x) if (ugendebug>(n)) printf x int ugendebug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, ugen, CTLFLAG_RW, 0, "USB ugen"); SYSCTL_INT(_hw_usb_ugen, OID_AUTO, debug, CTLFLAG_RW, &ugendebug, 0, "ugen debug level"); #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define UGEN_CHUNK 128 /* chunk size for read */ #define UGEN_IBSIZE 1020 /* buffer size */ #define UGEN_BBSIZE 1024 #define UGEN_NISOFRAMES 500 /* 0.5 seconds worth */ #define UGEN_NISOREQS 6 /* number of outstanding xfer requests */ #define UGEN_NISORFRMS 4 /* number of frames (miliseconds) per req */ struct ugen_endpoint { struct ugen_softc *sc; struct cdev *dev; usb_endpoint_descriptor_t *edesc; usbd_interface_handle iface; int state; #define UGEN_ASLP 0x02 /* waiting for data */ #define UGEN_SHORT_OK 0x04 /* short xfers are OK */ usbd_pipe_handle pipeh; struct clist q; struct selinfo rsel; u_char *ibuf; /* start of buffer (circular for isoc) */ u_char *fill; /* location for input (isoc) */ u_char *limit; /* end of circular buffer (isoc) */ u_char *cur; /* current read location (isoc) */ u_int32_t timeout; struct isoreq { struct ugen_endpoint *sce; usbd_xfer_handle xfer; void *dmabuf; u_int16_t sizes[UGEN_NISORFRMS]; } isoreqs[UGEN_NISOREQS]; }; struct ugen_softc { device_t sc_dev; /* base device */ usbd_device_handle sc_udev; struct cdev *dev; char sc_is_open[USB_MAX_ENDPOINTS]; struct ugen_endpoint sc_endpoints[USB_MAX_ENDPOINTS][2]; #define OUT 0 #define IN 1 #define UGEN_DEV_REF(dev, sc) \ if ((sc)->sc_dying || dev_refthread(dev) == NULL) \ return (ENXIO) #define UGEN_DEV_RELE(dev, sc) \ dev_relthread(dev) #define UGEN_DEV_OPEN(dev, sc) \ /* handled by dev layer */ #define UGEN_DEV_CLOSE(dev, sc) \ /* handled by dev layer */ u_char sc_dying; }; d_open_t ugenopen; d_close_t ugenclose; d_read_t ugenread; d_write_t ugenwrite; d_ioctl_t ugenioctl; d_poll_t ugenpoll; d_purge_t ugenpurge; static struct cdevsw ugenctl_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = ugenopen, .d_close = ugenclose, .d_ioctl = ugenioctl, .d_purge = ugenpurge, .d_name = "ugenctl", }; static struct cdevsw ugen_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = ugenopen, .d_close = ugenclose, .d_read = ugenread, .d_write = ugenwrite, .d_ioctl = ugenioctl, .d_poll = ugenpoll, .d_purge = ugenpurge, .d_name = "ugen", }; static void ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status); static void ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status); static int ugen_do_read(struct ugen_softc *, int, struct uio *, int); static int ugen_do_write(struct ugen_softc *, int, struct uio *, int); static int ugen_do_ioctl(struct ugen_softc *, int, u_long, caddr_t, int, struct thread *); static void ugen_make_devnodes(struct ugen_softc *sc); static void ugen_destroy_devnodes(struct ugen_softc *sc); static int ugen_set_config(struct ugen_softc *sc, int configno); static usb_config_descriptor_t *ugen_get_cdesc(struct ugen_softc *sc, int index, int *lenp); static usbd_status ugen_set_interface(struct ugen_softc *, int, int); static int ugen_get_alt_index(struct ugen_softc *sc, int ifaceidx); #define UGENUNIT(n) ((minor(n) >> 4) & 0xf) #define UGENENDPOINT(n) (minor(n) & 0xf) #define UGENMINOR(u, e) (((u) << 4) | (e)) static device_probe_t ugen_match; static device_attach_t ugen_attach; static device_detach_t ugen_detach; static devclass_t ugen_devclass; static device_method_t ugen_methods[] = { DEVMETHOD(device_probe, ugen_match), DEVMETHOD(device_attach, ugen_attach), DEVMETHOD(device_detach, ugen_detach), {0,0}, {0,0} }; static driver_t ugen_driver = { "ugen", ugen_methods, sizeof(struct ugen_softc) }; MODULE_DEPEND(ugen, usb, 1, 1, 1); static int ugen_match(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); #if 0 if (uaa->matchlvl) return (uaa->matchlvl); #endif if (uaa->usegeneric) return (UMATCH_GENERIC); else return (UMATCH_NONE); } static int ugen_attach(device_t self) { struct ugen_softc *sc = device_get_softc(self); struct usb_attach_arg *uaa = device_get_ivars(self); usbd_device_handle udev; usbd_status err; int conf; sc->sc_dev = self; sc->sc_udev = udev = uaa->device; memset(sc->sc_endpoints, 0, sizeof sc->sc_endpoints); /* First set configuration index 0, the default one for ugen. */ err = usbd_set_config_index(udev, 0, 0); if (err) { printf("%s: setting configuration index 0 failed\n", device_get_nameunit(sc->sc_dev)); sc->sc_dying = 1; return (ENXIO); } conf = usbd_get_config_descriptor(udev)->bConfigurationValue; /* Set up all the local state for this configuration. */ err = ugen_set_config(sc, conf); if (err) { printf("%s: setting configuration %d failed\n", device_get_nameunit(sc->sc_dev), conf); sc->sc_dying = 1; return (ENXIO); } /* the main device, ctrl endpoint */ sc->dev = make_dev(&ugenctl_cdevsw, UGENMINOR(device_get_unit(sc->sc_dev), 0), UID_ROOT, GID_OPERATOR, 0644, "%s", device_get_nameunit(sc->sc_dev)); ugen_make_devnodes(sc); usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); return (0); } static void ugen_make_devnodes(struct ugen_softc *sc) { int endptno; struct cdev *dev; for (endptno = 1; endptno < USB_MAX_ENDPOINTS; endptno++) { if (sc->sc_endpoints[endptno][IN].sc != NULL || sc->sc_endpoints[endptno][OUT].sc != NULL ) { /* endpt can be 0x81 and 0x01, representing * endpoint address 0x01 and IN/OUT directions. * We map both endpts to the same device, * IN is reading from it, OUT is writing to it. * * In the if clause above we check whether one * of the structs is populated. */ dev = make_dev(&ugen_cdevsw, UGENMINOR(device_get_unit(sc->sc_dev), endptno), UID_ROOT, GID_OPERATOR, 0644, "%s.%d", device_get_nameunit(sc->sc_dev), endptno); dev_depends(sc->dev, dev); if (sc->sc_endpoints[endptno][IN].sc != NULL) sc->sc_endpoints[endptno][IN].dev = dev; if (sc->sc_endpoints[endptno][OUT].sc != NULL) sc->sc_endpoints[endptno][OUT].dev = dev; } } } static void ugen_destroy_devnodes(struct ugen_softc *sc) { int endptno, prev_sc_dying; struct cdev *dev; prev_sc_dying = sc->sc_dying; sc->sc_dying = 1; /* destroy all devices for the other (existing) endpoints as well */ for (endptno = 1; endptno < USB_MAX_ENDPOINTS; endptno++) { if (sc->sc_endpoints[endptno][IN].sc != NULL || sc->sc_endpoints[endptno][OUT].sc != NULL ) { /* endpt can be 0x81 and 0x01, representing * endpoint address 0x01 and IN/OUT directions. * We map both endpoint addresses to the same device, * IN is reading from it, OUT is writing to it. * * In the if clause above we check whether one * of the structs is populated. */ if (sc->sc_endpoints[endptno][IN].sc != NULL) dev = sc->sc_endpoints[endptno][IN].dev; else dev = sc->sc_endpoints[endptno][OUT].dev; KASSERT(dev != NULL, ("ugen_destroy_devnodes: NULL dev")); if(dev != NULL) destroy_dev(dev); sc->sc_endpoints[endptno][IN].sc = NULL; sc->sc_endpoints[endptno][OUT].sc = NULL; } } sc->sc_dying = prev_sc_dying; } static int ugen_set_config(struct ugen_softc *sc, int configno) { usbd_device_handle dev = sc->sc_udev; usbd_interface_handle iface; usb_endpoint_descriptor_t *ed; struct ugen_endpoint *sce, **sce_cache, ***sce_cache_arr; u_int8_t niface, niface_cache, nendpt, *nendpt_cache; int ifaceno, endptno, endpt; usbd_status err; int dir; DPRINTFN(1,("ugen_set_config: %s to configno %d, sc=%p\n", device_get_nameunit(sc->sc_dev), configno, sc)); /* We start at 1, not 0, because we don't care whether the * control endpoint is open or not. It is always present. */ for (endptno = 1; endptno < USB_MAX_ENDPOINTS; endptno++) if (sc->sc_is_open[endptno]) { DPRINTFN(1, ("ugen_set_config: %s - endpoint %d is open\n", device_get_nameunit(sc->sc_dev), endptno)); return (USBD_IN_USE); } err = usbd_interface_count(dev, &niface); if (err) return (err); /* store an array of endpoint descriptors to clear if the configuration * change succeeds - these aren't available afterwards */ nendpt_cache = malloc(sizeof(u_int8_t) * niface, M_TEMP, M_WAITOK | M_ZERO); sce_cache_arr = malloc(sizeof(struct ugen_endpoint **) * niface, M_TEMP, M_WAITOK | M_ZERO); niface_cache = niface; for (ifaceno = 0; ifaceno < niface; ifaceno++) { DPRINTFN(1,("ugen_set_config: ifaceno %d\n", ifaceno)); err = usbd_device2interface_handle(dev, ifaceno, &iface); if (err) panic("ugen_set_config: can't obtain interface handle"); err = usbd_endpoint_count(iface, &nendpt); if (err) panic("ugen_set_config: endpoint count failed"); /* store endpoint descriptors for each interface */ nendpt_cache[ifaceno] = nendpt; sce_cache = malloc(sizeof(struct ugen_endpoint *) * nendpt, M_TEMP, M_WAITOK); sce_cache_arr[ifaceno] = sce_cache; for (endptno = 0; endptno < nendpt; endptno++) { ed = usbd_interface2endpoint_descriptor(iface,endptno); endpt = ed->bEndpointAddress; dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT; sce_cache[endptno] = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir]; } } /* Avoid setting the current value. */ if (usbd_get_config_descriptor(dev)->bConfigurationValue != configno) { /* attempt to perform the configuration change */ err = usbd_set_config_no(dev, configno, 1); if (err) { for(ifaceno = 0; ifaceno < niface_cache; ifaceno++) free(sce_cache_arr[ifaceno], M_TEMP); free(sce_cache_arr, M_TEMP); free(nendpt_cache, M_TEMP); return (err); } } ugen_destroy_devnodes(sc); /* now we can clear the old interface's ugen_endpoints */ for(ifaceno = 0; ifaceno < niface_cache; ifaceno++) { sce_cache = sce_cache_arr[ifaceno]; for(endptno = 0; endptno < nendpt_cache[ifaceno]; endptno++) { sce = sce_cache[endptno]; sce->sc = 0; sce->edesc = 0; sce->iface = 0; } } /* and free the cache storing them */ for(ifaceno = 0; ifaceno < niface_cache; ifaceno++) free(sce_cache_arr[ifaceno], M_TEMP); free(sce_cache_arr, M_TEMP); free(nendpt_cache, M_TEMP); /* no endpoints if the device is in the unconfigured state */ if (configno != USB_UNCONFIG_NO) { /* set the new configuration's ugen_endpoints */ err = usbd_interface_count(dev, &niface); if (err) panic("ugen_set_config: interface count failed"); memset(sc->sc_endpoints, 0, sizeof sc->sc_endpoints); for (ifaceno = 0; ifaceno < niface; ifaceno++) { DPRINTFN(1,("ugen_set_config: ifaceno %d\n", ifaceno)); err = usbd_device2interface_handle(dev, ifaceno, &iface); if (err) panic("ugen_set_config: can't obtain interface handle"); err = usbd_endpoint_count(iface, &nendpt); if (err) panic("ugen_set_config: endpoint count failed"); for (endptno = 0; endptno < nendpt; endptno++) { ed = usbd_interface2endpoint_descriptor(iface,endptno); endpt = ed->bEndpointAddress; dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT; sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir]; DPRINTFN(1,("ugen_set_config: endptno %d, endpt=0x%02x" "(%d,%d), sce=%p\n", endptno, endpt, UE_GET_ADDR(endpt), UE_GET_DIR(endpt), sce)); sce->sc = sc; sce->edesc = ed; sce->iface = iface; } } } return (USBD_NORMAL_COMPLETION); } int ugenopen(struct cdev *dev, int flag, int mode, struct thread *p) { struct ugen_softc *sc; int unit = UGENUNIT(dev); int endpt = UGENENDPOINT(dev); usb_endpoint_descriptor_t *edesc; struct ugen_endpoint *sce; int dir, isize; usbd_status err; usbd_xfer_handle xfer; void *buf; int i, j; sc = devclass_get_softc(ugen_devclass, unit); if (sc == NULL) return (ENXIO); DPRINTFN(5, ("ugenopen: flag=%d, mode=%d, unit=%d endpt=%d\n", flag, mode, unit, endpt)); if (sc == NULL || sc->sc_dying) return (ENXIO); if (sc->sc_is_open[endpt]) return (EBUSY); if (endpt == USB_CONTROL_ENDPOINT) { sc->sc_is_open[USB_CONTROL_ENDPOINT] = 1; UGEN_DEV_OPEN(dev, sc); return (0); } /* Make sure there are pipes for all directions. */ for (dir = OUT; dir <= IN; dir++) { if (flag & (dir == OUT ? FWRITE : FREAD)) { sce = &sc->sc_endpoints[endpt][dir]; if (sce->edesc == 0) return (ENXIO); } } /* Actually open the pipes. */ /* XXX Should back out properly if it fails. */ for (dir = OUT; dir <= IN; dir++) { if (!(flag & (dir == OUT ? FWRITE : FREAD))) continue; sce = &sc->sc_endpoints[endpt][dir]; sce->state = 0; sce->timeout = USBD_NO_TIMEOUT; DPRINTFN(5, ("ugenopen: sc=%p, endpt=%d, dir=%d, sce=%p\n", sc, endpt, dir, sce)); edesc = sce->edesc; switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: if (dir == OUT) { err = usbd_open_pipe(sce->iface, edesc->bEndpointAddress, 0, &sce->pipeh); if (err) return (EIO); break; } isize = UGETW(edesc->wMaxPacketSize); if (isize == 0) /* shouldn't happen */ return (EINVAL); sce->ibuf = malloc(isize, M_USBDEV, M_WAITOK); DPRINTFN(5, ("ugenopen: intr endpt=%d,isize=%d\n", endpt, isize)); if ((clist_alloc_cblocks(&sce->q, UGEN_IBSIZE, UGEN_IBSIZE), 0) == -1) return (ENOMEM); err = usbd_open_pipe_intr(sce->iface, edesc->bEndpointAddress, USBD_SHORT_XFER_OK, &sce->pipeh, sce, sce->ibuf, isize, ugenintr, USBD_DEFAULT_INTERVAL); if (err) { free(sce->ibuf, M_USBDEV); clist_free_cblocks(&sce->q); return (EIO); } DPRINTFN(5, ("ugenopen: interrupt open done\n")); break; case UE_BULK: err = usbd_open_pipe(sce->iface, edesc->bEndpointAddress, 0, &sce->pipeh); if (err) return (EIO); break; case UE_ISOCHRONOUS: if (dir == OUT) return (EINVAL); isize = UGETW(edesc->wMaxPacketSize); if (isize == 0) /* shouldn't happen */ return (EINVAL); sce->ibuf = malloc(isize * UGEN_NISOFRAMES, M_USBDEV, M_WAITOK); sce->cur = sce->fill = sce->ibuf; sce->limit = sce->ibuf + isize * UGEN_NISOFRAMES; DPRINTFN(5, ("ugenopen: isoc endpt=%d, isize=%d\n", endpt, isize)); err = usbd_open_pipe(sce->iface, edesc->bEndpointAddress, 0, &sce->pipeh); if (err) { free(sce->ibuf, M_USBDEV); return (EIO); } for(i = 0; i < UGEN_NISOREQS; ++i) { sce->isoreqs[i].sce = sce; xfer = usbd_alloc_xfer(sc->sc_udev); if (xfer == 0) goto bad; sce->isoreqs[i].xfer = xfer; buf = usbd_alloc_buffer (xfer, isize * UGEN_NISORFRMS); if (buf == 0) { i++; goto bad; } sce->isoreqs[i].dmabuf = buf; for(j = 0; j < UGEN_NISORFRMS; ++j) sce->isoreqs[i].sizes[j] = isize; usbd_setup_isoc_xfer (xfer, sce->pipeh, &sce->isoreqs[i], sce->isoreqs[i].sizes, UGEN_NISORFRMS, USBD_NO_COPY, ugen_isoc_rintr); (void)usbd_transfer(xfer); } DPRINTFN(5, ("ugenopen: isoc open done\n")); break; bad: while (--i >= 0) /* implicit buffer free */ usbd_free_xfer(sce->isoreqs[i].xfer); return (ENOMEM); case UE_CONTROL: sce->timeout = USBD_DEFAULT_TIMEOUT; return (EINVAL); } } sc->sc_is_open[endpt] = 1; UGEN_DEV_OPEN(dev, sc); return (0); } int ugenclose(struct cdev *dev, int flag, int mode, struct thread *p) { int endpt = UGENENDPOINT(dev); struct ugen_softc *sc; struct ugen_endpoint *sce; int dir; int i; sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev)); DPRINTFN(5, ("ugenclose: flag=%d, mode=%d, unit=%d, endpt=%d\n", flag, mode, UGENUNIT(dev), endpt)); #ifdef DIAGNOSTIC if (!sc->sc_is_open[endpt]) { printf("ugenclose: not open\n"); return (EINVAL); } #endif if (endpt == USB_CONTROL_ENDPOINT) { DPRINTFN(5, ("ugenclose: close control\n")); sc->sc_is_open[endpt] = 0; UGEN_DEV_CLOSE(dev, sc); return (0); } for (dir = OUT; dir <= IN; dir++) { if (!(flag & (dir == OUT ? FWRITE : FREAD))) continue; sce = &sc->sc_endpoints[endpt][dir]; if (sce->pipeh == NULL) continue; DPRINTFN(5, ("ugenclose: endpt=%d dir=%d sce=%p\n", endpt, dir, sce)); usbd_abort_pipe(sce->pipeh); usbd_close_pipe(sce->pipeh); sce->pipeh = NULL; switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: ndflush(&sce->q, sce->q.c_cc); clist_free_cblocks(&sce->q); break; case UE_ISOCHRONOUS: for (i = 0; i < UGEN_NISOREQS; ++i) usbd_free_xfer(sce->isoreqs[i].xfer); default: break; } if (sce->ibuf != NULL) { free(sce->ibuf, M_USBDEV); sce->ibuf = NULL; clist_free_cblocks(&sce->q); } } sc->sc_is_open[endpt] = 0; UGEN_DEV_CLOSE(dev, sc); return (0); } static int ugen_do_read(struct ugen_softc *sc, int endpt, struct uio *uio, int flag) { struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][IN]; u_int32_t n, tn; char buf[UGEN_BBSIZE]; usbd_xfer_handle xfer; usbd_status err; int s; int error = 0, doneone = 0; u_char buffer[UGEN_CHUNK]; DPRINTFN(5, ("%s: ugenread: %d\n", device_get_nameunit(sc->sc_dev), endpt)); if (sc->sc_dying) return (EIO); if (endpt == USB_CONTROL_ENDPOINT) return (ENODEV); #ifdef DIAGNOSTIC if (sce->edesc == NULL) { printf("ugenread: no edesc\n"); return (EIO); } if (sce->pipeh == NULL) { printf("ugenread: no pipe\n"); return (EIO); } #endif switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: /* Block until activity occurred. */ s = splusb(); while (sce->q.c_cc == 0) { if (flag & O_NONBLOCK) { splx(s); return (EWOULDBLOCK); } sce->state |= UGEN_ASLP; DPRINTFN(5, ("ugenread: sleep on %p\n", sce)); error = tsleep(sce, PZERO | PCATCH, "ugenri", (sce->timeout * hz + 999) / 1000); sce->state &= ~UGEN_ASLP; DPRINTFN(5, ("ugenread: woke, error=%d\n", error)); if (sc->sc_dying) error = EIO; if (error == EAGAIN) { error = 0; /* timeout, return 0 bytes */ break; } if (error) break; } splx(s); /* Transfer as many chunks as possible. */ while (sce->q.c_cc > 0 && uio->uio_resid > 0 && !error) { n = min(sce->q.c_cc, uio->uio_resid); if (n > sizeof(buffer)) n = sizeof(buffer); /* Remove a small chunk from the input queue. */ q_to_b(&sce->q, buffer, n); DPRINTFN(5, ("ugenread: got %d chars\n", n)); /* Copy the data to the user process. */ error = uiomove(buffer, n, uio); if (error) break; } break; case UE_BULK: xfer = usbd_alloc_xfer(sc->sc_udev); if (xfer == 0) return (ENOMEM); while ((n = min(UGEN_BBSIZE, uio->uio_resid)) != 0 || !doneone) { DPRINTFN(1, ("ugenread: start transfer %d bytes\n",n)); tn = n; doneone = 1; err = usbd_bulk_transfer( xfer, sce->pipeh, sce->state & UGEN_SHORT_OK ? USBD_SHORT_XFER_OK : 0, sce->timeout, buf, &tn, "ugenrb"); if (err) { if (err == USBD_INTERRUPTED) error = EINTR; else if (err == USBD_TIMEOUT) error = ETIMEDOUT; else error = EIO; break; } DPRINTFN(1, ("ugenread: got %d bytes\n", tn)); error = uiomove(buf, tn, uio); if (error || tn < n) break; } usbd_free_xfer(xfer); break; case UE_ISOCHRONOUS: s = splusb(); while (sce->cur == sce->fill) { if (flag & O_NONBLOCK) { splx(s); return (EWOULDBLOCK); } sce->state |= UGEN_ASLP; DPRINTFN(5, ("ugenread: sleep on %p\n", sce)); error = tsleep(sce, PZERO | PCATCH, "ugenri", (sce->timeout * hz + 999) / 1000); sce->state &= ~UGEN_ASLP; DPRINTFN(5, ("ugenread: woke, error=%d\n", error)); if (sc->sc_dying) error = EIO; if (error == EAGAIN) { error = 0; /* timeout, return 0 bytes */ break; } if (error) break; } while (sce->cur != sce->fill && uio->uio_resid > 0 && !error) { if (sce->fill > sce->cur) n = min(sce->fill - sce->cur, uio->uio_resid); else n = min(sce->limit - sce->cur, uio->uio_resid); DPRINTFN(5, ("ugenread: isoc got %d chars\n", n)); /* Copy the data to the user process. */ error = uiomove(sce->cur, n, uio); if (error) break; sce->cur += n; if(sce->cur >= sce->limit) sce->cur = sce->ibuf; } splx(s); break; default: return (ENXIO); } return (error); } int ugenread(struct cdev *dev, struct uio *uio, int flag) { int endpt = UGENENDPOINT(dev); struct ugen_softc *sc; int error; sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev)); if (sc->sc_dying) return (EIO); UGEN_DEV_REF(dev, sc); error = ugen_do_read(sc, endpt, uio, flag); UGEN_DEV_RELE(dev, sc); return (error); } static int ugen_do_write(struct ugen_softc *sc, int endpt, struct uio *uio, int flag) { struct ugen_endpoint *sce = &sc->sc_endpoints[endpt][OUT]; u_int32_t n; int error = 0, doneone = 0; char buf[UGEN_BBSIZE]; usbd_xfer_handle xfer; usbd_status err; DPRINTFN(5, ("%s: ugenwrite: %d\n", device_get_nameunit(sc->sc_dev), endpt)); if (sc->sc_dying) return (EIO); if (endpt == USB_CONTROL_ENDPOINT) return (ENODEV); #ifdef DIAGNOSTIC if (sce->edesc == NULL) { printf("ugenwrite: no edesc\n"); return (EIO); } if (sce->pipeh == NULL) { printf("ugenwrite: no pipe\n"); return (EIO); } #endif switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_BULK: xfer = usbd_alloc_xfer(sc->sc_udev); if (xfer == 0) return (EIO); while ((n = min(UGEN_BBSIZE, uio->uio_resid)) != 0 || !doneone) { doneone = 1; error = uiomove(buf, n, uio); if (error) break; DPRINTFN(1, ("ugenwrite: transfer %d bytes\n", n)); err = usbd_bulk_transfer(xfer, sce->pipeh, 0, sce->timeout, buf, &n,"ugenwb"); if (err) { if (err == USBD_INTERRUPTED) error = EINTR; else if (err == USBD_TIMEOUT) error = ETIMEDOUT; else error = EIO; break; } } usbd_free_xfer(xfer); break; case UE_INTERRUPT: xfer = usbd_alloc_xfer(sc->sc_udev); if (xfer == 0) return (EIO); while ((n = min(UGETW(sce->edesc->wMaxPacketSize), uio->uio_resid)) != 0 || !doneone) { doneone = 1; error = uiomove(buf, n, uio); if (error) break; DPRINTFN(1, ("ugenwrite: transfer %d bytes\n", n)); err = usbd_intr_transfer(xfer, sce->pipeh, 0, sce->timeout, buf, &n, "ugenwi"); if (err) { if (err == USBD_INTERRUPTED) error = EINTR; else if (err == USBD_TIMEOUT) error = ETIMEDOUT; else error = EIO; break; } } usbd_free_xfer(xfer); break; default: return (ENXIO); } return (error); } int ugenwrite(struct cdev *dev, struct uio *uio, int flag) { int endpt = UGENENDPOINT(dev); struct ugen_softc *sc; int error; sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev)); if (sc->sc_dying) return (EIO); UGEN_DEV_REF(dev, sc); error = ugen_do_write(sc, endpt, uio, flag); UGEN_DEV_RELE(dev, sc); return (error); } void ugenpurge(struct cdev *dev) { int endpt = UGENENDPOINT(dev); struct ugen_endpoint *sce; struct ugen_softc *sc; if (endpt == USB_CONTROL_ENDPOINT) return; sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev)); sce = &sc->sc_endpoints[endpt][IN]; if (sce->pipeh) usbd_abort_pipe(sce->pipeh); if (sce->state & UGEN_ASLP) { DPRINTFN(5, ("ugenpurge: waking %p\n", sce)); wakeup(sce); } selwakeuppri(&sce->rsel, PZERO); sce = &sc->sc_endpoints[endpt][OUT]; if (sce->pipeh) usbd_abort_pipe(sce->pipeh); if (sce->state & UGEN_ASLP) { DPRINTFN(5, ("ugenpurge: waking %p\n", sce)); wakeup(sce); } selwakeuppri(&sce->rsel, PZERO); } static int ugen_detach(device_t self) { struct ugen_softc *sc = device_get_softc(self); struct ugen_endpoint *sce; int i, dir; DPRINTF(("ugen_detach: sc=%p\n", sc)); sc->sc_dying = 1; /* Abort all pipes. Causes processes waiting for transfer to wake. */ for (i = 0; i < USB_MAX_ENDPOINTS; i++) { for (dir = OUT; dir <= IN; dir++) { sce = &sc->sc_endpoints[i][dir]; if (sce->pipeh) usbd_abort_pipe(sce->pipeh); selwakeuppri(&sce->rsel, PZERO); } } /* destroy the device for the control endpoint */ destroy_dev(sc->dev); usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); return (0); } static void ugenintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) { struct ugen_endpoint *sce = addr; /*struct ugen_softc *sc = sce->sc;*/ u_int32_t count; u_char *ibuf; if (status == USBD_CANCELLED) return; if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("ugenintr: status=%d\n", status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sce->pipeh); return; } usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL); ibuf = sce->ibuf; DPRINTFN(5, ("ugenintr: xfer=%p status=%d count=%d\n", xfer, status, count)); DPRINTFN(5, (" data = %02x %02x %02x\n", ibuf[0], ibuf[1], ibuf[2])); (void)b_to_q(ibuf, count, &sce->q); if (sce->state & UGEN_ASLP) { sce->state &= ~UGEN_ASLP; DPRINTFN(5, ("ugen_intr: waking %p\n", sce)); wakeup(sce); } selwakeuppri(&sce->rsel, PZERO); } static void ugen_isoc_rintr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status) { struct isoreq *req = addr; struct ugen_endpoint *sce = req->sce; u_int32_t count, n; int i, isize; /* Return if we are aborting. */ if (status == USBD_CANCELLED) return; usbd_get_xfer_status(xfer, NULL, NULL, &count, NULL); DPRINTFN(5,("ugen_isoc_rintr: xfer %td, count=%d\n", req - sce->isoreqs, count)); /* throw away oldest input if the buffer is full */ if(sce->fill < sce->cur && sce->cur <= sce->fill + count) { sce->cur += count; if(sce->cur >= sce->limit) sce->cur = sce->ibuf + (sce->limit - sce->cur); - DPRINTFN(5, ("ugen_isoc_rintr: throwing away %d bytes\n", - count)); + DPRINTF(("ugen_isoc_rintr: throwing away %d bytes\n", + count)); } isize = UGETW(sce->edesc->wMaxPacketSize); for (i = 0; i < UGEN_NISORFRMS; i++) { u_int32_t actlen = req->sizes[i]; char const *buf = (char const *)req->dmabuf + isize * i; /* copy data to buffer */ while (actlen > 0) { n = min(actlen, sce->limit - sce->fill); memcpy(sce->fill, buf, n); buf += n; actlen -= n; sce->fill += n; if(sce->fill == sce->limit) sce->fill = sce->ibuf; } /* setup size for next transfer */ req->sizes[i] = isize; } usbd_setup_isoc_xfer(xfer, sce->pipeh, req, req->sizes, UGEN_NISORFRMS, USBD_NO_COPY, ugen_isoc_rintr); (void)usbd_transfer(xfer); if (sce->state & UGEN_ASLP) { sce->state &= ~UGEN_ASLP; DPRINTFN(5, ("ugen_isoc_rintr: waking %p\n", sce)); wakeup(sce); } selwakeuppri(&sce->rsel, PZERO); } static usbd_status ugen_set_interface(struct ugen_softc *sc, int ifaceidx, int altno) { usbd_interface_handle iface; usb_endpoint_descriptor_t *ed; usbd_status err; struct ugen_endpoint *sce, **sce_cache; u_int8_t niface, nendpt, nendpt_cache, endptno, endpt; int dir; DPRINTFN(15, ("ugen_set_interface %d %d\n", ifaceidx, altno)); err = usbd_interface_count(sc->sc_udev, &niface); if (err) return (err); if (ifaceidx < 0 || ifaceidx >= niface) return (USBD_INVAL); err = usbd_device2interface_handle(sc->sc_udev, ifaceidx, &iface); if (err) return (err); err = usbd_endpoint_count(iface, &nendpt); if (err) return (err); /* store an array of endpoint descriptors to clear if the interface * change succeeds - these aren't available afterwards */ sce_cache = malloc(sizeof(struct ugen_endpoint *) * nendpt, M_TEMP, M_WAITOK); nendpt_cache = nendpt; for (endptno = 0; endptno < nendpt; endptno++) { ed = usbd_interface2endpoint_descriptor(iface,endptno); endpt = ed->bEndpointAddress; dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT; sce_cache[endptno] = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir]; } /* change setting */ err = usbd_set_interface(iface, altno); if (err) { free(sce_cache, M_TEMP); return (err); } err = usbd_endpoint_count(iface, &nendpt); if (err) panic("ugen_set_interface: endpoint count failed"); /* destroy the existing devices, we remake the new ones in a moment */ ugen_destroy_devnodes(sc); /* now we can clear the old interface's ugen_endpoints */ for (endptno = 0; endptno < nendpt_cache; endptno++) { sce = sce_cache[endptno]; sce->sc = 0; sce->edesc = 0; sce->iface = 0; } free(sce_cache, M_TEMP); /* set the new interface's ugen_endpoints */ for (endptno = 0; endptno < nendpt; endptno++) { ed = usbd_interface2endpoint_descriptor(iface,endptno); endpt = ed->bEndpointAddress; dir = UE_GET_DIR(endpt) == UE_DIR_IN ? IN : OUT; sce = &sc->sc_endpoints[UE_GET_ADDR(endpt)][dir]; sce->sc = sc; sce->edesc = ed; sce->iface = iface; } /* make the new devices */ ugen_make_devnodes(sc); return (0); } /* Retrieve a complete descriptor for a certain device and index. */ static usb_config_descriptor_t * ugen_get_cdesc(struct ugen_softc *sc, int index, int *lenp) { usb_config_descriptor_t *cdesc, *tdesc, cdescr; int len; usbd_status err; if (index == USB_CURRENT_CONFIG_INDEX) { tdesc = usbd_get_config_descriptor(sc->sc_udev); len = UGETW(tdesc->wTotalLength); if (lenp) *lenp = len; cdesc = malloc(len, M_TEMP, M_WAITOK); memcpy(cdesc, tdesc, len); DPRINTFN(5,("ugen_get_cdesc: current, len=%d\n", len)); } else { err = usbd_get_config_desc(sc->sc_udev, index, &cdescr); if (err) return (0); len = UGETW(cdescr.wTotalLength); DPRINTFN(5,("ugen_get_cdesc: index=%d, len=%d\n", index, len)); if (lenp) *lenp = len; cdesc = malloc(len, M_TEMP, M_WAITOK); err = usbd_get_config_desc_full(sc->sc_udev, index, cdesc, len); if (err) { free(cdesc, M_TEMP); return (0); } } return (cdesc); } static int ugen_get_alt_index(struct ugen_softc *sc, int ifaceidx) { usbd_interface_handle iface; usbd_status err; err = usbd_device2interface_handle(sc->sc_udev, ifaceidx, &iface); if (err) return (-1); return (usbd_get_interface_altindex(iface)); } static int ugen_do_ioctl(struct ugen_softc *sc, int endpt, u_long cmd, caddr_t addr, int flag, struct thread *p) { struct ugen_endpoint *sce; usbd_status err; usbd_interface_handle iface; struct usb_config_desc *cd; usb_config_descriptor_t *cdesc; struct usb_interface_desc *id; usb_interface_descriptor_t *idesc; struct usb_endpoint_desc *ed; usb_endpoint_descriptor_t *edesc; struct usb_alt_interface *ai; struct usb_string_desc *si; u_int8_t conf, alt; DPRINTFN(5, ("ugenioctl: cmd=%08lx\n", cmd)); if (sc->sc_dying) return (EIO); switch (cmd) { case FIONBIO: case FIOASYNC: /* All handled in the upper FS layer. */ return (0); case USB_SET_SHORT_XFER: if (endpt == USB_CONTROL_ENDPOINT) return (EINVAL); /* This flag only affects read */ sce = &sc->sc_endpoints[endpt][IN]; if (sce->pipeh == NULL) { printf("ugenioctl: USB_SET_SHORT_XFER, no pipe\n"); return (EIO); } if (*(int *)addr) sce->state |= UGEN_SHORT_OK; else sce->state &= ~UGEN_SHORT_OK; return (0); case USB_SET_TIMEOUT: sce = &sc->sc_endpoints[endpt][IN]; sce->timeout = *(int *)addr; sce = &sc->sc_endpoints[endpt][OUT]; sce->timeout = *(int *)addr; return (0); default: break; } if (endpt != USB_CONTROL_ENDPOINT) return (EINVAL); switch (cmd) { #ifdef USB_DEBUG case USB_SETDEBUG: ugendebug = *(int *)addr; break; #endif case USB_GET_CONFIG: err = usbd_get_config(sc->sc_udev, &conf); if (err) return (EIO); *(int *)addr = conf; break; case USB_SET_CONFIG: if (!(flag & FWRITE)) return (EPERM); err = ugen_set_config(sc, *(int *)addr); switch (err) { case USBD_NORMAL_COMPLETION: ugen_make_devnodes(sc); break; case USBD_IN_USE: return (EBUSY); default: return (EIO); } break; case USB_GET_ALTINTERFACE: ai = (struct usb_alt_interface *)addr; err = usbd_device2interface_handle(sc->sc_udev, ai->uai_interface_index, &iface); if (err) return (EINVAL); idesc = usbd_get_interface_descriptor(iface); if (idesc == NULL) return (EIO); ai->uai_alt_no = idesc->bAlternateSetting; break; case USB_SET_ALTINTERFACE: if (!(flag & FWRITE)) return (EPERM); ai = (struct usb_alt_interface *)addr; err = usbd_device2interface_handle(sc->sc_udev, ai->uai_interface_index, &iface); if (err) return (EINVAL); err = ugen_set_interface(sc, ai->uai_interface_index, ai->uai_alt_no); if (err) return (EINVAL); break; case USB_GET_NO_ALT: ai = (struct usb_alt_interface *)addr; cdesc = ugen_get_cdesc(sc, ai->uai_config_index, 0); if (cdesc == NULL) return (EINVAL); idesc = usbd_find_idesc(cdesc, ai->uai_interface_index, 0); if (idesc == NULL) { free(cdesc, M_TEMP); return (EINVAL); } ai->uai_alt_no = usbd_get_no_alts(cdesc, idesc->bInterfaceNumber); free(cdesc, M_TEMP); break; case USB_GET_DEVICE_DESC: *(usb_device_descriptor_t *)addr = *usbd_get_device_descriptor(sc->sc_udev); break; case USB_GET_CONFIG_DESC: cd = (struct usb_config_desc *)addr; cdesc = ugen_get_cdesc(sc, cd->ucd_config_index, 0); if (cdesc == NULL) return (EINVAL); cd->ucd_desc = *cdesc; free(cdesc, M_TEMP); break; case USB_GET_INTERFACE_DESC: id = (struct usb_interface_desc *)addr; cdesc = ugen_get_cdesc(sc, id->uid_config_index, 0); if (cdesc == NULL) return (EINVAL); if (id->uid_config_index == USB_CURRENT_CONFIG_INDEX && id->uid_alt_index == USB_CURRENT_ALT_INDEX) alt = ugen_get_alt_index(sc, id->uid_interface_index); else alt = id->uid_alt_index; idesc = usbd_find_idesc(cdesc, id->uid_interface_index, alt); if (idesc == NULL) { free(cdesc, M_TEMP); return (EINVAL); } id->uid_desc = *idesc; free(cdesc, M_TEMP); break; case USB_GET_ENDPOINT_DESC: ed = (struct usb_endpoint_desc *)addr; cdesc = ugen_get_cdesc(sc, ed->ued_config_index, 0); if (cdesc == NULL) return (EINVAL); if (ed->ued_config_index == USB_CURRENT_CONFIG_INDEX && ed->ued_alt_index == USB_CURRENT_ALT_INDEX) alt = ugen_get_alt_index(sc, ed->ued_interface_index); else alt = ed->ued_alt_index; edesc = usbd_find_edesc(cdesc, ed->ued_interface_index, alt, ed->ued_endpoint_index); if (edesc == NULL) { free(cdesc, M_TEMP); return (EINVAL); } ed->ued_desc = *edesc; free(cdesc, M_TEMP); break; case USB_GET_FULL_DESC: { int len; struct iovec iov; struct uio uio; struct usb_full_desc *fd = (struct usb_full_desc *)addr; int error; cdesc = ugen_get_cdesc(sc, fd->ufd_config_index, &len); if (len > fd->ufd_size) len = fd->ufd_size; iov.iov_base = (caddr_t)fd->ufd_data; iov.iov_len = len; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_resid = len; uio.uio_offset = 0; uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = UIO_READ; uio.uio_td = p; error = uiomove((void *)cdesc, len, &uio); free(cdesc, M_TEMP); return (error); } case USB_GET_STRING_DESC: { int len; si = (struct usb_string_desc *)addr; err = usbd_get_string_desc(sc->sc_udev, si->usd_string_index, si->usd_language_id, &si->usd_desc, &len); if (err) return (EINVAL); break; } case USB_DO_REQUEST: { struct usb_ctl_request *ur = (void *)addr; int len = UGETW(ur->ucr_request.wLength); struct iovec iov; struct uio uio; void *ptr = 0; - usbd_status err; int error = 0; if (!(flag & FWRITE)) return (EPERM); /* Avoid requests that would damage the bus integrity. */ if ((ur->ucr_request.bmRequestType == UT_WRITE_DEVICE && ur->ucr_request.bRequest == UR_SET_ADDRESS) || (ur->ucr_request.bmRequestType == UT_WRITE_DEVICE && ur->ucr_request.bRequest == UR_SET_CONFIG) || (ur->ucr_request.bmRequestType == UT_WRITE_INTERFACE && ur->ucr_request.bRequest == UR_SET_INTERFACE)) return (EINVAL); if (len < 0 || len > 32767) return (EINVAL); if (len != 0) { iov.iov_base = (caddr_t)ur->ucr_data; iov.iov_len = len; uio.uio_iov = &iov; uio.uio_iovcnt = 1; uio.uio_resid = len; uio.uio_offset = 0; uio.uio_segflg = UIO_USERSPACE; uio.uio_rw = ur->ucr_request.bmRequestType & UT_READ ? UIO_READ : UIO_WRITE; uio.uio_td = p; ptr = malloc(len, M_TEMP, M_WAITOK); if (uio.uio_rw == UIO_WRITE) { error = uiomove(ptr, len, &uio); if (error) goto ret; } } sce = &sc->sc_endpoints[endpt][IN]; err = usbd_do_request_flags(sc->sc_udev, &ur->ucr_request, ptr, ur->ucr_flags, &ur->ucr_actlen, sce->timeout); if (err) { error = EIO; goto ret; } if (len != 0) { if (uio.uio_rw == UIO_READ) { error = uiomove(ptr, len, &uio); if (error) goto ret; } } ret: if (ptr) free(ptr, M_TEMP); return (error); } case USB_GET_DEVICEINFO: usbd_fill_deviceinfo(sc->sc_udev, (struct usb_device_info *)addr, 1); + break; + case USB_RESET_DEVICE: + err = usbd_reset_device(sc->sc_udev); + if (err) + return EIO; break; default: return (EINVAL); } return (0); } int ugenioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *p) { int endpt = UGENENDPOINT(dev); struct ugen_softc *sc; int error; sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev)); if (sc->sc_dying) return (EIO); UGEN_DEV_REF(dev, sc); error = ugen_do_ioctl(sc, endpt, cmd, addr, flag, p); UGEN_DEV_RELE(dev, sc); return (error); } int ugenpoll(struct cdev *dev, int events, struct thread *p) { struct ugen_softc *sc; struct ugen_endpoint *sce_in, *sce_out; usb_endpoint_descriptor_t *edesc; int revents = 0; int s; sc = devclass_get_softc(ugen_devclass, UGENUNIT(dev)); if (sc->sc_dying) return ((events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP); /* Do not allow to poll a control endpoint */ if (UGENENDPOINT(dev) == USB_CONTROL_ENDPOINT) return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)); sce_in = &sc->sc_endpoints[UGENENDPOINT(dev)][IN]; sce_out = &sc->sc_endpoints[UGENENDPOINT(dev)][OUT]; edesc = (sce_in->edesc != NULL) ? sce_in->edesc : sce_out->edesc; KASSERT(edesc != NULL, ("ugenpoll: NULL edesc")); if (sce_in->edesc == NULL || sce_in->pipeh == NULL) sce_in = NULL; if (sce_out->edesc == NULL || sce_out->pipeh == NULL) sce_out = NULL; s = splusb(); switch (edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: if (sce_in != NULL && (events & (POLLIN | POLLRDNORM))) { if (sce_in->q.c_cc > 0) revents |= events & (POLLIN | POLLRDNORM); else selrecord(p, &sce_in->rsel); } if (sce_out != NULL && (events & (POLLOUT | POLLWRNORM))) { if (sce_out->q.c_cc > 0) revents |= events & (POLLOUT | POLLWRNORM); else selrecord(p, &sce_out->rsel); } break; case UE_ISOCHRONOUS: if (sce_in != NULL && (events & (POLLIN | POLLRDNORM))) { if (sce_in->cur != sce_in->fill) revents |= events & (POLLIN | POLLRDNORM); else selrecord(p, &sce_in->rsel); } if (sce_out != NULL && (events & (POLLOUT | POLLWRNORM))) { if (sce_out->cur != sce_out->fill) revents |= events & (POLLOUT | POLLWRNORM); else selrecord(p, &sce_out->rsel); } break; case UE_BULK: /* * We have no easy way of determining if a read will * yield any data or a write will happen. * Pretend they will. */ revents |= events & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM); break; default: break; } splx(s); return (revents); } DRIVER_MODULE(ugen, uhub, ugen_driver, ugen_devclass, usbd_driver_load, 0); Index: stable/7/sys/dev/usb/usb.h =================================================================== --- stable/7/sys/dev/usb/usb.h (revision 190250) +++ stable/7/sys/dev/usb/usb.h (revision 190251) @@ -1,705 +1,706 @@ /* $NetBSD: usb.h,v 1.69 2002/09/22 23:20:50 augustss Exp $ */ /* $FreeBSD$ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _USB_H_ #define _USB_H_ #include #include #if defined(_KERNEL) #include "opt_usb.h" #ifdef SYSCTL_DECL SYSCTL_DECL(_hw_usb); #endif #include MALLOC_DECLARE(M_USB); MALLOC_DECLARE(M_USBDEV); MALLOC_DECLARE(M_USBHC); #endif /* _KERNEL */ #define PWR_RESUME 0 #define PWR_SUSPEND 1 #define PWR_STANDBY 2 #define PWR_SOFTSUSPEND 3 #define PWR_SOFTSTANDBY 4 #define PWR_SOFTRESUME 5 /* These two defines are used by usbd to autoload the usb kld */ #define USB_KLD "usb" /* name of usb module */ #define USB_UHUB "usb/uhub" /* root hub */ #define USB_STACK_VERSION 2 #define USB_MAX_DEVICES 128 #define USB_START_ADDR 0 #define USB_CONTROL_ENDPOINT 0 #define USB_MAX_ENDPOINTS 16 #define USB_FRAMES_PER_SECOND 1000 /* * The USB records contain some unaligned little-endian word * components. The U[SG]ETW macros take care of both the alignment * and endian problem and should always be used to access non-byte * values. */ typedef u_int8_t uByte; typedef u_int8_t uWord[2]; typedef u_int8_t uDWord[4]; #define USETW2(w,h,l) ((w)[0] = (u_int8_t)(l), (w)[1] = (u_int8_t)(h)) #if 1 #define UGETW(w) ((w)[0] | ((w)[1] << 8)) #define USETW(w,v) ((w)[0] = (u_int8_t)(v), (w)[1] = (u_int8_t)((v) >> 8)) #define UGETDW(w) ((w)[0] | ((w)[1] << 8) | ((w)[2] << 16) | ((w)[3] << 24)) #define USETDW(w,v) ((w)[0] = (u_int8_t)(v), \ (w)[1] = (u_int8_t)((v) >> 8), \ (w)[2] = (u_int8_t)((v) >> 16), \ (w)[3] = (u_int8_t)((v) >> 24)) #else /* * On little-endian machines that can handle unanliged accesses * (e.g. i386) these macros can be replaced by the following. */ #define UGETW(w) (*(u_int16_t *)(w)) #define USETW(w,v) (*(u_int16_t *)(w) = (v)) #define UGETDW(w) (*(u_int32_t *)(w)) #define USETDW(w,v) (*(u_int32_t *)(w) = (v)) #endif #define UPACKED __packed typedef struct { uByte bmRequestType; uByte bRequest; uWord wValue; uWord wIndex; uWord wLength; } UPACKED usb_device_request_t; #define UT_WRITE 0x00 #define UT_READ 0x80 #define UT_STANDARD 0x00 #define UT_CLASS 0x20 #define UT_VENDOR 0x40 #define UT_DEVICE 0x00 #define UT_INTERFACE 0x01 #define UT_ENDPOINT 0x02 #define UT_OTHER 0x03 #define UT_READ_DEVICE (UT_READ | UT_STANDARD | UT_DEVICE) #define UT_READ_INTERFACE (UT_READ | UT_STANDARD | UT_INTERFACE) #define UT_READ_ENDPOINT (UT_READ | UT_STANDARD | UT_ENDPOINT) #define UT_WRITE_DEVICE (UT_WRITE | UT_STANDARD | UT_DEVICE) #define UT_WRITE_INTERFACE (UT_WRITE | UT_STANDARD | UT_INTERFACE) #define UT_WRITE_ENDPOINT (UT_WRITE | UT_STANDARD | UT_ENDPOINT) #define UT_READ_CLASS_DEVICE (UT_READ | UT_CLASS | UT_DEVICE) #define UT_READ_CLASS_INTERFACE (UT_READ | UT_CLASS | UT_INTERFACE) #define UT_READ_CLASS_OTHER (UT_READ | UT_CLASS | UT_OTHER) #define UT_READ_CLASS_ENDPOINT (UT_READ | UT_CLASS | UT_ENDPOINT) #define UT_WRITE_CLASS_DEVICE (UT_WRITE | UT_CLASS | UT_DEVICE) #define UT_WRITE_CLASS_INTERFACE (UT_WRITE | UT_CLASS | UT_INTERFACE) #define UT_WRITE_CLASS_OTHER (UT_WRITE | UT_CLASS | UT_OTHER) #define UT_WRITE_CLASS_ENDPOINT (UT_WRITE | UT_CLASS | UT_ENDPOINT) #define UT_READ_VENDOR_DEVICE (UT_READ | UT_VENDOR | UT_DEVICE) #define UT_READ_VENDOR_INTERFACE (UT_READ | UT_VENDOR | UT_INTERFACE) #define UT_READ_VENDOR_OTHER (UT_READ | UT_VENDOR | UT_OTHER) #define UT_READ_VENDOR_ENDPOINT (UT_READ | UT_VENDOR | UT_ENDPOINT) #define UT_WRITE_VENDOR_DEVICE (UT_WRITE | UT_VENDOR | UT_DEVICE) #define UT_WRITE_VENDOR_INTERFACE (UT_WRITE | UT_VENDOR | UT_INTERFACE) #define UT_WRITE_VENDOR_OTHER (UT_WRITE | UT_VENDOR | UT_OTHER) #define UT_WRITE_VENDOR_ENDPOINT (UT_WRITE | UT_VENDOR | UT_ENDPOINT) /* Requests */ #define UR_GET_STATUS 0x00 #define UR_CLEAR_FEATURE 0x01 #define UR_SET_FEATURE 0x03 #define UR_SET_ADDRESS 0x05 #define UR_GET_DESCRIPTOR 0x06 #define UDESC_DEVICE 0x01 #define UDESC_CONFIG 0x02 #define UDESC_STRING 0x03 #define UDESC_INTERFACE 0x04 #define UDESC_ENDPOINT 0x05 #define UDESC_DEVICE_QUALIFIER 0x06 #define UDESC_OTHER_SPEED_CONFIGURATION 0x07 #define UDESC_INTERFACE_POWER 0x08 #define UDESC_OTG 0x09 #define UDESC_CS_DEVICE 0x21 /* class specific */ #define UDESC_CS_CONFIG 0x22 #define UDESC_CS_STRING 0x23 #define UDESC_CS_INTERFACE 0x24 #define UDESC_CS_ENDPOINT 0x25 #define UDESC_HUB 0x29 #define UR_SET_DESCRIPTOR 0x07 #define UR_GET_CONFIG 0x08 #define UR_SET_CONFIG 0x09 #define UR_GET_INTERFACE 0x0a #define UR_SET_INTERFACE 0x0b #define UR_SYNCH_FRAME 0x0c /* Feature numbers */ #define UF_ENDPOINT_HALT 0 #define UF_DEVICE_REMOTE_WAKEUP 1 #define UF_TEST_MODE 2 #define USB_MAX_IPACKET 8 /* maximum size of the initial packet */ #define USB_2_MAX_CTRL_PACKET 64 #define USB_2_MAX_BULK_PACKET 512 typedef struct { uByte bLength; uByte bDescriptorType; uByte bDescriptorSubtype; } UPACKED usb_descriptor_t; typedef struct { uByte bLength; uByte bDescriptorType; uWord bcdUSB; #define UD_USB_2_0 0x0200 #define UD_IS_USB2(d) (UGETW((d)->bcdUSB) >= UD_USB_2_0) uByte bDeviceClass; uByte bDeviceSubClass; uByte bDeviceProtocol; uByte bMaxPacketSize; /* The fields below are not part of the initial descriptor. */ uWord idVendor; uWord idProduct; uWord bcdDevice; uByte iManufacturer; uByte iProduct; uByte iSerialNumber; uByte bNumConfigurations; } UPACKED usb_device_descriptor_t; #define USB_DEVICE_DESCRIPTOR_SIZE 18 typedef struct { uByte bLength; uByte bDescriptorType; uWord wTotalLength; uByte bNumInterface; uByte bConfigurationValue; uByte iConfiguration; uByte bmAttributes; #define UC_BUS_POWERED 0x80 #define UC_SELF_POWERED 0x40 #define UC_REMOTE_WAKEUP 0x20 uByte bMaxPower; /* max current in 2 mA units */ #define UC_POWER_FACTOR 2 } UPACKED usb_config_descriptor_t; #define USB_CONFIG_DESCRIPTOR_SIZE 9 typedef struct { uByte bLength; uByte bDescriptorType; uByte bInterfaceNumber; uByte bAlternateSetting; uByte bNumEndpoints; uByte bInterfaceClass; uByte bInterfaceSubClass; uByte bInterfaceProtocol; uByte iInterface; } UPACKED usb_interface_descriptor_t; #define USB_INTERFACE_DESCRIPTOR_SIZE 9 typedef struct { uByte bLength; uByte bDescriptorType; uByte bEndpointAddress; #define UE_GET_DIR(a) ((a) & 0x80) #define UE_SET_DIR(a,d) ((a) | (((d)&1) << 7)) #define UE_DIR_IN 0x80 #define UE_DIR_OUT 0x00 #define UE_ADDR 0x0f #define UE_GET_ADDR(a) ((a) & UE_ADDR) uByte bmAttributes; #define UE_XFERTYPE 0x03 #define UE_CONTROL 0x00 #define UE_ISOCHRONOUS 0x01 #define UE_BULK 0x02 #define UE_INTERRUPT 0x03 #define UE_GET_XFERTYPE(a) ((a) & UE_XFERTYPE) #define UE_ISO_TYPE 0x0c #define UE_ISO_ASYNC 0x04 #define UE_ISO_ADAPT 0x08 #define UE_ISO_SYNC 0x0c #define UE_GET_ISO_TYPE(a) ((a) & UE_ISO_TYPE) uWord wMaxPacketSize; uByte bInterval; } UPACKED usb_endpoint_descriptor_t; #define USB_ENDPOINT_DESCRIPTOR_SIZE 7 typedef struct { uByte bLength; uByte bDescriptorType; uWord bString[127]; } UPACKED usb_string_descriptor_t; #define USB_MAX_STRING_LEN 128 #define USB_LANGUAGE_TABLE 0 /* # of the string language id table */ /* Hub specific request */ #define UR_GET_BUS_STATE 0x02 #define UR_CLEAR_TT_BUFFER 0x08 #define UR_RESET_TT 0x09 #define UR_GET_TT_STATE 0x0a #define UR_STOP_TT 0x0b /* Hub features */ #define UHF_C_HUB_LOCAL_POWER 0 #define UHF_C_HUB_OVER_CURRENT 1 #define UHF_PORT_CONNECTION 0 #define UHF_PORT_ENABLE 1 #define UHF_PORT_SUSPEND 2 #define UHF_PORT_OVER_CURRENT 3 #define UHF_PORT_RESET 4 #define UHF_PORT_POWER 8 #define UHF_PORT_LOW_SPEED 9 #define UHF_C_PORT_CONNECTION 16 #define UHF_C_PORT_ENABLE 17 #define UHF_C_PORT_SUSPEND 18 #define UHF_C_PORT_OVER_CURRENT 19 #define UHF_C_PORT_RESET 20 #define UHF_PORT_TEST 21 #define UHF_PORT_INDICATOR 22 typedef struct { uByte bDescLength; uByte bDescriptorType; uByte bNbrPorts; uWord wHubCharacteristics; #define UHD_PWR 0x0003 #define UHD_PWR_GANGED 0x0000 #define UHD_PWR_INDIVIDUAL 0x0001 #define UHD_PWR_NO_SWITCH 0x0002 #define UHD_COMPOUND 0x0004 #define UHD_OC 0x0018 #define UHD_OC_GLOBAL 0x0000 #define UHD_OC_INDIVIDUAL 0x0008 #define UHD_OC_NONE 0x0010 #define UHD_TT_THINK 0x0060 #define UHD_TT_THINK_8 0x0000 #define UHD_TT_THINK_16 0x0020 #define UHD_TT_THINK_24 0x0040 #define UHD_TT_THINK_32 0x0060 #define UHD_PORT_IND 0x0080 uByte bPwrOn2PwrGood; /* delay in 2 ms units */ #define UHD_PWRON_FACTOR 2 uByte bHubContrCurrent; uByte DeviceRemovable[32]; /* max 255 ports */ #define UHD_NOT_REMOV(desc, i) \ (((desc)->DeviceRemovable[(i)/8] >> ((i) % 8)) & 1) /* deprecated */ uByte PortPowerCtrlMask[1]; } UPACKED usb_hub_descriptor_t; #define USB_HUB_DESCRIPTOR_SIZE 9 /* includes deprecated PortPowerCtrlMask */ typedef struct { uByte bLength; uByte bDescriptorType; uWord bcdUSB; uByte bDeviceClass; uByte bDeviceSubClass; uByte bDeviceProtocol; uByte bMaxPacketSize0; uByte bNumConfigurations; uByte bReserved; } UPACKED usb_device_qualifier_t; #define USB_DEVICE_QUALIFIER_SIZE 10 typedef struct { uByte bLength; uByte bDescriptorType; uByte bmAttributes; #define UOTG_SRP 0x01 #define UOTG_HNP 0x02 } UPACKED usb_otg_descriptor_t; /* OTG feature selectors */ #define UOTG_B_HNP_ENABLE 3 #define UOTG_A_HNP_SUPPORT 4 #define UOTG_A_ALT_HNP_SUPPORT 5 typedef struct { uWord wStatus; /* Device status flags */ #define UDS_SELF_POWERED 0x0001 #define UDS_REMOTE_WAKEUP 0x0002 /* Endpoint status flags */ #define UES_HALT 0x0001 } UPACKED usb_status_t; typedef struct { uWord wHubStatus; #define UHS_LOCAL_POWER 0x0001 #define UHS_OVER_CURRENT 0x0002 uWord wHubChange; } UPACKED usb_hub_status_t; typedef struct { uWord wPortStatus; #define UPS_CURRENT_CONNECT_STATUS 0x0001 #define UPS_PORT_ENABLED 0x0002 #define UPS_SUSPEND 0x0004 #define UPS_OVERCURRENT_INDICATOR 0x0008 #define UPS_RESET 0x0010 #define UPS_PORT_POWER 0x0100 #define UPS_LOW_SPEED 0x0200 #define UPS_HIGH_SPEED 0x0400 #define UPS_PORT_TEST 0x0800 #define UPS_PORT_INDICATOR 0x1000 uWord wPortChange; #define UPS_C_CONNECT_STATUS 0x0001 #define UPS_C_PORT_ENABLED 0x0002 #define UPS_C_SUSPEND 0x0004 #define UPS_C_OVERCURRENT_INDICATOR 0x0008 #define UPS_C_PORT_RESET 0x0010 } UPACKED usb_port_status_t; /* Device class codes */ #define UDCLASS_IN_INTERFACE 0x00 #define UDCLASS_COMM 0x02 #define UDCLASS_HUB 0x09 #define UDSUBCLASS_HUB 0x00 #define UDPROTO_FSHUB 0x00 #define UDPROTO_HSHUBSTT 0x01 #define UDPROTO_HSHUBMTT 0x02 #define UDCLASS_DIAGNOSTIC 0xdc #define UDCLASS_WIRELESS 0xe0 #define UDSUBCLASS_RF 0x01 #define UDPROTO_BLUETOOTH 0x01 #define UDCLASS_VENDOR 0xff /* Interface class codes */ #define UICLASS_UNSPEC 0x00 #define UICLASS_AUDIO 0x01 #define UISUBCLASS_AUDIOCONTROL 1 #define UISUBCLASS_AUDIOSTREAM 2 #define UISUBCLASS_MIDISTREAM 3 #define UICLASS_CDC 0x02 /* communication */ #define UISUBCLASS_DIRECT_LINE_CONTROL_MODEL 1 #define UISUBCLASS_ABSTRACT_CONTROL_MODEL 2 #define UISUBCLASS_TELEPHONE_CONTROL_MODEL 3 #define UISUBCLASS_MULTICHANNEL_CONTROL_MODEL 4 #define UISUBCLASS_CAPI_CONTROLMODEL 5 #define UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL 6 #define UISUBCLASS_ATM_NETWORKING_CONTROL_MODEL 7 #define UIPROTO_CDC_AT 1 #define UICLASS_HID 0x03 #define UISUBCLASS_BOOT 1 #define UIPROTO_BOOT_KEYBOARD 1 #define UIPROTO_MOUSE 2 #define UICLASS_PHYSICAL 0x05 #define UICLASS_IMAGE 0x06 #define UICLASS_PRINTER 0x07 #define UISUBCLASS_PRINTER 1 #define UIPROTO_PRINTER_UNI 1 #define UIPROTO_PRINTER_BI 2 #define UIPROTO_PRINTER_1284 3 #define UICLASS_MASS 0x08 #define UISUBCLASS_RBC 1 #define UISUBCLASS_SFF8020I 2 #define UISUBCLASS_QIC157 3 #define UISUBCLASS_UFI 4 #define UISUBCLASS_SFF8070I 5 #define UISUBCLASS_SCSI 6 #define UIPROTO_MASS_CBI_I 0 #define UIPROTO_MASS_CBI 1 #define UIPROTO_MASS_BBB_OLD 2 /* Not in the spec anymore */ #define UIPROTO_MASS_BBB 80 /* 'P' for the Iomega Zip drive */ #define UICLASS_HUB 0x09 #define UISUBCLASS_HUB 0 #define UIPROTO_FSHUB 0 #define UIPROTO_HSHUBSTT 0 /* Yes, same as previous */ #define UIPROTO_HSHUBMTT 1 #define UICLASS_CDC_DATA 0x0a #define UISUBCLASS_DATA 0 #define UIPROTO_DATA_ISDNBRI 0x30 /* Physical iface */ #define UIPROTO_DATA_HDLC 0x31 /* HDLC */ #define UIPROTO_DATA_TRANSPARENT 0x32 /* Transparent */ #define UIPROTO_DATA_Q921M 0x50 /* Management for Q921 */ #define UIPROTO_DATA_Q921 0x51 /* Data for Q921 */ #define UIPROTO_DATA_Q921TM 0x52 /* TEI multiplexer for Q921 */ #define UIPROTO_DATA_V42BIS 0x90 /* Data compression */ #define UIPROTO_DATA_Q931 0x91 /* Euro-ISDN */ #define UIPROTO_DATA_V120 0x92 /* V.24 rate adaption */ #define UIPROTO_DATA_CAPI 0x93 /* CAPI 2.0 commands */ #define UIPROTO_DATA_HOST_BASED 0xfd /* Host based driver */ #define UIPROTO_DATA_PUF 0xfe /* see Prot. Unit Func. Desc.*/ #define UIPROTO_DATA_VENDOR 0xff /* Vendor specific */ #define UICLASS_SMARTCARD 0x0b /*#define UICLASS_FIRM_UPD 0x0c*/ #define UICLASS_SECURITY 0x0d #define UICLASS_DIAGNOSTIC 0xdc #define UICLASS_WIRELESS 0xe0 #define UISUBCLASS_RF 0x01 #define UIPROTO_BLUETOOTH 0x01 #define UICLASS_APPL_SPEC 0xfe #define UISUBCLASS_FIRMWARE_DOWNLOAD 1 #define UISUBCLASS_IRDA 2 #define UIPROTO_IRDA 0 #define UICLASS_VENDOR 0xff #define UISUBCLASS_XBOX360_CONTROLLER 0x5d #define UIPROTO_XBOX360_GAMEPAD 0x01 #define USB_HUB_MAX_DEPTH 5 /* * Minimum time a device needs to be powered down to go through * a power cycle. XXX Are these time in the spec? */ #define USB_POWER_DOWN_TIME 200 /* ms */ #define USB_PORT_POWER_DOWN_TIME 100 /* ms */ #if 0 /* These are the values from the spec. */ #define USB_PORT_RESET_DELAY 10 /* ms */ #define USB_PORT_ROOT_RESET_DELAY 50 /* ms */ #define USB_PORT_RESET_RECOVERY 10 /* ms */ #define USB_PORT_POWERUP_DELAY 100 /* ms */ #define USB_SET_ADDRESS_SETTLE 2 /* ms */ #define USB_RESUME_DELAY (20*5) /* ms */ #define USB_RESUME_WAIT 10 /* ms */ #define USB_RESUME_RECOVERY 10 /* ms */ #define USB_EXTRA_POWER_UP_TIME 0 /* ms */ #else /* Allow for marginal (i.e. non-conforming) devices. */ #define USB_PORT_RESET_DELAY 50 /* ms */ #define USB_PORT_ROOT_RESET_DELAY 250 /* ms */ #define USB_PORT_RESET_RECOVERY 250 /* ms */ #define USB_PORT_POWERUP_DELAY 300 /* ms */ #define USB_SET_ADDRESS_SETTLE 10 /* ms */ #define USB_RESUME_DELAY (50*5) /* ms */ #define USB_RESUME_WAIT 50 /* ms */ #define USB_RESUME_RECOVERY 50 /* ms */ #define USB_EXTRA_POWER_UP_TIME 20 /* ms */ #endif #define USB_MIN_POWER 100 /* mA */ #define USB_MAX_POWER 500 /* mA */ #define USB_BUS_RESET_DELAY 100 /* ms XXX?*/ #define USB_UNCONFIG_NO 0 #define USB_UNCONFIG_INDEX (-1) /*** ioctl() related stuff ***/ struct usb_ctl_request { int ucr_addr; usb_device_request_t ucr_request; void *ucr_data; int ucr_flags; #define USBD_SHORT_XFER_OK 0x04 /* allow short reads */ int ucr_actlen; /* actual length transferred */ }; struct usb_alt_interface { int uai_config_index; int uai_interface_index; int uai_alt_no; }; #define USB_CURRENT_CONFIG_INDEX (-1) #define USB_CURRENT_ALT_INDEX (-1) struct usb_config_desc { int ucd_config_index; usb_config_descriptor_t ucd_desc; }; struct usb_interface_desc { int uid_config_index; int uid_interface_index; int uid_alt_index; usb_interface_descriptor_t uid_desc; }; struct usb_endpoint_desc { int ued_config_index; int ued_interface_index; int ued_alt_index; int ued_endpoint_index; usb_endpoint_descriptor_t ued_desc; }; struct usb_full_desc { int ufd_config_index; u_int ufd_size; u_char *ufd_data; }; struct usb_string_desc { int usd_string_index; int usd_language_id; usb_string_descriptor_t usd_desc; }; struct usb_ctl_report_desc { int ucrd_size; u_char ucrd_data[1024]; /* filled data size will vary */ }; typedef struct { u_int32_t cookie; } usb_event_cookie_t; #define USB_MAX_DEVNAMES 4 #define USB_MAX_DEVNAMELEN 16 struct usb_device_info { u_int8_t udi_bus; u_int8_t udi_addr; /* device address */ usb_event_cookie_t udi_cookie; char udi_product[USB_MAX_STRING_LEN]; char udi_vendor[USB_MAX_STRING_LEN]; char udi_release[8]; u_int16_t udi_productNo; u_int16_t udi_vendorNo; u_int16_t udi_releaseNo; u_int8_t udi_class; u_int8_t udi_subclass; u_int8_t udi_protocol; u_int8_t udi_config; u_int8_t udi_speed; #define USB_SPEED_LOW 1 #define USB_SPEED_FULL 2 #define USB_SPEED_HIGH 3 int udi_power; /* power consumption in mA, 0 if selfpowered */ int udi_nports; char udi_devnames[USB_MAX_DEVNAMES][USB_MAX_DEVNAMELEN]; u_int8_t udi_ports[16];/* hub only: addresses of devices on ports */ #define USB_PORT_ENABLED 0xff #define USB_PORT_SUSPENDED 0xfe #define USB_PORT_POWERED 0xfd #define USB_PORT_DISABLED 0xfc }; struct usb_ctl_report { int ucr_report; u_char ucr_data[1024]; /* filled data size will vary */ }; struct usb_device_stats { u_long uds_requests[4]; /* indexed by transfer type UE_* */ }; /* Events that can be read from /dev/usb */ struct usb_event { int ue_type; #define USB_EVENT_CTRLR_ATTACH 1 #define USB_EVENT_CTRLR_DETACH 2 #define USB_EVENT_DEVICE_ATTACH 3 #define USB_EVENT_DEVICE_DETACH 4 #define USB_EVENT_DRIVER_ATTACH 5 #define USB_EVENT_DRIVER_DETACH 6 #define USB_EVENT_IS_ATTACH(n) ((n) == USB_EVENT_CTRLR_ATTACH || (n) == USB_EVENT_DEVICE_ATTACH || (n) == USB_EVENT_DRIVER_ATTACH) #define USB_EVENT_IS_DETACH(n) ((n) == USB_EVENT_CTRLR_DETACH || (n) == USB_EVENT_DEVICE_DETACH || (n) == USB_EVENT_DRIVER_DETACH) struct timespec ue_time; union { struct { int ue_bus; } ue_ctrlr; struct usb_device_info ue_device; struct { usb_event_cookie_t ue_cookie; char ue_devname[16]; } ue_driver; } u; }; /* USB controller */ #define USB_REQUEST _IOWR('U', 1, struct usb_ctl_request) #define USB_SETDEBUG _IOW ('U', 2, int) #define USB_DISCOVER _IO ('U', 3) #define USB_DEVICEINFO _IOWR('U', 4, struct usb_device_info) #define USB_DEVICESTATS _IOR ('U', 5, struct usb_device_stats) /* Generic HID device */ #define USB_GET_REPORT_DESC _IOR ('U', 21, struct usb_ctl_report_desc) #define USB_SET_IMMED _IOW ('U', 22, int) #define USB_GET_REPORT _IOWR('U', 23, struct usb_ctl_report) #define USB_SET_REPORT _IOW ('U', 24, struct usb_ctl_report) #define USB_GET_REPORT_ID _IOR ('U', 25, int) /* Generic USB device */ #define USB_GET_CONFIG _IOR ('U', 100, int) #define USB_SET_CONFIG _IOW ('U', 101, int) #define USB_GET_ALTINTERFACE _IOWR('U', 102, struct usb_alt_interface) #define USB_SET_ALTINTERFACE _IOWR('U', 103, struct usb_alt_interface) #define USB_GET_NO_ALT _IOWR('U', 104, struct usb_alt_interface) #define USB_GET_DEVICE_DESC _IOR ('U', 105, usb_device_descriptor_t) #define USB_GET_CONFIG_DESC _IOWR('U', 106, struct usb_config_desc) #define USB_GET_INTERFACE_DESC _IOWR('U', 107, struct usb_interface_desc) #define USB_GET_ENDPOINT_DESC _IOWR('U', 108, struct usb_endpoint_desc) #define USB_GET_FULL_DESC _IOWR('U', 109, struct usb_full_desc) #define USB_GET_STRING_DESC _IOWR('U', 110, struct usb_string_desc) #define USB_DO_REQUEST _IOWR('U', 111, struct usb_ctl_request) #define USB_GET_DEVICEINFO _IOR ('U', 112, struct usb_device_info) #define USB_SET_SHORT_XFER _IOW ('U', 113, int) #define USB_SET_TIMEOUT _IOW ('U', 114, int) +#define USB_RESET_DEVICE _IO ('U', 115) /* Modem device */ #define USB_GET_CM_OVER_DATA _IOR ('U', 130, int) #define USB_SET_CM_OVER_DATA _IOW ('U', 131, int) #endif /* _USB_H_ */ Index: stable/7/sys/dev/usb/usb_subr.c =================================================================== --- stable/7/sys/dev/usb/usb_subr.c (revision 190250) +++ stable/7/sys/dev/usb/usb_subr.c (revision 190251) @@ -1,1392 +1,1388 @@ /* $NetBSD: usb_subr.c,v 1.99 2002/07/11 21:14:34 augustss Exp $ */ /* Also already have from NetBSD: * $NetBSD: usb_subr.c,v 1.102 2003/01/01 16:21:50 augustss Exp $ * $NetBSD: usb_subr.c,v 1.103 2003/01/10 11:19:13 augustss Exp $ * $NetBSD: usb_subr.c,v 1.111 2004/03/15 10:35:04 augustss Exp $ * $NetBSD: usb_subr.c,v 1.114 2004/06/23 02:30:52 mycroft Exp $ * $NetBSD: usb_subr.c,v 1.115 2004/06/23 05:23:19 mycroft Exp $ * $NetBSD: usb_subr.c,v 1.116 2004/06/23 06:27:54 mycroft Exp $ * $NetBSD: usb_subr.c,v 1.119 2004/10/23 13:26:33 augustss Exp $ */ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "opt_usb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include #define delay(d) DELAY(d) #ifdef USB_DEBUG #define DPRINTF(x) if (usbdebug) printf x #define DPRINTFN(n,x) if (usbdebug>(n)) printf x extern int usbdebug; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif static usbd_status usbd_set_config(usbd_device_handle, int); static void usbd_devinfo_vp(usbd_device_handle, char *, char *, int); static int usbd_getnewaddr(usbd_bus_handle bus); static void usbd_free_iface_data(usbd_device_handle dev, int ifcno); static void usbd_kill_pipe(usbd_pipe_handle); static usbd_status usbd_probe_and_attach(device_t parent, usbd_device_handle dev, int port, int addr); static u_int32_t usb_cookie_no = 0; #ifdef USBVERBOSE typedef u_int16_t usb_vendor_id_t; typedef u_int16_t usb_product_id_t; /* * Descriptions of of known vendors and devices ("products"). */ struct usb_knowndev { usb_vendor_id_t vendor; usb_product_id_t product; int flags; char *vendorname, *productname; }; #define USB_KNOWNDEV_NOPROD 0x01 /* match on vendor only */ #include "usbdevs_data.h" #endif /* USBVERBOSE */ static const char * const usbd_error_strs[] = { "NORMAL_COMPLETION", "IN_PROGRESS", "PENDING_REQUESTS", "NOT_STARTED", "INVAL", "NOMEM", "CANCELLED", "BAD_ADDRESS", "IN_USE", "NO_ADDR", "SET_ADDR_FAILED", "NO_POWER", "TOO_DEEP", "IOERROR", "NOT_CONFIGURED", "TIMEOUT", "SHORT_XFER", "STALLED", "INTERRUPTED", "XXX", }; const char * usbd_errstr(usbd_status err) { static char buffer[5]; if (err < USBD_ERROR_MAX) { return usbd_error_strs[err]; } else { snprintf(buffer, sizeof buffer, "%d", err); return buffer; } } usbd_status usbd_get_string_desc(usbd_device_handle dev, int sindex, int langid, usb_string_descriptor_t *sdesc, int *sizep) { usb_device_request_t req; usbd_status err; int actlen; req.bmRequestType = UT_READ_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW2(req.wValue, UDESC_STRING, sindex); USETW(req.wIndex, langid); USETW(req.wLength, 2); /* only size byte first */ err = usbd_do_request_flags(dev, &req, sdesc, USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT); if (err) return (err); if (actlen < 2) return (USBD_SHORT_XFER); USETW(req.wLength, sdesc->bLength); /* the whole string */ err = usbd_do_request_flags(dev, &req, sdesc, USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT); if (err) return (err); if (actlen != sdesc->bLength) { DPRINTFN(-1, ("usbd_get_string_desc: expected %d, got %d\n", sdesc->bLength, actlen)); } *sizep = actlen; return (USBD_NORMAL_COMPLETION); } static void usbd_trim_spaces(char *p) { char *q, *e; if (p == NULL) return; q = e = p; while (*q == ' ') /* skip leading spaces */ q++; while ((*p = *q++)) /* copy string */ if (*p++ != ' ') /* remember last non-space */ e = p; *e = 0; /* kill trailing spaces */ } static void usbd_devinfo_vp(usbd_device_handle dev, char *v, char *p, int usedev) { usb_device_descriptor_t *udd = &dev->ddesc; char *vendor = 0, *product = 0; #ifdef USBVERBOSE const struct usb_knowndev *kdp; #endif if (dev == NULL) { v[0] = p[0] = '\0'; return; } if (usedev) { if (usbd_get_string(dev, udd->iManufacturer, v, USB_MAX_STRING_LEN)) vendor = NULL; else vendor = v; usbd_trim_spaces(vendor); if (usbd_get_string(dev, udd->iProduct, p, USB_MAX_STRING_LEN)) product = NULL; else product = p; usbd_trim_spaces(product); if (vendor && !*vendor) vendor = NULL; if (product && !*product) product = NULL; } else { vendor = NULL; product = NULL; } #ifdef USBVERBOSE if (vendor == NULL || product == NULL) { for(kdp = usb_knowndevs; kdp->vendorname != NULL; kdp++) { if (kdp->vendor == UGETW(udd->idVendor) && (kdp->product == UGETW(udd->idProduct) || (kdp->flags & USB_KNOWNDEV_NOPROD) != 0)) break; } if (kdp->vendorname != NULL) { if (vendor == NULL) vendor = kdp->vendorname; if (product == NULL) product = (kdp->flags & USB_KNOWNDEV_NOPROD) == 0 ? kdp->productname : NULL; } } #endif if (vendor != NULL && *vendor) strcpy(v, vendor); else sprintf(v, "vendor 0x%04x", UGETW(udd->idVendor)); if (product != NULL && *product) strcpy(p, product); else sprintf(p, "product 0x%04x", UGETW(udd->idProduct)); } int usbd_printBCD(char *cp, int bcd) { return (sprintf(cp, "%x.%02x", bcd >> 8, bcd & 0xff)); } void usbd_devinfo(usbd_device_handle dev, int showclass, char *cp) { usb_device_descriptor_t *udd = &dev->ddesc; usbd_interface_handle iface; char vendor[USB_MAX_STRING_LEN]; char product[USB_MAX_STRING_LEN]; int bcdDevice, bcdUSB; usb_interface_descriptor_t *id; usbd_devinfo_vp(dev, vendor, product, 1); cp += sprintf(cp, "%s %s", vendor, product); if (showclass & USBD_SHOW_DEVICE_CLASS) cp += sprintf(cp, ", class %d/%d", udd->bDeviceClass, udd->bDeviceSubClass); bcdUSB = UGETW(udd->bcdUSB); bcdDevice = UGETW(udd->bcdDevice); cp += sprintf(cp, ", rev "); cp += usbd_printBCD(cp, bcdUSB); *cp++ = '/'; cp += usbd_printBCD(cp, bcdDevice); cp += sprintf(cp, ", addr %d", dev->address); if (showclass & USBD_SHOW_INTERFACE_CLASS) { /* fetch the interface handle for the first interface */ (void)usbd_device2interface_handle(dev, 0, &iface); id = usbd_get_interface_descriptor(iface); cp += sprintf(cp, ", iclass %d/%d", id->bInterfaceClass, id->bInterfaceSubClass); } *cp = 0; } /* Delay for a certain number of ms */ void usb_delay_ms(usbd_bus_handle bus, u_int ms) { /* Wait at least two clock ticks so we know the time has passed. */ if (bus->use_polling || cold) delay((ms+1) * 1000); else pause("usbdly", (ms*hz+999)/1000 + 1); } /* Delay given a device handle. */ void usbd_delay_ms(usbd_device_handle dev, u_int ms) { usb_delay_ms(dev->bus, ms); } usbd_status usbd_reset_port(usbd_device_handle dev, int port, usb_port_status_t *ps) { - usb_device_request_t req; usbd_status err; int n; - req.bmRequestType = UT_WRITE_CLASS_OTHER; - req.bRequest = UR_SET_FEATURE; - USETW(req.wValue, UHF_PORT_RESET); - USETW(req.wIndex, port); - USETW(req.wLength, 0); - err = usbd_do_request(dev, &req, 0); + err = usbd_set_port_feature(dev, port, UHF_PORT_RESET); DPRINTFN(1,("usbd_reset_port: port %d reset done, error=%s\n", port, usbd_errstr(err))); if (err) return (err); n = 10; do { /* Wait for device to recover from reset. */ usbd_delay_ms(dev, USB_PORT_RESET_DELAY); err = usbd_get_port_status(dev, port, ps); if (err) { DPRINTF(("usbd_reset_port: get status failed %d\n", err)); return (err); } /* If the device disappeared, just give up. */ if (!(UGETW(ps->wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) return (USBD_NORMAL_COMPLETION); } while ((UGETW(ps->wPortChange) & UPS_C_PORT_RESET) == 0 && --n > 0); if (n == 0) return (USBD_TIMEOUT); err = usbd_clear_port_feature(dev, port, UHF_C_PORT_RESET); #ifdef USB_DEBUG if (err) DPRINTF(("usbd_reset_port: clear port feature failed %d\n", err)); #endif /* Wait for the device to recover from reset. */ usbd_delay_ms(dev, USB_PORT_RESET_RECOVERY); return (err); } usb_interface_descriptor_t * usbd_find_idesc(usb_config_descriptor_t *cd, int ifaceidx, int altidx) { char *p = (char *)cd; char *end = p + UGETW(cd->wTotalLength); usb_interface_descriptor_t *d; int curidx, lastidx, curaidx = 0; for (curidx = lastidx = -1; p < end; ) { d = (usb_interface_descriptor_t *)p; DPRINTFN(4,("usbd_find_idesc: idx=%d(%d) altidx=%d(%d) len=%d " "type=%d\n", ifaceidx, curidx, altidx, curaidx, d->bLength, d->bDescriptorType)); if (d->bLength == 0) /* bad descriptor */ break; p += d->bLength; if (p <= end && d->bDescriptorType == UDESC_INTERFACE) { if (d->bInterfaceNumber != lastidx) { lastidx = d->bInterfaceNumber; curidx++; curaidx = 0; } else curaidx++; if (ifaceidx == curidx && altidx == curaidx) return (d); } } return (NULL); } usb_endpoint_descriptor_t * usbd_find_edesc(usb_config_descriptor_t *cd, int ifaceidx, int altidx, int endptidx) { char *p = (char *)cd; char *end = p + UGETW(cd->wTotalLength); usb_interface_descriptor_t *d; usb_endpoint_descriptor_t *e; int curidx; d = usbd_find_idesc(cd, ifaceidx, altidx); if (d == NULL) return (NULL); if (endptidx >= d->bNumEndpoints) /* quick exit */ return (NULL); curidx = -1; for (p = (char *)d + d->bLength; p < end; ) { e = (usb_endpoint_descriptor_t *)p; if (e->bLength == 0) /* bad descriptor */ break; p += e->bLength; if (p <= end && e->bDescriptorType == UDESC_INTERFACE) return (NULL); if (p <= end && e->bDescriptorType == UDESC_ENDPOINT) { curidx++; if (curidx == endptidx) return (e); } } return (NULL); } usbd_status usbd_fill_iface_data(usbd_device_handle dev, int ifaceidx, int altidx) { usbd_interface_handle ifc = &dev->ifaces[ifaceidx]; usb_interface_descriptor_t *idesc; char *p, *end; int endpt, nendpt; DPRINTFN(4,("usbd_fill_iface_data: ifaceidx=%d altidx=%d\n", ifaceidx, altidx)); idesc = usbd_find_idesc(dev->cdesc, ifaceidx, altidx); if (idesc == NULL) return (USBD_INVAL); ifc->device = dev; ifc->idesc = idesc; ifc->index = ifaceidx; ifc->altindex = altidx; nendpt = ifc->idesc->bNumEndpoints; DPRINTFN(4,("usbd_fill_iface_data: found idesc nendpt=%d\n", nendpt)); if (nendpt != 0) { ifc->endpoints = malloc(nendpt * sizeof(struct usbd_endpoint), M_USB, M_NOWAIT); if (ifc->endpoints == NULL) return (USBD_NOMEM); } else ifc->endpoints = NULL; ifc->priv = NULL; p = (char *)ifc->idesc + ifc->idesc->bLength; end = (char *)dev->cdesc + UGETW(dev->cdesc->wTotalLength); #define ed ((usb_endpoint_descriptor_t *)p) for (endpt = 0; endpt < nendpt; endpt++) { DPRINTFN(10,("usbd_fill_iface_data: endpt=%d\n", endpt)); for (; p < end; p += ed->bLength) { DPRINTFN(10,("usbd_fill_iface_data: p=%p end=%p " "len=%d type=%d\n", p, end, ed->bLength, ed->bDescriptorType)); if (p + ed->bLength <= end && ed->bLength != 0 && ed->bDescriptorType == UDESC_ENDPOINT) goto found; if (ed->bLength == 0 || ed->bDescriptorType == UDESC_INTERFACE) break; } /* passed end, or bad desc */ printf("usbd_fill_iface_data: bad descriptor(s): %s\n", ed->bLength == 0 ? "0 length" : ed->bDescriptorType == UDESC_INTERFACE ? "iface desc": "out of data"); goto bad; found: ifc->endpoints[endpt].edesc = ed; if (dev->speed == USB_SPEED_HIGH) { u_int mps; /* Control and bulk endpoints have max packet limits. */ switch (UE_GET_XFERTYPE(ed->bmAttributes)) { case UE_CONTROL: mps = USB_2_MAX_CTRL_PACKET; goto check; case UE_BULK: mps = USB_2_MAX_BULK_PACKET; check: if (UGETW(ed->wMaxPacketSize) != mps) { USETW(ed->wMaxPacketSize, mps); #ifdef DIAGNOSTIC printf("usbd_fill_iface_data: bad max " "packet size\n"); #endif } break; default: break; } } ifc->endpoints[endpt].refcnt = 0; ifc->endpoints[endpt].savedtoggle = 0; p += ed->bLength; } #undef ed LIST_INIT(&ifc->pipes); return (USBD_NORMAL_COMPLETION); bad: if (ifc->endpoints != NULL) { free(ifc->endpoints, M_USB); ifc->endpoints = NULL; } return (USBD_INVAL); } void usbd_free_iface_data(usbd_device_handle dev, int ifcno) { usbd_interface_handle ifc = &dev->ifaces[ifcno]; if (ifc->endpoints) free(ifc->endpoints, M_USB); } static usbd_status usbd_set_config(usbd_device_handle dev, int conf) { usb_device_request_t req; req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_SET_CONFIG; USETW(req.wValue, conf); USETW(req.wIndex, 0); USETW(req.wLength, 0); return (usbd_do_request(dev, &req, 0)); } usbd_status usbd_set_config_no(usbd_device_handle dev, int no, int msg) { int index; usb_config_descriptor_t cd; usbd_status err; if (no == USB_UNCONFIG_NO) return (usbd_set_config_index(dev, USB_UNCONFIG_INDEX, msg)); DPRINTFN(5,("usbd_set_config_no: %d\n", no)); /* Figure out what config index to use. */ for (index = 0; index < dev->ddesc.bNumConfigurations; index++) { err = usbd_get_config_desc(dev, index, &cd); if (err) return (err); if (cd.bConfigurationValue == no) return (usbd_set_config_index(dev, index, msg)); } return (USBD_INVAL); } usbd_status usbd_set_config_index(usbd_device_handle dev, int index, int msg) { usb_status_t ds; usb_config_descriptor_t cd, *cdp; usbd_status err; int i, ifcidx, nifc, len, selfpowered, power; DPRINTFN(5,("usbd_set_config_index: dev=%p index=%d\n", dev, index)); if (dev->config != USB_UNCONFIG_NO) { nifc = dev->cdesc->bNumInterface; /* Check that all interfaces are idle */ for (ifcidx = 0; ifcidx < nifc; ifcidx++) { if (LIST_EMPTY(&dev->ifaces[ifcidx].pipes)) continue; DPRINTF(("usbd_set_config_index: open pipes exist\n")); return (USBD_IN_USE); } DPRINTF(("usbd_set_config_index: free old config\n")); /* Free all configuration data structures. */ for (ifcidx = 0; ifcidx < nifc; ifcidx++) usbd_free_iface_data(dev, ifcidx); free(dev->ifaces, M_USB); free(dev->cdesc, M_USB); dev->ifaces = NULL; dev->cdesc = NULL; dev->config = USB_UNCONFIG_NO; } if (index == USB_UNCONFIG_INDEX) { /* We are unconfiguring the device, so leave unallocated. */ DPRINTF(("usbd_set_config_index: set config 0\n")); err = usbd_set_config(dev, USB_UNCONFIG_NO); if (err) DPRINTF(("usbd_set_config_index: setting config=0 " "failed, error=%s\n", usbd_errstr(err))); return (err); } /* Get the short descriptor. */ err = usbd_get_config_desc(dev, index, &cd); if (err) return (err); len = UGETW(cd.wTotalLength); cdp = malloc(len, M_USB, M_NOWAIT); if (cdp == NULL) return (USBD_NOMEM); /* Get the full descriptor. Try a few times for slow devices. */ for (i = 0; i < 3; i++) { err = usbd_get_desc(dev, UDESC_CONFIG, index, len, cdp); if (!err) break; usbd_delay_ms(dev, 200); } if (err) goto bad; if (cdp->bDescriptorType != UDESC_CONFIG) { DPRINTFN(-1,("usbd_set_config_index: bad desc %d\n", cdp->bDescriptorType)); err = USBD_INVAL; goto bad; } /* Figure out if the device is self or bus powered. */ selfpowered = 0; if (!(dev->quirks->uq_flags & UQ_BUS_POWERED) && (cdp->bmAttributes & UC_SELF_POWERED)) { /* May be self powered. */ if (cdp->bmAttributes & UC_BUS_POWERED) { /* Must ask device. */ if (dev->quirks->uq_flags & UQ_POWER_CLAIM) { /* * Hub claims to be self powered, but isn't. * It seems that the power status can be * determined by the hub characteristics. */ usb_hub_descriptor_t hd; usb_device_request_t req; req.bmRequestType = UT_READ_CLASS_DEVICE; req.bRequest = UR_GET_DESCRIPTOR; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, USB_HUB_DESCRIPTOR_SIZE); err = usbd_do_request(dev, &req, &hd); if (!err && (UGETW(hd.wHubCharacteristics) & UHD_PWR_INDIVIDUAL)) selfpowered = 1; DPRINTF(("usbd_set_config_index: charac=0x%04x" ", error=%s\n", UGETW(hd.wHubCharacteristics), usbd_errstr(err))); } else { err = usbd_get_device_status(dev, &ds); if (!err && (UGETW(ds.wStatus) & UDS_SELF_POWERED)) selfpowered = 1; DPRINTF(("usbd_set_config_index: status=0x%04x" ", error=%s\n", UGETW(ds.wStatus), usbd_errstr(err))); } } else selfpowered = 1; } DPRINTF(("usbd_set_config_index: (addr %d) cno=%d attr=0x%02x, " "selfpowered=%d, power=%d\n", cdp->bConfigurationValue, dev->address, cdp->bmAttributes, selfpowered, cdp->bMaxPower * 2)); /* Check if we have enough power. */ #ifdef USB_DEBUG if (dev->powersrc == NULL) { DPRINTF(("usbd_set_config_index: No power source?\n")); return (USBD_IOERROR); } #endif power = cdp->bMaxPower * 2; if (power > dev->powersrc->power) { DPRINTF(("power exceeded %d %d\n", power,dev->powersrc->power)); /* XXX print nicer message. */ if (msg) - printf("%s: device addr %d (config %d) exceeds power " - "budget, %d mA > %d mA\n", - device_get_nameunit(dev->bus->bdev), dev->address, - cdp->bConfigurationValue, - power, dev->powersrc->power); + device_printf(dev->bus->bdev, + "device addr %d (config %d) exceeds " + "power budget, %d mA > %d mA\n", + dev->address, cdp->bConfigurationValue, + power, dev->powersrc->power); err = USBD_NO_POWER; goto bad; } dev->power = power; dev->self_powered = selfpowered; /* Set the actual configuration value. */ DPRINTF(("usbd_set_config_index: set config %d\n", cdp->bConfigurationValue)); err = usbd_set_config(dev, cdp->bConfigurationValue); if (err) { DPRINTF(("usbd_set_config_index: setting config=%d failed, " "error=%s\n", cdp->bConfigurationValue, usbd_errstr(err))); goto bad; } /* Allocate and fill interface data. */ nifc = cdp->bNumInterface; dev->ifaces = malloc(nifc * sizeof(struct usbd_interface), M_USB, M_NOWAIT); if (dev->ifaces == NULL) { err = USBD_NOMEM; goto bad; } DPRINTFN(5,("usbd_set_config_index: dev=%p cdesc=%p\n", dev, cdp)); dev->cdesc = cdp; dev->config = cdp->bConfigurationValue; for (ifcidx = 0; ifcidx < nifc; ifcidx++) { err = usbd_fill_iface_data(dev, ifcidx, 0); if (err) { while (--ifcidx >= 0) usbd_free_iface_data(dev, ifcidx); goto bad; } } return (USBD_NORMAL_COMPLETION); bad: free(cdp, M_USB); return (err); } /* XXX add function for alternate settings */ usbd_status usbd_setup_pipe(usbd_device_handle dev, usbd_interface_handle iface, struct usbd_endpoint *ep, int ival, usbd_pipe_handle *pipe) { usbd_pipe_handle p; usbd_status err; DPRINTFN(1,("usbd_setup_pipe: dev=%p iface=%p ep=%p pipe=%p\n", dev, iface, ep, pipe)); p = malloc(dev->bus->pipe_size, M_USB, M_NOWAIT); if (p == NULL) return (USBD_NOMEM); p->device = dev; p->iface = iface; p->endpoint = ep; ep->refcnt++; p->refcnt = 1; p->intrxfer = 0; p->running = 0; p->aborting = 0; p->repeat = 0; p->interval = ival; STAILQ_INIT(&p->queue); err = dev->bus->methods->open_pipe(p); if (err) { DPRINTFN(-1,("usbd_setup_pipe: endpoint=0x%x failed, error=" "%s\n", ep->edesc->bEndpointAddress, usbd_errstr(err))); free(p, M_USB); return (err); } if (dev->quirks->uq_flags & UQ_OPEN_CLEARSTALL) { /* Clear any stall and make sure DATA0 toggle will be used next. */ if (UE_GET_ADDR(ep->edesc->bEndpointAddress) != USB_CONTROL_ENDPOINT) { err = usbd_clear_endpoint_stall(p); if (err && err != USBD_STALLED && err != USBD_TIMEOUT) { printf("usbd_setup_pipe: failed to start " "endpoint, %s\n", usbd_errstr(err)); return (err); } } } *pipe = p; return (USBD_NORMAL_COMPLETION); } /* Abort the device control pipe. */ void usbd_kill_pipe(usbd_pipe_handle pipe) { usbd_abort_pipe(pipe); pipe->methods->close(pipe); pipe->endpoint->refcnt--; free(pipe, M_USB); } int usbd_getnewaddr(usbd_bus_handle bus) { int addr; for (addr = 1; addr < USB_MAX_DEVICES; addr++) if (bus->devices[addr] == 0) return (addr); return (-1); } usbd_status usbd_probe_and_attach(device_t parent, usbd_device_handle dev, int port, int addr) { struct usb_attach_arg uaa; usb_device_descriptor_t *dd = &dev->ddesc; int found, i, confi, nifaces; usbd_status err; device_t *tmpdv; usbd_interface_handle ifaces[256]; /* 256 is the absolute max */ char *devinfo; /* XXX FreeBSD may leak resources on failure cases -- fixme */ device_t bdev; struct usb_attach_arg *uaap; devinfo = malloc(1024, M_USB, M_NOWAIT); if (devinfo == NULL) { device_printf(parent, "Can't allocate memory for probe string\n"); return (USBD_NOMEM); } bdev = device_add_child(parent, NULL, -1); if (!bdev) { free(devinfo, M_USB); device_printf(parent, "Device creation failed\n"); return (USBD_INVAL); } uaap = malloc(sizeof(uaa), M_USB, M_NOWAIT); if (uaap == NULL) { free(devinfo, M_USB); return (USBD_INVAL); } device_set_ivars(bdev, uaap); uaa.device = dev; uaa.iface = NULL; uaa.ifaces = NULL; uaa.nifaces = 0; uaa.usegeneric = 0; uaa.port = port; uaa.configno = UHUB_UNK_CONFIGURATION; uaa.ifaceno = UHUB_UNK_INTERFACE; uaa.vendor = UGETW(dd->idVendor); uaa.product = UGETW(dd->idProduct); uaa.release = UGETW(dd->bcdDevice); uaa.matchlvl = 0; /* First try with device specific drivers. */ DPRINTF(("usbd_probe_and_attach: trying device specific drivers\n")); dev->ifacenums = NULL; dev->subdevs = malloc(2 * sizeof(device_t), M_USB, M_NOWAIT); if (dev->subdevs == NULL) { free(devinfo, M_USB); return (USBD_NOMEM); } dev->subdevs[0] = bdev; dev->subdevs[1] = 0; *uaap = uaa; usbd_devinfo(dev, 1, devinfo); device_set_desc_copy(bdev, devinfo); if (device_probe_and_attach(bdev) == 0) { free(devinfo, M_USB); return (USBD_NORMAL_COMPLETION); } /* * Free subdevs so we can reallocate it larger for the number of * interfaces */ tmpdv = dev->subdevs; dev->subdevs = NULL; free(tmpdv, M_USB); DPRINTF(("usbd_probe_and_attach: no device specific driver found\n")); DPRINTF(("usbd_probe_and_attach: looping over %d configurations\n", dd->bNumConfigurations)); /* Next try with interface drivers. */ for (confi = 0; confi < dd->bNumConfigurations; confi++) { DPRINTFN(1,("usbd_probe_and_attach: trying config idx=%d\n", confi)); err = usbd_set_config_index(dev, confi, 1); if (err) { #ifdef USB_DEBUG DPRINTF(("%s: port %d, set config at addr %d failed, " "error=%s\n", device_get_nameunit(parent), port, addr, usbd_errstr(err))); #else printf("%s: port %d, set config at addr %d failed\n", device_get_nameunit(parent), port, addr); #endif free(devinfo, M_USB); return (err); } nifaces = dev->cdesc->bNumInterface; uaa.configno = dev->cdesc->bConfigurationValue; for (i = 0; i < nifaces; i++) ifaces[i] = &dev->ifaces[i]; uaa.ifaces = ifaces; uaa.nifaces = nifaces; dev->subdevs = malloc((nifaces+1) * sizeof(device_t), M_USB,M_NOWAIT); if (dev->subdevs == NULL) { free(devinfo, M_USB); return (USBD_NOMEM); } dev->ifacenums = malloc((nifaces) * sizeof(*dev->ifacenums), M_USB,M_NOWAIT); if (dev->ifacenums == NULL) { free(devinfo, M_USB); return (USBD_NOMEM); } found = 0; for (i = 0; i < nifaces; i++) { if (ifaces[i] == NULL) continue; /* interface already claimed */ uaa.iface = ifaces[i]; uaa.ifaceno = ifaces[i]->idesc->bInterfaceNumber; dev->subdevs[found] = bdev; dev->subdevs[found + 1] = 0; dev->ifacenums[found] = i; *uaap = uaa; usbd_devinfo(dev, 1, devinfo); device_set_desc_copy(bdev, devinfo); if (device_probe_and_attach(bdev) == 0) { ifaces[i] = 0; /* consumed */ found++; /* create another child for the next iface */ bdev = device_add_child(parent, NULL, -1); if (!bdev) { device_printf(parent, "Device add failed\n"); free(devinfo, M_USB); return (USBD_NORMAL_COMPLETION); } uaap = malloc(sizeof(uaa), M_USB, M_NOWAIT); if (uaap == NULL) { free(devinfo, M_USB); return (USBD_NOMEM); } device_set_ivars(bdev, uaap); } else { dev->subdevs[found] = 0; } } if (found != 0) { /* remove the last created child. It is unused */ free(uaap, M_USB); free(devinfo, M_USB); device_delete_child(parent, bdev); /* free(uaap, M_USB); */ /* May be needed? xxx */ return (USBD_NORMAL_COMPLETION); } tmpdv = dev->subdevs; dev->subdevs = NULL; free(tmpdv, M_USB); free(dev->ifacenums, M_USB); dev->ifacenums = NULL; } /* No interfaces were attached in any of the configurations. */ if (dd->bNumConfigurations > 1) /* don't change if only 1 config */ usbd_set_config_index(dev, 0, 0); DPRINTF(("usbd_probe_and_attach: no interface drivers found\n")); /* Finally try the generic driver. */ uaa.iface = NULL; uaa.usegeneric = 1; uaa.configno = UHUB_UNK_CONFIGURATION; uaa.ifaceno = UHUB_UNK_INTERFACE; dev->subdevs = malloc(2 * sizeof(device_t), M_USB, M_NOWAIT); if (dev->subdevs == 0) { free(devinfo, M_USB); return (USBD_NOMEM); } dev->subdevs[0] = bdev; dev->subdevs[1] = 0; *uaap = uaa; usbd_devinfo(dev, 1, devinfo); device_set_desc_copy(bdev, devinfo); free(devinfo, M_USB); if (device_probe_and_attach(bdev) == 0) return (USBD_NORMAL_COMPLETION); /* * The generic attach failed, but leave the device as it is. * We just did not find any drivers, that's all. The device is * fully operational and not harming anyone. */ DPRINTF(("usbd_probe_and_attach: generic attach failed\n")); return (USBD_NORMAL_COMPLETION); } /* * Called when a new device has been put in the powered state, * but not yet in the addressed state. * Get initial descriptor, set the address, get full descriptor, * and attach a driver. */ usbd_status usbd_new_device(device_t parent, usbd_bus_handle bus, int depth, int speed, int port, struct usbd_port *up) { usbd_device_handle dev, adev; struct usbd_device *hub; usb_device_descriptor_t *dd; usb_port_status_t ps; usbd_status err; int addr; int i; int p; DPRINTF(("usbd_new_device bus=%p port=%d depth=%d speed=%d\n", bus, port, depth, speed)); addr = usbd_getnewaddr(bus); if (addr < 0) { - printf("%s: No free USB addresses, new device ignored.\n", - device_get_nameunit(bus->bdev)); + device_printf(bus->bdev, "No free USB addresses\n"); return (USBD_NO_ADDR); } dev = malloc(sizeof *dev, M_USB, M_NOWAIT|M_ZERO); if (dev == NULL) return (USBD_NOMEM); dev->bus = bus; /* Set up default endpoint handle. */ dev->def_ep.edesc = &dev->def_ep_desc; /* Set up default endpoint descriptor. */ dev->def_ep_desc.bLength = USB_ENDPOINT_DESCRIPTOR_SIZE; dev->def_ep_desc.bDescriptorType = UDESC_ENDPOINT; dev->def_ep_desc.bEndpointAddress = USB_CONTROL_ENDPOINT; dev->def_ep_desc.bmAttributes = UE_CONTROL; USETW(dev->def_ep_desc.wMaxPacketSize, USB_MAX_IPACKET); dev->def_ep_desc.bInterval = 0; dev->quirks = &usbd_no_quirk; dev->address = USB_START_ADDR; dev->ddesc.bMaxPacketSize = 0; dev->depth = depth; dev->powersrc = up; dev->myhub = up->parent; up->device = dev; if (up->parent && speed > up->parent->speed) { #ifdef USB_DEBUG printf("%s: maxium speed of attached " "device, %d, is higher than speed " "of parent HUB, %d.\n", __FUNCTION__, speed, up->parent->speed); #endif /* * Reduce the speed, otherwise we won't setup the * proper transfer methods. */ speed = up->parent->speed; } /* Locate port on upstream high speed hub */ for (adev = dev, hub = up->parent; hub != NULL && hub->speed != USB_SPEED_HIGH; adev = hub, hub = hub->myhub) ; if (hub) { for (p = 0; p < hub->hub->hubdesc.bNbrPorts; p++) { if (hub->hub->ports[p].device == adev) { dev->myhsport = &hub->hub->ports[p]; goto found; } } panic("usbd_new_device: cannot find HS port\n"); found: DPRINTFN(1,("usbd_new_device: high speed port %d\n", p)); } else { dev->myhsport = NULL; } dev->speed = speed; dev->langid = USBD_NOLANG; dev->cookie.cookie = ++usb_cookie_no; /* Establish the default pipe. */ err = usbd_setup_pipe(dev, 0, &dev->def_ep, USBD_DEFAULT_INTERVAL, &dev->default_pipe); if (err) { usbd_remove_device(dev, up); return (err); } dd = &dev->ddesc; /* Try a few times in case the device is slow (i.e. outside specs.) */ DPRINTFN(5,("usbd_new_device: setting device address=%d\n", addr)); for (i = 0; i < 15; i++) { /* Get the first 8 bytes of the device descriptor. */ err = usbd_get_desc(dev, UDESC_DEVICE, 0, USB_MAX_IPACKET, dd); if (!err) break; usbd_delay_ms(dev, 200); if ((i & 3) == 3) { DPRINTFN(-1,("usb_new_device: set address %d " "failed - trying a port reset\n", addr)); usbd_reset_port(up->parent, port, &ps); } } if (err) { DPRINTFN(-1, ("usbd_new_device: addr=%d, getting first desc " "failed\n", addr)); usbd_remove_device(dev, up); return (err); } if (speed == USB_SPEED_HIGH) { /* Max packet size must be 64 (sec 5.5.3). */ if (dd->bMaxPacketSize != USB_2_MAX_CTRL_PACKET) { #ifdef DIAGNOSTIC printf("usbd_new_device: addr=%d bad max packet size\n", addr); #endif dd->bMaxPacketSize = USB_2_MAX_CTRL_PACKET; } } DPRINTF(("usbd_new_device: adding unit addr=%d, rev=%02x, class=%d, " "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n", addr,UGETW(dd->bcdUSB), dd->bDeviceClass, dd->bDeviceSubClass, dd->bDeviceProtocol, dd->bMaxPacketSize, dd->bLength, dev->speed)); if (dd->bDescriptorType != UDESC_DEVICE) { /* Illegal device descriptor */ DPRINTFN(-1,("usbd_new_device: illegal descriptor %d\n", dd->bDescriptorType)); usbd_remove_device(dev, up); return (USBD_INVAL); } if (dd->bLength < USB_DEVICE_DESCRIPTOR_SIZE) { DPRINTFN(-1,("usbd_new_device: bad length %d\n", dd->bLength)); usbd_remove_device(dev, up); return (USBD_INVAL); } USETW(dev->def_ep_desc.wMaxPacketSize, dd->bMaxPacketSize); /* Re-establish the default pipe with the new max packet size. */ usbd_kill_pipe(dev->default_pipe); err = usbd_setup_pipe(dev, 0, &dev->def_ep, USBD_DEFAULT_INTERVAL, &dev->default_pipe); if (err) { usbd_remove_device(dev, up); return (err); } err = usbd_reload_device_desc(dev); if (err) { DPRINTFN(-1, ("usbd_new_device: addr=%d, getting full desc " "failed\n", addr)); usbd_remove_device(dev, up); return (err); } /* Set the address */ DPRINTFN(5,("usbd_new_device: setting device address=%d\n", addr)); err = usbd_set_address(dev, addr); if (err) { DPRINTFN(-1,("usb_new_device: set address %d failed\n", addr)); err = USBD_SET_ADDR_FAILED; usbd_remove_device(dev, up); return (err); } /* Allow device time to set new address */ usbd_delay_ms(dev, USB_SET_ADDRESS_SETTLE); dev->address = addr; /* New device address now */ bus->devices[addr] = dev; /* Re-establish the default pipe with the new address. */ usbd_kill_pipe(dev->default_pipe); err = usbd_setup_pipe(dev, 0, &dev->def_ep, USBD_DEFAULT_INTERVAL, &dev->default_pipe); if (err) { usbd_remove_device(dev, up); return (err); } /* Assume 100mA bus powered for now. Changed when configured. */ dev->power = USB_MIN_POWER; dev->self_powered = 0; DPRINTF(("usbd_new_device: new dev (addr %d), dev=%p, parent=%p\n", addr, dev, parent)); err = usbd_probe_and_attach(parent, dev, port, addr); if (err) { usbd_remove_device(dev, up); return (err); } usbd_add_dev_event(USB_EVENT_DEVICE_ATTACH, dev); return (USBD_NORMAL_COMPLETION); } usbd_status usbd_reload_device_desc(usbd_device_handle dev) { usbd_status err; int i; /* Get the full device descriptor. */ for (i = 0; i < 3; ++i) { err = usbd_get_device_desc(dev, &dev->ddesc); if (!err) break; usbd_delay_ms(dev, 200); } if (err) return (err); /* Figure out what's wrong with this device. */ dev->quirks = usbd_find_quirk(&dev->ddesc); return (USBD_NORMAL_COMPLETION); } void usbd_remove_device(usbd_device_handle dev, struct usbd_port *up) { DPRINTF(("usbd_remove_device: %p\n", dev)); if (dev->default_pipe != NULL) usbd_kill_pipe(dev->default_pipe); up->device = NULL; dev->bus->devices[dev->address] = NULL; free(dev, M_USB); } void usbd_fill_deviceinfo(usbd_device_handle dev, struct usb_device_info *di, int usedev) { struct usbd_port *p; int i, err, s; di->udi_bus = device_get_unit(dev->bus->bdev); di->udi_addr = dev->address; di->udi_cookie = dev->cookie; usbd_devinfo_vp(dev, di->udi_vendor, di->udi_product, usedev); usbd_printBCD(di->udi_release, UGETW(dev->ddesc.bcdDevice)); di->udi_vendorNo = UGETW(dev->ddesc.idVendor); di->udi_productNo = UGETW(dev->ddesc.idProduct); di->udi_releaseNo = UGETW(dev->ddesc.bcdDevice); di->udi_class = dev->ddesc.bDeviceClass; di->udi_subclass = dev->ddesc.bDeviceSubClass; di->udi_protocol = dev->ddesc.bDeviceProtocol; di->udi_config = dev->config; di->udi_power = dev->self_powered ? 0 : dev->power; di->udi_speed = dev->speed; if (dev->subdevs != NULL) { for (i = 0; dev->subdevs[i] && i < USB_MAX_DEVNAMES; i++) { if (device_is_attached(dev->subdevs[i])) strlcpy(di->udi_devnames[i], device_get_nameunit(dev->subdevs[i]), USB_MAX_DEVNAMELEN); else di->udi_devnames[i][0] = 0; } } else { i = 0; } for (/*i is set */; i < USB_MAX_DEVNAMES; i++) di->udi_devnames[i][0] = 0; /* empty */ if (dev->hub) { for (i = 0; i < sizeof(di->udi_ports) / sizeof(di->udi_ports[0]) && i < dev->hub->hubdesc.bNbrPorts; i++) { p = &dev->hub->ports[i]; if (p->device) err = p->device->address; else { s = UGETW(p->status.wPortStatus); if (s & UPS_PORT_ENABLED) err = USB_PORT_ENABLED; else if (s & UPS_SUSPEND) err = USB_PORT_SUSPENDED; else if (s & UPS_PORT_POWER) err = USB_PORT_POWERED; else err = USB_PORT_DISABLED; } di->udi_ports[i] = err; } di->udi_nports = dev->hub->hubdesc.bNbrPorts; } else di->udi_nports = 0; } void usb_free_device(usbd_device_handle dev) { int ifcidx, nifc; if (dev->default_pipe != NULL) usbd_kill_pipe(dev->default_pipe); if (dev->ifaces != NULL) { nifc = dev->cdesc->bNumInterface; for (ifcidx = 0; ifcidx < nifc; ifcidx++) usbd_free_iface_data(dev, ifcidx); free(dev->ifaces, M_USB); } if (dev->cdesc != NULL) free(dev->cdesc, M_USB); if (dev->subdevs != NULL) free(dev->subdevs, M_USB); if (dev->ifacenums != NULL) free(dev->ifacenums, M_USB); free(dev, M_USB); } /* * The general mechanism for detaching drivers works as follows: Each * driver is responsible for maintaining a reference count on the * number of outstanding references to its softc (e.g. from * processing hanging in a read or write). The detach method of the * driver decrements this counter and flags in the softc that the * driver is dying and then wakes any sleepers. It then sleeps on the * softc. Each place that can sleep must maintain the reference * count. When the reference count drops to -1 (0 is the normal value * of the reference count) the a wakeup on the softc is performed * signaling to the detach waiter that all references are gone. */ /* * Called from process context when we discover that a port has * been disconnected. */ void usb_disconnect_port(struct usbd_port *up, device_t parent) { usbd_device_handle dev = up->device; const char *hubname = device_get_nameunit(parent); int i; DPRINTFN(3,("uhub_disconnect: up=%p dev=%p port=%d\n", up, dev, up->portno)); #ifdef DIAGNOSTIC if (dev == NULL) { printf("usb_disconnect_port: no device\n"); return; } #endif if (dev->subdevs != NULL) { DPRINTFN(3,("usb_disconnect_port: disconnect subdevs\n")); for (i = 0; dev->subdevs[i]; i++) { - printf("%s: at %s", device_get_nameunit(dev->subdevs[i]), - hubname); - if (up->portno != 0) - printf(" port %d", up->portno); - printf(" (addr %d) disconnected\n", dev->address); + if (!device_is_quiet(dev->subdevs[i])) { + device_printf(dev->subdevs[i], + "at %s", hubname); + if (up->portno != 0) + printf(" port %d", up->portno); + printf(" (addr %d) disconnected\n", dev->address); + } + struct usb_attach_arg *uaap = device_get_ivars(dev->subdevs[i]); device_detach(dev->subdevs[i]); free(uaap, M_USB); device_delete_child(device_get_parent(dev->subdevs[i]), dev->subdevs[i]); dev->subdevs[i] = NULL; } } usbd_add_dev_event(USB_EVENT_DEVICE_DETACH, dev); dev->bus->devices[dev->address] = NULL; up->device = NULL; usb_free_device(dev); } Index: stable/7/sys/dev/usb/usbdi.c =================================================================== --- stable/7/sys/dev/usb/usbdi.c (revision 190250) +++ stable/7/sys/dev/usb/usbdi.c (revision 190251) @@ -1,1369 +1,1378 @@ /* $NetBSD: usbdi.c,v 1.106 2004/10/24 12:52:40 augustss Exp $ */ #include __FBSDID("$FreeBSD$"); /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include "usb_if.h" #if defined(DIAGNOSTIC) && defined(__i386__) #include #endif #include #include #include #include #include #include #include #include #include #include "usb_if.h" #define delay(d) DELAY(d) #ifdef USB_DEBUG #define DPRINTF(x) if (usbdebug) printf x #define DPRINTFN(n,x) if (usbdebug>(n)) printf x extern int usbdebug; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif static usbd_status usbd_ar_pipe(usbd_pipe_handle pipe); static void usbd_do_request_async_cb (usbd_xfer_handle, usbd_private_handle, usbd_status); static void usbd_start_next(usbd_pipe_handle pipe); static usbd_status usbd_open_pipe_ival (usbd_interface_handle, u_int8_t, u_int8_t, usbd_pipe_handle *, int); static int usbd_xfer_isread(usbd_xfer_handle xfer); static void usbd_start_transfer(void *arg, bus_dma_segment_t *segs, int nseg, int error); static void usbd_alloc_callback(void *arg, bus_dma_segment_t *segs, int nseg, int error); static int usbd_nbuses = 0; void usbd_init(void) { usbd_nbuses++; } void usbd_finish(void) { --usbd_nbuses; } static __inline int usbd_xfer_isread(usbd_xfer_handle xfer) { if (xfer->rqflags & URQ_REQUEST) return (xfer->request.bmRequestType & UT_READ); else return (xfer->pipe->endpoint->edesc->bEndpointAddress & UE_DIR_IN); } #ifdef USB_DEBUG void usbd_dump_iface(struct usbd_interface *iface) { printf("usbd_dump_iface: iface=%p\n", iface); if (iface == NULL) return; printf(" device=%p idesc=%p index=%d altindex=%d priv=%p\n", iface->device, iface->idesc, iface->index, iface->altindex, iface->priv); } void usbd_dump_device(struct usbd_device *dev) { printf("usbd_dump_device: dev=%p\n", dev); if (dev == NULL) return; printf(" bus=%p default_pipe=%p\n", dev->bus, dev->default_pipe); printf(" address=%d config=%d depth=%d speed=%d self_powered=%d " "power=%d langid=%d\n", dev->address, dev->config, dev->depth, dev->speed, dev->self_powered, dev->power, dev->langid); } void usbd_dump_endpoint(struct usbd_endpoint *endp) { printf("usbd_dump_endpoint: endp=%p\n", endp); if (endp == NULL) return; printf(" edesc=%p refcnt=%d\n", endp->edesc, endp->refcnt); if (endp->edesc) printf(" bEndpointAddress=0x%02x\n", endp->edesc->bEndpointAddress); } void usbd_dump_queue(usbd_pipe_handle pipe) { usbd_xfer_handle xfer; printf("usbd_dump_queue: pipe=%p\n", pipe); STAILQ_FOREACH(xfer, &pipe->queue, next) { printf(" xfer=%p\n", xfer); } } void usbd_dump_pipe(usbd_pipe_handle pipe) { printf("usbd_dump_pipe: pipe=%p\n", pipe); if (pipe == NULL) return; usbd_dump_iface(pipe->iface); usbd_dump_device(pipe->device); usbd_dump_endpoint(pipe->endpoint); printf(" (usbd_dump_pipe:)\n refcnt=%d running=%d aborting=%d\n", pipe->refcnt, pipe->running, pipe->aborting); printf(" intrxfer=%p, repeat=%d, interval=%d\n", pipe->intrxfer, pipe->repeat, pipe->interval); } #endif usbd_status usbd_open_pipe(usbd_interface_handle iface, u_int8_t address, u_int8_t flags, usbd_pipe_handle *pipe) { return (usbd_open_pipe_ival(iface, address, flags, pipe, USBD_DEFAULT_INTERVAL)); } usbd_status usbd_open_pipe_ival(usbd_interface_handle iface, u_int8_t address, u_int8_t flags, usbd_pipe_handle *pipe, int ival) { usbd_pipe_handle p; struct usbd_endpoint *ep; usbd_status err; int i; DPRINTFN(3,("usbd_open_pipe: iface=%p address=0x%x flags=0x%x\n", iface, address, flags)); for (i = 0; i < iface->idesc->bNumEndpoints; i++) { ep = &iface->endpoints[i]; if (ep->edesc == NULL) return (USBD_IOERROR); if (ep->edesc->bEndpointAddress == address) goto found; } return (USBD_BAD_ADDRESS); found: if ((flags & USBD_EXCLUSIVE_USE) && ep->refcnt != 0) return (USBD_IN_USE); err = usbd_setup_pipe(iface->device, iface, ep, ival, &p); if (err) return (err); LIST_INSERT_HEAD(&iface->pipes, p, next); *pipe = p; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_open_pipe_intr(usbd_interface_handle iface, u_int8_t address, u_int8_t flags, usbd_pipe_handle *pipe, usbd_private_handle priv, void *buffer, u_int32_t len, usbd_callback cb, int ival) { usbd_status err; usbd_xfer_handle xfer; usbd_pipe_handle ipipe; DPRINTFN(3,("usbd_open_pipe_intr: address=0x%x flags=0x%x len=%d\n", address, flags, len)); err = usbd_open_pipe_ival(iface, address, USBD_EXCLUSIVE_USE, &ipipe, ival); if (err) return (err); xfer = usbd_alloc_xfer(iface->device); if (xfer == NULL) { err = USBD_NOMEM; goto bad1; } usbd_setup_xfer(xfer, ipipe, priv, buffer, len, flags, USBD_NO_TIMEOUT, cb); ipipe->intrxfer = xfer; ipipe->repeat = 1; err = usbd_transfer(xfer); *pipe = ipipe; if (err != USBD_IN_PROGRESS && err) goto bad2; return (USBD_NORMAL_COMPLETION); bad2: ipipe->intrxfer = NULL; ipipe->repeat = 0; usbd_free_xfer(xfer); bad1: usbd_close_pipe(ipipe); return (err); } usbd_status usbd_close_pipe(usbd_pipe_handle pipe) { #ifdef DIAGNOSTIC if (pipe == NULL) { printf("usbd_close_pipe: pipe==NULL\n"); return (USBD_NORMAL_COMPLETION); } #endif if (--pipe->refcnt != 0) return (USBD_NORMAL_COMPLETION); if (! STAILQ_EMPTY(&pipe->queue)) return (USBD_PENDING_REQUESTS); LIST_REMOVE(pipe, next); pipe->endpoint->refcnt--; pipe->methods->close(pipe); if (pipe->intrxfer != NULL) usbd_free_xfer(pipe->intrxfer); free(pipe, M_USB); return (USBD_NORMAL_COMPLETION); } usbd_status usbd_transfer(usbd_xfer_handle xfer) { usbd_pipe_handle pipe = xfer->pipe; struct usb_dma_mapping *dmap = &xfer->dmamap; usbd_status err; u_int size; int s; DPRINTFN(5,("usbd_transfer: xfer=%p, flags=%d, pipe=%p, running=%d\n", xfer, xfer->flags, pipe, pipe->running)); #ifdef USB_DEBUG if (usbdebug > 5) usbd_dump_queue(pipe); #endif xfer->done = 0; if (pipe->aborting) return (USBD_CANCELLED); size = xfer->length; /* If there is no buffer, allocate one. */ if (!(xfer->rqflags & URQ_DEV_DMABUF) && size != 0) { bus_dma_tag_t tag = pipe->device->bus->buffer_dmatag; #ifdef DIAGNOSTIC if (xfer->rqflags & URQ_AUTO_DMABUF) printf("usbd_transfer: has old buffer!\n"); #endif err = bus_dmamap_create(tag, 0, &dmap->map); if (err) return (USBD_NOMEM); xfer->rqflags |= URQ_AUTO_DMABUF; err = bus_dmamap_load(tag, dmap->map, xfer->buffer, size, usbd_start_transfer, xfer, 0); if (err != 0 && err != EINPROGRESS) { xfer->rqflags &= ~URQ_AUTO_DMABUF; bus_dmamap_destroy(tag, dmap->map); return (USBD_INVAL); } } else if (size != 0) { usbd_start_transfer(xfer, dmap->segs, dmap->nsegs, 0); } else { usbd_start_transfer(xfer, NULL, 0, 0); } if (!(xfer->flags & USBD_SYNCHRONOUS)) return (xfer->done ? 0 : USBD_IN_PROGRESS); /* Sync transfer, wait for completion. */ s = splusb(); while (!xfer->done) { if (pipe->device->bus->use_polling) panic("usbd_transfer: not done"); tsleep(xfer, PRIBIO, "usbsyn", 0); } splx(s); return (xfer->status); } static void usbd_start_transfer(void *arg, bus_dma_segment_t *segs, int nseg, int error) { usbd_xfer_handle xfer = arg; usbd_pipe_handle pipe = xfer->pipe; struct usb_dma_mapping *dmap = &xfer->dmamap; bus_dma_tag_t tag = pipe->device->bus->buffer_dmatag; int err, i; if (error != 0) { KASSERT(xfer->rqflags & URQ_AUTO_DMABUF, ("usbd_start_transfer: error with non-auto buf")); if (nseg > 0) bus_dmamap_unload(tag, dmap->map); bus_dmamap_destroy(tag, dmap->map); /* XXX */ usb_insert_transfer(xfer); xfer->status = USBD_IOERROR; usb_transfer_complete(xfer); return; } if (segs != dmap->segs) { for (i = 0; i < nseg; i++) dmap->segs[i] = segs[i]; } dmap->nsegs = nseg; if (nseg > 0) { if (!usbd_xfer_isread(xfer)) { /* * Copy data if it is not already in the correct * buffer. */ if (!(xfer->flags & USBD_NO_COPY) && xfer->allocbuf != NULL && xfer->buffer != xfer->allocbuf) memcpy(xfer->allocbuf, xfer->buffer, xfer->length); bus_dmamap_sync(tag, dmap->map, BUS_DMASYNC_PREWRITE); } else if (xfer->rqflags & URQ_REQUEST) { /* * Even if we have no data portion we still need to * sync the dmamap for the request data in the SETUP * packet. */ bus_dmamap_sync(tag, dmap->map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); } else bus_dmamap_sync(tag, dmap->map, BUS_DMASYNC_PREREAD); } err = pipe->methods->transfer(xfer); if (err != USBD_IN_PROGRESS && err) { if (xfer->rqflags & URQ_AUTO_DMABUF) { bus_dmamap_unload(tag, dmap->map); bus_dmamap_destroy(tag, dmap->map); xfer->rqflags &= ~URQ_AUTO_DMABUF; } xfer->status = err; usb_transfer_complete(xfer); return; } } /* Like usbd_transfer(), but waits for completion. */ usbd_status usbd_sync_transfer(usbd_xfer_handle xfer) { xfer->flags |= USBD_SYNCHRONOUS; return (usbd_transfer(xfer)); } struct usbd_allocstate { usbd_xfer_handle xfer; int done; int error; int waiting; }; void * usbd_alloc_buffer(usbd_xfer_handle xfer, u_int32_t size) { struct usbd_allocstate allocstate; struct usb_dma_mapping *dmap = &xfer->dmamap; bus_dma_tag_t tag = xfer->device->bus->buffer_dmatag; void *buf; usbd_status err; int error, s; KASSERT((xfer->rqflags & (URQ_DEV_DMABUF | URQ_AUTO_DMABUF)) == 0, ("usbd_alloc_buffer: xfer already has a buffer")); err = bus_dmamap_create(tag, 0, &dmap->map); if (err) return (NULL); buf = malloc(size, M_USB, M_WAITOK); allocstate.xfer = xfer; allocstate.done = 0; allocstate.error = 0; allocstate.waiting = 0; error = bus_dmamap_load(tag, dmap->map, buf, size, usbd_alloc_callback, &allocstate, 0); if (error && error != EINPROGRESS) { bus_dmamap_destroy(tag, dmap->map); free(buf, M_USB); return (NULL); } if (error == EINPROGRESS) { /* Wait for completion. */ s = splusb(); allocstate.waiting = 1; while (!allocstate.done) tsleep(&allocstate, PRIBIO, "usbdab", 0); splx(s); error = allocstate.error; } if (error) { bus_dmamap_unload(tag, dmap->map); bus_dmamap_destroy(tag, dmap->map); free(buf, M_USB); return (NULL); } xfer->allocbuf = buf; xfer->rqflags |= URQ_DEV_DMABUF; return (buf); } void usbd_free_buffer(usbd_xfer_handle xfer) { struct usb_dma_mapping *dmap = &xfer->dmamap; bus_dma_tag_t tag = xfer->device->bus->buffer_dmatag; KASSERT((xfer->rqflags & (URQ_DEV_DMABUF | URQ_AUTO_DMABUF)) == URQ_DEV_DMABUF, ("usbd_free_buffer: no/auto buffer")); xfer->rqflags &= ~URQ_DEV_DMABUF; bus_dmamap_unload(tag, dmap->map); bus_dmamap_destroy(tag, dmap->map); free(xfer->allocbuf, M_USB); xfer->allocbuf = NULL; } void * usbd_get_buffer(usbd_xfer_handle xfer) { if (!(xfer->rqflags & URQ_DEV_DMABUF)) return (NULL); return (xfer->allocbuf); } static void usbd_alloc_callback(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct usbd_allocstate *allocstate = arg; usbd_xfer_handle xfer = allocstate->xfer; struct usb_dma_mapping *dmap = &xfer->dmamap; int i; allocstate->error = error; if (error == 0) { for (i = 0; i < nseg; i++) dmap->segs[i] = segs[i]; dmap->nsegs = nseg; } allocstate->done = 1; if (allocstate->waiting) wakeup(&allocstate); } usbd_xfer_handle usbd_alloc_xfer(usbd_device_handle dev) { usbd_xfer_handle xfer; xfer = dev->bus->methods->allocx(dev->bus); if (xfer == NULL) return (NULL); xfer->device = dev; callout_init(&xfer->timeout_handle, 0); DPRINTFN(5,("usbd_alloc_xfer() = %p\n", xfer)); return (xfer); } usbd_status usbd_free_xfer(usbd_xfer_handle xfer) { DPRINTFN(5,("usbd_free_xfer: %p\n", xfer)); if (xfer->rqflags & URQ_DEV_DMABUF) usbd_free_buffer(xfer); /* XXX Does FreeBSD need to do something similar? */ #if defined(__NetBSD__) && defined(DIAGNOSTIC) if (callout_pending(&xfer->timeout_handle)) { callout_stop(&xfer->timeout_handle); printf("usbd_free_xfer: timout_handle pending"); } #endif xfer->device->bus->methods->freex(xfer->device->bus, xfer); return (USBD_NORMAL_COMPLETION); } void usbd_setup_xfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe, usbd_private_handle priv, void *buffer, u_int32_t length, u_int16_t flags, u_int32_t timeout, usbd_callback callback) { xfer->pipe = pipe; xfer->priv = priv; xfer->buffer = buffer; xfer->length = length; xfer->actlen = 0; xfer->flags = flags; xfer->timeout = timeout; xfer->status = USBD_NOT_STARTED; xfer->callback = callback; xfer->rqflags &= ~URQ_REQUEST; xfer->nframes = 0; } void usbd_setup_default_xfer(usbd_xfer_handle xfer, usbd_device_handle dev, usbd_private_handle priv, u_int32_t timeout, usb_device_request_t *req, void *buffer, u_int32_t length, u_int16_t flags, usbd_callback callback) { xfer->pipe = dev->default_pipe; xfer->priv = priv; xfer->buffer = buffer; xfer->length = length; xfer->actlen = 0; xfer->flags = flags; xfer->timeout = timeout; xfer->status = USBD_NOT_STARTED; xfer->callback = callback; xfer->request = *req; xfer->rqflags |= URQ_REQUEST; xfer->nframes = 0; } void usbd_setup_isoc_xfer(usbd_xfer_handle xfer, usbd_pipe_handle pipe, usbd_private_handle priv, u_int16_t *frlengths, u_int32_t nframes, u_int16_t flags, usbd_callback callback) { int i; xfer->pipe = pipe; xfer->priv = priv; xfer->buffer = 0; xfer->length = 0; for (i = 0; i < nframes; i++) xfer->length += frlengths[i]; xfer->actlen = 0; xfer->flags = flags; xfer->timeout = USBD_NO_TIMEOUT; xfer->status = USBD_NOT_STARTED; xfer->callback = callback; xfer->rqflags &= ~URQ_REQUEST; xfer->frlengths = frlengths; xfer->nframes = nframes; } void usbd_get_xfer_status(usbd_xfer_handle xfer, usbd_private_handle *priv, void **buffer, u_int32_t *count, usbd_status *status) { if (priv != NULL) *priv = xfer->priv; if (buffer != NULL) *buffer = xfer->buffer; if (count != NULL) *count = xfer->actlen; if (status != NULL) *status = xfer->status; } usb_config_descriptor_t * usbd_get_config_descriptor(usbd_device_handle dev) { #ifdef DIAGNOSTIC if (dev == NULL) { printf("usbd_get_config_descriptor: dev == NULL\n"); return (NULL); } #endif return (dev->cdesc); } int usbd_get_speed(usbd_device_handle dev) { return (dev->speed); } usb_interface_descriptor_t * usbd_get_interface_descriptor(usbd_interface_handle iface) { #ifdef DIAGNOSTIC if (iface == NULL) { printf("usbd_get_interface_descriptor: dev == NULL\n"); return (NULL); } #endif return (iface->idesc); } usb_device_descriptor_t * usbd_get_device_descriptor(usbd_device_handle dev) { return (&dev->ddesc); } usb_endpoint_descriptor_t * usbd_interface2endpoint_descriptor(usbd_interface_handle iface, u_int8_t index) { if (index >= iface->idesc->bNumEndpoints) return (0); return (iface->endpoints[index].edesc); } usbd_status usbd_abort_pipe(usbd_pipe_handle pipe) { usbd_status err; int s; #ifdef DIAGNOSTIC if (pipe == NULL) { printf("usbd_close_pipe: pipe==NULL\n"); return (USBD_NORMAL_COMPLETION); } #endif s = splusb(); err = usbd_ar_pipe(pipe); splx(s); return (err); } usbd_status usbd_abort_default_pipe(usbd_device_handle dev) { return (usbd_abort_pipe(dev->default_pipe)); } usbd_status usbd_clear_endpoint_stall(usbd_pipe_handle pipe) { usbd_device_handle dev = pipe->device; usb_device_request_t req; usbd_status err; DPRINTFN(8, ("usbd_clear_endpoint_stall\n")); /* * Clearing en endpoint stall resets the endpoint toggle, so * do the same to the HC toggle. */ pipe->methods->cleartoggle(pipe); req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); USETW(req.wIndex, pipe->endpoint->edesc->bEndpointAddress); USETW(req.wLength, 0); err = usbd_do_request(dev, &req, 0); #if 0 XXX should we do this? if (!err) { pipe->state = USBD_PIPE_ACTIVE; /* XXX activate pipe */ } #endif return (err); } usbd_status usbd_clear_endpoint_stall_async(usbd_pipe_handle pipe) { usbd_device_handle dev = pipe->device; usb_device_request_t req; usbd_status err; pipe->methods->cleartoggle(pipe); req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); USETW(req.wIndex, pipe->endpoint->edesc->bEndpointAddress); USETW(req.wLength, 0); err = usbd_do_request_async(dev, &req, 0); return (err); } void usbd_clear_endpoint_toggle(usbd_pipe_handle pipe) { pipe->methods->cleartoggle(pipe); } usbd_status usbd_endpoint_count(usbd_interface_handle iface, u_int8_t *count) { #ifdef DIAGNOSTIC if (iface == NULL || iface->idesc == NULL) { printf("usbd_endpoint_count: NULL pointer\n"); return (USBD_INVAL); } #endif *count = iface->idesc->bNumEndpoints; return (USBD_NORMAL_COMPLETION); } usbd_status usbd_interface_count(usbd_device_handle dev, u_int8_t *count) { if (dev->cdesc == NULL) return (USBD_NOT_CONFIGURED); *count = dev->cdesc->bNumInterface; return (USBD_NORMAL_COMPLETION); } void usbd_interface2device_handle(usbd_interface_handle iface, usbd_device_handle *dev) { *dev = iface->device; } usbd_status usbd_device2interface_handle(usbd_device_handle dev, u_int8_t ifaceno, usbd_interface_handle *iface) { if (dev->cdesc == NULL) return (USBD_NOT_CONFIGURED); if (ifaceno >= dev->cdesc->bNumInterface) return (USBD_INVAL); *iface = &dev->ifaces[ifaceno]; return (USBD_NORMAL_COMPLETION); } usbd_device_handle usbd_pipe2device_handle(usbd_pipe_handle pipe) { return (pipe->device); } /* XXXX use altno */ usbd_status usbd_set_interface(usbd_interface_handle iface, int altidx) { usb_device_request_t req; usbd_status err; void *endpoints; if (LIST_FIRST(&iface->pipes) != 0) return (USBD_IN_USE); endpoints = iface->endpoints; err = usbd_fill_iface_data(iface->device, iface->index, altidx); if (err) return (err); /* new setting works, we can free old endpoints */ if (endpoints != NULL) free(endpoints, M_USB); #ifdef DIAGNOSTIC if (iface->idesc == NULL) { printf("usbd_set_interface: NULL pointer\n"); return (USBD_INVAL); } #endif req.bmRequestType = UT_WRITE_INTERFACE; req.bRequest = UR_SET_INTERFACE; USETW(req.wValue, iface->idesc->bAlternateSetting); USETW(req.wIndex, iface->idesc->bInterfaceNumber); USETW(req.wLength, 0); return (usbd_do_request(iface->device, &req, 0)); } int usbd_get_no_alts(usb_config_descriptor_t *cdesc, int ifaceno) { char *p = (char *)cdesc; char *end = p + UGETW(cdesc->wTotalLength); usb_interface_descriptor_t *d; int n; for (n = 0; p < end; p += d->bLength) { d = (usb_interface_descriptor_t *)p; if (p + d->bLength <= end && d->bDescriptorType == UDESC_INTERFACE && d->bInterfaceNumber == ifaceno) n++; } return (n); } int usbd_get_interface_altindex(usbd_interface_handle iface) { return (iface->altindex); } usbd_status usbd_get_interface(usbd_interface_handle iface, u_int8_t *aiface) { usb_device_request_t req; req.bmRequestType = UT_READ_INTERFACE; req.bRequest = UR_GET_INTERFACE; USETW(req.wValue, 0); USETW(req.wIndex, iface->idesc->bInterfaceNumber); USETW(req.wLength, 1); return (usbd_do_request(iface->device, &req, aiface)); } /*** Internal routines ***/ /* Dequeue all pipe operations, called at splusb(). */ static usbd_status usbd_ar_pipe(usbd_pipe_handle pipe) { usbd_xfer_handle xfer; SPLUSBCHECK; DPRINTFN(2,("usbd_ar_pipe: pipe=%p\n", pipe)); #ifdef USB_DEBUG if (usbdebug > 5) usbd_dump_queue(pipe); #endif pipe->repeat = 0; pipe->aborting = 1; while ((xfer = STAILQ_FIRST(&pipe->queue)) != NULL) { DPRINTFN(2,("usbd_ar_pipe: pipe=%p xfer=%p (methods=%p)\n", pipe, xfer, pipe->methods)); /* Make the HC abort it (and invoke the callback). */ pipe->methods->abort(xfer); KASSERT(STAILQ_FIRST(&pipe->queue) != xfer, ("usbd_ar_pipe")); /* XXX only for non-0 usbd_clear_endpoint_stall(pipe); */ } pipe->aborting = 0; return (USBD_NORMAL_COMPLETION); } /* Called at splusb() */ void usb_transfer_complete(usbd_xfer_handle xfer) { usbd_pipe_handle pipe = xfer->pipe; struct usb_dma_mapping *dmap = &xfer->dmamap; bus_dma_tag_t tag = pipe->device->bus->buffer_dmatag; int sync = xfer->flags & USBD_SYNCHRONOUS; int erred = xfer->status == USBD_CANCELLED || xfer->status == USBD_TIMEOUT; int repeat = pipe->repeat; int polling; SPLUSBCHECK; DPRINTFN(5, ("usb_transfer_complete: pipe=%p xfer=%p status=%d " "actlen=%d\n", pipe, xfer, xfer->status, xfer->actlen)); #ifdef DIAGNOSTIC if (xfer->busy_free != XFER_ONQU) { printf("usb_transfer_complete: xfer=%p not busy 0x%08x\n", xfer, xfer->busy_free); return; } #endif #ifdef DIAGNOSTIC if (pipe == NULL) { printf("usbd_transfer_cb: pipe==0, xfer=%p\n", xfer); return; } #endif polling = pipe->device->bus->use_polling; /* XXXX */ if (polling) pipe->running = 0; if (xfer->actlen != 0 && usbd_xfer_isread(xfer)) { bus_dmamap_sync(tag, dmap->map, BUS_DMASYNC_POSTREAD); /* Copy data if it is not already in the correct buffer. */ if (!(xfer->flags & USBD_NO_COPY) && xfer->allocbuf != NULL && xfer->buffer != xfer->allocbuf) memcpy(xfer->buffer, xfer->allocbuf, xfer->actlen); } /* if we mapped the buffer in usbd_transfer() we unmap it here. */ if (xfer->rqflags & URQ_AUTO_DMABUF) { if (!repeat) { bus_dmamap_unload(tag, dmap->map); bus_dmamap_destroy(tag, dmap->map); xfer->rqflags &= ~URQ_AUTO_DMABUF; } } if (!repeat) { /* Remove request from queue. */ #ifdef DIAGNOSTIC xfer->busy_free = XFER_BUSY; #endif KASSERT(STAILQ_FIRST(&pipe->queue) == xfer, ("usb_transfer_complete: bad dequeue")); STAILQ_REMOVE_HEAD(&pipe->queue, next); } DPRINTFN(5,("usb_transfer_complete: repeat=%d new head=%p\n", repeat, STAILQ_FIRST(&pipe->queue))); /* Count completed transfers. */ ++pipe->device->bus->stats.uds_requests [pipe->endpoint->edesc->bmAttributes & UE_XFERTYPE]; xfer->done = 1; if (!xfer->status && xfer->actlen < xfer->length && !(xfer->flags & USBD_SHORT_XFER_OK)) { DPRINTFN(-1,("usbd_transfer_cb: short transfer %d<%d\n", xfer->actlen, xfer->length)); xfer->status = USBD_SHORT_XFER; } /* * For repeat operations, call the callback first, as the xfer * will not go away and the "done" method may modify it. Otherwise * reverse the order in case the callback wants to free or reuse * the xfer. */ if (repeat) { if (xfer->callback) xfer->callback(xfer, xfer->priv, xfer->status); pipe->methods->done(xfer); } else { pipe->methods->done(xfer); if (xfer->callback) xfer->callback(xfer, xfer->priv, xfer->status); } if (sync && !polling) wakeup(xfer); if (!repeat) { /* XXX should we stop the queue on all errors? */ if (erred && pipe->iface != NULL) /* not control pipe */ pipe->running = 0; else usbd_start_next(pipe); } } usbd_status usb_insert_transfer(usbd_xfer_handle xfer) { usbd_pipe_handle pipe = xfer->pipe; usbd_status err; int s; DPRINTFN(5,("usb_insert_transfer: pipe=%p running=%d timeout=%d\n", pipe, pipe->running, xfer->timeout)); #ifdef DIAGNOSTIC if (xfer->busy_free != XFER_BUSY) { printf("usb_insert_transfer: xfer=%p not busy 0x%08x\n", xfer, xfer->busy_free); return (USBD_INVAL); } xfer->busy_free = XFER_ONQU; #endif s = splusb(); KASSERT(STAILQ_FIRST(&pipe->queue) != xfer, ("usb_insert_transfer")); STAILQ_INSERT_TAIL(&pipe->queue, xfer, next); if (pipe->running) err = USBD_IN_PROGRESS; else { pipe->running = 1; err = USBD_NORMAL_COMPLETION; } splx(s); return (err); } /* Called at splusb() */ void usbd_start_next(usbd_pipe_handle pipe) { usbd_xfer_handle xfer; usbd_status err; SPLUSBCHECK; #ifdef DIAGNOSTIC if (pipe == NULL) { printf("usbd_start_next: pipe == NULL\n"); return; } if (pipe->methods == NULL || pipe->methods->start == NULL) { printf("usbd_start_next: pipe=%p no start method\n", pipe); return; } #endif /* Get next request in queue. */ xfer = STAILQ_FIRST(&pipe->queue); DPRINTFN(5, ("usbd_start_next: pipe=%p, xfer=%p\n", pipe, xfer)); if (xfer == NULL) { pipe->running = 0; } else { err = pipe->methods->start(xfer); if (err != USBD_IN_PROGRESS) { printf("usbd_start_next: error=%d\n", err); pipe->running = 0; /* XXX do what? */ } } } usbd_status usbd_do_request(usbd_device_handle dev, usb_device_request_t *req, void *data) { return (usbd_do_request_flags(dev, req, data, 0, 0, USBD_DEFAULT_TIMEOUT)); } usbd_status usbd_do_request_flags(usbd_device_handle dev, usb_device_request_t *req, void *data, u_int16_t flags, int *actlen, u_int32_t timo) { return (usbd_do_request_flags_pipe(dev, dev->default_pipe, req, data, flags, actlen, timo)); } usbd_status usbd_do_request_flags_pipe(usbd_device_handle dev, usbd_pipe_handle pipe, usb_device_request_t *req, void *data, u_int16_t flags, int *actlen, u_int32_t timeout) { usbd_xfer_handle xfer; usbd_status err; #ifdef DIAGNOSTIC /* XXX amd64 too? */ #if defined(__i386__) KASSERT(curthread->td_intr_nesting_level == 0, ("usbd_do_request: in interrupt context")); #endif if (dev->bus->intr_context) { printf("usbd_do_request: not in process context\n"); return (USBD_INVAL); } #endif xfer = usbd_alloc_xfer(dev); if (xfer == NULL) return (USBD_NOMEM); usbd_setup_default_xfer(xfer, dev, 0, timeout, req, data, UGETW(req->wLength), flags, 0); xfer->pipe = pipe; err = usbd_sync_transfer(xfer); #if defined(USB_DEBUG) || defined(DIAGNOSTIC) if (xfer->actlen > xfer->length) DPRINTF(("usbd_do_request: overrun addr=%d type=0x%02x req=0x" "%02x val=%d index=%d rlen=%d length=%d actlen=%d\n", dev->address, xfer->request.bmRequestType, xfer->request.bRequest, UGETW(xfer->request.wValue), UGETW(xfer->request.wIndex), UGETW(xfer->request.wLength), xfer->length, xfer->actlen)); #endif if (actlen != NULL) *actlen = xfer->actlen; if (err == USBD_STALLED) { /* * The control endpoint has stalled. Control endpoints * should not halt, but some may do so anyway so clear * any halt condition. */ usb_device_request_t treq; usb_status_t status; u_int16_t s; usbd_status nerr; treq.bmRequestType = UT_READ_ENDPOINT; treq.bRequest = UR_GET_STATUS; USETW(treq.wValue, 0); USETW(treq.wIndex, 0); USETW(treq.wLength, sizeof(usb_status_t)); usbd_setup_default_xfer(xfer, dev, 0, USBD_DEFAULT_TIMEOUT, &treq, &status,sizeof(usb_status_t), 0, 0); nerr = usbd_sync_transfer(xfer); if (nerr) goto bad; s = UGETW(status.wStatus); DPRINTF(("usbd_do_request: status = 0x%04x\n", s)); if (!(s & UES_HALT)) goto bad; treq.bmRequestType = UT_WRITE_ENDPOINT; treq.bRequest = UR_CLEAR_FEATURE; USETW(treq.wValue, UF_ENDPOINT_HALT); USETW(treq.wIndex, 0); USETW(treq.wLength, 0); usbd_setup_default_xfer(xfer, dev, 0, USBD_DEFAULT_TIMEOUT, &treq, &status, 0, 0, 0); nerr = usbd_sync_transfer(xfer); if (nerr) goto bad; } bad: usbd_free_xfer(xfer); return (err); } void usbd_do_request_async_cb(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { #if defined(USB_DEBUG) || defined(DIAGNOSTIC) if (xfer->actlen > xfer->length) DPRINTF(("usbd_do_request: overrun addr=%d type=0x%02x req=0x" "%02x val=%d index=%d rlen=%d length=%d actlen=%d\n", xfer->pipe->device->address, xfer->request.bmRequestType, xfer->request.bRequest, UGETW(xfer->request.wValue), UGETW(xfer->request.wIndex), UGETW(xfer->request.wLength), xfer->length, xfer->actlen)); #endif usbd_free_xfer(xfer); } /* * Execute a request without waiting for completion. * Can be used from interrupt context. */ usbd_status usbd_do_request_async(usbd_device_handle dev, usb_device_request_t *req, void *data) { usbd_xfer_handle xfer; usbd_status err; xfer = usbd_alloc_xfer(dev); if (xfer == NULL) return (USBD_NOMEM); usbd_setup_default_xfer(xfer, dev, 0, USBD_DEFAULT_TIMEOUT, req, data, UGETW(req->wLength), 0, usbd_do_request_async_cb); err = usbd_transfer(xfer); if (err != USBD_IN_PROGRESS && err) { usbd_free_xfer(xfer); return (err); } return (USBD_NORMAL_COMPLETION); } const struct usbd_quirks * usbd_get_quirks(usbd_device_handle dev) { #ifdef DIAGNOSTIC if (dev == NULL) { printf("usbd_get_quirks: dev == NULL\n"); return 0; } #endif return (dev->quirks); } /* XXX do periodic free() of free list */ /* * Called from keyboard driver when in polling mode. */ void usbd_dopoll(usbd_interface_handle iface) { iface->device->bus->methods->do_poll(iface->device->bus); } void usbd_set_polling(usbd_device_handle dev, int on) { if (on) dev->bus->use_polling++; else dev->bus->use_polling--; /* When polling we need to make sure there is nothing pending to do. */ if (dev->bus->use_polling) dev->bus->methods->soft_intr(dev->bus); } +usbd_status +usbd_reset_device(usbd_device_handle dev) +{ + usbd_device_handle parent = dev->myhub; + struct usbd_port *up = dev->powersrc; + + return usbd_reset_port(parent, up->portno, &up->status); +} + usb_endpoint_descriptor_t * usbd_get_endpoint_descriptor(usbd_interface_handle iface, u_int8_t address) { struct usbd_endpoint *ep; int i; for (i = 0; i < iface->idesc->bNumEndpoints; i++) { ep = &iface->endpoints[i]; if (ep->edesc->bEndpointAddress == address) return (iface->endpoints[i].edesc); } return (0); } /* * usbd_ratecheck() can limit the number of error messages that occurs. * When a device is unplugged it may take up to 0.25s for the hub driver * to notice it. If the driver continuosly tries to do I/O operations * this can generate a large number of messages. */ int usbd_ratecheck(struct timeval *last) { if (last->tv_sec == time_second) return (0); last->tv_sec = time_second; return (1); } /* * Search for a vendor/product pair in an array. The item size is * given as an argument. */ const struct usb_devno * usb_match_device(const struct usb_devno *tbl, u_int nentries, u_int sz, u_int16_t vendor, u_int16_t product) { while (nentries-- > 0) { u_int16_t tproduct = tbl->ud_product; if (tbl->ud_vendor == vendor && (tproduct == product || tproduct == USB_PRODUCT_ANY)) return (tbl); tbl = (const struct usb_devno *)((const char *)tbl + sz); } return (NULL); } void usb_desc_iter_init(usbd_device_handle dev, usbd_desc_iter_t *iter) { const usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev); iter->cur = (const uByte *)cd; iter->end = (const uByte *)cd + UGETW(cd->wTotalLength); } const usb_descriptor_t * usb_desc_iter_next(usbd_desc_iter_t *iter) { const usb_descriptor_t *desc; if (iter->cur + sizeof(usb_descriptor_t) >= iter->end) { if (iter->cur != iter->end) printf("usb_desc_iter_next: bad descriptor\n"); return NULL; } desc = (const usb_descriptor_t *)iter->cur; if (desc->bLength == 0) { printf("usb_desc_iter_next: descriptor length = 0\n"); return NULL; } iter->cur += desc->bLength; if (iter->cur > iter->end) { printf("usb_desc_iter_next: descriptor length too large\n"); return NULL; } return desc; } usbd_status usbd_get_string(usbd_device_handle dev, int si, char *buf, size_t len) { int swap = dev->quirks->uq_flags & UQ_SWAP_UNICODE; usb_string_descriptor_t us; char *s; int i, n; u_int16_t c; usbd_status err; int size; buf[0] = '\0'; if (len == 0) return (USBD_NORMAL_COMPLETION); if (si == 0) return (USBD_INVAL); if (dev->quirks->uq_flags & UQ_NO_STRINGS) return (USBD_STALLED); if (dev->langid == USBD_NOLANG) { /* Set up default language */ err = usbd_get_string_desc(dev, USB_LANGUAGE_TABLE, 0, &us, &size); if (err || size < 4) { DPRINTFN(-1,("usbd_get_string: getting lang failed, using 0\n")); dev->langid = 0; /* Well, just pick something then */ } else { /* Pick the first language as the default. */ dev->langid = UGETW(us.bString[0]); } } err = usbd_get_string_desc(dev, si, dev->langid, &us, &size); if (err) return (err); s = buf; n = size / 2 - 1; for (i = 0; i < n && i < len - 1; i++) { c = UGETW(us.bString[i]); /* Convert from Unicode, handle buggy strings. */ if ((c & 0xff00) == 0) *s++ = c; else if ((c & 0x00ff) == 0 && swap) *s++ = c >> 8; else *s++ = '?'; } *s++ = 0; return (USBD_NORMAL_COMPLETION); } int usbd_driver_load(module_t mod, int what, void *arg) { /* XXX should implement something like a function that removes all generic devices */ return (0); } Index: stable/7/sys/dev/usb/usbdi.h =================================================================== --- stable/7/sys/dev/usb/usbdi.h (revision 190250) +++ stable/7/sys/dev/usb/usbdi.h (revision 190251) @@ -1,285 +1,286 @@ /* $NetBSD: usbdi.h,v 1.64 2004/10/23 13:26:34 augustss Exp $ */ /* $FreeBSD$ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _USBDI_H_ #define _USBDI_H_ typedef struct usbd_bus *usbd_bus_handle; typedef struct usbd_device *usbd_device_handle; typedef struct usbd_interface *usbd_interface_handle; typedef struct usbd_pipe *usbd_pipe_handle; typedef struct usbd_xfer *usbd_xfer_handle; typedef void *usbd_private_handle; typedef enum { /* keep in sync with usbd_status_msgs */ USBD_NORMAL_COMPLETION = 0, /* must be 0 */ USBD_IN_PROGRESS, /* 1 */ /* errors */ USBD_PENDING_REQUESTS, /* 2 */ USBD_NOT_STARTED, /* 3 */ USBD_INVAL, /* 4 */ USBD_NOMEM, /* 5 */ USBD_CANCELLED, /* 6 */ USBD_BAD_ADDRESS, /* 7 */ USBD_IN_USE, /* 8 */ USBD_NO_ADDR, /* 9 */ USBD_SET_ADDR_FAILED, /* 10 */ USBD_NO_POWER, /* 11 */ USBD_TOO_DEEP, /* 12 */ USBD_IOERROR, /* 13 */ USBD_NOT_CONFIGURED, /* 14 */ USBD_TIMEOUT, /* 15 */ USBD_SHORT_XFER, /* 16 */ USBD_STALLED, /* 17 */ USBD_INTERRUPTED, /* 18 */ USBD_ERROR_MAX /* must be last */ } usbd_status; typedef void (*usbd_callback)(usbd_xfer_handle, usbd_private_handle, usbd_status); /* Open flags */ #define USBD_EXCLUSIVE_USE 0x01 /* Use default (specified by ep. desc.) interval on interrupt pipe */ #define USBD_DEFAULT_INTERVAL (-1) /* Request flags */ #define USBD_NO_COPY 0x01 /* do not copy data to DMA buffer */ #define USBD_SYNCHRONOUS 0x02 /* wait for completion */ /* in usb.h #define USBD_SHORT_XFER_OK 0x04*/ /* allow short reads */ #define USBD_FORCE_SHORT_XFER 0x08 /* force last short packet on write */ #define USBD_NO_TIMEOUT 0 #define USBD_DEFAULT_TIMEOUT 5000 /* ms = 5 s */ usbd_status usbd_open_pipe(usbd_interface_handle, u_int8_t, u_int8_t, usbd_pipe_handle *); usbd_status usbd_close_pipe(usbd_pipe_handle); usbd_status usbd_transfer(usbd_xfer_handle); usbd_xfer_handle usbd_alloc_xfer(usbd_device_handle); usbd_status usbd_free_xfer(usbd_xfer_handle); void usbd_setup_xfer(usbd_xfer_handle, usbd_pipe_handle, usbd_private_handle, void *, u_int32_t, u_int16_t, u_int32_t, usbd_callback); void usbd_setup_default_xfer(usbd_xfer_handle, usbd_device_handle, usbd_private_handle, u_int32_t, usb_device_request_t *, void *, u_int32_t, u_int16_t, usbd_callback); void usbd_setup_isoc_xfer(usbd_xfer_handle, usbd_pipe_handle, usbd_private_handle, u_int16_t *, u_int32_t, u_int16_t, usbd_callback); void usbd_get_xfer_status(usbd_xfer_handle, usbd_private_handle *, void **, u_int32_t *, usbd_status *); usb_endpoint_descriptor_t *usbd_interface2endpoint_descriptor (usbd_interface_handle, u_int8_t); usbd_status usbd_abort_pipe(usbd_pipe_handle); usbd_status usbd_abort_default_pipe(usbd_device_handle); usbd_status usbd_clear_endpoint_stall(usbd_pipe_handle); usbd_status usbd_clear_endpoint_stall_async(usbd_pipe_handle); void usbd_clear_endpoint_toggle(usbd_pipe_handle); usbd_status usbd_endpoint_count(usbd_interface_handle, u_int8_t *); usbd_status usbd_interface_count(usbd_device_handle, u_int8_t *); void usbd_interface2device_handle(usbd_interface_handle, usbd_device_handle *); usbd_status usbd_device2interface_handle(usbd_device_handle, u_int8_t, usbd_interface_handle *); usbd_device_handle usbd_pipe2device_handle(usbd_pipe_handle); void *usbd_alloc_buffer(usbd_xfer_handle, u_int32_t); void usbd_free_buffer(usbd_xfer_handle); void *usbd_get_buffer(usbd_xfer_handle); usbd_status usbd_sync_transfer(usbd_xfer_handle); usbd_status usbd_open_pipe_intr(usbd_interface_handle, u_int8_t, u_int8_t, usbd_pipe_handle *, usbd_private_handle, void *, u_int32_t, usbd_callback, int); usbd_status usbd_do_request(usbd_device_handle, usb_device_request_t *, void *); usbd_status usbd_do_request_async(usbd_device_handle, usb_device_request_t *, void *); usbd_status usbd_do_request_flags(usbd_device_handle, usb_device_request_t *, void *, u_int16_t, int*, u_int32_t); usbd_status usbd_do_request_flags_pipe(usbd_device_handle, usbd_pipe_handle, usb_device_request_t *, void *, u_int16_t, int *, u_int32_t); usb_interface_descriptor_t *usbd_get_interface_descriptor (usbd_interface_handle); usb_config_descriptor_t *usbd_get_config_descriptor(usbd_device_handle); usb_device_descriptor_t *usbd_get_device_descriptor(usbd_device_handle); int usbd_get_speed(usbd_device_handle); usbd_status usbd_set_interface(usbd_interface_handle, int); int usbd_get_no_alts(usb_config_descriptor_t *, int); usbd_status usbd_get_interface(usbd_interface_handle, u_int8_t *); void usbd_fill_deviceinfo(usbd_device_handle, struct usb_device_info *, int); int usbd_get_interface_altindex(usbd_interface_handle); usb_interface_descriptor_t *usbd_find_idesc(usb_config_descriptor_t *, int, int); usb_endpoint_descriptor_t *usbd_find_edesc(usb_config_descriptor_t *, int, int, int); void usbd_dopoll(usbd_interface_handle); void usbd_set_polling(usbd_device_handle, int); +usbd_status usbd_reset_device(usbd_device_handle); const char *usbd_errstr(usbd_status); void usbd_add_dev_event(int, usbd_device_handle); void usbd_add_drv_event(int, usbd_device_handle, device_t); void usbd_devinfo(usbd_device_handle, int, char *); const struct usbd_quirks *usbd_get_quirks(usbd_device_handle); usb_endpoint_descriptor_t *usbd_get_endpoint_descriptor (usbd_interface_handle, u_int8_t); usbd_status usbd_reload_device_desc(usbd_device_handle); int usbd_ratecheck(struct timeval *last); usbd_status usbd_get_string(usbd_device_handle dev, int si, char *buf, size_t len); /* An iterator for descriptors. */ typedef struct { const uByte *cur; const uByte *end; } usbd_desc_iter_t; void usb_desc_iter_init(usbd_device_handle dev, usbd_desc_iter_t *iter); const usb_descriptor_t *usb_desc_iter_next(usbd_desc_iter_t *iter); /* * The usb_task structs form a queue of things to run in the USB event * thread. Normally this is just device discovery when a connect/disconnect * has been detected. But it may also be used by drivers that need to * perform (short) tasks that must have a process context. */ struct usb_task { TAILQ_ENTRY(usb_task) next; void (*fun)(void *); void *arg; int queue; }; #define USB_TASKQ_HC 0 #define USB_TASKQ_DRIVER 1 #define USB_NUM_TASKQS 2 #define USB_TASKQ_NAMES {"usbtask-hc", "usbtask-dr"} void usb_add_task(usbd_device_handle, struct usb_task *, int queue); void usb_rem_task(usbd_device_handle, struct usb_task *); #define usb_init_task(t, f, a) ((t)->fun = (f), (t)->arg = (a), (t)->queue = -1) struct usb_devno { u_int16_t ud_vendor; u_int16_t ud_product; }; const struct usb_devno *usb_match_device(const struct usb_devno *, u_int, u_int, u_int16_t, u_int16_t); #define usb_lookup(tbl, vendor, product) \ usb_match_device((const struct usb_devno *)(tbl), sizeof (tbl) / sizeof ((tbl)[0]), sizeof ((tbl)[0]), (vendor), (product)) #define USB_PRODUCT_ANY 0xffff /* NetBSD attachment information */ /* Attach data */ struct usb_attach_arg { int port; int configno; int ifaceno; int vendor; int product; int release; int matchlvl; usbd_device_handle device; /* current device */ usbd_interface_handle iface; /* current interface */ int usegeneric; usbd_interface_handle *ifaces; /* all interfaces */ int nifaces; /* number of interfaces */ }; /* FreeBSD needs values less than zero */ #define UMATCH_VENDOR_PRODUCT_REV (-10) #define UMATCH_VENDOR_PRODUCT (-20) #define UMATCH_VENDOR_DEVCLASS_DEVPROTO (-30) #define UMATCH_DEVCLASS_DEVSUBCLASS_DEVPROTO (-40) #define UMATCH_DEVCLASS_DEVSUBCLASS (-50) #define UMATCH_VENDOR_PRODUCT_REV_CONF_IFACE (-60) #define UMATCH_VENDOR_PRODUCT_CONF_IFACE (-70) #define UMATCH_VENDOR_IFACESUBCLASS_IFACEPROTO (-80) #define UMATCH_VENDOR_IFACESUBCLASS (-90) #define UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO (-100) #define UMATCH_IFACECLASS_IFACESUBCLASS (-110) #define UMATCH_IFACECLASS (-120) #define UMATCH_IFACECLASS_GENERIC (-130) #define UMATCH_GENERIC (-140) #define UMATCH_NONE (ENXIO) #define USBD_SHOW_DEVICE_CLASS 0x1 #define USBD_SHOW_INTERFACE_CLASS 0x2 int usbd_driver_load(module_t mod, int what, void *arg); static inline int usb_get_port(device_t dev) { struct usb_attach_arg *uap = device_get_ivars(dev); return (uap->port); } static inline struct usbd_interface * usb_get_iface(device_t dev) { struct usb_attach_arg *uap = device_get_ivars(dev); return (uap->iface); } /* XXX Perhaps USB should have its own levels? */ #ifdef USB_USE_SOFTINTR #ifdef __HAVE_GENERIC_SOFT_INTERRUPTS #define splusb splsoftnet #else #define splusb splsoftclock #endif /* __HAVE_GENERIC_SOFT_INTERRUPTS */ #else #define splusb splbio #endif /* USB_USE_SOFTINTR */ #define splhardusb splbio #define IPL_USB IPL_BIO #endif /* _USBDI_H_ */