diff options
| -rw-r--r-- | drivers/usb/class/cdc-acm.c | 39 | ||||
| -rw-r--r-- | drivers/usb/class/cdc-acm.h | 1 |
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) | |||
| 136 | static int acm_wb_is_avail(struct acm *acm) | 146 | static 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 */ |
| 468 | static void acm_write_bulk(struct urb *urb) | 481 | static 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 | ||
| 481 | static void acm_softint(struct work_struct *work) | 500 | static 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 | ||
| 625 | static int acm_tty_chars_in_buffer(struct tty_struct *tty); | ||
| 626 | |||
| 606 | static void acm_tty_close(struct tty_struct *tty, struct file *filp) | 627 | static 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) */ |
