aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDavid Brownell <dbrownell@users.sourceforge.net>2008-08-06 21:46:10 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2008-08-13 20:32:57 -0400
commite5fbab51b4219fbd1dab28666affe38a920b5f7e (patch)
treedc2d6ff5467f4eff183de712eb13403d35d13351 /drivers
parent934da4635c2d05cef474e5243ef05df95b2ad264 (diff)
usb: cdc-acm: drain writes on close
Add a mechanism to let the write queue drain naturally before closing the TTY, rather than always losing that data. There is a timeout, so it can't wait too long. Provide missing locking inside acm_wb_is_avail(); it matters more now. Note, this presumes an earlier patch was applied, removing a call to this routine where the lock was held. Slightly improved diagnostics on write URB completion, so we can tell when a write URB gets killed and, if so, how much data it wrote first ... and so that I/O path is normally silent (and can't much change timings). Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/class/cdc-acm.c39
-rw-r--r--drivers/usb/class/cdc-acm.h1
2 files changed, 35 insertions, 5 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index a9dd28f446d8..efc4373ededb 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -51,6 +51,7 @@
51 */ 51 */
52 52
53#undef DEBUG 53#undef DEBUG
54#undef VERBOSE_DEBUG
54 55
55#include <linux/kernel.h> 56#include <linux/kernel.h>
56#include <linux/errno.h> 57#include <linux/errno.h>
@@ -70,6 +71,9 @@
70 71
71#include "cdc-acm.h" 72#include "cdc-acm.h"
72 73
74
75#define ACM_CLOSE_TIMEOUT 15 /* seconds to let writes drain */
76
73/* 77/*
74 * Version Information 78 * Version Information
75 */ 79 */
@@ -85,6 +89,12 @@ static DEFINE_MUTEX(open_mutex);
85 89
86#define ACM_READY(acm) (acm && acm->dev && acm->used) 90#define ACM_READY(acm) (acm && acm->dev && acm->used)
87 91
92#ifdef VERBOSE_DEBUG
93#define verbose 1
94#else
95#define verbose 0
96#endif
97
88/* 98/*
89 * Functions for ACM control messages. 99 * Functions for ACM control messages.
90 */ 100 */
@@ -136,11 +146,14 @@ static int acm_wb_alloc(struct acm *acm)
136static int acm_wb_is_avail(struct acm *acm) 146static int acm_wb_is_avail(struct acm *acm)
137{ 147{
138 int i, n; 148 int i, n;
149 unsigned long flags;
139 150
140 n = ACM_NW; 151 n = ACM_NW;
152 spin_lock_irqsave(&acm->write_lock, flags);
141 for (i = 0; i < ACM_NW; i++) { 153 for (i = 0; i < ACM_NW; i++) {
142 n -= acm->wb[i].use; 154 n -= acm->wb[i].use;
143 } 155 }
156 spin_unlock_irqrestore(&acm->write_lock, flags);
144 return n; 157 return n;
145} 158}
146 159
@@ -467,22 +480,28 @@ urbs:
467/* data interface wrote those outgoing bytes */ 480/* data interface wrote those outgoing bytes */
468static void acm_write_bulk(struct urb *urb) 481static void acm_write_bulk(struct urb *urb)
469{ 482{
470 struct acm *acm;
471 struct acm_wb *wb = urb->context; 483 struct acm_wb *wb = urb->context;
484 struct acm *acm = wb->instance;
472 485
473 dbg("Entering acm_write_bulk with status %d", urb->status); 486 if (verbose || urb->status
487 || (urb->actual_length != urb->transfer_buffer_length))
488 dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
489 urb->actual_length,
490 urb->transfer_buffer_length,
491 urb->status);
474 492
475 acm = wb->instance;
476 acm_write_done(acm, wb); 493 acm_write_done(acm, wb);
477 if (ACM_READY(acm)) 494 if (ACM_READY(acm))
478 schedule_work(&acm->work); 495 schedule_work(&acm->work);
496 else
497 wake_up_interruptible(&acm->drain_wait);
479} 498}
480 499
481static void acm_softint(struct work_struct *work) 500static void acm_softint(struct work_struct *work)
482{ 501{
483 struct acm *acm = container_of(work, struct acm, work); 502 struct acm *acm = container_of(work, struct acm, work);
484 dbg("Entering acm_softint."); 503
485 504 dev_vdbg(&acm->data->dev, "tx work\n");
486 if (!ACM_READY(acm)) 505 if (!ACM_READY(acm))
487 return; 506 return;
488 tty_wakeup(acm->tty); 507 tty_wakeup(acm->tty);
@@ -603,6 +622,8 @@ static void acm_tty_unregister(struct acm *acm)
603 kfree(acm); 622 kfree(acm);
604} 623}
605 624
625static int acm_tty_chars_in_buffer(struct tty_struct *tty);
626
606static void acm_tty_close(struct tty_struct *tty, struct file *filp) 627static void acm_tty_close(struct tty_struct *tty, struct file *filp)
607{ 628{
608 struct acm *acm = tty->driver_data; 629 struct acm *acm = tty->driver_data;
@@ -617,6 +638,13 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
617 if (acm->dev) { 638 if (acm->dev) {
618 usb_autopm_get_interface(acm->control); 639 usb_autopm_get_interface(acm->control);
619 acm_set_control(acm, acm->ctrlout = 0); 640 acm_set_control(acm, acm->ctrlout = 0);
641
642 /* try letting the last writes drain naturally */
643 wait_event_interruptible_timeout(acm->drain_wait,
644 (ACM_NW == acm_wb_is_avail(acm))
645 || !acm->dev,
646 ACM_CLOSE_TIMEOUT * HZ);
647
620 usb_kill_urb(acm->ctrlurb); 648 usb_kill_urb(acm->ctrlurb);
621 for (i = 0; i < ACM_NW; i++) 649 for (i = 0; i < ACM_NW; i++)
622 usb_kill_urb(acm->wb[i].urb); 650 usb_kill_urb(acm->wb[i].urb);
@@ -1047,6 +1075,7 @@ skip_normal_probe:
1047 acm->urb_task.data = (unsigned long) acm; 1075 acm->urb_task.data = (unsigned long) acm;
1048 INIT_WORK(&acm->work, acm_softint); 1076 INIT_WORK(&acm->work, acm_softint);
1049 INIT_WORK(&acm->waker, acm_waker); 1077 INIT_WORK(&acm->waker, acm_waker);
1078 init_waitqueue_head(&acm->drain_wait);
1050 spin_lock_init(&acm->throttle_lock); 1079 spin_lock_init(&acm->throttle_lock);
1051 spin_lock_init(&acm->write_lock); 1080 spin_lock_init(&acm->write_lock);
1052 spin_lock_init(&acm->read_lock); 1081 spin_lock_init(&acm->read_lock);
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 94266362ca68..1f95e7aa1b66 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -113,6 +113,7 @@ struct acm {
113 struct usb_cdc_line_coding line; /* bits, stop, parity */ 113 struct usb_cdc_line_coding line; /* bits, stop, parity */
114 struct work_struct work; /* work queue entry for line discipline waking up */ 114 struct work_struct work; /* work queue entry for line discipline waking up */
115 struct work_struct waker; 115 struct work_struct waker;
116 wait_queue_head_t drain_wait; /* close processing */
116 struct tasklet_struct urb_task; /* rx processing */ 117 struct tasklet_struct urb_task; /* rx processing */
117 spinlock_t throttle_lock; /* synchronize throtteling and read callback */ 118 spinlock_t throttle_lock; /* synchronize throtteling and read callback */
118 unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ 119 unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */