diff options
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 38 |
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 | ||
801 | static 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 | |||
797 | static int acm_tty_ioctl(struct tty_struct *tty, | 832 | static 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; |