/* * Close the character device. */ static int cmx_close(struct cdev *cdev, int flags, int fmt, struct thread *td) { struct cmx_softc *sc = cdev->si_drv1; if (sc == NULL || sc->dying) return ENXIO; CMX_LOCK(sc); if (!sc->open) { CMX_UNLOCK(sc); return EINVAL; } if (sc->polling) { DEBUG_printf(sc->dev, "disabling polling\n"); callout_stop(&sc->ch); sc->polling = 0; CMX_UNLOCK(sc); callout_drain(&sc->ch); selwakeuppri(&sc->sel, PZERO); CMX_LOCK(sc); } sc->open = 0; CMX_UNLOCK(sc); DEBUG_printf(sc->dev, "close (flags=%b thread=%p)\n", flags, MODEBITS, td); return 0; }
/* * Bus independant device detachment routine. Makes sure all * allocated resources are freed, callouts disabled and waiting * processes unblocked. */ int cmx_detach(device_t dev) { struct cmx_softc *sc = device_get_softc(dev); DEBUG_printf(dev, "called\n"); sc->dying = 1; CMX_LOCK(sc); if (sc->polling) { DEBUG_printf(sc->dev, "disabling polling\n"); callout_stop(&sc->ch); sc->polling = 0; CMX_UNLOCK(sc); callout_drain(&sc->ch); selwakeuppri(&sc->sel, PZERO); } else { CMX_UNLOCK(sc); } wakeup(sc); destroy_dev(sc->cdev); DEBUG_printf(dev, "releasing resources\n"); cmx_release_resources(dev); return 0; }
/* * Close the character device. */ static int cmx_close(struct dev_close_args *ap) { cdev_t dev = ap->a_head.a_dev; struct cmx_softc *sc; sc = devclass_get_softc(cmx_devclass, minor(dev)); if (sc == NULL || sc->dying) return ENXIO; CMX_LOCK(sc); if (!sc->open) { CMX_UNLOCK(sc); return EINVAL; } if (sc->polling) { DEBUG_printf(sc->dev, "disabling polling\n"); callout_stop(&sc->ch); sc->polling = 0; CMX_UNLOCK(sc); KNOTE(&sc->kq.ki_note, 0); CMX_LOCK(sc); } sc->open = 0; CMX_UNLOCK(sc); DEBUG_printf(sc->dev, "close (flags=%b thread=%p)\n", ap->a_fflag, MODEBITS, curthread); return 0; }
/* * Bus independant device detachment routine. Makes sure all * allocated resources are freed, callouts disabled and waiting * processes unblocked. */ int cmx_detach(device_t dev) { struct cmx_softc *sc = device_get_softc(dev); DEBUG_printf(dev, "called\n"); sc->dying = 1; CMX_LOCK(sc); if (sc->polling) { DEBUG_printf(sc->dev, "disabling polling\n"); callout_stop(&sc->ch); sc->polling = 0; CMX_UNLOCK(sc); KNOTE(&sc->kq.ki_note, 0); } else { CMX_UNLOCK(sc); } wakeup(sc); DEBUG_printf(dev, "releasing resources\n"); cmx_release_resources(dev); dev_ops_remove_minor(&cmx_ops, device_get_unit(dev)); return 0; }
static int cmx_filter_read(struct knote *kn, long hint) { struct cmx_softc *sc = (struct cmx_softc *)kn->kn_hook; int ready = 0; uint8_t bsr = 0; if (sc == NULL || sc->dying) { kn->kn_flags |= EV_EOF; return (1); } bsr = cmx_read_BSR(sc); if (cmx_test(bsr, BSR_BULK_IN_FULL, 1)) { ready = 1; } else { CMX_LOCK(sc); if (!sc->polling) { sc->polling = 1; callout_reset(&sc->ch, POLL_TICKS, cmx_tick, sc); } CMX_UNLOCK(sc); } return (ready); }
/* * Open the character device. Only a single process may open the * device at a time. */ static int cmx_open(struct cdev *cdev, int flags, int fmt, struct thread *td) { struct cmx_softc *sc = cdev->si_drv1; if (sc == NULL || sc->dying) return ENXIO; CMX_LOCK(sc); if (sc->open) { CMX_UNLOCK(sc); return EBUSY; } sc->open = 1; CMX_UNLOCK(sc); DEBUG_printf(sc->dev, "open (flags=%b thread=%p)\n", flags, MODEBITS, td); return 0; }
/* * Open the character device. Only a single process may open the * device at a time. */ static int cmx_open(struct dev_open_args *ap) { cdev_t dev = ap->a_head.a_dev; struct cmx_softc *sc; sc = devclass_get_softc(cmx_devclass, minor(dev)); if (sc == NULL || sc->dying) return ENXIO; CMX_LOCK(sc); if (sc->open) { CMX_UNLOCK(sc); return EBUSY; } sc->open = 1; CMX_UNLOCK(sc); DEBUG_printf(sc->dev, "open (flags=%b thread=%p)\n", ap->a_oflags, MODEBITS, curthread); return 0; }
/* * Periodical callout routine, polling the reader for data * availability. If the reader signals data ready for reading, * wakes up the processes which are waiting in select()/poll(). * Otherwise, reschedules itself with a delay of POLL_TICKS. */ static void cmx_tick(void *xsc) { struct cmx_softc *sc = xsc; uint8_t bsr; CMX_LOCK(sc); if (sc->polling && !sc->dying) { bsr = cmx_read_BSR(sc); DEBUG_printf(sc->dev, "BSR=%b\n", bsr, BSRBITS); if (cmx_test(bsr, BSR_BULK_IN_FULL, 1)) { sc->polling = 0; selwakeuppri(&sc->sel, PZERO); } else { callout_reset(&sc->ch, POLL_TICKS, cmx_tick, sc); } } CMX_UNLOCK(sc); }
/* * Poll handler. Writing is always possible, reading is only possible * if BSR_BULK_IN_FULL is set. Will start the cmx_tick callout and * set sc->polling. */ static int cmx_poll(struct cdev *cdev, int events, struct thread *td) { struct cmx_softc *sc = cdev->si_drv1; int revents = 0; uint8_t bsr = 0; if (sc == NULL || sc->dying) return ENXIO; bsr = cmx_read_BSR(sc); DEBUG_printf(sc->dev, "called (events=%b BSR=%b)\n", events, POLLBITS, bsr, BSRBITS); revents = events & (POLLOUT | POLLWRNORM); if (events & (POLLIN | POLLRDNORM)) { if (cmx_test(bsr, BSR_BULK_IN_FULL, 1)) { revents |= events & (POLLIN | POLLRDNORM); } else { selrecord(td, &sc->sel); CMX_LOCK(sc); if (!sc->polling) { DEBUG_printf(sc->dev, "enabling polling\n"); sc->polling = 1; callout_reset(&sc->ch, POLL_TICKS, cmx_tick, sc); } else { DEBUG_printf(sc->dev, "already polling\n"); } CMX_UNLOCK(sc); } } DEBUG_printf(sc->dev, "success (revents=%b)\n", revents, POLLBITS); return revents; }
/* * Read from the character device. * Returns zero if successful, ENXIO if dying, EINVAL if an attempt * was made to read less than CMX_MIN_RDLEN bytes or less than the * device has available, or any of the errors that cmx_sync_write_SCR * can return. Partial reads are not supported. */ static int cmx_read(struct cdev *cdev, struct uio *uio, int flag) { struct cmx_softc *sc = cdev->si_drv1; unsigned long bytes_left; uint8_t uc; int rv, amnt, offset; if (sc == NULL || sc->dying) return ENXIO; DEBUG_printf(sc->dev, "called (len=%d flag=%b)\n", uio->uio_resid, flag, MODEBITS); CMX_LOCK(sc); if (sc->polling) { DEBUG_printf(sc->dev, "disabling polling\n"); callout_stop(&sc->ch); sc->polling = 0; CMX_UNLOCK(sc); callout_drain(&sc->ch); selwakeuppri(&sc->sel, PZERO); } else { CMX_UNLOCK(sc); } if (uio->uio_resid == 0) { return 0; } if (uio->uio_resid < CMX_MIN_RDLEN) { return EINVAL; } if (flag & O_NONBLOCK) { if (cmx_test_BSR(sc, BSR_BULK_IN_FULL, 0)) { return EAGAIN; } } for (int i = 0; i < 5; i++) { if ((rv = cmx_wait_BSR(sc, BSR_BULK_IN_FULL, 1)) != 0) { return rv; } sc->buf[i] = cmx_read_DTR(sc); DEBUG_printf(sc->dev, "buf[%02x]=%02x\n", i, sc->buf[i]); } bytes_left = CMX_MIN_RDLEN + (0x000000FF&((char)sc->buf[1])) + (0x0000FF00&((char)sc->buf[2] << 8)) + (0x00FF0000&((char)sc->buf[3] << 16)) + (0xFF000000&((char)sc->buf[4] << 24)); DEBUG_printf(sc->dev, "msgsz=%lu\n", bytes_left); if (uio->uio_resid < bytes_left) { return EINVAL; } offset = 5; /* prefetched header */ while (bytes_left > 0) { amnt = MIN(bytes_left, sizeof(sc->buf)); for (int i = offset; i < amnt; i++) { if ((rv = cmx_wait_BSR(sc, BSR_BULK_IN_FULL, 1))!=0) { return rv; } sc->buf[i] = cmx_read_DTR(sc); DEBUG_printf(sc->dev, "buf[%02x]=%02x\n", i, sc->buf[i]); } if ((rv = uiomove(sc->buf, amnt, uio)) != 0) { DEBUG_printf(sc->dev, "uiomove failed (%d)\n", rv); return rv; } if (offset) offset = 0; bytes_left -= amnt; } if ((rv = cmx_wait_BSR(sc, BSR_BULK_IN_FULL, 1)) != 0) { return rv; } if ((rv = cmx_sync_write_SCR(sc, SCR_READER_TO_HOST_DONE)) != 0) { return rv; } uc = cmx_read_DTR(sc); DEBUG_printf(sc->dev, "success (DTR=%02x)\n", uc); return 0; }