aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/class/cdc-acm.c
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2012-11-08 13:47:41 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-11-15 20:39:03 -0500
commitba2d8ce9db0a61505362bb17b8899df3d3326146 (patch)
tree0349f13ab8483458d99815676347f7d5d247ca03 /drivers/usb/class/cdc-acm.c
parent73050d7144272f2485e22685fb266c9a6c2eb2d6 (diff)
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 <dcbw@redhat.com> Cc: stable <stable@vger.kernel.org> Acked-by: Oliver Neukum <oneukum@suse.de> Tested-by: Aleksander Morgado <aleksander@gnu.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r--drivers/usb/class/cdc-acm.c38
1 files changed, 38 insertions, 0 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 6e49ec6f3adc..8d809a811e16 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)
787 tmp.flags = ASYNC_LOW_LATENCY; 787 tmp.flags = ASYNC_LOW_LATENCY;
788 tmp.xmit_fifo_size = acm->writesize; 788 tmp.xmit_fifo_size = acm->writesize;
789 tmp.baud_base = le32_to_cpu(acm->line.dwDTERate); 789 tmp.baud_base = le32_to_cpu(acm->line.dwDTERate);
790 tmp.close_delay = acm->port.close_delay / 10;
791 tmp.closing_wait = acm->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
792 ASYNC_CLOSING_WAIT_NONE :
793 acm->port.closing_wait / 10;
790 794
791 if (copy_to_user(info, &tmp, sizeof(tmp))) 795 if (copy_to_user(info, &tmp, sizeof(tmp)))
792 return -EFAULT; 796 return -EFAULT;
@@ -794,6 +798,37 @@ static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
794 return 0; 798 return 0;
795} 799}
796 800
801static int set_serial_info(struct acm *acm,
802 struct serial_struct __user *newinfo)
803{
804 struct serial_struct new_serial;
805 unsigned int closing_wait, close_delay;
806 int retval = 0;
807
808 if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
809 return -EFAULT;
810
811 close_delay = new_serial.close_delay * 10;
812 closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
813 ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10;
814
815 mutex_lock(&acm->port.mutex);
816
817 if (!capable(CAP_SYS_ADMIN)) {
818 if ((close_delay != acm->port.close_delay) ||
819 (closing_wait != acm->port.closing_wait))
820 retval = -EPERM;
821 else
822 retval = -EOPNOTSUPP;
823 } else {
824 acm->port.close_delay = close_delay;
825 acm->port.closing_wait = closing_wait;
826 }
827
828 mutex_unlock(&acm->port.mutex);
829 return retval;
830}
831
797static int acm_tty_ioctl(struct tty_struct *tty, 832static int acm_tty_ioctl(struct tty_struct *tty,
798 unsigned int cmd, unsigned long arg) 833 unsigned int cmd, unsigned long arg)
799{ 834{
@@ -804,6 +839,9 @@ static int acm_tty_ioctl(struct tty_struct *tty,
804 case TIOCGSERIAL: /* gets serial port data */ 839 case TIOCGSERIAL: /* gets serial port data */
805 rv = get_serial_info(acm, (struct serial_struct __user *) arg); 840 rv = get_serial_info(acm, (struct serial_struct __user *) arg);
806 break; 841 break;
842 case TIOCSSERIAL:
843 rv = set_serial_info(acm, (struct serial_struct __user *) arg);
844 break;
807 } 845 }
808 846
809 return rv; 847 return rv;