From ba2d8ce9db0a61505362bb17b8899df3d3326146 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 8 Nov 2012 12:47:41 -0600 Subject: cdc-acm: implement TIOCSSERIAL to avoid blocking close(2) Some devices (ex Nokia C7) simply don't respond at all when data is sent to some of their USB interfaces. The data gets stuck in the TTYs queue and sits there until close(2), which them blocks because closing_wait defaults to 30 seconds (even though the fd is O_NONBLOCK). This is rarely desired. Implement the standard mechanism to adjust closing_wait and let applications handle it how they want to. See also 02303f73373aa1da19dbec510ec5a4e2576f9610 for usb_wwan.c. Signed-off-by: Dan Williams Cc: stable Acked-by: Oliver Neukum Tested-by: Aleksander Morgado Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 6e49ec6..8d809a8 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -787,6 +787,10 @@ static int get_serial_info(struct acm *acm, struct serial_struct __user *info) tmp.flags = ASYNC_LOW_LATENCY; tmp.xmit_fifo_size = acm->writesize; tmp.baud_base = le32_to_cpu(acm->line.dwDTERate); + tmp.close_delay = acm->port.close_delay / 10; + tmp.closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? + ASYNC_CLOSING_WAIT_NONE : + acm->port.closing_wait / 10; if (copy_to_user(info, &tmp, sizeof(tmp))) return -EFAULT; @@ -794,6 +798,37 @@ static int get_serial_info(struct acm *acm, struct serial_struct __user *info) return 0; } +static int set_serial_info(struct acm *acm, + struct serial_struct __user *newinfo) +{ + struct serial_struct new_serial; + unsigned int closing_wait, close_delay; + int retval = 0; + + if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) + return -EFAULT; + + close_delay = new_serial.close_delay * 10; + closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? + ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; + + mutex_lock(&acm->port.mutex); + + if (!capable(CAP_SYS_ADMIN)) { + if ((close_delay != acm->port.close_delay) || + (closing_wait != acm->port.closing_wait)) + retval = -EPERM; + else + retval = -EOPNOTSUPP; + } else { + acm->port.close_delay = close_delay; + acm->port.closing_wait = closing_wait; + } + + mutex_unlock(&acm->port.mutex); + return retval; +} + static int acm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { @@ -804,6 +839,9 @@ static int acm_tty_ioctl(struct tty_struct *tty, case TIOCGSERIAL: /* gets serial port data */ rv = get_serial_info(acm, (struct serial_struct __user *) arg); break; + case TIOCSSERIAL: + rv = set_serial_info(acm, (struct serial_struct __user *) arg); + break; } return rv; -- cgit v1.1 From 036915a7a402753c05b8d0529f5fd08805ab46d0 Mon Sep 17 00:00:00 2001 From: Denis N Ladin Date: Wed, 26 Dec 2012 18:29:44 +0500 Subject: USB: cdc-acm: Add support for "PSC Scanning, Magellan 800i" Adding support "PSC Scanning, Magellan 800i" in cdc-acm Very simple, but very necessary. Suitable for all versions of the kernel > 2.6 Signed-off-by: Denis N Ladin Cc: stable Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 8d809a8..2d92cce 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1602,6 +1602,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */ .driver_info = NO_UNION_NORMAL, }, + { USB_DEVICE(0x05f9, 0x4002), /* PSC Scanning, Magellan 800i */ + .driver_info = NO_UNION_NORMAL, + }, { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */ .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ }, -- cgit v1.1 From 05c7cd39907184328f48d3e7899f9cdd653ad336 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 3 Jan 2013 15:53:04 +0100 Subject: TTY: switch tty_insert_flip_string Now, we start converting tty buffer functions to actually use tty_port. This will allow us to get rid of the need of tty in many call sites. Only tty_port will needed and hence no more tty_port_tty_get in those paths. tty_insert_flip_string this time. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 8d809a8..20dc2ad 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -419,7 +419,8 @@ static void acm_process_read_urb(struct acm *acm, struct urb *urb) if (!tty) return; - tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length); + tty_insert_flip_string(&acm->port, urb->transfer_buffer, + urb->actual_length); tty_flip_buffer_push(tty); tty_kref_put(tty); -- cgit v1.1 From 2e124b4a390ca85325fae75764bef92f0547fa25 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 3 Jan 2013 15:53:06 +0100 Subject: TTY: switch tty_flip_buffer_push Now, we start converting tty buffer functions to actually use tty_port. This will allow us to get rid of the need of tty in many call sites. Only tty_port will needed and hence no more tty_port_tty_get in those paths. Now, the one where most of tty_port_tty_get gets removed: tty_flip_buffer_push. IOW we also closed all the races in drivers not using tty_port_tty_get at all yet. Also we move tty_flip_buffer_push declaration from include/linux/tty.h to include/linux/tty_flip.h to all others while we are changing it anyway. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 20dc2ad..15b36e2 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -410,20 +410,12 @@ static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags) static void acm_process_read_urb(struct acm *acm, struct urb *urb) { - struct tty_struct *tty; - if (!urb->actual_length) return; - tty = tty_port_tty_get(&acm->port); - if (!tty) - return; - tty_insert_flip_string(&acm->port, urb->transfer_buffer, urb->actual_length); - tty_flip_buffer_push(tty); - - tty_kref_put(tty); + tty_flip_buffer_push(&acm->port); } static void acm_read_bulk_callback(struct urb *urb) -- cgit v1.1 From 4f73bc4dd3e8563ef4109f293a092820dff66d92 Mon Sep 17 00:00:00 2001 From: Joe Millenbach Date: Thu, 17 Jan 2013 22:44:22 -0800 Subject: tty: Added a CONFIG_TTY option to allow removal of TTY The option allows you to remove TTY and compile without errors. This saves space on systems that won't support TTY interfaces anyway. bloat-o-meter output is below. The bulk of this patch consists of Kconfig changes adding "depends on TTY" to various serial devices and similar drivers that require the TTY layer. Ideally, these dependencies would occur on a common intermediate symbol such as SERIO, but most drivers "select SERIO" rather than "depends on SERIO", and "select" does not respect dependencies. bloat-o-meter output comparing our previous minimal to new minimal by removing TTY. The list is filtered to not show removed entries with awk '$3 != "-"' as the list was very long. add/remove: 0/226 grow/shrink: 2/14 up/down: 6/-35356 (-35350) function old new delta chr_dev_init 166 170 +4 allow_signal 80 82 +2 static.__warned 143 142 -1 disallow_signal 63 62 -1 __set_special_pids 95 94 -1 unregister_console 126 121 -5 start_kernel 546 541 -5 register_console 593 588 -5 copy_from_user 45 40 -5 sys_setsid 128 120 -8 sys_vhangup 32 19 -13 do_exit 1543 1526 -17 bitmap_zero 60 40 -20 arch_local_irq_save 137 117 -20 release_task 674 652 -22 static.spin_unlock_irqrestore 308 260 -48 Signed-off-by: Joe Millenbach Reviewed-by: Jamey Sharp Reviewed-by: Josh Triplett Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig index 2519e32..316aac8 100644 --- a/drivers/usb/class/Kconfig +++ b/drivers/usb/class/Kconfig @@ -6,7 +6,7 @@ comment "USB Device Class drivers" config USB_ACM tristate "USB Modem (CDC ACM) support" - depends on USB + depends on USB && TTY ---help--- This driver supports USB modems and ISDN adapters which support the Communication Device Class Abstract Control Model interface. -- cgit v1.1 From c0f5ecee4e741667b2493c742b60b6218d40b3aa Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 12 Mar 2013 14:52:42 +0100 Subject: USB: cdc-wdm: fix buffer overflow The buffer for responses must not overflow. If this would happen, set a flag, drop the data and return an error after user space has read all remaining data. Signed-off-by: Oliver Neukum CC: stable@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-wdm.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'drivers/usb/class') diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 5f0cb41..122d056 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -56,6 +56,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids); #define WDM_RESPONDING 7 #define WDM_SUSPENDING 8 #define WDM_RESETTING 9 +#define WDM_OVERFLOW 10 #define WDM_MAX 16 @@ -155,6 +156,7 @@ static void wdm_in_callback(struct urb *urb) { struct wdm_device *desc = urb->context; int status = urb->status; + int length = urb->actual_length; spin_lock(&desc->iuspin); clear_bit(WDM_RESPONDING, &desc->flags); @@ -185,9 +187,17 @@ static void wdm_in_callback(struct urb *urb) } desc->rerr = status; - desc->reslength = urb->actual_length; - memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength); - desc->length += desc->reslength; + if (length + desc->length > desc->wMaxCommand) { + /* The buffer would overflow */ + set_bit(WDM_OVERFLOW, &desc->flags); + } else { + /* we may already be in overflow */ + if (!test_bit(WDM_OVERFLOW, &desc->flags)) { + memmove(desc->ubuf + desc->length, desc->inbuf, length); + desc->length += length; + desc->reslength = length; + } + } skip_error: wake_up(&desc->wait); @@ -435,6 +445,11 @@ retry: rv = -ENODEV; goto err; } + if (test_bit(WDM_OVERFLOW, &desc->flags)) { + clear_bit(WDM_OVERFLOW, &desc->flags); + rv = -ENOBUFS; + goto err; + } i++; if (file->f_flags & O_NONBLOCK) { if (!test_bit(WDM_READ, &desc->flags)) { @@ -478,6 +493,7 @@ retry: spin_unlock_irq(&desc->iuspin); goto retry; } + if (!desc->reslength) { /* zero length read */ dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__); clear_bit(WDM_READ, &desc->flags); @@ -1004,6 +1020,7 @@ static int wdm_post_reset(struct usb_interface *intf) struct wdm_device *desc = wdm_find_device(intf); int rv; + clear_bit(WDM_OVERFLOW, &desc->flags); clear_bit(WDM_RESETTING, &desc->flags); rv = recover_from_urb_loss(desc); mutex_unlock(&desc->wlock); -- cgit v1.1