On Sun, Nov 06, 2016 at 10:30:02PM +0100, Oliver Neukum wrote:
> Hi,
> 
> almost. Two issues left with this section.
> 
> 1. It makes no sense to check the results of usb_clear_halt()
> If it fails, we cannot do anything sensible. We have to restart
> IO and hope for the best. Not doing it that way risks introducing
> a regression.
> 
> 2. usb_autopm_put_interface() must be after acm_submit_read_urbs()
> because the URBs are to be killed when the device is suspended.
> 
> And on a related note:
> 
> 3. The device may be reset externally before the work queue
> is executed. pre_reset() needs to clear the flag EVENT_RX_STALL.

Hi,

thanks for feedback. Here's an update:
-- >8 --
Subject: [PATCHv3 4/4] cdc-acm: clear halt condition

Signed-off-by: Ladislav Michl <la...@linux-mips.org>
---
 drivers/usb/class/cdc-acm.c | 59 +++++++++++++++++++++++++++++++++++++++------
 drivers/usb/class/cdc-acm.h |  3 +++
 2 files changed, 54 insertions(+), 8 deletions(-)

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index b76c95c..711d680 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -424,15 +424,29 @@ static void acm_read_bulk_callback(struct urb *urb)
                return;
        }
 
-       if (status) {
-               set_bit(rb->index, &acm->read_urbs_free);
-               if ((status != -ENOENT) || (urb->actual_length == 0))
-                       return;
+       switch (status) {
+       case 0:
+               usb_mark_last_busy(acm->dev);
+               acm_process_read_urb(acm, urb);
+               break;
+       case -EPIPE:
+               set_bit(EVENT_RX_STALL, &acm->flags);
+               schedule_work(&acm->work);
+               return;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               dev_dbg(&acm->data->dev,
+                       "%s - urb shutting down with status: %d\n",
+                       __func__, status);
+               return;
+       default:
+               dev_dbg(&acm->data->dev,
+                       "%s - nonzero urb status received: %d\n",
+                       __func__, status);
+               break;
        }
 
-       usb_mark_last_busy(acm->dev);
-
-       acm_process_read_urb(acm, urb);
        /*
         * Unthrottle may run on another CPU which needs to see events
         * in the same order. Submission has an implict barrier
@@ -468,14 +482,33 @@ static void acm_write_bulk(struct urb *urb)
        spin_lock_irqsave(&acm->write_lock, flags);
        acm_write_done(acm, wb);
        spin_unlock_irqrestore(&acm->write_lock, flags);
+       set_bit(EVENT_TTY_WAKEUP, &acm->flags);
        schedule_work(&acm->work);
 }
 
 static void acm_softint(struct work_struct *work)
 {
+       int i;
        struct acm *acm = container_of(work, struct acm, work);
 
-       tty_port_tty_wakeup(&acm->port);
+       if (test_bit(EVENT_RX_STALL, &acm->flags)) {
+               if (!(usb_autopm_get_interface(acm->data))) {
+                       for (i = 0; i < acm->rx_buflimit; i++) {
+                               usb_kill_urb(acm->read_urbs[i]);
+                               set_bit(i, &acm->read_urbs_free);
+                       }
+                       usb_clear_halt(acm->dev, acm->in);
+                       acm_submit_read_urbs(acm, GFP_KERNEL);
+                       usb_autopm_put_interface(acm->data);
+
+               }
+               clear_bit(EVENT_RX_STALL, &acm->flags);
+       }
+
+       if (test_bit(EVENT_TTY_WAKEUP, &acm->flags)) {
+               tty_port_tty_wakeup(&acm->port);
+               clear_bit(EVENT_TTY_WAKEUP, &acm->flags);
+       }
 }
 
 /*
@@ -1654,6 +1687,15 @@ static int acm_reset_resume(struct usb_interface *intf)
 
 #endif /* CONFIG_PM */
 
+static int acm_pre_reset(struct usb_interface *intf)
+{
+       struct acm *acm = usb_get_intfdata(intf);
+
+       clear_bit(EVENT_RX_STALL, &acm->flags);
+
+       return 0;
+}
+
 #define NOKIA_PCSUITE_ACM_INFO(x) \
                USB_DEVICE_AND_INTERFACE_INFO(0x0421, x, \
                USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, \
@@ -1895,6 +1937,7 @@ static struct usb_driver acm_driver = {
        .resume =       acm_resume,
        .reset_resume = acm_reset_resume,
 #endif
+       .pre_reset =    acm_pre_reset,
        .id_table =     acm_ids,
 #ifdef CONFIG_PM
        .supports_autosuspend = 1,
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 58ddd25..1db974d 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -103,6 +103,9 @@ struct acm {
        spinlock_t write_lock;
        struct mutex mutex;
        bool disconnected;
+       unsigned long flags;
+#              define EVENT_TTY_WAKEUP 0
+#              define EVENT_RX_STALL   1
        struct usb_cdc_line_coding line;                /* bits, stop, parity */
        struct work_struct work;                        /* work queue entry for 
line discipline waking up */
        unsigned int ctrlin;                            /* input control lines 
(DCD, DSR, RI, break, overruns) */
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to