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"

Reply via email to