]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/class/cdc-acm.c
usb: cdc-acm: stop dropping tx buffers
[linux-2.6-omap-h63xx.git] / drivers / usb / class / cdc-acm.c
index ba86fec872b41abe4a0e8c8909607f0d5deb0c43..a9dd28f446d8f1fad580eda15ff2c75dc49afc75 100644 (file)
@@ -144,11 +144,6 @@ static int acm_wb_is_avail(struct acm *acm)
        return n;
 }
 
-static inline int acm_wb_is_used(struct acm *acm, int wbn)
-{
-       return acm->wb[wbn].use;
-}
-
 /*
  * Finish write.
  */
@@ -157,7 +152,6 @@ static void acm_write_done(struct acm *acm, struct acm_wb *wb)
        unsigned long flags;
 
        spin_lock_irqsave(&acm->write_lock, flags);
-       acm->write_ready = 1;
        wb->use = 0;
        acm->transmitting--;
        spin_unlock_irqrestore(&acm->write_lock, flags);
@@ -190,40 +184,25 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
 static int acm_write_start(struct acm *acm, int wbn)
 {
        unsigned long flags;
-       struct acm_wb *wb;
+       struct acm_wb *wb = &acm->wb[wbn];
        int rc;
 
        spin_lock_irqsave(&acm->write_lock, flags);
        if (!acm->dev) {
+               wb->use = 0;
                spin_unlock_irqrestore(&acm->write_lock, flags);
                return -ENODEV;
        }
 
-       if (!acm->write_ready) {
-               spin_unlock_irqrestore(&acm->write_lock, flags);
-               return 0;       /* A white lie */
-       }
-
-       wb = &acm->wb[wbn];
-       if(acm_wb_is_avail(acm) <= 1)
-               acm->write_ready = 0;
-
        dbg("%s susp_count: %d", __func__, acm->susp_count);
        if (acm->susp_count) {
-               acm->old_ready = acm->write_ready;
                acm->delayed_wb = wb;
-               acm->write_ready = 0;
                schedule_work(&acm->waker);
                spin_unlock_irqrestore(&acm->write_lock, flags);
                return 0;       /* A white lie */
        }
        usb_mark_last_busy(acm->dev);
 
-       if (!acm_wb_is_used(acm, wbn)) {
-               spin_unlock_irqrestore(&acm->write_lock, flags);
-               return 0;
-       }
-
        rc = acm_start_wb(acm, wb);
        spin_unlock_irqrestore(&acm->write_lock, flags);
 
@@ -512,7 +491,6 @@ static void acm_softint(struct work_struct *work)
 static void acm_waker(struct work_struct *waker)
 {
        struct acm *acm = container_of(waker, struct acm, waker);
-       long flags;
        int rv;
 
        rv = usb_autopm_get_interface(acm->control);
@@ -524,9 +502,6 @@ static void acm_waker(struct work_struct *waker)
                acm_start_wb(acm, acm->delayed_wb);
                acm->delayed_wb = NULL;
        }
-       spin_lock_irqsave(&acm->write_lock, flags);
-       acm->write_ready = acm->old_ready;
-       spin_unlock_irqrestore(&acm->write_lock, flags);
        usb_autopm_put_interface(acm->control);
 }
 
@@ -697,7 +672,7 @@ static int acm_tty_write_room(struct tty_struct *tty)
         * Do not let the line discipline to know that we have a reserve,
         * or it might get too enthusiastic.
         */
-       return (acm->write_ready && acm_wb_is_avail(acm)) ? acm->writesize : 0;
+       return acm_wb_is_avail(acm) ? acm->writesize : 0;
 }
 
 static int acm_tty_chars_in_buffer(struct tty_struct *tty)
@@ -732,13 +707,16 @@ static void acm_tty_unthrottle(struct tty_struct *tty)
        tasklet_schedule(&acm->urb_task);
 }
 
-static void acm_tty_break_ctl(struct tty_struct *tty, int state)
+static int acm_tty_break_ctl(struct tty_struct *tty, int state)
 {
        struct acm *acm = tty->driver_data;
+       int retval;
        if (!ACM_READY(acm))
-               return;
-       if (acm_send_break(acm, state ? 0xffff : 0))
+               return -EINVAL;
+       retval = acm_send_break(acm, state ? 0xffff : 0);
+       if (retval < 0)
                dbg("send break failed");
+       return retval;
 }
 
 static int acm_tty_tiocmget(struct tty_struct *tty, struct file *file)
@@ -838,7 +816,7 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios
  * USB probe and disconnect routines.
  */
 
