Author: hselasky
Date: Tue Jun  4 16:40:18 2019
New Revision: 348631
URL: https://svnweb.freebsd.org/changeset/base/348631

Log:
  In usb(4) fix a lost completion event issue towards libusb(3). It may happen
  if a USB transfer is cancelled that we need to fake a completion event.
  Implement missing support in ugen_fs_copy_out() to handle this.
  
  This fixes issues with webcamd(8) and firefox.
  
  MFC after:    3 days
  Sponsored by: Mellanox Technologies

Modified:
  head/sys/dev/usb/usb_generic.c

Modified: head/sys/dev/usb/usb_generic.c
==============================================================================
--- head/sys/dev/usb/usb_generic.c      Tue Jun  4 16:21:14 2019        
(r348630)
+++ head/sys/dev/usb/usb_generic.c      Tue Jun  4 16:40:18 2019        
(r348631)
@@ -1219,6 +1219,40 @@ complete:
 }
 
 static int
+ugen_fs_copy_out_cancelled(struct usb_fs_endpoint *fs_ep_uptr)
+{
+       struct usb_fs_endpoint fs_ep;
+       int error;
+
+       error = copyin(fs_ep_uptr, &fs_ep, sizeof(fs_ep));
+       if (error)
+               return (error);
+
+       fs_ep.status = USB_ERR_CANCELLED;
+       fs_ep.aFrames = 0;
+       fs_ep.isoc_time_complete = 0;
+
+       /* update "aFrames" */
+       error = copyout(&fs_ep.aFrames, &fs_ep_uptr->aFrames,
+           sizeof(fs_ep.aFrames));
+       if (error)
+               goto done;
+
+       /* update "isoc_time_complete" */
+       error = copyout(&fs_ep.isoc_time_complete,
+           &fs_ep_uptr->isoc_time_complete,
+           sizeof(fs_ep.isoc_time_complete));
+       if (error)
+               goto done;
+
+       /* update "status" */
+       error = copyout(&fs_ep.status, &fs_ep_uptr->status,
+           sizeof(fs_ep.status));
+done:
+       return (error);
+}
+
+static int
 ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index)
 {
        struct usb_device_request *req;
@@ -1243,8 +1277,13 @@ ugen_fs_copy_out(struct usb_fifo *f, uint8_t ep_index)
                return (EINVAL);
 
        mtx_lock(f->priv_mtx);
-       if (usbd_transfer_pending(xfer)) {
+       if (!xfer->flags_int.transferring &&
+           !xfer->flags_int.started) {
                mtx_unlock(f->priv_mtx);
+               DPRINTF("Returning fake cancel event\n");
+               return (ugen_fs_copy_out_cancelled(f->fs_ep_ptr + ep_index));
+       } else if (usbd_transfer_pending(xfer)) {
+               mtx_unlock(f->priv_mtx);
                return (EBUSY);         /* should not happen */
        }
        mtx_unlock(f->priv_mtx);
@@ -1364,6 +1403,7 @@ complete:
            sizeof(fs_ep.isoc_time_complete));
        if (error)
                goto done;
+
        /* update "status" */
        error = copyout(&fs_ep.status, &fs_ep_uptr->status,
            sizeof(fs_ep.status));
@@ -1452,12 +1492,15 @@ ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr,
                xfer = f->fs_xfer[u.pstart->ep_index];
                if (usbd_transfer_pending(xfer)) {
                        usbd_transfer_stop(xfer);
+
                        /*
                         * Check if the USB transfer was stopped
-                        * before it was even started. Else a cancel
-                        * callback will be pending.
+                        * before it was even started and fake a
+                        * cancel event.
                         */
-                       if (!xfer->flags_int.transferring) {
+                       if (!xfer->flags_int.transferring &&
+                           !xfer->flags_int.started) {
+                               DPRINTF("Issuing fake completion event\n");
                                ugen_fs_set_complete(xfer->priv_sc,
                                    USB_P2U(xfer->priv_fifo));
                        }
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to