Author: hselasky Date: Fri Mar 14 09:11:09 2014 New Revision: 263162 URL: http://svnweb.freebsd.org/changeset/base/263162
Log: MFC r262550, r262551 and r262554: Add support for kqfilter to USB character devices. Modified: stable/10/sys/dev/usb/usb_dev.c Directory Properties: stable/10/ (props changed) Modified: stable/10/sys/dev/usb/usb_dev.c ============================================================================== --- stable/10/sys/dev/usb/usb_dev.c Fri Mar 14 08:56:19 2014 (r263161) +++ stable/10/sys/dev/usb/usb_dev.c Fri Mar 14 09:11:09 2014 (r263162) @@ -109,7 +109,7 @@ static void usb_dev_uninit(void *); static int usb_fifo_uiomove(struct usb_fifo *, void *, int, struct uio *); static void usb_fifo_check_methods(struct usb_fifo_methods *); -static struct usb_fifo *usb_fifo_alloc(void); +static struct usb_fifo *usb_fifo_alloc(struct mtx *); static struct usb_endpoint *usb_dev_get_ep(struct usb_device *, uint8_t, uint8_t); static void usb_loc_fill(struct usb_fs_privdata *, @@ -124,6 +124,7 @@ static d_ioctl_t usb_ioctl; static d_read_t usb_read; static d_write_t usb_write; static d_poll_t usb_poll; +static d_kqfilter_t usb_kqfilter; static d_ioctl_t usb_static_ioctl; @@ -141,7 +142,8 @@ struct cdevsw usb_devsw = { .d_flags = D_TRACKCLOSE, .d_read = usb_read, .d_write = usb_write, - .d_poll = usb_poll + .d_poll = usb_poll, + .d_kqfilter = usb_kqfilter, }; static struct cdev* usb_dev = NULL; @@ -368,15 +370,17 @@ usb_unref_device(struct usb_cdev_privdat } static struct usb_fifo * -usb_fifo_alloc(void) +usb_fifo_alloc(struct mtx *mtx) { struct usb_fifo *f; f = malloc(sizeof(*f), M_USBDEV, M_WAITOK | M_ZERO); - if (f) { + if (f != NULL) { cv_init(&f->cv_io, "FIFO-IO"); cv_init(&f->cv_drain, "FIFO-DRAIN"); + f->priv_mtx = mtx; f->refcount = 1; + knlist_init_mtx(&f->selinfo.si_note, mtx); } return (f); } @@ -500,7 +504,7 @@ usb_fifo_create(struct usb_cdev_privdata DPRINTFN(5, "dev_get_endpoint returned NULL\n"); return (EINVAL); } - f = usb_fifo_alloc(); + f = usb_fifo_alloc(&udev->device_mtx); if (f == NULL) { DPRINTFN(5, "could not alloc tx fifo\n"); return (ENOMEM); @@ -508,7 +512,6 @@ usb_fifo_create(struct usb_cdev_privdata /* update some fields */ f->fifo_index = n + USB_FIFO_TX; f->dev_ep_index = e; - f->priv_mtx = &udev->device_mtx; f->priv_sc0 = ep; f->methods = &usb_ugen_methods; f->iface_index = ep->iface_index; @@ -527,7 +530,7 @@ usb_fifo_create(struct usb_cdev_privdata DPRINTFN(5, "dev_get_endpoint returned NULL\n"); return (EINVAL); } - f = usb_fifo_alloc(); + f = usb_fifo_alloc(&udev->device_mtx); if (f == NULL) { DPRINTFN(5, "could not alloc rx fifo\n"); return (ENOMEM); @@ -535,7 +538,6 @@ usb_fifo_create(struct usb_cdev_privdata /* update some fields */ f->fifo_index = n + USB_FIFO_RX; f->dev_ep_index = e; - f->priv_mtx = &udev->device_mtx; f->priv_sc0 = ep; f->methods = &usb_ugen_methods; f->iface_index = ep->iface_index; @@ -620,6 +622,10 @@ usb_fifo_free(struct usb_fifo *f) cv_destroy(&f->cv_io); cv_destroy(&f->cv_drain); + knlist_clear(&f->selinfo.si_note, 0); + seldrain(&f->selinfo); + knlist_destroy(&f->selinfo.si_note); + free(f, M_USBDEV); } @@ -774,7 +780,12 @@ usb_fifo_close(struct usb_fifo *f, int f mtx_lock(f->priv_mtx); /* clear current cdev private data pointer */ + mtx_lock(&usb_ref_lock); f->curr_cpd = NULL; + mtx_unlock(&usb_ref_lock); + + /* check if we are watched by kevent */ + KNOTE_LOCKED(&f->selinfo.si_note, 0); /* check if we are selected */ if (f->flag_isselect) { @@ -1117,6 +1128,162 @@ done: return (err); } +static void +usb_filter_detach(struct knote *kn) +{ + struct usb_fifo *f = kn->kn_hook; + knlist_remove(&f->selinfo.si_note, kn, 0); +} + +static int +usb_filter_write(struct knote *kn, long hint) +{ + struct usb_cdev_privdata* cpd; + struct usb_fifo *f; + struct usb_mbuf *m; + + DPRINTFN(2, "\n"); + + f = kn->kn_hook; + + mtx_assert(f->priv_mtx, MA_OWNED); + + cpd = f->curr_cpd; + if (cpd == NULL) { + m = (void *)1; + } else if (f->fs_ep_max == 0) { + if (f->flag_iserror) { + /* we got an error */ + m = (void *)1; + } else { + if (f->queue_data == NULL) { + /* + * start write transfer, if not + * already started + */ + (f->methods->f_start_write) (f); + } + /* check if any packets are available */ + USB_IF_POLL(&f->free_q, m); + } + } else { + if (f->flag_iscomplete) { + m = (void *)1; + } else { + m = NULL; + } + } + return (m ? 1 : 0); +} + +static int +usb_filter_read(struct knote *kn, long hint) +{ + struct usb_cdev_privdata* cpd; + struct usb_fifo *f; + struct usb_mbuf *m; + + DPRINTFN(2, "\n"); + + f = kn->kn_hook; + + mtx_assert(f->priv_mtx, MA_OWNED); + + cpd = f->curr_cpd; + if (cpd == NULL) { + m = (void *)1; + } else if (f->fs_ep_max == 0) { + if (f->flag_iserror) { + /* we have an error */ + m = (void *)1; + } else { + if (f->queue_data == NULL) { + /* + * start read transfer, if not + * already started + */ + (f->methods->f_start_read) (f); + } + /* check if any packets are available */ + USB_IF_POLL(&f->used_q, m); + + /* start reading data, if any */ + if (m == NULL) + (f->methods->f_start_read) (f); + } + } else { + if (f->flag_iscomplete) { + m = (void *)1; + } else { + m = NULL; + } + } + return (m ? 1 : 0); +} + +static struct filterops usb_filtops_write = { + .f_isfd = 1, + .f_detach = usb_filter_detach, + .f_event = usb_filter_write, +}; + +static struct filterops usb_filtops_read = { + .f_isfd = 1, + .f_detach = usb_filter_detach, + .f_event = usb_filter_read, +}; + + +/* ARGSUSED */ +static int +usb_kqfilter(struct cdev* dev, struct knote *kn) +{ + struct usb_cdev_refdata refs; + struct usb_cdev_privdata* cpd; + struct usb_fifo *f; + int fflags; + int err = EINVAL; + + DPRINTFN(2, "\n"); + + if (devfs_get_cdevpriv((void **)&cpd) != 0 || + usb_ref_device(cpd, &refs, 0) != 0) + return (ENXIO); + + fflags = cpd->fflags; + + /* Figure out who needs service */ + switch (kn->kn_filter) { + case EVFILT_WRITE: + if (fflags & FWRITE) { + f = refs.txfifo; + kn->kn_fop = &usb_filtops_write; + err = 0; + } + break; + case EVFILT_READ: + if (fflags & FREAD) { + f = refs.rxfifo; + kn->kn_fop = &usb_filtops_read; + err = 0; + } + break; + default: + err = EOPNOTSUPP; + break; + } + + if (err == 0) { + kn->kn_hook = f; + mtx_lock(f->priv_mtx); + knlist_add(&f->selinfo.si_note, kn, 1); + mtx_unlock(f->priv_mtx); + } + + usb_unref_device(cpd, &refs); + return (err); +} + /* ARGSUSED */ static int usb_poll(struct cdev* dev, int events, struct thread* td) @@ -1184,7 +1351,7 @@ usb_poll(struct cdev* dev, int events, s if (!refs.is_usbfs) { if (f->flag_iserror) { - /* we have and error */ + /* we have an error */ m = (void *)1; } else { if (f->queue_data == NULL) { @@ -1581,6 +1748,8 @@ usb_fifo_wakeup(struct usb_fifo *f) { usb_fifo_signal(f); + KNOTE_LOCKED(&f->selinfo.si_note, 0); + if (f->flag_isselect) { selwakeup(&f->selinfo); f->flag_isselect = 0; @@ -1696,8 +1865,8 @@ usb_fifo_attach(struct usb_device *udev, break; } - f_tx = usb_fifo_alloc(); - f_rx = usb_fifo_alloc(); + f_tx = usb_fifo_alloc(priv_mtx); + f_rx = usb_fifo_alloc(priv_mtx); if ((f_tx == NULL) || (f_rx == NULL)) { usb_fifo_free(f_tx); @@ -1708,7 +1877,6 @@ usb_fifo_attach(struct usb_device *udev, f_tx->fifo_index = n + USB_FIFO_TX; f_tx->dev_ep_index = -1; - f_tx->priv_mtx = priv_mtx; f_tx->priv_sc0 = priv_sc; f_tx->methods = pm; f_tx->iface_index = iface_index; @@ -1716,7 +1884,6 @@ usb_fifo_attach(struct usb_device *udev, f_rx->fifo_index = n + USB_FIFO_RX; f_rx->dev_ep_index = -1; - f_rx->priv_mtx = priv_mtx; f_rx->priv_sc0 = priv_sc; f_rx->methods = pm; f_rx->iface_index = iface_index; _______________________________________________ svn-src-all@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"