Index: ulpt.c =================================================================== RCS file: /usr/ncvs/src/sys/dev/usb/ulpt.c,v retrieving revision 1.66.2.1 diff -u -r1.66.2.1 ulpt.c --- ulpt.c 20 Nov 2005 03:18:22 -0000 1.66.2.1 +++ ulpt.c 9 Sep 2006 18:03:48 -0000 @@ -61,6 +61,8 @@ #include #include #include +#include +#include #include #include @@ -114,6 +116,8 @@ usbd_pipe_handle sc_in_pipe; /* bulk in pipe */ usbd_xfer_handle sc_in_xfer; void *sc_in_buf; + int sc_read_bytes; + struct selinfo sc_rsel; /* process waiting in select */ usb_callout_t sc_read_callout; int sc_has_callout; @@ -122,6 +126,9 @@ #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 */ +#define ULPT_SELECT 0x08 /* waiting for events */ +#define ULPT_SLEEP 0x10 /* waiting for read event */ +#define ULPT_READ 0x20 /* background read in progress */ u_char sc_flags; #define ULPT_NOPRIME 0x40 /* don't prime on open */ u_char sc_laststatus; @@ -140,11 +147,12 @@ dev_type_close(ulptclose); dev_type_write(ulptwrite); dev_type_read(ulptread); +dev_type_poll(ulptpoll); dev_type_ioctl(ulptioctl); const struct cdevsw ulpt_cdevsw = { ulptopen, ulptclose, ulptread, ulptwrite, ulptioctl, - nostop, notty, nopoll, nommap, nokqfilter, + nostop, notty, ulptpoll, nommap, nokqfilter, }; #elif defined(__OpenBSD__) cdev_decl(ulpt); @@ -153,6 +161,7 @@ Static d_close_t ulptclose; Static d_write_t ulptwrite; Static d_read_t ulptread; +Static d_poll_t ulptpoll; Static d_ioctl_t ulptioctl; @@ -163,6 +172,7 @@ .d_close = ulptclose, .d_write = ulptwrite, .d_read = ulptread, + .d_poll = ulptpoll, .d_ioctl = ulptioctl, .d_name = "ulpt", #if __FreeBSD_version < 500014 @@ -402,6 +412,16 @@ if (sc->sc_in_pipe != NULL) usbd_abort_pipe(sc->sc_in_pipe); + if (sc->sc_state & ULPT_SELECT) { + sc->sc_state &= ~ULPT_SELECT; + selwakeup(&sc->sc_rsel); + } + if (sc->sc_state & ULPT_SLEEP) { + sc->sc_state &= ~ULPT_SLEEP; + wakeup((caddr_t) sc); + } + sc->sc_state &= ~ULPT_READ; + s = splusb(); if (--sc->sc_refcnt >= 0) { /* There is noone to wake, aborting the pipe is enough */ @@ -660,6 +680,16 @@ USB_GET_SC(ulpt, ULPTUNIT(dev), sc); + if (sc->sc_state & ULPT_SELECT) { + selwakeup(&sc->sc_rsel); + sc->sc_state &= ~ULPT_SELECT; + } + if (sc->sc_state & ULPT_SLEEP) { + wakeup((caddr_t) sc); + sc->sc_state &= ~ULPT_SLEEP; + } + sc->sc_state &= ~ULPT_READ; + if (sc->sc_state != ULPT_OPEN) /* We are being forced to close before the open completed. */ return (0); @@ -741,6 +771,64 @@ if (--sc->sc_refcnt < 0) usb_detach_wakeup(USBDEV(sc->sc_dev)); return (error); +} + +Static void ulpt_bulk_read_cb(usbd_xfer_handle xfer, + usbd_private_handle priv, usbd_status status); + +/* called when the background read xfer is complete */ +Static void +ulpt_bulk_read_cb(usbd_xfer_handle xfer, usbd_private_handle priv, + usbd_status status) +{ + struct ulpt_softc *sc; + usbd_private_handle xsc; + usbd_status err; + int n; + + DPRINTF(("ulpt_bulk_read_cb\n")); + usbd_get_xfer_status(xfer, &xsc, NULL, &n, &err); + sc = xsc; + if (sc == NULL || sc->sc_dying) + return; + + DPRINTFN(1,("ulpt_bulk_read: transferred %d\n", n)); + if (err) { + DPRINTF(("ulpt_bulk_read: error=%d\n", err)); + usbd_clear_endpoint_stall(sc->sc_in_pipe); + } + sc->sc_read_bytes = n; + if (n == 0) /* did the xfer fail? */ + sc->sc_state &= ~ULPT_READ; + if (sc->sc_state & ULPT_SELECT) { + selwakeup(&sc->sc_rsel); + sc->sc_state &= ~ULPT_SELECT; + } + if (sc->sc_state & ULPT_SLEEP) { + sc->sc_state &= ~ULPT_SLEEP; + wakeup((caddr_t) sc); + } +} + +/* start background read xfer */ +Static int +ulpt_bulk_read_init(struct ulpt_softc *sc, int flags) +{ + usbd_xfer_handle xfer; + usbd_status err; + void *bufp; + + DPRINTF(("ulpt_bulk_read_init\n")); + xfer = sc->sc_in_xfer; + bufp = sc->sc_in_buf; + usbd_setup_xfer(xfer, sc->sc_in_pipe, 0, bufp, ULPT_BSIZE, + flags, USBD_NO_TIMEOUT, ulpt_bulk_read_cb); + err = usbd_transfer(xfer); + if (err != 0 && err != USBD_IN_PROGRESS) + return EIO; /* failed to start read xfer */ + sc->sc_state |= ULPT_READ; + sc->sc_read_bytes = 0; + return 0; } int @@ -757,8 +845,52 @@ if (sc->sc_in_pipe == NULL) return 0; - xfer = sc->sc_in_xfer; bufp = sc->sc_in_buf; + + if (sc->sc_state & ULPT_READ) { + /* Background read */ + if (!sc->sc_read_bytes) { + /* Background read transfer already in progress. + * Either return or wait for completion. */ + if (flags & O_NONBLOCK) + return EWOULDBLOCK; + sc->sc_state |= ULPT_SLEEP; + error = tsleep((caddr_t) sc, PZERO | PCATCH, "ulprdw", 0); + sc->sc_state &= ~ULPT_SLEEP; + if (error) + return error; + } + /* background read is done. If it was successful + * sc->sc_read_bytes will be the number of bytes + * read in sc->sc_in_buf (bufp). In case of + * failure sc->sc_read_bytes will be 0, and + * ULPT_READ flag unset in sc->sc_flags */ + n = min(sc->sc_read_bytes, uio->uio_resid); + error = uiomove(bufp, n, uio); + if (error) + return error; + sc->sc_read_bytes -= n; + if (sc->sc_read_bytes != 0) + bcopy((char *) bufp + n, bufp, sc->sc_read_bytes); + else if (n != 0) /* If buffer is emptied clear read flag */ + sc->sc_state &= ~ULPT_READ; /* ready to do next xfer */ + DPRINTF(("ulpt_do_read: background=%d\n", n)); + if (n == 0) + return EIO; + return 0; + } + + if (flags & O_NONBLOCK) { + /* start background read xfer */ + if (!(sc->sc_state & ULPT_READ)) + error = ulpt_bulk_read_init(sc, 0); + if (error) + return error; + return EWOULDBLOCK; + } + + DPRINTF(("ulpt_do_read: to xfer\n")); + xfer = sc->sc_in_xfer; while ((n = min(ULPT_BSIZE, uio->uio_resid)) != 0) { DPRINTFN(1, ("ulptread: transfer %d bytes\n", n)); on = n; @@ -821,6 +953,45 @@ ulpt_tick, sc); } +Static int +ulptpoll(struct cdev *dev, int events, usb_proc_ptr p) +{ + struct ulpt_softc *sc; + int revents = 0; + + DPRINTF(("ulptpoll: events=%x\n", (unsigned int) events)); + + USB_GET_SC(ulpt, ULPTUNIT(dev), sc); + + if (!sc || sc->sc_dying) + return 0; + + /* Always accept write requests */ + if (events & (POLLOUT | POLLWRNORM)) + revents |= events & (POLLOUT | POLLWRNORM); + + if (events & (POLLIN | POLLRDNORM)) { + if ((sc->sc_state & ULPT_READ) && sc->sc_read_bytes) + revents |= events & (POLLIN | POLLRDNORM); + else { + /* + * Just ignore failure in + * ulpt_bulk_read_init. It will not be + * possible to read anything anyway. + */ + if (!(sc->sc_state & ULPT_READ)) + ulpt_bulk_read_init(sc, 0); + DPRINTF(("ulptpoll waiting in select\n")); + sc->sc_state |= ULPT_SELECT; + selrecord(p, &sc->sc_rsel); + } + } + + DPRINTF(("ulptpoll: revents=%x\n", (unsigned int) revents)); + + return revents; +} + void ulpt_tick(void *xsc) {