diff --git a/sys/dev/usb/ugen.c b/sys/dev/usb/ugen.c index 9a295094a7d3..ae18038a8594 100644 --- a/sys/dev/usb/ugen.c +++ b/sys/dev/usb/ugen.c @@ -1,1423 +1,1426 @@ /* $NetBSD: ugen.c,v 1.51 2001/11/13 07:59:32 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. */ #include #include #include #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include #include #elif defined(__FreeBSD__) #include #include #include #include #include #include #endif #include #include #if __FreeBSD_version >= 500014 #include #else #include #endif #include #include #include #include #include #ifdef UGEN_DEBUG #define DPRINTF(x) if (ugendebug) logprintf x #define DPRINTFN(n,x) if (ugendebug>(n)) logprintf x int ugendebug = 0; #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; 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 { USBBASEDEVICE sc_dev; /* base device */ usbd_device_handle sc_udev; char sc_is_open[USB_MAX_ENDPOINTS]; struct ugen_endpoint sc_endpoints[USB_MAX_ENDPOINTS][2]; #define OUT 0 #define IN 1 int sc_refcnt; u_char sc_dying; }; #if defined(__NetBSD__) || defined(__OpenBSD__) cdev_decl(ugen); #elif defined(__FreeBSD__) d_open_t ugenopen; d_close_t ugenclose; d_read_t ugenread; d_write_t ugenwrite; d_ioctl_t ugenioctl; d_poll_t ugenpoll; #define UGEN_CDEV_MAJOR 114 Static struct cdevsw ugen_cdevsw = { /* open */ ugenopen, /* close */ ugenclose, /* read */ ugenread, /* write */ ugenwrite, /* ioctl */ ugenioctl, /* poll */ ugenpoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "ugen", /* maj */ UGEN_CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, +#if !defined(__FreeBSD__) || (__FreeBSD__ < 5) + /* bmaj */ -1 +#endif }; #endif 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, usb_proc_ptr); #if defined(__FreeBSD__) Static void ugen_make_devnodes(struct ugen_softc *sc); Static void ugen_destroy_devnodes(struct ugen_softc *sc); #endif 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)) USB_DECLARE_DRIVER(ugen); USB_MATCH(ugen) { USB_MATCH_START(ugen, uaa); #if 0 if (uaa->matchlvl) return (uaa->matchlvl); #endif if (uaa->usegeneric) return (UMATCH_GENERIC); else return (UMATCH_NONE); } USB_ATTACH(ugen) { USB_ATTACH_START(ugen, sc, uaa); usbd_device_handle udev; char devinfo[1024]; usbd_status err; int conf; usbd_devinfo(uaa->device, 0, devinfo); USB_ATTACH_SETUP; printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo); sc->sc_udev = udev = uaa->device; #if defined(__FreeBSD__) /* the main device, ctrl endpoint */ make_dev(&ugen_cdevsw, UGENMINOR(USBDEVUNIT(sc->sc_dev), 0), UID_ROOT, GID_OPERATOR, 0644, "%s", USBDEVNAME(sc->sc_dev)); #endif 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", USBDEVNAME(sc->sc_dev)); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } 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", USBDEVNAME(sc->sc_dev), conf); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } USB_ATTACH_SUCCESS_RETURN; } #if defined(__FreeBSD__) Static void ugen_make_devnodes(struct ugen_softc *sc) { int endptno; 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. */ make_dev(&ugen_cdevsw, UGENMINOR(USBDEVUNIT(sc->sc_dev), endptno), UID_ROOT, GID_OPERATOR, 0644, "%s.%d", USBDEVNAME(sc->sc_dev), endptno); } } } Static void ugen_destroy_devnodes(struct ugen_softc *sc) { int endptno; dev_t dev; struct vnode *vp; /* 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. */ dev = makedev(UGEN_CDEV_MAJOR, UGENMINOR(USBDEVUNIT(sc->sc_dev), endptno)); vp = SLIST_FIRST(&dev->si_hlist); if (vp) VOP_REVOKE(vp, REVOKEALL); destroy_dev(dev); } } } #endif 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; u_int8_t niface, nendpt; int ifaceno, endptno, endpt; usbd_status err; int dir; DPRINTFN(1,("ugen_set_config: %s to configno %d, sc=%p\n", USBDEVNAME(sc->sc_dev), configno, sc)); #if defined(__FreeBSD__) ugen_destroy_devnodes(sc); #endif /* 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", USBDEVNAME(sc->sc_dev), endptno)); return (USBD_IN_USE); } /* Avoid setting the current value. */ if (usbd_get_config_descriptor(dev)->bConfigurationValue != configno) { err = usbd_set_config_no(dev, configno, 1); if (err) return (err); } err = usbd_interface_count(dev, &niface); if (err) return (err); 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) return (err); err = usbd_endpoint_count(iface, &nendpt); if (err) return (err); 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; } } #if defined(__FreeBSD__) ugen_make_devnodes(sc); #endif return (USBD_NORMAL_COMPLETION); } int ugenopen(dev_t dev, int flag, int mode, usb_proc_ptr 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; USB_GET_SC_OPEN(ugen, unit, sc); 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; 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 == 0 || 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: 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 (clalloc(&sce->q, 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); clfree(&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: return (EINVAL); } } sc->sc_is_open[endpt] = 1; return (0); } int ugenclose(dev_t dev, int flag, int mode, usb_proc_ptr p) { int endpt = UGENENDPOINT(dev); struct ugen_softc *sc; struct ugen_endpoint *sce; int dir; int i; USB_GET_SC(ugen, UGENUNIT(dev), sc); 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; return (0); } for (dir = OUT; dir <= IN; dir++) { if (!(flag & (dir == OUT ? FWRITE : FREAD))) continue; sce = &sc->sc_endpoints[endpt][dir]; if (sce == NULL || 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); clfree(&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; clfree(&sce->q); } } sc->sc_is_open[endpt] = 0; 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; u_char buffer[UGEN_CHUNK]; DPRINTFN(5, ("%s: ugenread: %d\n", USBDEVNAME(sc->sc_dev), endpt)); if (sc->sc_dying) return (EIO); if (endpt == USB_CONTROL_ENDPOINT) return (ENODEV); if (sce == NULL) return (EINVAL); if (sce->edesc == NULL) { printf("ugenread: no edesc\n"); return (EIO); } if (sce->pipeh == NULL) { printf("ugenread: no pipe\n"); return (EIO); } switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: /* Block until activity occurred. */ s = splusb(); while (sce->q.c_cc == 0) { if (flag & IO_NDELAY) { splx(s); return (EWOULDBLOCK); } sce->state |= UGEN_ASLP; DPRINTFN(5, ("ugenread: sleep on %p\n", sce)); error = tsleep(sce, PZERO | PCATCH, "ugenri", 0); DPRINTFN(5, ("ugenread: woke, error=%d\n", error)); if (sc->sc_dying) error = EIO; if (error) { sce->state &= ~UGEN_ASLP; 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) { DPRINTFN(1, ("ugenread: start transfer %d bytes\n",n)); tn = n; 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 & IO_NDELAY) { splx(s); return (EWOULDBLOCK); } sce->state |= UGEN_ASLP; DPRINTFN(5, ("ugenread: sleep on %p\n", sce)); error = tsleep(sce, PZERO | PCATCH, "ugenri", 0); DPRINTFN(5, ("ugenread: woke, error=%d\n", error)); if (sc->sc_dying) error = EIO; if (error) { sce->state &= ~UGEN_ASLP; 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(dev_t dev, struct uio *uio, int flag) { int endpt = UGENENDPOINT(dev); struct ugen_softc *sc; int error; USB_GET_SC(ugen, UGENUNIT(dev), sc); sc->sc_refcnt++; error = ugen_do_read(sc, endpt, uio, flag); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); 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; char buf[UGEN_BBSIZE]; usbd_xfer_handle xfer; usbd_status err; DPRINTFN(5, ("%s: ugenwrite: %d\n", USBDEVNAME(sc->sc_dev), endpt)); if (sc->sc_dying) return (EIO); if (endpt == USB_CONTROL_ENDPOINT) return (ENODEV); if (sce == NULL) return (EINVAL); if (sce->edesc == NULL) { printf("ugenwrite: no edesc\n"); return (EIO); } if (sce->pipeh == NULL) { printf("ugenwrite: no pipe\n"); return (EIO); } 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) { 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; default: return (ENXIO); } return (error); } int ugenwrite(dev_t dev, struct uio *uio, int flag) { int endpt = UGENENDPOINT(dev); struct ugen_softc *sc; int error; USB_GET_SC(ugen, UGENUNIT(dev), sc); sc->sc_refcnt++; error = ugen_do_write(sc, endpt, uio, flag); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); return (error); } #if defined(__NetBSD__) || defined(__OpenBSD__) int ugen_activate(device_ptr_t self, enum devact act) { struct ugen_softc *sc = (struct ugen_softc *)self; switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); break; case DVACT_DEACTIVATE: sc->sc_dying = 1; break; } return (0); } #endif USB_DETACH(ugen) { USB_DETACH_START(ugen, sc); struct ugen_endpoint *sce; int i, dir; int s; #if defined(__NetBSD__) || defined(__OpenBSD__) int maj, mn; #elif defined(__FreeBSD__) dev_t dev; struct vnode *vp; #endif #if defined(__NetBSD__) || defined(__OpenBSD__) DPRINTF(("ugen_detach: sc=%p flags=%d\n", sc, flags)); #elif defined(__FreeBSD__) DPRINTF(("ugen_detach: sc=%p\n", sc)); #endif 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 && sce->pipeh) usbd_abort_pipe(sce->pipeh); } } s = splusb(); if (--sc->sc_refcnt >= 0) { /* Wake everyone */ for (i = 0; i < USB_MAX_ENDPOINTS; i++) wakeup(&sc->sc_endpoints[i][IN]); /* Wait for processes to go away. */ usb_detach_wait(USBDEV(sc->sc_dev)); } splx(s); #if defined(__NetBSD__) || defined(__OpenBSD__) /* locate the major number */ for (maj = 0; maj < nchrdev; maj++) if (cdevsw[maj].d_open == ugenopen) break; /* Nuke the vnodes for any open instances (calls close). */ mn = self->dv_unit * USB_MAX_ENDPOINTS; vdevgone(maj, mn, mn + USB_MAX_ENDPOINTS - 1, VCHR); #elif defined(__FreeBSD__) /* destroy the device for the control endpoint */ dev = makedev(UGEN_CDEV_MAJOR, UGENMINOR(USBDEVUNIT(sc->sc_dev), 0)); vp = SLIST_FIRST(&dev->si_hlist); if (vp) VOP_REVOKE(vp, REVOKEALL); destroy_dev(dev); ugen_destroy_devnodes(sc); #endif 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); } selwakeup(&sce->rsel); } 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 %d, 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)); } 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); } selwakeup(&sce->rsel); } 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; u_int8_t niface, nendpt, 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); /* XXX should only do this after setting new altno has succeeded */ 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 = 0; sce->edesc = 0; sce->iface = 0; } /* change setting */ err = usbd_set_interface(iface, altno); if (err) return (err); err = usbd_endpoint_count(iface, &nendpt); if (err) return (err); 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; } 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, usb_proc_ptr 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: /* All handled in the upper FS layer. */ return (0); case USB_SET_SHORT_XFER: /* This flag only affects read */ if (endpt == USB_CONTROL_ENDPOINT) return (EINVAL); sce = &sc->sc_endpoints[endpt][IN]; if (sce == NULL) return (EINVAL); 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: if (endpt == USB_CONTROL_ENDPOINT) { /* XXX the lower levels don't support this yet. */ return (EINVAL); } sce = &sc->sc_endpoints[endpt][IN]; if (sce == NULL) return (EINVAL); if (sce->pipeh == NULL) { printf("ugenioctl: USB_SET_TIMEOUT, no pipe\n"); return (EIO); } sce->timeout = *(int *)addr; return (0); default: break; } if (endpt != USB_CONTROL_ENDPOINT) return (EINVAL); switch (cmd) { #ifdef UGEN_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: 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->interface_index, &iface); if (err) return (EINVAL); idesc = usbd_get_interface_descriptor(iface); if (idesc == NULL) return (EIO); ai->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->interface_index, &iface); if (err) return (EINVAL); err = ugen_set_interface(sc, ai->interface_index, ai->alt_no); if (err) return (EINVAL); break; case USB_GET_NO_ALT: ai = (struct usb_alt_interface *)addr; cdesc = ugen_get_cdesc(sc, ai->config_index, 0); if (cdesc == NULL) return (EINVAL); idesc = usbd_find_idesc(cdesc, ai->interface_index, 0); if (idesc == NULL) { free(cdesc, M_TEMP); return (EINVAL); } ai->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->config_index, 0); if (cdesc == NULL) return (EINVAL); cd->desc = *cdesc; free(cdesc, M_TEMP); break; case USB_GET_INTERFACE_DESC: id = (struct usb_interface_desc *)addr; cdesc = ugen_get_cdesc(sc, id->config_index, 0); if (cdesc == NULL) return (EINVAL); if (id->config_index == USB_CURRENT_CONFIG_INDEX && id->alt_index == USB_CURRENT_ALT_INDEX) alt = ugen_get_alt_index(sc, id->interface_index); else alt = id->alt_index; idesc = usbd_find_idesc(cdesc, id->interface_index, alt); if (idesc == NULL) { free(cdesc, M_TEMP); return (EINVAL); } id->desc = *idesc; free(cdesc, M_TEMP); break; case USB_GET_ENDPOINT_DESC: ed = (struct usb_endpoint_desc *)addr; cdesc = ugen_get_cdesc(sc, ed->config_index, 0); if (cdesc == NULL) return (EINVAL); if (ed->config_index == USB_CURRENT_CONFIG_INDEX && ed->alt_index == USB_CURRENT_ALT_INDEX) alt = ugen_get_alt_index(sc, ed->interface_index); else alt = ed->alt_index; edesc = usbd_find_edesc(cdesc, ed->interface_index, alt, ed->endpoint_index); if (edesc == NULL) { free(cdesc, M_TEMP); return (EINVAL); } ed->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->config_index, &len); if (len > fd->size) len = fd->size; iov.iov_base = (caddr_t)fd->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_procp = p; error = uiomove((void *)cdesc, len, &uio); free(cdesc, M_TEMP); return (error); } case USB_GET_STRING_DESC: si = (struct usb_string_desc *)addr; err = usbd_get_string_desc(sc->sc_udev, si->string_index, si->language_id, &si->desc); if (err) return (EINVAL); break; case USB_DO_REQUEST: { struct usb_ctl_request *ur = (void *)addr; int len = UGETW(ur->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->request.bmRequestType == UT_WRITE_DEVICE && ur->request.bRequest == UR_SET_ADDRESS) || (ur->request.bmRequestType == UT_WRITE_DEVICE && ur->request.bRequest == UR_SET_CONFIG) || (ur->request.bmRequestType == UT_WRITE_INTERFACE && ur->request.bRequest == UR_SET_INTERFACE)) return (EINVAL); if (len < 0 || len > 32767) return (EINVAL); if (len != 0) { iov.iov_base = (caddr_t)ur->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->request.bmRequestType & UT_READ ? UIO_READ : UIO_WRITE; uio.uio_procp = p; ptr = malloc(len, M_TEMP, M_WAITOK); if (uio.uio_rw == UIO_WRITE) { error = uiomove(ptr, len, &uio); if (error) goto ret; } } err = usbd_do_request_flags(sc->sc_udev, &ur->request, ptr, ur->flags, &ur->actlen); 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; default: return (EINVAL); } return (0); } int ugenioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) { int endpt = UGENENDPOINT(dev); struct ugen_softc *sc; int error; USB_GET_SC(ugen, UGENUNIT(dev), sc); sc->sc_refcnt++; error = ugen_do_ioctl(sc, endpt, cmd, addr, flag, p); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); return (error); } int ugenpoll(dev_t dev, int events, usb_proc_ptr p) { struct ugen_softc *sc; struct ugen_endpoint *sce; int revents = 0; int s; USB_GET_SC(ugen, UGENUNIT(dev), sc); if (sc->sc_dying) return (EIO); /* XXX always IN */ sce = &sc->sc_endpoints[UGENENDPOINT(dev)][IN]; if (sce == NULL) return (EINVAL); if (!sce->edesc) { printf("ugenpoll: no edesc\n"); return (EIO); } if (!sce->pipeh) { printf("ugenpoll: no pipe\n"); return (EIO); } s = splusb(); switch (sce->edesc->bmAttributes & UE_XFERTYPE) { case UE_INTERRUPT: if (events & (POLLIN | POLLRDNORM)) { if (sce->q.c_cc > 0) revents |= events & (POLLIN | POLLRDNORM); else selrecord(p, &sce->rsel); } break; case UE_ISOCHRONOUS: if (events & (POLLIN | POLLRDNORM)) { if (sce->cur != sce->fill) revents |= events & (POLLIN | POLLRDNORM); else selrecord(p, &sce->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); } #if defined(__FreeBSD__) DRIVER_MODULE(ugen, uhub, ugen_driver, ugen_devclass, usbd_driver_load, 0); #endif diff --git a/sys/dev/usb/uhid.c b/sys/dev/usb/uhid.c index 766098c995af..be7e8dc62e7d 100644 --- a/sys/dev/usb/uhid.c +++ b/sys/dev/usb/uhid.c @@ -1,756 +1,759 @@ /* $NetBSD: uhid.c,v 1.38 2000/04/27 15:26:48 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. */ /* * HID spec: http://www.usb.org/developers/data/usbhid10.pdf */ #include #include #include #include #include #include #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include #include #include #elif defined(__FreeBSD__) #include #include #include #include #include #endif #include #include #if __FreeBSD_version >= 500014 #include #else #include #endif #include #include #include #include #include #include #include #include #ifdef UHID_DEBUG #define DPRINTF(x) if (uhiddebug) logprintf x #define DPRINTFN(n,x) if (uhiddebug>(n)) logprintf x int uhiddebug = 0; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif struct uhid_softc { USBBASEDEVICE sc_dev; /* base device */ usbd_device_handle sc_udev; usbd_interface_handle sc_iface; /* interface */ usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ int sc_ep_addr; int sc_isize; int sc_osize; int sc_fsize; u_int8_t sc_iid; u_int8_t sc_oid; u_int8_t sc_fid; u_char *sc_ibuf; u_char *sc_obuf; void *sc_repdesc; int sc_repdesc_size; struct clist sc_q; struct selinfo sc_rsel; struct proc *sc_async; /* process that wants SIGIO */ u_char sc_state; /* driver state */ #define UHID_OPEN 0x01 /* device is open */ #define UHID_ASLP 0x02 /* waiting for device data */ #define UHID_NEEDCLEAR 0x04 /* needs clearing endpoint stall */ #define UHID_IMMED 0x08 /* return read data immediately */ int sc_refcnt; u_char sc_dying; #if defined(__FreeBSD__) dev_t dev; #endif }; #define UHIDUNIT(dev) (minor(dev)) #define UHID_CHUNK 128 /* chunk size for read */ #define UHID_BSIZE 1020 /* buffer size */ #if defined(__NetBSD__) || defined(__OpenBSD__) cdev_decl(uhid); #elif defined(__FreeBSD__) d_open_t uhidopen; d_close_t uhidclose; d_read_t uhidread; d_write_t uhidwrite; d_ioctl_t uhidioctl; d_poll_t uhidpoll; #define UHID_CDEV_MAJOR 122 Static struct cdevsw uhid_cdevsw = { /* open */ uhidopen, /* close */ uhidclose, /* read */ uhidread, /* write */ uhidwrite, /* ioctl */ uhidioctl, /* poll */ uhidpoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "uhid", /* maj */ UHID_CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, +#if !defined(__FreeBSD__) || (__FreeBSD__ < 5) + /* bmaj */ -1 +#endif }; #endif Static void uhid_intr(usbd_xfer_handle, usbd_private_handle, usbd_status); Static int uhid_do_read(struct uhid_softc *, struct uio *uio, int); Static int uhid_do_write(struct uhid_softc *, struct uio *uio, int); Static int uhid_do_ioctl(struct uhid_softc *, u_long, caddr_t, int, usb_proc_ptr); USB_DECLARE_DRIVER(uhid); USB_MATCH(uhid) { USB_MATCH_START(uhid, uaa); usb_interface_descriptor_t *id; if (uaa->iface == NULL) return (UMATCH_NONE); id = usbd_get_interface_descriptor(uaa->iface); if (id == NULL || id->bInterfaceClass != UICLASS_HID) return (UMATCH_NONE); return (UMATCH_IFACECLASS_GENERIC); } USB_ATTACH(uhid) { USB_ATTACH_START(uhid, sc, uaa); usbd_interface_handle iface = uaa->iface; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; int size; void *desc; usbd_status err; char devinfo[1024]; sc->sc_udev = uaa->device; sc->sc_iface = iface; id = usbd_get_interface_descriptor(iface); usbd_devinfo(uaa->device, 0, devinfo); USB_ATTACH_SETUP; printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), devinfo, id->bInterfaceClass, id->bInterfaceSubClass); ed = usbd_interface2endpoint_descriptor(iface, 0); if (ed == NULL) { printf("%s: could not read endpoint descriptor\n", USBDEVNAME(sc->sc_dev)); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } DPRINTFN(10,("uhid_attach: bLength=%d bDescriptorType=%d " "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" " bInterval=%d\n", ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR, UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out", ed->bmAttributes & UE_XFERTYPE, UGETW(ed->wMaxPacketSize), ed->bInterval)); if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN || (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) { printf("%s: unexpected endpoint\n", USBDEVNAME(sc->sc_dev)); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } sc->sc_ep_addr = ed->bEndpointAddress; desc = 0; err = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_USBDEV); if (err) { printf("%s: no report descriptor\n", USBDEVNAME(sc->sc_dev)); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } (void)usbd_set_idle(iface, 0, 0); sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid); sc->sc_osize = hid_report_size(desc, size, hid_output, &sc->sc_oid); sc->sc_fsize = hid_report_size(desc, size, hid_feature, &sc->sc_fid); sc->sc_repdesc = desc; sc->sc_repdesc_size = size; #if defined(__FreeBSD__) sc->dev = make_dev(&uhid_cdevsw, device_get_unit(self), UID_ROOT, GID_OPERATOR, 0644, "uhid%d", device_get_unit(self)); #endif USB_ATTACH_SUCCESS_RETURN; } #if defined(__NetBSD__) || defined(__OpenBSD__) int uhid_activate(self, act) device_ptr_t self; enum devact act; { struct uhid_softc *sc = (struct uhid_softc *)self; switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); break; case DVACT_DEACTIVATE: sc->sc_dying = 1; break; } return (0); } #endif USB_DETACH(uhid) { USB_DETACH_START(uhid, sc); int s; #if defined(__NetBSD__) || defined(__OpenBSD__) int maj, mn; #elif defined(__FreeBSD__) struct vnode *vp; #endif #if defined(__NetBSD__) || defined(__OpenBSD__) DPRINTF(("uhid_detach: sc=%p flags=%d\n", sc, flags)); #else DPRINTF(("uhid_detach: sc=%p\n", sc)); #endif sc->sc_dying = 1; if (sc->sc_intrpipe != NULL) usbd_abort_pipe(sc->sc_intrpipe); if (sc->sc_state & UHID_OPEN) { s = splusb(); if (--sc->sc_refcnt >= 0) { /* Wake everyone */ wakeup(&sc->sc_q); /* Wait for processes to go away. */ usb_detach_wait(USBDEV(sc->sc_dev)); } splx(s); } #if defined(__NetBSD__) || defined(__OpenBSD__) /* locate the major number */ for (maj = 0; maj < nchrdev; maj++) if (cdevsw[maj].d_open == uhidopen) break; /* Nuke the vnodes for any open instances (calls close). */ mn = self->dv_unit; vdevgone(maj, mn, mn, VCHR); #elif defined(__FreeBSD__) vp = SLIST_FIRST(&sc->dev->si_hlist); if (vp) VOP_REVOKE(vp, REVOKEALL); destroy_dev(sc->dev); #endif free(sc->sc_repdesc, M_USBDEV); return (0); } void uhid_intr(xfer, addr, status) usbd_xfer_handle xfer; usbd_private_handle addr; usbd_status status; { struct uhid_softc *sc = addr; #ifdef UHID_DEBUG if (uhiddebug > 5) { u_int32_t cc, i; usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL); DPRINTF(("uhid_intr: status=%d cc=%d\n", status, cc)); DPRINTF(("uhid_intr: data =")); for (i = 0; i < cc; i++) DPRINTF((" %02x", sc->sc_ibuf[i])); DPRINTF(("\n")); } #endif if (status == USBD_CANCELLED) return; if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("uhid_intr: status=%d\n", status)); if (status == USBD_STALLED) sc->sc_state |= UHID_NEEDCLEAR; return; } (void) b_to_q(sc->sc_ibuf, sc->sc_isize, &sc->sc_q); if (sc->sc_state & UHID_ASLP) { sc->sc_state &= ~UHID_ASLP; DPRINTFN(5, ("uhid_intr: waking %p\n", sc)); wakeup(&sc->sc_q); } selwakeup(&sc->sc_rsel); if (sc->sc_async != NULL) { DPRINTFN(3, ("uhid_intr: sending SIGIO %p\n", sc->sc_async)); PROC_LOCK(sc->sc_async); psignal(sc->sc_async, SIGIO); PROC_UNLOCK(sc->sc_async); } } int uhidopen(dev, flag, mode, p) dev_t dev; int flag; int mode; usb_proc_ptr p; { struct uhid_softc *sc; usbd_status err; USB_GET_SC_OPEN(uhid, UHIDUNIT(dev), sc); DPRINTF(("uhidopen: sc=%p\n", sc)); if (sc->sc_dying) return (ENXIO); if (sc->sc_state & UHID_OPEN) return (EBUSY); sc->sc_state |= UHID_OPEN; if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1) { sc->sc_state &= ~UHID_OPEN; return (ENOMEM); } sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK); sc->sc_obuf = malloc(sc->sc_osize, M_USBDEV, M_WAITOK); /* Set up interrupt pipe. */ err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, sc->sc_ibuf, sc->sc_isize, uhid_intr, USBD_DEFAULT_INTERVAL); if (err) { DPRINTF(("uhidopen: usbd_open_pipe_intr failed, " "error=%d\n",err)); free(sc->sc_ibuf, M_USBDEV); free(sc->sc_obuf, M_USBDEV); sc->sc_state &= ~UHID_OPEN; return (EIO); } sc->sc_state &= ~UHID_IMMED; sc->sc_async = 0; return (0); } int uhidclose(dev, flag, mode, p) dev_t dev; int flag; int mode; usb_proc_ptr p; { struct uhid_softc *sc; USB_GET_SC(uhid, UHIDUNIT(dev), sc); DPRINTF(("uhidclose: sc=%p\n", sc)); /* Disable interrupts. */ usbd_abort_pipe(sc->sc_intrpipe); usbd_close_pipe(sc->sc_intrpipe); sc->sc_intrpipe = 0; ndflush(&sc->sc_q, sc->sc_q.c_cc); clfree(&sc->sc_q); free(sc->sc_ibuf, M_USBDEV); free(sc->sc_obuf, M_USBDEV); sc->sc_state &= ~UHID_OPEN; sc->sc_async = 0; return (0); } int uhid_do_read(sc, uio, flag) struct uhid_softc *sc; struct uio *uio; int flag; { int s; int error = 0; size_t length; u_char buffer[UHID_CHUNK]; usbd_status err; DPRINTFN(1, ("uhidread\n")); if (sc->sc_state & UHID_IMMED) { DPRINTFN(1, ("uhidread immed\n")); err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT, sc->sc_iid, buffer, sc->sc_isize); if (err) return (EIO); return (uiomove(buffer, sc->sc_isize, uio)); } s = splusb(); while (sc->sc_q.c_cc == 0) { if (flag & IO_NDELAY) { splx(s); return (EWOULDBLOCK); } sc->sc_state |= UHID_ASLP; DPRINTFN(5, ("uhidread: sleep on %p\n", sc)); error = tsleep(&sc->sc_q, PZERO | PCATCH, "uhidrea", 0); DPRINTFN(5, ("uhidread: woke, error=%d\n", error)); if (sc->sc_dying) error = EIO; if (error) { sc->sc_state &= ~UHID_ASLP; break; } if (sc->sc_state & UHID_NEEDCLEAR) { DPRINTFN(-1,("uhidread: clearing stall\n")); sc->sc_state &= ~UHID_NEEDCLEAR; usbd_clear_endpoint_stall(sc->sc_intrpipe); } } splx(s); /* Transfer as many chunks as possible. */ while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) { length = min(sc->sc_q.c_cc, uio->uio_resid); if (length > sizeof(buffer)) length = sizeof(buffer); /* Remove a small chunk from the input queue. */ (void) q_to_b(&sc->sc_q, buffer, length); DPRINTFN(5, ("uhidread: got %lu chars\n", (u_long)length)); /* Copy the data to the user process. */ if ((error = uiomove(buffer, length, uio)) != 0) break; } return (error); } int uhidread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct uhid_softc *sc; int error; USB_GET_SC(uhid, UHIDUNIT(dev), sc); sc->sc_refcnt++; error = uhid_do_read(sc, uio, flag); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); return (error); } int uhid_do_write(sc, uio, flag) struct uhid_softc *sc; struct uio *uio; int flag; { int error; int size; usbd_status err; DPRINTFN(1, ("uhidwrite\n")); if (sc->sc_dying) return (EIO); size = sc->sc_osize; error = 0; if (uio->uio_resid != size) return (EINVAL); error = uiomove(sc->sc_obuf, size, uio); if (!error) { if (sc->sc_oid) err = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT, sc->sc_obuf[0], sc->sc_obuf+1, size-1); else err = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT, 0, sc->sc_obuf, size); if (err) error = EIO; } return (error); } int uhidwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct uhid_softc *sc; int error; USB_GET_SC(uhid, UHIDUNIT(dev), sc); sc->sc_refcnt++; error = uhid_do_write(sc, uio, flag); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); return (error); } int uhid_do_ioctl(sc, cmd, addr, flag, p) struct uhid_softc *sc; u_long cmd; caddr_t addr; int flag; usb_proc_ptr p; { struct usb_ctl_report_desc *rd; struct usb_ctl_report *re; int size, id; usbd_status err; DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd)); if (sc->sc_dying) return (EIO); switch (cmd) { case FIONBIO: /* All handled in the upper FS layer. */ break; case FIOASYNC: if (*(int *)addr) { if (sc->sc_async != NULL) return (EBUSY); sc->sc_async = p->td_proc; /* XXXKSE */ DPRINTF(("uhid_do_ioctl: FIOASYNC %p\n", p->td_proc)); } else sc->sc_async = NULL; break; /* XXX this is not the most general solution. */ case TIOCSPGRP: if (sc->sc_async == NULL) return (EINVAL); if (*(int *)addr != sc->sc_async->p_pgid) return (EPERM); break; case USB_GET_REPORT_DESC: rd = (struct usb_ctl_report_desc *)addr; size = min(sc->sc_repdesc_size, sizeof rd->data); rd->size = size; memcpy(rd->data, sc->sc_repdesc, size); break; case USB_SET_IMMED: if (*(int *)addr) { /* XXX should read into ibuf, but does it matter? */ err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT, sc->sc_iid, sc->sc_ibuf, sc->sc_isize); if (err) return (EOPNOTSUPP); sc->sc_state |= UHID_IMMED; } else sc->sc_state &= ~UHID_IMMED; break; case USB_GET_REPORT: re = (struct usb_ctl_report *)addr; switch (re->report) { case UHID_INPUT_REPORT: size = sc->sc_isize; id = sc->sc_iid; break; case UHID_OUTPUT_REPORT: size = sc->sc_osize; id = sc->sc_oid; break; case UHID_FEATURE_REPORT: size = sc->sc_fsize; id = sc->sc_fid; break; default: return (EINVAL); } err = usbd_get_report(sc->sc_iface, re->report, id, re->data, size); if (err) return (EIO); break; case USB_SET_REPORT: re = (struct usb_ctl_report *)addr; switch (re->report) { case UHID_INPUT_REPORT: size = sc->sc_isize; id = sc->sc_iid; break; case UHID_OUTPUT_REPORT: size = sc->sc_osize; id = sc->sc_oid; break; case UHID_FEATURE_REPORT: size = sc->sc_fsize; id = sc->sc_fid; break; default: return (EINVAL); } err = usbd_set_report(sc->sc_iface, re->report, id, re->data, size); if (err) return (EIO); break; default: return (EINVAL); } return (0); } int uhidioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; usb_proc_ptr p; { struct uhid_softc *sc; int error; USB_GET_SC(uhid, UHIDUNIT(dev), sc); sc->sc_refcnt++; error = uhid_do_ioctl(sc, cmd, addr, flag, p); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); return (error); } int uhidpoll(dev, events, p) dev_t dev; int events; usb_proc_ptr p; { struct uhid_softc *sc; int revents = 0; int s; USB_GET_SC(uhid, UHIDUNIT(dev), sc); if (sc->sc_dying) return (EIO); s = splusb(); if (events & (POLLOUT | POLLWRNORM)) revents |= events & (POLLOUT | POLLWRNORM); if (events & (POLLIN | POLLRDNORM)) { if (sc->sc_q.c_cc > 0) revents |= events & (POLLIN | POLLRDNORM); else selrecord(p, &sc->sc_rsel); } splx(s); return (revents); } #if defined(__FreeBSD__) DRIVER_MODULE(uhid, uhub, uhid_driver, uhid_devclass, usbd_driver_load, 0); #endif diff --git a/sys/dev/usb/ulpt.c b/sys/dev/usb/ulpt.c index 6e48286a92cd..d53346984d29 100644 --- a/sys/dev/usb/ulpt.c +++ b/sys/dev/usb/ulpt.c @@ -1,737 +1,740 @@ /* $NetBSD: ulpt.c,v 1.46 2001/12/31 12:15:21 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. */ /* * Printer Class spec: http://www.usb.org/developers/data/devclass/usbprint109.PDF */ #include #include #include #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include #include #elif defined(__FreeBSD__) #include #include #include #endif #include #include #include #include #include #include #include #include #include #define TIMEOUT hz*16 /* wait up to 16 seconds for a ready */ #define STEP hz/4 #define LPTPRI (PZERO+8) #define ULPT_BSIZE 16384 #ifdef ULPT_DEBUG #define DPRINTF(x) if (ulptdebug) logprintf x #define DPRINTFN(n,x) if (ulptdebug>(n)) logprintf x int ulptdebug = 0; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define UR_GET_DEVICE_ID 0 #define UR_GET_PORT_STATUS 1 #define UR_SOFT_RESET 2 #define LPS_NERR 0x08 /* printer no error */ #define LPS_SELECT 0x10 /* printer selected */ #define LPS_NOPAPER 0x20 /* printer out of paper */ #define LPS_INVERT (LPS_SELECT|LPS_NERR) #define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NOPAPER) struct ulpt_softc { USBBASEDEVICE sc_dev; usbd_device_handle sc_udev; /* device */ usbd_interface_handle sc_iface; /* interface */ int sc_ifaceno; int sc_out; usbd_pipe_handle sc_out_pipe; /* bulk out pipe */ int sc_in; usbd_pipe_handle sc_in_pipe; /* bulk in pipe */ usbd_xfer_handle sc_in_xfer1; usbd_xfer_handle sc_in_xfer2; u_char sc_junk[64]; /* somewhere to dump input */ u_char sc_state; #define ULPT_OPEN 0x01 /* device is open */ #define ULPT_OBUSY 0x02 /* printer is busy doing output */ #define ULPT_INIT 0x04 /* waiting to initialize for open */ u_char sc_flags; #define ULPT_NOPRIME 0x40 /* don't prime on open */ u_char sc_laststatus; int sc_refcnt; u_char sc_dying; #if defined(__FreeBSD__) dev_t dev; dev_t dev_noprime; #endif }; #if defined(__NetBSD__) || defined(__OpenBSD__) cdev_decl(ulpt); #elif defined(__FreeBSD__) Static d_open_t ulptopen; Static d_close_t ulptclose; Static d_write_t ulptwrite; Static d_ioctl_t ulptioctl; #define ULPT_CDEV_MAJOR 113 Static struct cdevsw ulpt_cdevsw = { /* open */ ulptopen, /* close */ ulptclose, /* read */ noread, /* write */ ulptwrite, /* ioctl */ ulptioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "ulpt", /* maj */ ULPT_CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, +#if !defined(__FreeBSD__) || (__FreeBSD__ < 5) + /* bmaj */ -1 +#endif }; #endif void ulpt_disco(void *); int ulpt_do_write(struct ulpt_softc *, struct uio *uio, int); int ulpt_status(struct ulpt_softc *); void ulpt_reset(struct ulpt_softc *); int ulpt_statusmsg(u_char, struct ulpt_softc *); #if 0 void ieee1284_print_id(char *); #endif #define ULPTUNIT(s) (minor(s) & 0x1f) #define ULPTFLAGS(s) (minor(s) & 0xe0) USB_DECLARE_DRIVER(ulpt); USB_MATCH(ulpt) { USB_MATCH_START(ulpt, uaa); usb_interface_descriptor_t *id; DPRINTFN(10,("ulpt_match\n")); if (uaa->iface == NULL) return (UMATCH_NONE); id = usbd_get_interface_descriptor(uaa->iface); if (id != NULL && id->bInterfaceClass == UICLASS_PRINTER && id->bInterfaceSubClass == UISUBCLASS_PRINTER && (id->bInterfaceProtocol == UIPROTO_PRINTER_UNI || id->bInterfaceProtocol == UIPROTO_PRINTER_BI || id->bInterfaceProtocol == UIPROTO_PRINTER_1284)) return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO); return (UMATCH_NONE); } USB_ATTACH(ulpt) { USB_ATTACH_START(ulpt, sc, uaa); usbd_device_handle dev = uaa->device; usbd_interface_handle iface = uaa->iface; usb_interface_descriptor_t *ifcd = usbd_get_interface_descriptor(iface); usb_interface_descriptor_t *id, *iend; usb_config_descriptor_t *cdesc; usbd_status err; char devinfo[1024]; usb_endpoint_descriptor_t *ed; u_int8_t epcount; int i, altno; DPRINTFN(10,("ulpt_attach: sc=%p\n", sc)); usbd_devinfo(dev, 0, devinfo); USB_ATTACH_SETUP; printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), devinfo, ifcd->bInterfaceClass, ifcd->bInterfaceSubClass); /* XXX * Stepping through the alternate settings needs to be abstracted out. */ cdesc = usbd_get_config_descriptor(dev); if (cdesc == NULL) { printf("%s: failed to get configuration descriptor\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } iend = (usb_interface_descriptor_t *) ((char *)cdesc + UGETW(cdesc->wTotalLength)); #ifdef DIAGNOSTIC if (ifcd < (usb_interface_descriptor_t *)cdesc || ifcd >= iend) panic("ulpt: iface desc out of range\n"); #endif /* Step through all the descriptors looking for bidir mode */ for (id = ifcd, altno = 0; id < iend; id = (void *)((char *)id + id->bLength)) { if (id->bDescriptorType == UDESC_INTERFACE && id->bInterfaceNumber == ifcd->bInterfaceNumber) { if (id->bInterfaceClass == UICLASS_PRINTER && id->bInterfaceSubClass == UISUBCLASS_PRINTER && (id->bInterfaceProtocol == UIPROTO_PRINTER_BI || id->bInterfaceProtocol == UIPROTO_PRINTER_1284)) goto found; altno++; } } id = ifcd; /* not found, use original */ found: if (id != ifcd) { /* Found a new bidir setting */ DPRINTF(("ulpt_attach: set altno = %d\n", altno)); err = usbd_set_interface(iface, altno); if (err) { printf("%s: setting alternate interface failed\n", USBDEVNAME(sc->sc_dev)); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } } epcount = 0; (void)usbd_endpoint_count(iface, &epcount); sc->sc_in = -1; sc->sc_out = -1; for (i = 0; i < epcount; i++) { ed = usbd_interface2endpoint_descriptor(iface, i); if (ed == NULL) { printf("%s: couldn't get ep %d\n", USBDEVNAME(sc->sc_dev), i); USB_ATTACH_ERROR_RETURN; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { sc->sc_in = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { sc->sc_out = ed->bEndpointAddress; } } if (sc->sc_out == -1) { printf("%s: could not find bulk endpoint\n", USBDEVNAME(sc->sc_dev)); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } printf("%s: using %s-directional mode\n", USBDEVNAME(sc->sc_dev), sc->sc_in >= 0 ? "bi" : "uni"); if (usbd_get_quirks(dev)->uq_flags & UQ_BROKEN_BIDIR) { /* This device doesn't handle reading properly. */ sc->sc_in = -1; } DPRINTFN(10, ("ulpt_attach: bulk=%d\n", sc->sc_out)); sc->sc_iface = iface; sc->sc_ifaceno = id->bInterfaceNumber; sc->sc_udev = dev; #if 0 /* * This code is disabled because for some mysterious reason it causes * printing not to work. But only sometimes, and mostly with * UHCI and less often with OHCI. *sigh* */ { usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev); usb_device_request_t req; int len, alen; req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_DEVICE_ID; USETW(req.wValue, cd->bConfigurationValue); USETW2(req.wIndex, id->bInterfaceNumber, id->bAlternateSetting); USETW(req.wLength, sizeof devinfo - 1); err = usbd_do_request_flags(dev, &req, devinfo, USBD_SHORT_XFER_OK, &alen); if (err) { printf("%s: cannot get device id\n", USBDEVNAME(sc->sc_dev)); } else if (alen <= 2) { printf("%s: empty device id, no printer connected?\n", USBDEVNAME(sc->sc_dev)); } else { /* devinfo now contains an IEEE-1284 device ID */ len = ((devinfo[0] & 0xff) << 8) | (devinfo[1] & 0xff); if (len > sizeof devinfo - 3) len = sizeof devinfo - 3; devinfo[len] = 0; printf("%s: device id <", USBDEVNAME(sc->sc_dev)); ieee1284_print_id(devinfo+2); printf(">\n"); } } #endif #if defined(__FreeBSD__) sc->dev = make_dev(&ulpt_cdevsw, device_get_unit(self), UID_ROOT, GID_OPERATOR, 0644, "ulpt%d", device_get_unit(self)); sc->dev_noprime = make_dev(&ulpt_cdevsw, device_get_unit(self)|ULPT_NOPRIME, UID_ROOT, GID_OPERATOR, 0644, "unlpt%d", device_get_unit(self)); #endif usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, USBDEV(sc->sc_dev)); USB_ATTACH_SUCCESS_RETURN; } #if defined(__NetBSD__) || defined(__OpenBSD__) int ulpt_activate(device_ptr_t self, enum devact act) { struct ulpt_softc *sc = (struct ulpt_softc *)self; switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); break; case DVACT_DEACTIVATE: sc->sc_dying = 1; break; } return (0); } #endif USB_DETACH(ulpt) { USB_DETACH_START(ulpt, sc); int s; #if defined(__NetBSD__) || defined(__OpenBSD__) int maj, mn; #elif defined(__FreeBSD__) struct vnode *vp; #endif #if defined(__NetBSD__) || defined(__OpenBSD__) DPRINTF(("ulpt_detach: sc=%p flags=%d\n", sc, flags)); #elif defined(__FreeBSD__) DPRINTF(("ulpt_detach: sc=%p\n", sc)); #endif sc->sc_dying = 1; if (sc->sc_out_pipe != NULL) usbd_abort_pipe(sc->sc_out_pipe); if (sc->sc_in_pipe != NULL) usbd_abort_pipe(sc->sc_in_pipe); s = splusb(); if (--sc->sc_refcnt >= 0) { /* There is noone to wake, aborting the pipe is enough */ /* Wait for processes to go away. */ usb_detach_wait(USBDEV(sc->sc_dev)); } splx(s); #if defined(__NetBSD__) || defined(__OpenBSD__) /* locate the major number */ for (maj = 0; maj < nchrdev; maj++) if (cdevsw[maj].d_open == ulptopen) break; /* Nuke the vnodes for any open instances (calls close). */ mn = self->dv_unit; vdevgone(maj, mn, mn, VCHR); #elif defined(__FreeBSD__) vp = SLIST_FIRST(&sc->dev->si_hlist); if (vp) VOP_REVOKE(vp, REVOKEALL); vp = SLIST_FIRST(&sc->dev_noprime->si_hlist); if (vp) VOP_REVOKE(vp, REVOKEALL); destroy_dev(sc->dev); destroy_dev(sc->dev_noprime); #endif usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, USBDEV(sc->sc_dev)); return (0); } int ulpt_status(struct ulpt_softc *sc) { usb_device_request_t req; usbd_status err; u_char status; req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_PORT_STATUS; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_ifaceno); USETW(req.wLength, 1); err = usbd_do_request(sc->sc_udev, &req, &status); DPRINTFN(1, ("ulpt_status: status=0x%02x err=%d\n", status, err)); if (!err) return (status); else return (0); } void ulpt_reset(struct ulpt_softc *sc) { usb_device_request_t req; DPRINTFN(1, ("ulpt_reset\n")); req.bRequest = UR_SOFT_RESET; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_ifaceno); USETW(req.wLength, 0); /* * There was a mistake in the USB printer 1.0 spec that gave the * request type as UT_WRITE_CLASS_OTHER; it should have been * UT_WRITE_CLASS_INTERFACE. Many printers use the old one, * so we try both. */ if (usbd_do_request(sc->sc_udev, &req, 0)) { /* 1.0 */ req.bmRequestType = UT_WRITE_CLASS_INTERFACE; (void)usbd_do_request(sc->sc_udev, &req, 0); /* 1.1 */ } } static void ulpt_input(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct ulpt_softc *sc = priv; DPRINTFN(2,("ulpt_input: got some data\n")); /* Do it again. */ if (xfer == sc->sc_in_xfer1) usbd_transfer(sc->sc_in_xfer2); else usbd_transfer(sc->sc_in_xfer1); } int ulptusein = 1; /* * Reset the printer, then wait until it's selected and not busy. */ int ulptopen(dev_t dev, int flag, int mode, usb_proc_ptr p) { u_char flags = ULPTFLAGS(dev); struct ulpt_softc *sc; usbd_status err; int spin, error; USB_GET_SC_OPEN(ulpt, ULPTUNIT(dev), sc); if (sc == NULL || sc->sc_iface == NULL || sc->sc_dying) return (ENXIO); if (sc->sc_state) return (EBUSY); sc->sc_state = ULPT_INIT; sc->sc_flags = flags; DPRINTF(("ulptopen: flags=0x%x\n", (unsigned)flags)); #if defined(ULPT_DEBUG) && defined(__FreeBSD__) /* Ignoring these flags might not be a good idea */ if ((flags & ~ULPT_NOPRIME) != 0) printf("ulptopen: flags ignored: %b\n", flags, "\20\3POS_INIT\4POS_ACK\6PRIME_OPEN\7AUTOLF\10BYPASS"); #endif error = 0; sc->sc_refcnt++; if ((flags & ULPT_NOPRIME) == 0) ulpt_reset(sc); for (spin = 0; (ulpt_status(sc) & LPS_SELECT) == 0; spin += STEP) { DPRINTF(("ulpt_open: waiting a while\n")); if (spin >= TIMEOUT) { error = EBUSY; sc->sc_state = 0; goto done; } /* wait 1/4 second, give up if we get a signal */ error = tsleep((caddr_t)sc, LPTPRI | PCATCH, "ulptop", STEP); if (error != EWOULDBLOCK) { sc->sc_state = 0; goto done; } if (sc->sc_dying) { error = ENXIO; sc->sc_state = 0; goto done; } } err = usbd_open_pipe(sc->sc_iface, sc->sc_out, 0, &sc->sc_out_pipe); if (err) { sc->sc_state = 0; error = EIO; goto done; } if (ulptusein && sc->sc_in != -1) { DPRINTF(("ulpt_open: open input pipe\n")); err = usbd_open_pipe(sc->sc_iface, sc->sc_in,0,&sc->sc_in_pipe); if (err) { error = EIO; usbd_close_pipe(sc->sc_out_pipe); sc->sc_out_pipe = NULL; sc->sc_state = 0; goto done; } sc->sc_in_xfer1 = usbd_alloc_xfer(sc->sc_udev); sc->sc_in_xfer2 = usbd_alloc_xfer(sc->sc_udev); if (sc->sc_in_xfer1 == NULL || sc->sc_in_xfer2 == NULL) { error = ENOMEM; if (sc->sc_in_xfer1 != NULL) { usbd_free_xfer(sc->sc_in_xfer1); sc->sc_in_xfer1 = NULL; } if (sc->sc_in_xfer2 != NULL) { usbd_free_xfer(sc->sc_in_xfer2); sc->sc_in_xfer2 = NULL; } usbd_close_pipe(sc->sc_out_pipe); sc->sc_out_pipe = NULL; usbd_close_pipe(sc->sc_in_pipe); sc->sc_in_pipe = NULL; sc->sc_state = 0; goto done; } usbd_setup_xfer(sc->sc_in_xfer1, sc->sc_in_pipe, sc, sc->sc_junk, sizeof sc->sc_junk, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, ulpt_input); usbd_setup_xfer(sc->sc_in_xfer2, sc->sc_in_pipe, sc, sc->sc_junk, sizeof sc->sc_junk, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, ulpt_input); usbd_transfer(sc->sc_in_xfer1); /* ignore failed start */ } sc->sc_state = ULPT_OPEN; done: if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); DPRINTF(("ulptopen: done, error=%d\n", error)); return (error); } int ulpt_statusmsg(u_char status, struct ulpt_softc *sc) { u_char new; status = (status ^ LPS_INVERT) & LPS_MASK; new = status & ~sc->sc_laststatus; sc->sc_laststatus = status; if (new & LPS_SELECT) log(LOG_NOTICE, "%s: offline\n", USBDEVNAME(sc->sc_dev)); else if (new & LPS_NOPAPER) log(LOG_NOTICE, "%s: out of paper\n", USBDEVNAME(sc->sc_dev)); else if (new & LPS_NERR) log(LOG_NOTICE, "%s: output error\n", USBDEVNAME(sc->sc_dev)); return (status); } int ulptclose(dev_t dev, int flag, int mode, usb_proc_ptr p) { struct ulpt_softc *sc; USB_GET_SC(ulpt, ULPTUNIT(dev), sc); if (sc->sc_state != ULPT_OPEN) /* We are being forced to close before the open completed. */ return (0); if (sc->sc_out_pipe != NULL) { usbd_close_pipe(sc->sc_out_pipe); sc->sc_out_pipe = NULL; } if (sc->sc_in_pipe != NULL) { usbd_abort_pipe(sc->sc_in_pipe); usbd_close_pipe(sc->sc_in_pipe); sc->sc_in_pipe = NULL; if (sc->sc_in_xfer1 != NULL) { usbd_free_xfer(sc->sc_in_xfer1); sc->sc_in_xfer1 = NULL; } if (sc->sc_in_xfer2 != NULL) { usbd_free_xfer(sc->sc_in_xfer2); sc->sc_in_xfer2 = NULL; } } sc->sc_state = 0; DPRINTF(("ulptclose: closed\n")); return (0); } int ulpt_do_write(struct ulpt_softc *sc, struct uio *uio, int flags) { u_int32_t n; int error = 0; void *bufp; usbd_xfer_handle xfer; usbd_status err; DPRINTF(("ulptwrite\n")); xfer = usbd_alloc_xfer(sc->sc_udev); if (xfer == NULL) return (ENOMEM); bufp = usbd_alloc_buffer(xfer, ULPT_BSIZE); if (bufp == NULL) { usbd_free_xfer(xfer); return (ENOMEM); } while ((n = min(ULPT_BSIZE, uio->uio_resid)) != 0) { ulpt_statusmsg(ulpt_status(sc), sc); error = uiomove(bufp, n, uio); if (error) break; DPRINTFN(1, ("ulptwrite: transfer %d bytes\n", n)); err = usbd_bulk_transfer(xfer, sc->sc_out_pipe, USBD_NO_COPY, USBD_NO_TIMEOUT, bufp, &n, "ulptwr"); if (err) { DPRINTF(("ulptwrite: error=%d\n", err)); error = EIO; break; } } usbd_free_xfer(xfer); return (error); } int ulptwrite(dev_t dev, struct uio *uio, int flags) { struct ulpt_softc *sc; int error; USB_GET_SC(ulpt, ULPTUNIT(dev), sc); if (sc->sc_dying) return (EIO); sc->sc_refcnt++; error = ulpt_do_write(sc, uio, flags); if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); return (error); } int ulptioctl(dev_t dev, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) { int error = 0; switch (cmd) { default: error = ENODEV; } return (error); } #if 0 /* XXX This does not belong here. */ /* * Print select parts of a IEEE 1284 device ID. */ void ieee1284_print_id(char *str) { char *p, *q; for (p = str-1; p; p = strchr(p, ';')) { p++; /* skip ';' */ if (strncmp(p, "MFG:", 4) == 0 || strncmp(p, "MANUFACTURER:", 14) == 0 || strncmp(p, "MDL:", 4) == 0 || strncmp(p, "MODEL:", 6) == 0) { q = strchr(p, ';'); if (q) printf("%.*s", (int)(q - p + 1), p); } } } #endif #if defined(__FreeBSD__) DRIVER_MODULE(ulpt, uhub, ulpt_driver, ulpt_devclass, usbd_driver_load, 0); #endif diff --git a/sys/dev/usb/umodem.c b/sys/dev/usb/umodem.c index be03540bd2c4..91a32d01b6bb 100644 --- a/sys/dev/usb/umodem.c +++ b/sys/dev/usb/umodem.c @@ -1,1238 +1,1241 @@ /* $NetBSD: umodem.c,v 1.5 1999/01/08 11:58:25 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. */ /* * Comm Class spec: http://www.usb.org/developers/data/usbcdc11.pdf */ /* * TODO: * - Add error recovery in various places; the big problem is what * to do in a callback if there is an error. * - Implement a Call Device for modems without multiplexed commands. * */ #include #include #include #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include #include #elif defined(__FreeBSD__) #include #include #include #endif #include #include #include #include #if __FreeBSD_version >= 500014 #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #ifdef UMODEM_DEBUG #define DPRINTF(x) if(umodemdebug) logprintf x #define DPRINTFN(n, x) if(umodemdebug > (n)) logprintf x int umodemdebug = 1; #else #define DPRINTF(x) #define DPRINTFN(n, x) #endif /* Macros to clear/set/test flags. */ #define SET(t, f) (t) |= (f) #define CLR(t, f) (t) &= ~((unsigned)(f)) #define ISSET(t, f) ((t) & (f)) #define UMODEMUNIT_MASK 0x3ffff #define UMODEMDIALOUT_MASK 0x80000 #define UMODEMCALLUNIT_MASK 0x40000 #define UMODEMUNIT(x) (minor(x) & UMODEMUNIT_MASK) #define UMODEMDIALOUT(x) (minor(x) & UMODEMDIALOUT_MASK) #define UMODEMCALLUNIT(x) (minor(x) & UMODEMCALLUNIT_MASK) #define UMODEMIBUFSIZE 64 struct umodem_softc { USBBASEDEVICE sc_dev; /* base device */ usbd_device_handle sc_udev; /* USB device */ int sc_ctl_iface_no; usbd_interface_handle sc_ctl_iface; /* control interface */ int sc_data_iface_no; usbd_interface_handle sc_data_iface; /* data interface */ int sc_bulkin_no; /* bulk in endpoint address */ usbd_pipe_handle sc_bulkin_pipe; /* bulk in pipe */ usbd_xfer_handle sc_ixfer; /* read request */ u_char *sc_ibuf; /* read buffer */ int sc_bulkout_no; /* bulk out endpoint address */ usbd_pipe_handle sc_bulkout_pipe;/* bulk out pipe */ usbd_xfer_handle sc_oxfer; /* read request */ int sc_cm_cap; /* CM capabilities */ int sc_acm_cap; /* ACM capabilities */ int sc_cm_over_data; struct tty *sc_tty; /* our tty */ usb_cdc_line_state_t sc_line_state; /* current line state */ u_char sc_dtr; /* current DTR state */ u_char sc_opening; /* lock during open */ u_char sc_dying; /* disconnecting */ #if defined(__FreeBSD__) dev_t dev; /* special device node */ #endif }; #if defined(__NetBSD__) || defined(__OpenBSD__) cdev_decl(umodem); #elif defined(__FreeBSD__) d_open_t umodemopen; d_close_t umodemclose; d_read_t umodemread; d_write_t umodemwrite; d_ioctl_t umodemioctl; #define UMODEM_CDEV_MAJOR 124 static struct cdevsw umodem_cdevsw = { /* open */ umodemopen, /* close */ umodemclose, /* read */ umodemread, /* write */ umodemwrite, /* ioctl */ umodemioctl, /* poll */ ttypoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "umodem", /* maj */ UMODEM_CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_TTY | D_KQFILTER, +#if !defined(__FreeBSD__) || (__FreeBSD__ < 5) + /* bmaj */ -1, +#endif /* kqfilter */ ttykqfilter, }; #endif void *umodem_get_desc (usbd_device_handle dev, int type, int subtype); usbd_status umodem_set_comm_feature (struct umodem_softc *sc, int feature, int state); usbd_status umodem_set_line_coding (struct umodem_softc *sc, usb_cdc_line_state_t *state); void umodem_get_caps (usbd_device_handle, int *, int *); void umodem_cleanup (struct umodem_softc *); int umodemparam (struct tty *, struct termios *); void umodemstart (struct tty *); void umodemstop (struct tty *, int); struct tty * umodemtty (dev_t dev); void umodem_shutdown (struct umodem_softc *); void umodem_modem (struct umodem_softc *, int); void umodem_break (struct umodem_softc *, int); usbd_status umodemstartread (struct umodem_softc *); void umodemreadcb (usbd_xfer_handle, usbd_private_handle, usbd_status status); void umodemwritecb (usbd_xfer_handle, usbd_private_handle, usbd_status status); USB_DECLARE_DRIVER(umodem); USB_MATCH(umodem) { USB_MATCH_START(umodem, uaa); usb_interface_descriptor_t *id; int cm, acm; if (!uaa->iface) return (UMATCH_NONE); id = usbd_get_interface_descriptor(uaa->iface); if (id == 0 || id->bInterfaceClass != UICLASS_CDC || id->bInterfaceSubClass != UISUBCLASS_ABSTRACT_CONTROL_MODEL || id->bInterfaceProtocol != UIPROTO_CDC_AT) return (UMATCH_NONE); umodem_get_caps(uaa->device, &cm, &acm); if (!(cm & USB_CDC_CM_DOES_CM) || !(cm & USB_CDC_CM_OVER_DATA) || !(acm & USB_CDC_ACM_HAS_LINE)) return (UMATCH_NONE); return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO); } USB_ATTACH(umodem) { USB_ATTACH_START(umodem, sc, uaa); usbd_device_handle dev = uaa->device; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; usb_cdc_cm_descriptor_t *cmd; char devinfo[1024]; usbd_status err; int data_ifaceno; int i; struct tty *tp; usbd_devinfo(uaa->device, 0, devinfo); USB_ATTACH_SETUP; sc->sc_udev = dev; sc->sc_ctl_iface = uaa->iface; id = usbd_get_interface_descriptor(sc->sc_ctl_iface); printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), devinfo, id->bInterfaceClass, id->bInterfaceSubClass); sc->sc_ctl_iface_no = id->bInterfaceNumber; umodem_get_caps(dev, &sc->sc_cm_cap, &sc->sc_acm_cap); /* Get the data interface no. */ cmd = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); if (!cmd) { DPRINTF(("%s: no CM desc\n", USBDEVNAME(sc->sc_dev))); goto bad; } sc->sc_data_iface_no = data_ifaceno = cmd->bDataInterface; printf("%s: data interface %d, has %sCM over data, has %sbreak\n", USBDEVNAME(sc->sc_dev), data_ifaceno, sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ", sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no "); /* Get the data interface too. */ for (i = 0; i < uaa->nifaces; i++) { if (uaa->ifaces[i]) { id = usbd_get_interface_descriptor(uaa->ifaces[i]); if (id->bInterfaceNumber == data_ifaceno) { sc->sc_data_iface = uaa->ifaces[i]; uaa->ifaces[i] = 0; } } } if (!sc->sc_data_iface) { printf("%s: no data interface\n", USBDEVNAME(sc->sc_dev)); goto bad; } /* * Find the bulk endpoints. * Iterate over all endpoints in the data interface and take note. */ sc->sc_bulkin_no = sc->sc_bulkout_no = -1; id = usbd_get_interface_descriptor(sc->sc_data_iface); for (i = 0; i < id->bNumEndpoints; i++) { ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface, i); if (!ed) { printf("%s: no endpoint descriptor for %d\n", USBDEVNAME(sc->sc_dev), i); goto bad; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { sc->sc_bulkin_no = ed->bEndpointAddress; } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { sc->sc_bulkout_no = ed->bEndpointAddress; } } if (sc->sc_bulkin_no == -1) { DPRINTF(("%s: Could not find data bulk in\n", USBDEVNAME(sc->sc_dev))); goto bad; } if (sc->sc_bulkout_no == -1) { DPRINTF(("%s: Could not find data bulk out\n", USBDEVNAME(sc->sc_dev))); goto bad; } if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_ASSUME_CM_OVER_DATA) { sc->sc_cm_over_data = 1; } else { if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) { err = umodem_set_comm_feature(sc, UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED); if (err) goto bad; sc->sc_cm_over_data = 1; } } #if defined(__NetBSD__) || defined(__OpenBSD__) sc->sc_tty = tp = ttymalloc(); #elif defined(__FreeBSD__) sc->sc_tty = tp = ttymalloc(sc->sc_tty); #endif tp->t_oproc = umodemstart; tp->t_param = umodemparam; tp->t_stop = umodemstop; DPRINTF(("umodem_attach: tty_attach %p\n", tp)); #if defined(__NetBSD__) || defined(__OpenBSD__) tty_attach(tp); #endif #if defined(__FreeBSD__) DPRINTF(("umodem_attach: make_dev: umodem%d\n", device_get_unit(self))); sc->dev = make_dev(&umodem_cdevsw, device_get_unit(self), UID_UUCP, GID_DIALER, 0660, "umodem%d", device_get_unit(self)); sc->dev->si_tty = tp; #endif sc->sc_dtr = -1; USB_ATTACH_SUCCESS_RETURN; bad: DPRINTF(("umodem_attach: BAD -> DYING\n")); sc->sc_dying = 1; USB_ATTACH_ERROR_RETURN; } void umodem_get_caps(dev, cm, acm) usbd_device_handle dev; int *cm, *acm; { usb_cdc_cm_descriptor_t *cmd; usb_cdc_acm_descriptor_t *cad; *cm = *acm = 0; cmd = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); if (!cmd) { DPRINTF(("umodem_get_desc: no CM desc\n")); return; } *cm = cmd->bmCapabilities; cad = umodem_get_desc(dev, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM); if (!cad) { DPRINTF(("umodem_get_desc: no ACM desc\n")); return; } *acm = cad->bmCapabilities; } void umodemstart(tp) struct tty *tp; { struct umodem_softc *sc; struct cblock *cbp; int s; u_char *data; int cnt; USB_GET_SC(umodem, UMODEMUNIT(tp->t_dev), sc); if (sc->sc_dying) return; s = spltty(); if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); DPRINTFN(4,("umodemstart: stopped\n")); goto out; } #if defined(__NetBSD__) || defined(__OpenBSD__) if (tp->t_outq.c_cc <= tp->t_lowat) { if (ISSET(tp->t_state, TS_ASLEEP)) { CLR(tp->t_state, TS_ASLEEP); wakeup(&tp->t_outq); } selwakeup(&tp->t_wsel); if (tp->t_outq.c_cc == 0) goto out; } #elif defined(__FreeBSD__) if (tp->t_outq.c_cc <= tp->t_olowat) { if (ISSET(tp->t_state, TS_SO_OLOWAT)) { CLR(tp->t_state, TS_SO_OLOWAT); wakeup(TSA_OLOWAT(tp)); } selwakeup(&tp->t_wsel); if (tp->t_outq.c_cc == 0) { if (ISSET(tp->t_state, TS_BUSY | TS_SO_OCOMPLETE) == TS_SO_OCOMPLETE && tp->t_outq.c_cc == 0) { CLR(tp->t_state, TS_SO_OCOMPLETE); wakeup(TSA_OCOMPLETE(tp)); } goto out; } } #endif /* Grab the first contiguous region of buffer space. */ data = tp->t_outq.c_cf; #if defined(__NetBSD__) || defined(__OpenBSD__) cnt = ndqb(&tp->t_outq, 0); #elif defined(__FreeBSD__) cbp = (struct cblock *) ((intptr_t) tp->t_outq.c_cf & ~CROUND); cnt = min((char *) (cbp+1) - tp->t_outq.c_cf, tp->t_outq.c_cc); #endif if (cnt == 0) { DPRINTF(("umodemstart: cnt==0\n")); splx(s); return; } SET(tp->t_state, TS_BUSY); DPRINTFN(4,("umodemstart: %d chars\n", cnt)); /* XXX what can we do on error? */ usbd_setup_xfer(sc->sc_oxfer, sc->sc_bulkout_pipe, (usbd_private_handle)sc, data, cnt, 0, USBD_NO_TIMEOUT, umodemwritecb); (void)usbd_transfer(sc->sc_oxfer); ttwwakeup(tp); out: splx(s); } void umodemwritecb(xfer, priv, status) usbd_xfer_handle xfer; usbd_private_handle priv; usbd_status status; { struct umodem_softc *sc = (struct umodem_softc *)priv; struct tty *tp = sc->sc_tty; u_int32_t cc; int s; DPRINTFN(5,("umodemwritecb: status=%d\n", status)); if (status == USBD_CANCELLED) return; if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("umodemwritecb: status=%d\n", status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe); /* XXX we should restart after some delay. */ return; } usbd_get_xfer_status(xfer, 0, 0, &cc, 0); DPRINTFN(5,("umodemwritecb: cc=%d\n", cc)); s = spltty(); CLR(tp->t_state, TS_BUSY); if (ISSET(tp->t_state, TS_FLUSH)) CLR(tp->t_state, TS_FLUSH); else ndflush(&tp->t_outq, cc); (*linesw[tp->t_line].l_start)(tp); splx(s); } int umodemparam(tp, t) struct tty *tp; struct termios *t; { struct umodem_softc *sc; usb_cdc_line_state_t ls; USB_GET_SC(umodem, UMODEMUNIT(tp->t_dev), sc); if (sc->sc_dying) return (EIO); /* Check requested parameters. */ if (t->c_ospeed < 0) return (EINVAL); if (t->c_ispeed && t->c_ispeed != t->c_ospeed) return (EINVAL); /* * If there were no changes, don't do anything. This avoids dropping * input and improves performance when all we did was frob things like * VMIN and VTIME. */ if (tp->t_ospeed == t->c_ospeed && tp->t_cflag == t->c_cflag) return (0); /* And copy to tty. */ tp->t_ispeed = 0; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; USETDW(ls.dwDTERate, t->c_ospeed); if (ISSET(t->c_cflag, CSTOPB)) ls.bCharFormat = UCDC_STOP_BIT_2; else ls.bCharFormat = UCDC_STOP_BIT_1; if (ISSET(t->c_cflag, PARENB)) { if (ISSET(t->c_cflag, PARODD)) ls.bParityType = UCDC_PARITY_ODD; else ls.bParityType = UCDC_PARITY_EVEN; } else ls.bParityType = UCDC_PARITY_NONE; switch (ISSET(t->c_cflag, CSIZE)) { case CS5: ls.bDataBits = 5; break; case CS6: ls.bDataBits = 6; break; case CS7: ls.bDataBits = 7; break; case CS8: ls.bDataBits = 8; break; } /* XXX what can we if it fails? */ (void)umodem_set_line_coding(sc, &ls); /* * Update the tty layer's idea of the carrier bit, in case we changed * CLOCAL or MDMBUF. We don't hang up here; we only do that by * explicit request. */ (void) (*linesw[tp->t_line].l_modem)(tp, 1 /* XXX carrier */ ); return (0); } int umodemopen(dev, flag, mode, p) dev_t dev; int flag, mode; usb_proc_ptr p; { int unit = UMODEMUNIT(dev); struct umodem_softc *sc; usbd_status err; struct tty *tp; int s; int error; USB_GET_SC_OPEN(umodem, unit, sc); if (sc->sc_dying) return (EIO); #if defined(__NetBSD__) || defined(__OpenBBSD__) if (ISSET(sc->sc_dev.dv_flags, DVF_ACTIVE) == 0) return (ENXIO); #endif tp = sc->sc_tty; DPRINTF(("%s: umodemopen: tp=%p\n", USBDEVNAME(sc->sc_dev), tp)); if (ISSET(tp->t_state, TS_ISOPEN) && ISSET(tp->t_state, TS_XCLUDE) && suser_td(p)) return (EBUSY); /* * Do the following iff this is a first open. */ s = spltty(); while (sc->sc_opening) tsleep(&sc->sc_opening, PRIBIO, "umdmop", 0); sc->sc_opening = 1; #if defined(__NetBSD__) || defined(__OpenBSD__) if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { #elif defined(__FreeBSD__) if (!ISSET(tp->t_state, TS_ISOPEN)) { #endif struct termios t; tp->t_dev = dev; /* * Initialize the termios status to the defaults. Add in the * sticky bits from TIOCSFLAGS. */ t.c_ispeed = 0; t.c_ospeed = TTYDEF_SPEED; t.c_cflag = TTYDEF_CFLAG; /* Make sure umodemparam() will do something. */ tp->t_ospeed = 0; (void) umodemparam(tp, &t); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_lflag = TTYDEF_LFLAG; ttychars(tp); ttsetwater(tp); /* * Turn on DTR. We must always do this, even if carrier is not * present, because otherwise we'd have to use TIOCSDTR * immediately after setting CLOCAL, which applications do not * expect. We always assert DTR while the device is open * unless explicitly requested to deassert it. */ umodem_modem(sc, 1); DPRINTF(("umodemopen: open pipes\n")); /* Open the bulk pipes */ err = usbd_open_pipe(sc->sc_data_iface, sc->sc_bulkin_no, 0, &sc->sc_bulkin_pipe); if (err) { DPRINTF(("%s: cannot open bulk out pipe (addr %d)\n", USBDEVNAME(sc->sc_dev), sc->sc_bulkin_no)); return (EIO); } err = usbd_open_pipe(sc->sc_data_iface, sc->sc_bulkout_no, USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe); if (err) { DPRINTF(("%s: cannot open bulk in pipe (addr %d)\n", USBDEVNAME(sc->sc_dev), sc->sc_bulkout_no)); usbd_close_pipe(sc->sc_bulkin_pipe); return (EIO); } /* Allocate a request and an input buffer and start reading. */ sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev); if (sc->sc_ixfer == 0) { usbd_close_pipe(sc->sc_bulkin_pipe); usbd_close_pipe(sc->sc_bulkout_pipe); return (ENOMEM); } sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev); if (sc->sc_oxfer == 0) { usbd_close_pipe(sc->sc_bulkin_pipe); usbd_close_pipe(sc->sc_bulkout_pipe); usbd_free_xfer(sc->sc_ixfer); return (ENOMEM); } sc->sc_ibuf = malloc(UMODEMIBUFSIZE, M_USBDEV, M_WAITOK); umodemstartread(sc); } sc->sc_opening = 0; wakeup(&sc->sc_opening); splx(s); #if defined(__NetBSD__) || defined(__OpenBSD__) error = ttyopen(tp, UMODEMDIALOUT(dev), ISSET(flag, O_NONBLOCK)); if (error) goto bad; #elif defined(__FreeBSD__) error = ttyopen(dev, tp); if (error) goto bad; #endif error = (*linesw[tp->t_line].l_open)(dev, tp); if (error) goto bad; return (0); bad: #if defined(__NetBSD__) || defined(__OpenBSD__) if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { #elif defined(__FreeBSD__) if (!ISSET(tp->t_state, TS_ISOPEN)) { #endif /* * We failed to open the device, and nobody else had it opened. * Clean up the state as appropriate. */ umodem_cleanup(sc); } return (error); } usbd_status umodemstartread(sc) struct umodem_softc *sc; { usbd_status err; DPRINTFN(5,("umodemstartread: start\n")); usbd_setup_xfer(sc->sc_ixfer, sc->sc_bulkin_pipe, (usbd_private_handle)sc, sc->sc_ibuf, UMODEMIBUFSIZE, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, umodemreadcb); err = usbd_transfer(sc->sc_ixfer); if (err != USBD_IN_PROGRESS) return (err); return (USBD_NORMAL_COMPLETION); } void umodemreadcb(xfer, p, status) usbd_xfer_handle xfer; usbd_private_handle p; usbd_status status; { struct umodem_softc *sc = (struct umodem_softc *)p; struct tty *tp = sc->sc_tty; int (*rint) (int c, struct tty *tp) = linesw[tp->t_line].l_rint; usbd_status err; u_int32_t cc; u_char *cp; int s; if (status == USBD_CANCELLED) return; if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("umodemreadcb: status=%d\n", status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_bulkin_pipe); /* XXX we should restart after some delay. */ return; } usbd_get_xfer_status(xfer, 0, (void **)&cp, &cc, 0); DPRINTFN(5,("umodemreadcb: got %d chars, tp=%p\n", cc, tp)); s = spltty(); /* Give characters to tty layer. */ while (cc-- > 0) { DPRINTFN(7,("umodemreadcb: char=0x%02x\n", *cp)); if ((*rint)(*cp++, tp) == -1) { /* XXX what should we do? */ break; } } splx(s); err = umodemstartread(sc); if (err) { printf("%s: read start failed\n", USBDEVNAME(sc->sc_dev)); /* XXX what should we dow now? */ } } int umodemclose(dev, flag, mode, p) dev_t dev; int flag, mode; usb_proc_ptr p; { struct umodem_softc *sc; struct tty *tp; int s; USB_GET_SC(umodem, UMODEMUNIT(dev), sc); tp = sc->sc_tty; DPRINTF(("%s: umodemclose sc_tty=%p\n", USBDEVNAME(sc->sc_dev), tp)); if (!ISSET(tp->t_state, TS_ISOPEN)) return (0); DPRINTF(("%s: umodemclose lclose(%p,%d)\n", USBDEVNAME(sc->sc_dev), tp,flag)); s=spltty(); DPRINTF(("%s: umodemclose lclose=%p\n", USBDEVNAME(sc->sc_dev), linesw[tp->t_line].l_close)); (*linesw[tp->t_line].l_close)(tp, flag); DPRINTF(("%s: umodemclose ttyclose(%p)\n", USBDEVNAME(sc->sc_dev), tp)); ttyclose(tp); splx(s); DPRINTF(("%s: umodemclose sc->sc_dying=%d\n", USBDEVNAME(sc->sc_dev), sc->sc_dying)); if (sc->sc_dying) return (0); DPRINTF(("%s: umodemclose tp->t_state=%d\n", USBDEVNAME(sc->sc_dev), tp->t_state)); #if defined(__NetBSD__) || defined(__OpenBSD__) if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { #elif defined(__FreeBSD__) if (!ISSET(tp->t_state, TS_ISOPEN)) { #endif /* * Although we got a last close, the device may still be in * use; e.g. if this was the dialout node, and there are still * processes waiting for carrier on the non-dialout node. */ DPRINTF(("%s: umodemclose umodem_cleanup(%p)\n", USBDEVNAME(sc->sc_dev), sc)); umodem_cleanup(sc); } DPRINTF(("%s: umodemclose return\n", USBDEVNAME(sc->sc_dev))); return (0); } void umodem_cleanup(sc) struct umodem_softc *sc; { umodem_shutdown(sc); DPRINTFN(5, ("%s: umodem_cleanup: closing pipes\n", USBDEVNAME(sc->sc_dev))); usbd_abort_pipe(sc->sc_bulkin_pipe); usbd_close_pipe(sc->sc_bulkin_pipe); usbd_abort_pipe(sc->sc_bulkout_pipe); usbd_close_pipe(sc->sc_bulkout_pipe); usbd_free_xfer(sc->sc_ixfer); usbd_free_xfer(sc->sc_oxfer); free(sc->sc_ibuf, M_USBDEV); } int umodemread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct umodem_softc *sc; struct tty *tp; USB_GET_SC(umodem, UMODEMUNIT(dev), sc); tp = sc->sc_tty; if (sc->sc_dying) return (EIO); return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } int umodemwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct umodem_softc *sc; struct tty *tp; USB_GET_SC(umodem, UMODEMUNIT(dev), sc); tp = sc->sc_tty; if (sc->sc_dying) return (EIO); return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } void umodemstop(tp, flag) struct tty *tp; int flag; { struct umodem_softc *sc; int s; USB_GET_SC(umodem, UMODEMUNIT(tp->t_dev), sc); DPRINTF(("umodemstop: %d\n", flag)); s = spltty(); if (ISSET(tp->t_state, TS_BUSY)) { DPRINTF(("umodemstop: XXX\n")); /* XXX do what? */ if (!ISSET(tp->t_state, TS_TTSTOP)) SET(tp->t_state, TS_FLUSH); } splx(s); } struct tty * umodemtty(dev) dev_t dev; { struct umodem_softc *sc; struct tty *tp; USB_GET_SC(umodem, UMODEMUNIT(dev), sc); tp = sc->sc_tty; return (tp); } int umodemioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; usb_proc_ptr p; { struct umodem_softc *sc; struct tty *tp; int error; int s; int bits; USB_GET_SC(umodem, UMODEMUNIT(dev), sc); tp = sc->sc_tty; if (sc->sc_dying) return (EIO); DPRINTF(("umodemioctl: cmd=0x%08lx\n", cmd)); error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return (error); #if defined(__NetBSD__) || defined(__OpenBSD__) error = ttioctl(tp, cmd, data, flag, p); #elif defined(__FreeBSD__) error = ttioctl(tp, cmd, data, flag); #endif if (error >= 0) return (error); DPRINTF(("umodemioctl: our cmd=0x%08lx\n", cmd)); s = spltty(); switch (cmd) { case TIOCSBRK: umodem_break(sc, 1); break; case TIOCCBRK: umodem_break(sc, 0); break; case TIOCSDTR: umodem_modem(sc, 1); break; case TIOCCDTR: umodem_modem(sc, 0); break; case TIOCMGET: bits = TIOCM_LE; if(sc->sc_dtr) bits |= TIOCM_DTR; *(int *)data = bits; break; case TIOCMSET: break; case USB_GET_CM_OVER_DATA: *(int *)data = sc->sc_cm_over_data; break; case USB_SET_CM_OVER_DATA: if (*(int *)data != sc->sc_cm_over_data) { /* XXX change it */ } break; default: DPRINTF(("umodemioctl: unknown\n")); error = ENOTTY; splx(s); return (error); } splx(s); return(0); } void umodem_shutdown(sc) struct umodem_softc *sc; { struct tty *tp = sc->sc_tty; DPRINTF(("%s: umodem_shutdown\n", USBDEVNAME(sc->sc_dev))); /* * Hang up if necessary. Wait a bit, so the other side has time to * notice even if we immediately open the port again. */ if (ISSET(tp->t_cflag, HUPCL)) { umodem_modem(sc, 0); #if defined(__NetBSD__) || defined(__OpenBSD__) (void) tsleep(sc, TTIPRI, ttclos, hz); #elif defined(__FreeBSD__) (void) tsleep(sc, TTIPRI, "umdmsd", hz); #endif } } void umodem_modem(sc, onoff) struct umodem_softc *sc; int onoff; { usb_device_request_t req; DPRINTF(("%s: umodem_modem: onoff=%d\n", USBDEVNAME(sc->sc_dev),onoff)); if (sc->sc_dtr == onoff) return; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_CONTROL_LINE_STATE; USETW(req.wValue, onoff ? UCDC_LINE_DTR : 0); USETW(req.wIndex, sc->sc_ctl_iface_no); USETW(req.wLength, 0); (void)usbd_do_request(sc->sc_udev, &req, 0); sc->sc_dtr = onoff; } void umodem_break(sc, onoff) struct umodem_softc *sc; int onoff; { usb_device_request_t req; DPRINTF(("%s: umodem_break: onoff=%d\n", USBDEVNAME(sc->sc_dev),onoff)); if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK)) return; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SEND_BREAK; USETW(req.wValue, onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF); USETW(req.wIndex, sc->sc_ctl_iface_no); USETW(req.wLength, 0); (void)usbd_do_request(sc->sc_udev, &req, 0); } void * umodem_get_desc(dev, type, subtype) usbd_device_handle dev; int type; int subtype; { usb_descriptor_t *desc; usb_config_descriptor_t *cd = usbd_get_config_descriptor(dev); uByte *p = (uByte *)cd; uByte *end = p + UGETW(cd->wTotalLength); while (p < end) { desc = (usb_descriptor_t *)p; if (desc->bDescriptorType == type && desc->bDescriptorSubtype == subtype) return (desc); p += desc->bLength; } return (0); } usbd_status umodem_set_comm_feature(sc, feature, state) struct umodem_softc *sc; int feature; int state; { usb_device_request_t req; usbd_status err; usb_cdc_abstract_state_t ast; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_COMM_FEATURE; USETW(req.wValue, feature); USETW(req.wIndex, sc->sc_ctl_iface_no); USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH); USETW(ast.wState, state); err = usbd_do_request(sc->sc_udev, &req, &ast); if (err) { DPRINTF(("%s: umodem_set_comm_feat: feature=%d failed, err=%d\n", USBDEVNAME(sc->sc_dev), feature, err)); return (err); } return (USBD_NORMAL_COMPLETION); } usbd_status umodem_set_line_coding(sc, state) struct umodem_softc *sc; usb_cdc_line_state_t *state; { usb_device_request_t req; usbd_status err; DPRINTF(("%s: umodem_set_line_cod: rate=%d fmt=%d parity=%d bits=%d\n", USBDEVNAME(sc->sc_dev), UGETDW(state->dwDTERate), state->bCharFormat, state->bParityType, state->bDataBits)); if (bcmp(state, &sc->sc_line_state, UCDC_LINE_STATE_LENGTH) == 0) { DPRINTF(("%s: umodem_set_line_coding: already set\n", USBDEVNAME(sc->sc_dev))); return (USBD_NORMAL_COMPLETION); } req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_LINE_CODING; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_ctl_iface_no); USETW(req.wLength, UCDC_LINE_STATE_LENGTH); err = usbd_do_request(sc->sc_udev, &req, state); if (err) { DPRINTF(("%s: umodem_set_line_coding: failed, err=%d\n", USBDEVNAME(sc->sc_dev), err)); return (err); } sc->sc_line_state = *state; return (USBD_NORMAL_COMPLETION); } #if defined(__NetBSD__) || defined(__OpenBSD__) int umodem_activate(self, act) device_ptr_t self; enum devact act; { struct umodem_softc *sc = (struct umodem_softc *)self; switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); break; case DVACT_DEACTIVATE: sc->sc_dying = 1; break; } return (0); } #endif USB_DETACH(umodem) { USB_DETACH_START(umodem, sc); #if defined(__NetBSD__) || defined(__OpenBSD__) int maj, mn; DPRINTF(("umodem_detach: sc=%p flags=%d tp=%p\n", sc, flags, sc->sc_tty)); #elif defined(__FreeBSD__) DPRINTF(("umodem_detach: sc=%p flags=%d, tp=%p\n", sc, 0, sc->sc_tty)); #endif sc->sc_dying = 1; #ifdef DIAGNOSTIC if (sc->sc_tty == 0) { DPRINTF(("umodem_detach: no tty\n")); return (0); } #endif /* use refernce count? XXX */ #if defined(__NetBSD__) || defined(__OpenBSD__) /* locate the major number */ for (maj = 0; maj < nchrdev; maj++) if (cdevsw[maj].d_open == umodemopen) break; /* Nuke the vnodes for any open instances. */ mn = self->dv_unit; vdevgone(maj, mn, mn, VCHR); vdevgone(maj, mn, mn | UMODEMDIALOUT_MASK, VCHR); vdevgone(maj, mn, mn | UMODEMCALLUNIT_MASK, VCHR); #elif defined(__FreeBSD__) /* XXX not yet implemented */ #endif #if defined(__FreeBSD__) destroy_dev(sc->dev); #endif /* Detach and free the tty. */ #if defined(__NetBSD__) || defined(__OpenBSD__) tty_detach(sc->sc_tty); ttyfree(sc->sc_tty); sc->sc_tty = 0; #endif return (0); } #if defined(__FreeBSD__) DRIVER_MODULE(umodem, uhub, umodem_driver, umodem_devclass, usbd_driver_load,0); #endif diff --git a/sys/dev/usb/ums.c b/sys/dev/usb/ums.c index 7aeee20c580f..f42e7b5bfc77 100644 --- a/sys/dev/usb/ums.c +++ b/sys/dev/usb/ums.c @@ -1,825 +1,828 @@ /* $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. */ /* * HID spec: http://www.usb.org/developers/data/usbhid10.pdf */ #include #include #include #include #include #include #include #include #include #include #if __FreeBSD_version >= 500014 #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #ifdef UMS_DEBUG #define DPRINTF(x) if (umsdebug) logprintf x #define DPRINTFN(n,x) if (umsdebug>(n)) logprintf x int umsdebug = 1; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define UMSUNIT(s) (minor(s)&0x1f) #define MS_TO_TICKS(ms) ((ms) * hz / 1000) #define QUEUE_BUFSIZE 400 /* MUST be divisible by 5 _and_ 8 */ struct ums_softc { device_t sc_dev; /* base device */ usbd_interface_handle sc_iface; /* interface */ usbd_pipe_handle sc_intrpipe; /* interrupt pipe */ int sc_ep_addr; u_char *sc_ibuf; u_int8_t sc_iid; int sc_isize; struct hid_location sc_loc_x, sc_loc_y, sc_loc_z; struct hid_location *sc_loc_btn; struct callout_handle timeout_handle; /* for spurious button ups */ int sc_enabled; int sc_disconnected; /* device is gone */ int flags; /* device configuration */ #define UMS_Z 0x01 /* z direction available */ #define UMS_SPUR_BUT_UP 0x02 /* spurious button up events */ int nbuttons; #define MAX_BUTTONS 7 /* chosen because sc_buttons is u_char */ u_char qbuf[QUEUE_BUFSIZE]; /* must be divisable by 3&4 */ u_char dummy[100]; /* XXX just for safety and for now */ int qcount, qhead, qtail; mousehw_t hw; mousemode_t mode; mousestatus_t status; int state; # define UMS_ASLEEP 0x01 /* readFromDevice is waiting */ # define UMS_SELECT 0x02 /* select is waiting */ struct selinfo rsel; /* process waiting in select */ dev_t dev; /* specfs */ }; #define MOUSE_FLAGS_MASK (HIO_CONST|HIO_RELATIVE) #define MOUSE_FLAGS (HIO_RELATIVE) Static void ums_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status); Static void ums_add_to_queue(struct ums_softc *sc, int dx, int dy, int dz, int buttons); Static void ums_add_to_queue_timeout(void *priv); Static int ums_enable(void *); Static void ums_disable(void *); Static d_open_t ums_open; Static d_close_t ums_close; Static d_read_t ums_read; Static d_ioctl_t ums_ioctl; Static d_poll_t ums_poll; #define UMS_CDEV_MAJOR 111 Static struct cdevsw ums_cdevsw = { /* open */ ums_open, /* close */ ums_close, /* read */ ums_read, /* write */ nowrite, /* ioctl */ ums_ioctl, /* poll */ ums_poll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "ums", /* maj */ UMS_CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, +#if !defined(__FreeBSD__) || (__FreeBSD__ < 5) + /* bmaj */ -1 +#endif }; USB_DECLARE_DRIVER(ums); USB_MATCH(ums) { USB_MATCH_START(ums, uaa); usb_interface_descriptor_t *id; int size, ret; void *desc; usbd_status err; if (!uaa->iface) return (UMATCH_NONE); id = usbd_get_interface_descriptor(uaa->iface); if (!id || id->bInterfaceClass != UICLASS_HID) return (UMATCH_NONE); err = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP); if (err) return (UMATCH_NONE); if (hid_is_collection(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) ret = UMATCH_IFACECLASS; else ret = UMATCH_NONE; free(desc, M_TEMP); return (ret); } USB_ATTACH(ums) { USB_ATTACH_START(ums, sc, uaa); usbd_interface_handle iface = uaa->iface; usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; int size; void *desc; usbd_status err; char devinfo[1024]; u_int32_t flags; int i; struct hid_location loc_btn; sc->sc_disconnected = 1; sc->sc_iface = iface; id = usbd_get_interface_descriptor(iface); usbd_devinfo(uaa->device, 0, devinfo); USB_ATTACH_SETUP; printf("%s: %s, iclass %d/%d\n", USBDEVNAME(sc->sc_dev), devinfo, id->bInterfaceClass, id->bInterfaceSubClass); ed = usbd_interface2endpoint_descriptor(iface, 0); if (!ed) { printf("%s: could not read endpoint descriptor\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } DPRINTFN(10,("ums_attach: bLength=%d bDescriptorType=%d " "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d" " bInterval=%d\n", ed->bLength, ed->bDescriptorType, UE_GET_ADDR(ed->bEndpointAddress), UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN ? "in":"out", UE_GET_XFERTYPE(ed->bmAttributes), UGETW(ed->wMaxPacketSize), ed->bInterval)); if (UE_GET_DIR(ed->bEndpointAddress) != UE_DIR_IN || UE_GET_XFERTYPE(ed->bmAttributes) != UE_INTERRUPT) { printf("%s: unexpected endpoint\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } err = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_TEMP); if (err) USB_ATTACH_ERROR_RETURN; if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X), hid_input, &sc->sc_loc_x, &flags)) { printf("%s: mouse has no X report\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { printf("%s: X report 0x%04x not supported\n", USBDEVNAME(sc->sc_dev), flags); USB_ATTACH_ERROR_RETURN; } if (!hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y), hid_input, &sc->sc_loc_y, &flags)) { printf("%s: mouse has no Y report\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { printf("%s: Y report 0x%04x not supported\n", USBDEVNAME(sc->sc_dev), flags); USB_ATTACH_ERROR_RETURN; } /* try to guess the Z activator: first check Z, then WHEEL */ if (hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Z), hid_input, &sc->sc_loc_z, &flags) || hid_locate(desc, size, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_WHEEL), hid_input, &sc->sc_loc_z, &flags)) { if ((flags & MOUSE_FLAGS_MASK) != MOUSE_FLAGS) { sc->sc_loc_z.size = 0; /* Bad Z coord, ignore it */ } else { sc->flags |= UMS_Z; } } /* figure out the number of buttons */ for (i = 1; i <= MAX_BUTTONS; i++) if (!hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), hid_input, &loc_btn, 0)) break; sc->nbuttons = i - 1; sc->sc_loc_btn = malloc(sizeof(struct hid_location)*sc->nbuttons, M_USBDEV, M_NOWAIT); if (!sc->sc_loc_btn) { printf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } printf("%s: %d buttons%s\n", USBDEVNAME(sc->sc_dev), sc->nbuttons, sc->flags & UMS_Z? " and Z dir." : ""); for (i = 1; i <= sc->nbuttons; i++) hid_locate(desc, size, HID_USAGE2(HUP_BUTTON, i), hid_input, &sc->sc_loc_btn[i-1], 0); sc->sc_isize = hid_report_size(desc, size, hid_input, &sc->sc_iid); sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_NOWAIT); if (!sc->sc_ibuf) { printf("%s: no memory\n", USBDEVNAME(sc->sc_dev)); free(sc->sc_loc_btn, M_USB); USB_ATTACH_ERROR_RETURN; } sc->sc_ep_addr = ed->bEndpointAddress; sc->sc_disconnected = 0; free(desc, M_TEMP); #ifdef UMS_DEBUG DPRINTF(("ums_attach: sc=%p\n", sc)); DPRINTF(("ums_attach: X\t%d/%d\n", sc->sc_loc_x.pos, sc->sc_loc_x.size)); DPRINTF(("ums_attach: Y\t%d/%d\n", sc->sc_loc_y.pos, sc->sc_loc_y.size)); if (sc->flags & UMS_Z) DPRINTF(("ums_attach: Z\t%d/%d\n", sc->sc_loc_z.pos, sc->sc_loc_z.size)); for (i = 1; i <= sc->nbuttons; i++) { DPRINTF(("ums_attach: B%d\t%d/%d\n", i, sc->sc_loc_btn[i-1].pos,sc->sc_loc_btn[i-1].size)); } DPRINTF(("ums_attach: size=%d, id=%d\n", sc->sc_isize, sc->sc_iid)); #endif if (sc->nbuttons > MOUSE_MSC_MAXBUTTON) sc->hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->hw.buttons = sc->nbuttons; sc->hw.iftype = MOUSE_IF_USB; sc->hw.type = MOUSE_MOUSE; sc->hw.model = MOUSE_MODEL_GENERIC; sc->hw.hwid = 0; sc->mode.protocol = MOUSE_PROTO_MSC; sc->mode.rate = -1; sc->mode.resolution = MOUSE_RES_UNKNOWN; sc->mode.accelfactor = 0; sc->mode.level = 0; sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->mode.syncmask[1] = MOUSE_MSC_SYNC; sc->status.flags = 0; sc->status.button = sc->status.obutton = 0; sc->status.dx = sc->status.dy = sc->status.dz = 0; sc->rsel.si_flags = 0; sc->rsel.si_pid = 0; sc->dev = make_dev(&ums_cdevsw, device_get_unit(self), UID_ROOT, GID_OPERATOR, 0644, "ums%d", device_get_unit(self)); if (usbd_get_quirks(uaa->device)->uq_flags & UQ_SPUR_BUT_UP) { DPRINTF(("%s: Spurious button up events\n", USBDEVNAME(sc->sc_dev))); sc->flags |= UMS_SPUR_BUT_UP; } USB_ATTACH_SUCCESS_RETURN; } Static int ums_detach(device_t self) { struct ums_softc *sc = device_get_softc(self); struct vnode *vp; if (sc->sc_enabled) ums_disable(sc); DPRINTF(("%s: disconnected\n", USBDEVNAME(self))); free(sc->sc_loc_btn, M_USB); free(sc->sc_ibuf, M_USB); vp = SLIST_FIRST(&sc->dev->si_hlist); if (vp) VOP_REVOKE(vp, REVOKEALL); /* someone waiting for data */ /* * XXX If we wakeup the process here, the device will be gone by * the time the process gets a chance to notice. *_close and friends * should be fixed to handle this case. * Or we should do a delayed detach for this. * Does this delay now force tsleep to exit with an error? */ if (sc->state & UMS_ASLEEP) { sc->state &= ~UMS_ASLEEP; wakeup(sc); } if (sc->state & UMS_SELECT) { sc->state &= ~UMS_SELECT; selwakeup(&sc->rsel); } destroy_dev(sc->dev); return 0; } void ums_intr(xfer, addr, status) usbd_xfer_handle xfer; usbd_private_handle addr; usbd_status status; { struct ums_softc *sc = addr; u_char *ibuf; int dx, dy, dz; u_char buttons = 0; int i; #define UMS_BUT(i) ((i) < 3 ? (((i) + 2) % 3) : (i)) DPRINTFN(5, ("ums_intr: sc=%p status=%d\n", sc, status)); DPRINTFN(5, ("ums_intr: data = %02x %02x %02x\n", sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2])); if (status == USBD_CANCELLED) return; if (status != USBD_NORMAL_COMPLETION) { DPRINTF(("ums_intr: status=%d\n", status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_intrpipe); return; } ibuf = sc->sc_ibuf; if (sc->sc_iid) { if (*ibuf++ != sc->sc_iid) return; } dx = hid_get_data(ibuf, &sc->sc_loc_x); dy = -hid_get_data(ibuf, &sc->sc_loc_y); dz = -hid_get_data(ibuf, &sc->sc_loc_z); for (i = 0; i < sc->nbuttons; i++) if (hid_get_data(ibuf, &sc->sc_loc_btn[i])) buttons |= (1 << UMS_BUT(i)); if (dx || dy || dz || (sc->flags & UMS_Z) || buttons != sc->status.button) { DPRINTFN(5, ("ums_intr: x:%d y:%d z:%d buttons:0x%x\n", dx, dy, dz, buttons)); sc->status.button = buttons; sc->status.dx += dx; sc->status.dy += dy; sc->status.dz += dz; /* Discard data in case of full buffer */ if (sc->qcount == sizeof(sc->qbuf)) { DPRINTF(("Buffer full, discarded packet")); return; } /* * The Qtronix keyboard has a built in PS/2 port for a mouse. * The firmware once in a while posts a spurious button up * event. This event we ignore by doing a timeout for 50 msecs. * If we receive dx=dy=dz=buttons=0 before we add the event to * the queue. * In any other case we delete the timeout event. */ if (sc->flags & UMS_SPUR_BUT_UP && dx == 0 && dy == 0 && dz == 0 && buttons == 0) { usb_timeout(ums_add_to_queue_timeout, (void *) sc, MS_TO_TICKS(50 /*msecs*/), sc->timeout_handle); } else { usb_untimeout(ums_add_to_queue_timeout, (void *) sc, sc->timeout_handle); ums_add_to_queue(sc, dx, dy, dz, buttons); } } } Static void ums_add_to_queue_timeout(void *priv) { struct ums_softc *sc = priv; int s; s = splusb(); ums_add_to_queue(sc, 0, 0, 0, 0); splx(s); } Static void ums_add_to_queue(struct ums_softc *sc, int dx, int dy, int dz, int buttons) { /* Discard data in case of full buffer */ if (sc->qhead+sc->mode.packetsize > sizeof(sc->qbuf)) { DPRINTF(("Buffer full, discarded packet")); return; } if (dx > 254) dx = 254; if (dx < -256) dx = -256; if (dy > 254) dy = 254; if (dy < -256) dy = -256; if (dz > 126) dz = 126; if (dz < -128) dz = -128; sc->qbuf[sc->qhead] = sc->mode.syncmask[1]; sc->qbuf[sc->qhead] |= ~buttons & MOUSE_MSC_BUTTONS; sc->qbuf[sc->qhead+1] = dx >> 1; sc->qbuf[sc->qhead+2] = dy >> 1; sc->qbuf[sc->qhead+3] = dx - (dx >> 1); sc->qbuf[sc->qhead+4] = dy - (dy >> 1); if (sc->mode.level == 1) { sc->qbuf[sc->qhead+5] = dz >> 1; sc->qbuf[sc->qhead+6] = dz - (dz >> 1); sc->qbuf[sc->qhead+7] = ((~buttons >> 3) & MOUSE_SYS_EXTBUTTONS); } sc->qhead += sc->mode.packetsize; sc->qcount += sc->mode.packetsize; /* wrap round at end of buffer */ if (sc->qhead >= sizeof(sc->qbuf)) sc->qhead = 0; /* someone waiting for data */ if (sc->state & UMS_ASLEEP) { sc->state &= ~UMS_ASLEEP; wakeup(sc); } if (sc->state & UMS_SELECT) { sc->state &= ~UMS_SELECT; selwakeup(&sc->rsel); } } Static int ums_enable(v) void *v; { struct ums_softc *sc = v; usbd_status err; if (sc->sc_enabled) return EBUSY; sc->sc_enabled = 1; sc->qcount = 0; sc->qhead = sc->qtail = 0; sc->status.flags = 0; sc->status.button = sc->status.obutton = 0; sc->status.dx = sc->status.dy = sc->status.dz = 0; callout_handle_init(&sc->timeout_handle); /* Set up interrupt pipe. */ err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr, USBD_SHORT_XFER_OK, &sc->sc_intrpipe, sc, sc->sc_ibuf, sc->sc_isize, ums_intr, USBD_DEFAULT_INTERVAL); if (err) { DPRINTF(("ums_enable: usbd_open_pipe_intr failed, error=%d\n", err)); sc->sc_enabled = 0; return (EIO); } return (0); } Static void ums_disable(priv) void *priv; { struct ums_softc *sc = priv; usb_untimeout(ums_add_to_queue_timeout, sc, sc->timeout_handle); /* Disable interrupts. */ usbd_abort_pipe(sc->sc_intrpipe); usbd_close_pipe(sc->sc_intrpipe); sc->sc_enabled = 0; if (sc->qcount != 0) DPRINTF(("Discarded %d bytes in queue\n", sc->qcount)); } Static int ums_open(dev_t dev, int flag, int fmt, usb_proc_ptr p) { struct ums_softc *sc; USB_GET_SC_OPEN(ums, UMSUNIT(dev), sc); return ums_enable(sc); } Static int ums_close(dev_t dev, int flag, int fmt, usb_proc_ptr p) { struct ums_softc *sc; USB_GET_SC(ums, UMSUNIT(dev), sc); if (!sc) return 0; if (sc->sc_enabled) ums_disable(sc); return 0; } Static int ums_read(dev_t dev, struct uio *uio, int flag) { struct ums_softc *sc; int s; char buf[sizeof(sc->qbuf)]; int l = 0; int error; USB_GET_SC(ums, UMSUNIT(dev), sc); s = splusb(); if (!sc) { splx(s); return EIO; } while (sc->qcount == 0 ) { if (flag & IO_NDELAY) { /* non-blocking I/O */ splx(s); return EWOULDBLOCK; } sc->state |= UMS_ASLEEP; /* blocking I/O */ error = tsleep(sc, PZERO | PCATCH, "umsrea", 0); if (error) { splx(s); return error; } else if (!sc->sc_enabled) { splx(s); return EINTR; } /* check whether the device is still there */ sc = devclass_get_softc(ums_devclass, UMSUNIT(dev)); if (!sc) { splx(s); return EIO; } } /* * XXX we could optimise the use of splx/splusb somewhat. The writer * process only extends qcount and qtail. We could copy them and use the copies * to do the copying out of the queue. */ while ((sc->qcount > 0) && (uio->uio_resid > 0)) { l = (sc->qcount < uio->uio_resid? sc->qcount:uio->uio_resid); if (l > sizeof(buf)) l = sizeof(buf); if (l > sizeof(sc->qbuf) - sc->qtail) /* transfer till end of buf */ l = sizeof(sc->qbuf) - sc->qtail; splx(s); uiomove(&sc->qbuf[sc->qtail], l, uio); s = splusb(); if ( sc->qcount - l < 0 ) { DPRINTF(("qcount below 0, count=%d l=%d\n", sc->qcount, l)); sc->qcount = l; } sc->qcount -= l; /* remove the bytes from the buffer */ sc->qtail = (sc->qtail + l) % sizeof(sc->qbuf); } splx(s); return 0; } Static int ums_poll(dev_t dev, int events, usb_proc_ptr p) { struct ums_softc *sc; int revents = 0; int s; USB_GET_SC(ums, UMSUNIT(dev), sc); if (!sc) return 0; s = splusb(); if (events & (POLLIN | POLLRDNORM)) { if (sc->qcount) { revents = events & (POLLIN | POLLRDNORM); } else { sc->state |= UMS_SELECT; selrecord(p, &sc->rsel); } } splx(s); return revents; } int ums_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) { struct ums_softc *sc; int error = 0; int s; mousemode_t mode; USB_GET_SC(ums, UMSUNIT(dev), sc); if (!sc) return EIO; switch(cmd) { case MOUSE_GETHWINFO: *(mousehw_t *)addr = sc->hw; break; case MOUSE_GETMODE: *(mousemode_t *)addr = sc->mode; break; case MOUSE_SETMODE: mode = *(mousemode_t *)addr; if (mode.level == -1) /* don't change the current setting */ ; else if ((mode.level < 0) || (mode.level > 1)) return (EINVAL); s = splusb(); sc->mode.level = mode.level; if (sc->mode.level == 0) { if (sc->nbuttons > MOUSE_MSC_MAXBUTTON) sc->hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->hw.buttons = sc->nbuttons; sc->mode.protocol = MOUSE_PROTO_MSC; sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->mode.syncmask[1] = MOUSE_MSC_SYNC; } else if (sc->mode.level == 1) { if (sc->nbuttons > MOUSE_SYS_MAXBUTTON) sc->hw.buttons = MOUSE_SYS_MAXBUTTON; else sc->hw.buttons = sc->nbuttons; sc->mode.protocol = MOUSE_PROTO_SYSMOUSE; sc->mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->mode.syncmask[1] = MOUSE_SYS_SYNC; } bzero(sc->qbuf, sizeof(sc->qbuf)); sc->qhead = sc->qtail = sc->qcount = 0; splx(s); break; case MOUSE_GETLEVEL: *(int *)addr = sc->mode.level; break; case MOUSE_SETLEVEL: if (*(int *)addr < 0 || *(int *)addr > 1) return (EINVAL); s = splusb(); sc->mode.level = *(int *)addr; if (sc->mode.level == 0) { if (sc->nbuttons > MOUSE_MSC_MAXBUTTON) sc->hw.buttons = MOUSE_MSC_MAXBUTTON; else sc->hw.buttons = sc->nbuttons; sc->mode.protocol = MOUSE_PROTO_MSC; sc->mode.packetsize = MOUSE_MSC_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_MSC_SYNCMASK; sc->mode.syncmask[1] = MOUSE_MSC_SYNC; } else if (sc->mode.level == 1) { if (sc->nbuttons > MOUSE_SYS_MAXBUTTON) sc->hw.buttons = MOUSE_SYS_MAXBUTTON; else sc->hw.buttons = sc->nbuttons; sc->mode.protocol = MOUSE_PROTO_SYSMOUSE; sc->mode.packetsize = MOUSE_SYS_PACKETSIZE; sc->mode.syncmask[0] = MOUSE_SYS_SYNCMASK; sc->mode.syncmask[1] = MOUSE_SYS_SYNC; } bzero(sc->qbuf, sizeof(sc->qbuf)); sc->qhead = sc->qtail = sc->qcount = 0; splx(s); break; case MOUSE_GETSTATUS: { mousestatus_t *status = (mousestatus_t *) addr; s = splusb(); *status = sc->status; sc->status.obutton = sc->status.button; sc->status.button = 0; sc->status.dx = sc->status.dy = sc->status.dz = 0; splx(s); if (status->dx || status->dy || status->dz) status->flags |= MOUSE_POSCHANGED; if (status->button != status->obutton) status->flags |= MOUSE_BUTTONSCHANGED; break; } default: error = ENOTTY; } return error; } DRIVER_MODULE(ums, uhub, ums_driver, ums_devclass, usbd_driver_load, 0); diff --git a/sys/dev/usb/usb.c b/sys/dev/usb/usb.c index f7fe5b726796..e54311c8c737 100644 --- a/sys/dev/usb/usb.c +++ b/sys/dev/usb/usb.c @@ -1,778 +1,781 @@ /* $NetBSD: usb.c,v 1.38 2000/02/02 07:33:59 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. */ /* * USB specifications and other documentation can be found at * http://www.usb.org/developers/data/ and * http://www.usb.org/developers/index.html . */ #include #include #include #include #include #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include #elif defined(__FreeBSD__) #include #include #include #include #include #endif #include #include #include #include #if __FreeBSD_version >= 500014 #include #else #include #endif #include #include #include #include #include #define USBUNIT(d) (minor(d)) /* usb_discover device nodes, kthread */ #define USB_DEV_MINOR 255 /* event queue device */ #if defined(__FreeBSD__) MALLOC_DEFINE(M_USB, "USB", "USB"); MALLOC_DEFINE(M_USBDEV, "USBdev", "USB device"); MALLOC_DEFINE(M_USBHC, "USBHC", "USB host controller"); #include "usb_if.h" #endif /* defined(__FreeBSD__) */ #include #include #include #ifdef USB_DEBUG #define DPRINTF(x) if (usbdebug) logprintf x #define DPRINTFN(n,x) if (usbdebug>(n)) logprintf x int usbdebug = 0; #ifdef UHCI_DEBUG extern int uhcidebug; #endif #ifdef OHCI_DEBUG extern int ohcidebug; #endif /* * 0 - do usual exploration * 1 - do not use timeout exploration * >1 - do no exploration */ int usb_noexplore = 0; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif struct usb_softc { USBBASEDEVICE sc_dev; /* base device */ usbd_bus_handle sc_bus; /* USB controller */ struct usbd_port sc_port; /* dummy port for root hub */ struct proc *sc_event_thread; char sc_dying; }; #if defined(__NetBSD__) || defined(__OpenBSD__) cdev_decl(usb); #elif defined(__FreeBSD__) d_open_t usbopen; d_close_t usbclose; d_read_t usbread; d_ioctl_t usbioctl; d_poll_t usbpoll; struct cdevsw usb_cdevsw = { /* open */ usbopen, /* close */ usbclose, /* read */ usbread, /* write */ nowrite, /* ioctl */ usbioctl, /* poll */ usbpoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "usb", /* maj */ USB_CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, +#if !defined(__FreeBSD__) || (__FreeBSD__ < 5) + /* bmaj */ -1 +#endif }; #endif Static usbd_status usb_discover(void *); Static void usb_create_event_thread(void *); Static void usb_event_thread(void *); #define USB_MAX_EVENTS 100 struct usb_event_q { struct usb_event ue; TAILQ_ENTRY(usb_event_q) next; }; Static TAILQ_HEAD(, usb_event_q) usb_events = TAILQ_HEAD_INITIALIZER(usb_events); Static int usb_nevents = 0; Static struct selinfo usb_selevent; Static struct proc *usb_async_proc; /* process that wants USB SIGIO */ Static int usb_dev_open = 0; Static void usb_add_event(int, struct usb_event *); Static int usb_get_next_event(struct usb_event *); #if defined(__NetBSD__) || defined(__OpenBSD__) /* Flag to see if we are in the cold boot process. */ extern int cold; #endif Static const char *usbrev_str[] = USBREV_STR; USB_DECLARE_DRIVER_INIT(usb, DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_shutdown, bus_generic_shutdown) ); #if defined(__FreeBSD__) MODULE_VERSION(usb, 1); #endif USB_MATCH(usb) { DPRINTF(("usbd_match\n")); return (UMATCH_GENERIC); } USB_ATTACH(usb) { #if defined(__NetBSD__) || defined(__OpenBSD__) struct usb_softc *sc = (struct usb_softc *)self; #elif defined(__FreeBSD__) struct usb_softc *sc = device_get_softc(self); void *aux = device_get_ivars(self); static int global_init_done = 0; #endif usbd_device_handle dev; usbd_status err; int usbrev; struct usb_event ue; sc->sc_dev = self; DPRINTF(("usbd_attach\n")); usbd_init(); sc->sc_bus = aux; sc->sc_bus->usbctl = sc; sc->sc_port.power = USB_MAX_POWER; #if defined(__FreeBSD__) printf("%s", USBDEVNAME(sc->sc_dev)); #endif usbrev = sc->sc_bus->usbrev; printf(": USB revision %s", usbrev_str[usbrev]); if (usbrev != USBREV_1_0 && usbrev != USBREV_1_1) { printf(", not supported\n"); USB_ATTACH_ERROR_RETURN; } printf("\n"); /* Make sure not to use tsleep() if we are cold booting. */ if (cold) sc->sc_bus->use_polling++; ue.u.ue_ctrlr.ue_bus = USBDEVUNIT(sc->sc_dev); usb_add_event(USB_EVENT_CTRLR_ATTACH, &ue); err = usbd_new_device(USBDEV(sc->sc_dev), sc->sc_bus, 0, 0, 0, &sc->sc_port); if (!err) { dev = sc->sc_port.device; if (dev->hub == NULL) { sc->sc_dying = 1; printf("%s: root device is not a hub\n", USBDEVNAME(sc->sc_dev)); USB_ATTACH_ERROR_RETURN; } sc->sc_bus->root_hub = dev; #if 1 /* * Turning this code off will delay attachment of USB devices * until the USB event thread is running, which means that * the keyboard will not work until after cold boot. */ #if defined(__FreeBSD__) if (cold) #else if (cold && (sc->sc_dev.dv_cfdata->cf_flags & 1)) #endif dev->hub->explore(sc->sc_bus->root_hub); #endif } else { printf("%s: root hub problem, error=%d\n", USBDEVNAME(sc->sc_dev), err); sc->sc_dying = 1; } if (cold) sc->sc_bus->use_polling--; config_pending_incr(); #if defined(__NetBSD__) || defined(__OpenBSD__) kthread_create(usb_create_event_thread, sc); #endif #if defined(__FreeBSD__) usb_create_event_thread(sc); /* The per controller devices (used for usb_discover) */ /* XXX This is redundant now, but old usbd's will want it */ make_dev(&usb_cdevsw, device_get_unit(self), UID_ROOT, GID_OPERATOR, 0660, "usb%d", device_get_unit(self)); if (!global_init_done) { /* The device spitting out events */ make_dev(&usb_cdevsw, USB_DEV_MINOR, UID_ROOT, GID_OPERATOR, 0660, "usb"); global_init_done = 1; } #endif USB_ATTACH_SUCCESS_RETURN; } void usb_create_event_thread(void *arg) { struct usb_softc *sc = arg; if (kthread_create1(usb_event_thread, sc, &sc->sc_event_thread, "%s", USBDEVNAME(sc->sc_dev))) { printf("%s: unable to create event thread for\n", USBDEVNAME(sc->sc_dev)); panic("usb_create_event_thread"); } } void usb_event_thread(void *arg) { struct usb_softc *sc = arg; int to; int first = 1; #ifdef __FreeBSD__ mtx_lock(&Giant); #endif DPRINTF(("usb_event_thread: start\n")); while (!sc->sc_dying) { #ifdef USB_DEBUG if (usb_noexplore < 2) #endif usb_discover(sc); if (first) { config_pending_decr(); first = 0; } to = hz * 60; #ifdef USB_DEBUG if (usb_noexplore) to = 0; #endif (void)tsleep(&sc->sc_bus->needs_explore, PWAIT, "usbevt", to); DPRINTFN(2,("usb_event_thread: woke up\n")); } sc->sc_event_thread = NULL; /* In case parent is waiting for us to exit. */ wakeup(sc); DPRINTF(("usb_event_thread: exit\n")); kthread_exit(0); } #if defined(__NetBSD__) || defined(__OpenBSD__) int usbctlprint(void *aux, const char *pnp) { /* only "usb"es can attach to host controllers */ if (pnp) printf("usb at %s", pnp); return (UNCONF); } #endif /* defined(__NetBSD__) || defined(__OpenBSD__) */ int usbopen(dev_t dev, int flag, int mode, usb_proc_ptr p) { int unit = USBUNIT(dev); struct usb_softc *sc; if (unit == USB_DEV_MINOR) { if (usb_dev_open) return (EBUSY); usb_dev_open = 1; usb_async_proc = 0; return (0); } else { USB_GET_SC_OPEN(usb, unit, sc); if (sc->sc_dying) return (EIO); return (0); } } int usbread(dev_t dev, struct uio *uio, int flag) { struct usb_event ue; int unit = USBUNIT(dev); int s, error, n; if (unit != USB_DEV_MINOR) return (ENODEV); if (uio->uio_resid != sizeof(struct usb_event)) return (EINVAL); error = 0; s = splusb(); for (;;) { n = usb_get_next_event(&ue); if (n != 0) break; if (flag & IO_NDELAY) { error = EWOULDBLOCK; break; } error = tsleep(&usb_events, PZERO | PCATCH, "usbrea", 0); if (error) break; } splx(s); if (!error) error = uiomove((void *)&ue, uio->uio_resid, uio); return (error); } int usbclose(dev_t dev, int flag, int mode, usb_proc_ptr p) { int unit = USBUNIT(dev); if (unit == USB_DEV_MINOR) { usb_async_proc = 0; usb_dev_open = 0; } return (0); } int usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, usb_proc_ptr p) { struct usb_softc *sc; int unit = USBUNIT(devt); if (unit == USB_DEV_MINOR) { switch (cmd) { case FIONBIO: /* All handled in the upper FS layer. */ return (0); case FIOASYNC: if (*(int *)data) usb_async_proc = p->td_proc; else usb_async_proc = 0; return (0); default: return (EINVAL); } } USB_GET_SC(usb, unit, sc); if (sc->sc_dying) return (EIO); switch (cmd) { #if defined(__FreeBSD__) /* This part should be deleted */ case USB_DISCOVER: break; #endif #ifdef USB_DEBUG case USB_SETDEBUG: usbdebug = ((*(int *)data) & 0x000000ff); #ifdef UHCI_DEBUG uhcidebug = ((*(int *)data) & 0x0000ff00) >> 8; #endif #ifdef OHCI_DEBUG ohcidebug = ((*(int *)data) & 0x00ff0000) >> 16; #endif break; #endif /* USB_DEBUG */ case USB_REQUEST: { struct usb_ctl_request *ur = (void *)data; int len = UGETW(ur->request.wLength); struct iovec iov; struct uio uio; void *ptr = 0; int addr = ur->addr; usbd_status err; int error = 0; DPRINTF(("usbioctl: USB_REQUEST addr=%d len=%d\n", addr, len)); if (len < 0 || len > 32768) return (EINVAL); if (addr < 0 || addr >= USB_MAX_DEVICES || sc->sc_bus->devices[addr] == 0) return (EINVAL); if (len != 0) { iov.iov_base = (caddr_t)ur->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->request.bmRequestType & UT_READ ? UIO_READ : UIO_WRITE; uio.uio_procp = p; ptr = malloc(len, M_TEMP, M_WAITOK); if (uio.uio_rw == UIO_WRITE) { error = uiomove(ptr, len, &uio); if (error) goto ret; } } err = usbd_do_request_flags(sc->sc_bus->devices[addr], &ur->request, ptr, ur->flags, &ur->actlen); 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_DEVICEINFO: { struct usb_device_info *di = (void *)data; int addr = di->addr; usbd_device_handle dev; if (addr < 1 || addr >= USB_MAX_DEVICES) return (EINVAL); dev = sc->sc_bus->devices[addr]; if (dev == NULL) return (ENXIO); usbd_fill_deviceinfo(dev, di, 1); break; } case USB_DEVICESTATS: *(struct usb_device_stats *)data = sc->sc_bus->stats; break; default: return (EINVAL); } return (0); } int usbpoll(dev_t dev, int events, usb_proc_ptr p) { int revents, mask, s; int unit = USBUNIT(dev); if (unit == USB_DEV_MINOR) { revents = 0; mask = POLLIN | POLLRDNORM; s = splusb(); if (events & mask && usb_nevents > 0) revents |= events & mask; if (revents == 0 && events & mask) selrecord(p, &usb_selevent); splx(s); return (revents); } else { #if defined(__FreeBSD__) return (0); /* select/poll never wakes up - back compat */ #else return (ENXIO); #endif } } /* Explore device tree from the root. */ usbd_status usb_discover(void *v) { struct usb_softc *sc = v; #if defined(__FreeBSD__) /* splxxx should be changed to mutexes for preemption safety some day */ int s; #endif /* * We need mutual exclusion while traversing the device tree, * but this is guaranteed since this function is only called * from the event thread for the controller. */ #if defined(__FreeBSD__) s = splusb(); #endif while (sc->sc_bus->needs_explore && !sc->sc_dying) { sc->sc_bus->needs_explore = 0; #if defined(__FreeBSD__) splx(s); #endif sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub); #if defined(__FreeBSD__) s = splusb(); #endif } #if defined(__FreeBSD__) splx(s); #endif return (USBD_NORMAL_COMPLETION); } void usb_needs_explore(usbd_bus_handle bus) { bus->needs_explore = 1; wakeup(&bus->needs_explore); } /* Called at splusb() */ int usb_get_next_event(struct usb_event *ue) { struct usb_event_q *ueq; if (usb_nevents <= 0) return (0); ueq = TAILQ_FIRST(&usb_events); if (ueq == NULL) { printf("usb: usb_nevents got out of sync! %d\n", usb_nevents); usb_nevents = 0; return (0); } *ue = ueq->ue; TAILQ_REMOVE(&usb_events, ueq, next); free(ueq, M_USBDEV); usb_nevents--; return (1); } void usbd_add_dev_event(int type, usbd_device_handle udev) { struct usb_event ue; usbd_fill_deviceinfo(udev, &ue.u.ue_device, USB_EVENT_IS_ATTACH(type)); usb_add_event(type, &ue); } void usbd_add_drv_event(int type, usbd_device_handle udev, device_ptr_t dev) { struct usb_event ue; ue.u.ue_driver.ue_cookie = udev->cookie; strncpy(ue.u.ue_driver.ue_devname, USBDEVPTRNAME(dev), sizeof ue.u.ue_driver.ue_devname); usb_add_event(type, &ue); } void usb_add_event(int type, struct usb_event *uep) { struct usb_event_q *ueq; struct usb_event ue; struct timeval thetime; int s; ueq = malloc(sizeof *ueq, M_USBDEV, M_WAITOK); ueq->ue = *uep; ueq->ue.ue_type = type; microtime(&thetime); TIMEVAL_TO_TIMESPEC(&thetime, &ueq->ue.ue_time); s = splusb(); if (USB_EVENT_IS_DETACH(type)) { struct usb_event_q *ueqi, *ueqi_next; for (ueqi = TAILQ_FIRST(&usb_events); ueqi; ueqi = ueqi_next) { ueqi_next = TAILQ_NEXT(ueqi, next); if (ueqi->ue.u.ue_driver.ue_cookie.cookie == uep->u.ue_device.cookie.cookie) { TAILQ_REMOVE(&usb_events, ueqi, next); free(ueqi, M_USBDEV); usb_nevents--; ueqi_next = TAILQ_FIRST(&usb_events); } } } if (usb_nevents >= USB_MAX_EVENTS) { /* Too many queued events, drop an old one. */ DPRINTF(("usb: event dropped\n")); (void)usb_get_next_event(&ue); } TAILQ_INSERT_TAIL(&usb_events, ueq, next); usb_nevents++; wakeup(&usb_events); selwakeup(&usb_selevent); if (usb_async_proc != NULL) { PROC_LOCK(usb_async_proc); psignal(usb_async_proc, SIGIO); PROC_UNLOCK(usb_async_proc); } splx(s); } void usb_schedsoftintr(struct usbd_bus *bus) { bus->methods->soft_intr(bus); } #if defined(__NetBSD__) || defined(__OpenBSD__) int usb_activate(device_ptr_t self, enum devact act) { struct usb_softc *sc = (struct usb_softc *)self; usbd_device_handle dev = sc->sc_port.device; int i, rv = 0; switch (act) { case DVACT_ACTIVATE: return (EOPNOTSUPP); break; case DVACT_DEACTIVATE: sc->sc_dying = 1; if (dev && dev->cdesc && dev->subdevs) { for (i = 0; dev->subdevs[i]; i++) rv |= config_deactivate(dev->subdevs[i]); } break; } return (rv); } int usb_detach(device_ptr_t self, int flags) { struct usb_softc *sc = (struct usb_softc *)self; struct usb_event ue; DPRINTF(("usb_detach: start\n")); sc->sc_dying = 1; /* Make all devices disconnect. */ if (sc->sc_port.device) usb_disconnect_port(&sc->sc_port, self); /* Kill off event thread. */ if (sc->sc_event_thread) { wakeup(&sc->sc_bus->needs_explore); if (tsleep(sc, PWAIT, "usbdet", hz * 60)) printf("%s: event thread didn't die\n", USBDEVNAME(sc->sc_dev)); DPRINTF(("usb_detach: event thread dead\n")); } usbd_finish(); ue.u.ue_ctrlr.ue_bus = USBDEVUNIT(sc->sc_dev); usb_add_event(USB_EVENT_CTRLR_DETACH, &ue); return (0); } #elif defined(__FreeBSD__) int usb_detach(device_t self) { DPRINTF(("%s: unload, prevented\n", USBDEVNAME(self))); return (EINVAL); } #endif #if defined(__FreeBSD__) DRIVER_MODULE(usb, ohci, usb_driver, usb_devclass, 0, 0); DRIVER_MODULE(usb, uhci, usb_driver, usb_devclass, 0, 0); #endif