-/* Little helper: write buffers free */
+/* Little helpers: write/read buffers free */
 static void acm_write_buffers_free(struct acm *acm)
 {
        int i;
@@ -849,6 +827,15 @@ static void acm_write_buffers_free(struct acm *acm)
        }
 }
 
+static void acm_read_buffers_free(struct acm *acm)
+{
+       struct usb_device *usb_dev = interface_to_usbdev(acm->control);
+       int i, n = acm->rx_buflimit;
+
+       for (i = 0; i < n; i++)
+               usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
+}
+
 /* Little helper: write buffers allocate */
 static int acm_write_buffers_alloc(struct acm *acm)
 {
@@ -1064,7 +1051,6 @@ skip_normal_probe:
        spin_lock_init(&acm->write_lock);
        spin_lock_init(&acm->read_lock);
        mutex_init(&acm->mutex);
-       acm->write_ready = 1;
        acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
 
        buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
@@ -1096,9 +1082,11 @@ skip_normal_probe:
                rcv->instance = acm;
        }
        for (i = 0; i < num_rx_buf; i++) {
-               struct acm_rb *buf = &(acm->rb[i]);
+               struct acm_rb *rb = &(acm->rb[i]);
 
-               if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) {
+               rb->base = usb_buffer_alloc(acm->dev, readsize,
+                               GFP_KERNEL, &rb->dma);
+               if (!rb->base) {
                        dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n");
                        goto alloc_fail7;
                }
@@ -1160,6 +1148,7 @@ skip_countries:
        acm_set_line(acm, &acm->line);
 
        usb_driver_claim_interface(&acm_driver, data_interface, acm);
+       usb_set_intfdata(data_interface, acm);
 
        usb_get_intf(control_interface);
        tty_register_device(acm_tty_driver, minor, &control_interface->dev);
@@ -1171,8 +1160,7 @@ alloc_fail8:
        for (i = 0; i < ACM_NW; i++)
                usb_free_urb(acm->wb[i].urb);
 alloc_fail7:
-       for (i = 0; i < num_rx_buf; i++)
-               usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
+       acm_read_buffers_free(acm);
        for (i = 0; i < num_rx_buf; i++)
                usb_free_urb(acm->ru[i].urb);
        usb_free_urb(acm->ctrlurb);
@@ -1209,18 +1197,12 @@ static void acm_disconnect(struct usb_interface *intf)
 {
        struct acm *acm = usb_get_intfdata(intf);
        struct usb_device *usb_dev = interface_to_usbdev(intf);
-       int i;
 
-       if (!acm || !acm->dev) {
-               dbg("disconnect on nonexisting interface");
+       /* sibling interface is already cleaning up */
+       if (!acm)
                return;
-       }
 
        mutex_lock(&open_mutex);
-       if (!usb_get_intfdata(intf)) {
-               mutex_unlock(&open_mutex);
-               return;
-       }
        if (acm->country_codes){
                device_remove_file(&acm->control->dev,
                                &dev_attr_wCountryCodes);
@@ -1236,10 +1218,10 @@ static void acm_disconnect(struct usb_interface *intf)
 
        acm_write_buffers_free(acm);
        usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
-       for (i = 0; i < acm->rx_buflimit; i++)
-               usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
+       acm_read_buffers_free(acm);
 
-       usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf);
+       usb_driver_release_interface(&acm_driver, intf == acm->control ?
+                                       acm->data : acm->control);
 
        if (!acm->used) {
                acm_tty_unregister(acm);
@@ -1253,6 +1235,7 @@ static void acm_disconnect(struct usb_interface *intf)
                tty_hangup(acm->tty);
 }
 
+#ifdef CONFIG_PM
 static int acm_suspend(struct usb_interface *intf, pm_message_t message)
 {
        struct acm *acm = usb_get_intfdata(intf);
@@ -1318,6 +1301,8 @@ err_out:
        mutex_unlock(&acm->mutex);
        return rv;
 }
+
+#endif /* CONFIG_PM */
 /*
  * USB driver structure.
  */
@@ -1373,10 +1358,14 @@ static struct usb_driver acm_driver = {
        .name =         "cdc_acm",
        .probe =        acm_probe,
        .disconnect =   acm_disconnect,
+#ifdef CONFIG_PM
        .suspend =      acm_suspend,
        .resume =       acm_resume,
+#endif
        .id_table =     acm_ids,
+#ifdef CONFIG_PM
        .supports_autosuspend = 1,
+#endif
 };
 
 /*