Merge branch 'work.tty-ioctl' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 24 Oct 2018 13:43:41 +0000 (14:43 +0100)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 24 Oct 2018 13:43:41 +0000 (14:43 +0100)
Pull tty ioctl updates from Al Viro:
 "This is the compat_ioctl work related to tty ioctls.

  Quite a bit of dead code taken out, all tty-related stuff gone from
  fs/compat_ioctl.c. A bunch of compat bugs fixed - some still remain,
  but all more or less generic tty-related ioctls should be covered
  (remaining issues are in things like driver-private ioctls in a pcmcia
  serial card driver not getting properly handled in 32bit processes on
  64bit host, etc)"

* 'work.tty-ioctl' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (53 commits)
  kill TIOCSERGSTRUCT
  change semantics of ldisc ->compat_ioctl()
  kill TIOCSER[SG]WILD
  synclink_gt(): fix compat_ioctl()
  pty: fix compat ioctls
  compat_ioctl - kill keyboard ioctl handling
  gigaset: add ->compat_ioctl()
  vt_compat_ioctl(): clean up, use compat_ptr() properly
  gigaset: don't try to printk userland buffer contents
  dgnc: don't bother with (empty) stub for TCXONC
  dgnc: leave TIOC[GS]SOFTCAR to ldisc
  remove fallback to drivers for TIOCGICOUNT
  dgnc: break-related ioctls won't reach ->ioctl()
  kill the rest of tty COMPAT_IOCTL() entries
  dgnc: TIOCM... won't reach ->ioctl()
  isdn_tty: TCSBRK{,P} won't reach ->ioctl()
  kill capinc_tty_ioctl()
  take compat TIOC[SG]SERIAL treatment into tty_compat_ioctl()
  synclink: reduce pointless checks in ->ioctl()
  complete ->[sg]et_serial() switchover
  ...

1  2 
drivers/bluetooth/hci_ldisc.c
drivers/tty/tty_io.c
drivers/tty/vt/vt_ioctl.c
drivers/usb/class/cdc-acm.c
drivers/usb/serial/option.c
drivers/usb/serial/ti_usb_3410_5052.c
net/nfc/nci/uart.c

index ea6238ed5c0eaa095ada2c2b0f88aea27707dcbf,ae0dd57a8e9963e1ecfd86d5bf3beb0e1e98abd2..fbf7b4df23ab94ed2f3b3f2c4a75c3d6320283dd
@@@ -543,8 -543,6 +543,8 @@@ static void hci_uart_tty_close(struct t
        }
        clear_bit(HCI_UART_PROTO_SET, &hu->flags);
  
 +      percpu_free_rwsem(&hu->proto_lock);
 +
        kfree(hu);
  }
  
@@@ -823,6 -821,7 +823,7 @@@ static int __init hci_uart_init(void
        hci_uart_ldisc.read             = hci_uart_tty_read;
        hci_uart_ldisc.write            = hci_uart_tty_write;
        hci_uart_ldisc.ioctl            = hci_uart_tty_ioctl;
+       hci_uart_ldisc.compat_ioctl     = hci_uart_tty_ioctl;
        hci_uart_ldisc.poll             = hci_uart_tty_poll;
        hci_uart_ldisc.receive_buf      = hci_uart_tty_receive;
        hci_uart_ldisc.write_wakeup     = hci_uart_tty_wakeup;
diff --combined drivers/tty/tty_io.c
index 816d8ba971a26a798efb510545d5bc8d47d57bb8,cd8df45f09d902259a9b7edcaeef9df88aa0b0e7..da3c1c2f73c4dadc9e32d3ec8477296b29ba843f
@@@ -97,6 -97,7 +97,7 @@@
  #include <linux/seq_file.h>
  #include <linux/serial.h>
  #include <linux/ratelimit.h>
+ #include <linux/compat.h>
  
  #include <linux/uaccess.h>
  
@@@ -1255,7 -1256,6 +1256,7 @@@ static void tty_driver_remove_tty(struc
  static int tty_reopen(struct tty_struct *tty)
  {
        struct tty_driver *driver = tty->driver;
 +      int retval;
  
        if (driver->type == TTY_DRIVER_TYPE_PTY &&
            driver->subtype == PTY_TYPE_MASTER)
  
        tty->count++;
  
 -      if (!tty->ldisc)
 -              return tty_ldisc_reinit(tty, tty->termios.c_line);
 +      if (tty->ldisc)
 +              return 0;
  
 -      return 0;
 +      retval = tty_ldisc_reinit(tty, tty->termios.c_line);
 +      if (retval)
 +              tty->count--;
 +
 +      return retval;
  }
  
  /**
@@@ -2292,34 -2288,6 +2293,6 @@@ static int tioccons(struct file *file
        return 0;
  }
  
- /**
-  *    fionbio         -       non blocking ioctl
-  *    @file: file to set blocking value
-  *    @p: user parameter
-  *
-  *    Historical tty interfaces had a blocking control ioctl before
-  *    the generic functionality existed. This piece of history is preserved
-  *    in the expected tty API of posix OS's.
-  *
-  *    Locking: none, the open file handle ensures it won't go away.
-  */
- static int fionbio(struct file *file, int __user *p)
- {
-       int nonblock;
-       if (get_user(nonblock, p))
-               return -EFAULT;
-       spin_lock(&file->f_lock);
-       if (nonblock)
-               file->f_flags |= O_NONBLOCK;
-       else
-               file->f_flags &= ~O_NONBLOCK;
-       spin_unlock(&file->f_lock);
-       return 0;
- }
  /**
   *    tiocsetd        -       set line discipline
   *    @tty: tty device
@@@ -2488,22 -2456,40 +2461,40 @@@ static int tty_tiocgicount(struct tty_s
        return 0;
  }
  
- static void tty_warn_deprecated_flags(struct serial_struct __user *ss)
+ static int tty_tiocsserial(struct tty_struct *tty, struct serial_struct __user *ss)
  {
        static DEFINE_RATELIMIT_STATE(depr_flags,
                        DEFAULT_RATELIMIT_INTERVAL,
                        DEFAULT_RATELIMIT_BURST);
        char comm[TASK_COMM_LEN];
+       struct serial_struct v;
        int flags;
  
-       if (get_user(flags, &ss->flags))
-               return;
+       if (copy_from_user(&v, ss, sizeof(struct serial_struct)))
+               return -EFAULT;
  
-       flags &= ASYNC_DEPRECATED;
+       flags = v.flags & ASYNC_DEPRECATED;
  
        if (flags && __ratelimit(&depr_flags))
                pr_warn("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n",
                        __func__, get_task_comm(comm, current), flags);
+       if (!tty->ops->set_serial)
+               return -ENOTTY;
+       return tty->ops->set_serial(tty, &v);
+ }
+ static int tty_tiocgserial(struct tty_struct *tty, struct serial_struct __user *ss)
+ {
+       struct serial_struct v;
+       int err;
+       memset(&v, 0, sizeof(struct serial_struct));
+       if (!tty->ops->get_serial)
+               return -ENOTTY;
+       err = tty->ops->get_serial(tty, &v);
+       if (!err && copy_to_user(ss, &v, sizeof(struct serial_struct)))
+               err = -EFAULT;
+       return err;
  }
  
  /*
@@@ -2566,8 -2552,6 +2557,6 @@@ long tty_ioctl(struct file *file, unsig
                return tiocswinsz(real_tty, p);
        case TIOCCONS:
                return real_tty != tty ? -EINVAL : tioccons(file);
-       case FIONBIO:
-               return fionbio(file, p);
        case TIOCEXCL:
                set_bit(TTY_EXCLUSIVE, &tty->flags);
                return 0;
        case TIOCMBIS:
                return tty_tiocmset(tty, cmd, p);
        case TIOCGICOUNT:
-               retval = tty_tiocgicount(tty, p);
-               /* For the moment allow fall through to the old method */
-               if (retval != -EINVAL)
-                       return retval;
-               break;
+               return tty_tiocgicount(tty, p);
        case TCFLSH:
                switch (arg) {
                case TCIFLUSH:
                }
                break;
        case TIOCSSERIAL:
-               tty_warn_deprecated_flags(p);
-               break;
+               return tty_tiocsserial(tty, p);
+       case TIOCGSERIAL:
+               return tty_tiocgserial(tty, p);
        case TIOCGPTPEER:
                /* Special because the struct file is needed */
                return ptm_open_peer(file, tty, (int)arg);
  }
  
  #ifdef CONFIG_COMPAT
+ struct serial_struct32 {
+         compat_int_t    type;
+         compat_int_t    line;
+         compat_uint_t   port;
+         compat_int_t    irq;
+         compat_int_t    flags;
+         compat_int_t    xmit_fifo_size;
+         compat_int_t    custom_divisor;
+         compat_int_t    baud_base;
+         unsigned short  close_delay;
+         char    io_type;
+         char    reserved_char[1];
+         compat_int_t    hub6;
+         unsigned short  closing_wait; /* time to wait before closing */
+         unsigned short  closing_wait2; /* no longer used... */
+         compat_uint_t   iomem_base;
+         unsigned short  iomem_reg_shift;
+         unsigned int    port_high;
+      /* compat_ulong_t  iomap_base FIXME */
+         compat_int_t    reserved[1];
+ };
+ static int compat_tty_tiocsserial(struct tty_struct *tty,
+               struct serial_struct32 __user *ss)
+ {
+       static DEFINE_RATELIMIT_STATE(depr_flags,
+                       DEFAULT_RATELIMIT_INTERVAL,
+                       DEFAULT_RATELIMIT_BURST);
+       char comm[TASK_COMM_LEN];
+       struct serial_struct32 v32;
+       struct serial_struct v;
+       int flags;
+       if (copy_from_user(&v32, ss, sizeof(struct serial_struct32)))
+               return -EFAULT;
+       memcpy(&v, &v32, offsetof(struct serial_struct32, iomem_base));
+       v.iomem_base = compat_ptr(v32.iomem_base);
+       v.iomem_reg_shift = v32.iomem_reg_shift;
+       v.port_high = v32.port_high;
+       v.iomap_base = 0;
+       flags = v.flags & ASYNC_DEPRECATED;
+       if (flags && __ratelimit(&depr_flags))
+               pr_warn("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n",
+                       __func__, get_task_comm(comm, current), flags);
+       if (!tty->ops->set_serial)
+               return -ENOTTY;
+       return tty->ops->set_serial(tty, &v);
+ }
+ static int compat_tty_tiocgserial(struct tty_struct *tty,
+                       struct serial_struct32 __user *ss)
+ {
+       struct serial_struct32 v32;
+       struct serial_struct v;
+       int err;
+       memset(&v, 0, sizeof(struct serial_struct));
+       if (!tty->ops->set_serial)
+               return -ENOTTY;
+       err = tty->ops->get_serial(tty, &v);
+       if (!err) {
+               memcpy(&v32, &v, offsetof(struct serial_struct32, iomem_base));
+               v32.iomem_base = (unsigned long)v.iomem_base >> 32 ?
+                       0xfffffff : ptr_to_compat(v.iomem_base);
+               v32.iomem_reg_shift = v.iomem_reg_shift;
+               v32.port_high = v.port_high;
+               if (copy_to_user(ss, &v32, sizeof(struct serial_struct32)))
+                       err = -EFAULT;
+       }
+       return err;
+ }
  static long tty_compat_ioctl(struct file *file, unsigned int cmd,
                                unsigned long arg)
  {
        struct tty_ldisc *ld;
        int retval = -ENOIOCTLCMD;
  
+       switch (cmd) {
+       case TIOCSTI:
+       case TIOCGWINSZ:
+       case TIOCSWINSZ:
+       case TIOCGEXCL:
+       case TIOCGETD:
+       case TIOCSETD:
+       case TIOCGDEV:
+       case TIOCMGET:
+       case TIOCMSET:
+       case TIOCMBIC:
+       case TIOCMBIS:
+       case TIOCGICOUNT:
+       case TIOCGPGRP:
+       case TIOCSPGRP:
+       case TIOCGSID:
+       case TIOCSERGETLSR:
+       case TIOCGRS485:
+       case TIOCSRS485:
+ #ifdef TIOCGETP
+       case TIOCGETP:
+       case TIOCSETP:
+       case TIOCSETN:
+ #endif
+ #ifdef TIOCGETC
+       case TIOCGETC:
+       case TIOCSETC:
+ #endif
+ #ifdef TIOCGLTC
+       case TIOCGLTC:
+       case TIOCSLTC:
+ #endif
+       case TCSETSF:
+       case TCSETSW:
+       case TCSETS:
+       case TCGETS:
+ #ifdef TCGETS2
+       case TCGETS2:
+       case TCSETSF2:
+       case TCSETSW2:
+       case TCSETS2:
+ #endif
+       case TCGETA:
+       case TCSETAF:
+       case TCSETAW:
+       case TCSETA:
+       case TIOCGLCKTRMIOS:
+       case TIOCSLCKTRMIOS:
+ #ifdef TCGETX
+       case TCGETX:
+       case TCSETX:
+       case TCSETXW:
+       case TCSETXF:
+ #endif
+       case TIOCGSOFTCAR:
+       case TIOCSSOFTCAR:
+               return tty_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+       case TIOCCONS:
+       case TIOCEXCL:
+       case TIOCNXCL:
+       case TIOCVHANGUP:
+       case TIOCSBRK:
+       case TIOCCBRK:
+       case TCSBRK:
+       case TCSBRKP:
+       case TCFLSH:
+       case TIOCGPTPEER:
+       case TIOCNOTTY:
+       case TIOCSCTTY:
+       case TCXONC:
+       case TIOCMIWAIT:
+       case TIOCSERCONFIG:
+               return tty_ioctl(file, cmd, arg);
+       }
        if (tty_paranoia_check(tty, file_inode(file), "tty_ioctl"))
                return -EINVAL;
  
+       switch (cmd) {
+       case TIOCSSERIAL:
+               return compat_tty_tiocsserial(tty, compat_ptr(arg));
+       case TIOCGSERIAL:
+               return compat_tty_tiocgserial(tty, compat_ptr(arg));
+       }
        if (tty->ops->compat_ioctl) {
                retval = tty->ops->compat_ioctl(tty, cmd, arg);
                if (retval != -ENOIOCTLCMD)
                return hung_up_tty_compat_ioctl(file, cmd, arg);
        if (ld->ops->compat_ioctl)
                retval = ld->ops->compat_ioctl(tty, file, cmd, arg);
-       else
-               retval = n_tty_compat_ioctl_helper(tty, file, cmd, arg);
+       if (retval == -ENOIOCTLCMD && ld->ops->ioctl)
+               retval = ld->ops->ioctl(tty, file,
+                               (unsigned long)compat_ptr(cmd), arg);
        tty_ldisc_deref(ld);
  
        return retval;
@@@ -2743,7 -2881,7 +2886,7 @@@ void __do_SAK(struct tty_struct *tty
        do_each_pid_task(session, PIDTYPE_SID, p) {
                tty_notice(tty, "SAK: killed process %d (%s): by session\n",
                           task_pid_nr(p), p->comm);
 -              send_sig(SIGKILL, p, 1);
 +              group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p, PIDTYPE_SID);
        } while_each_pid_task(session, PIDTYPE_SID, p);
  
        /* Now kill any processes that happen to have the tty open */
                if (p->signal->tty == tty) {
                        tty_notice(tty, "SAK: killed process %d (%s): by controlling tty\n",
                                   task_pid_nr(p), p->comm);
 -                      send_sig(SIGKILL, p, 1);
 +                      group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p, PIDTYPE_SID);
                        continue;
                }
                task_lock(p);
                if (i != 0) {
                        tty_notice(tty, "SAK: killed process %d (%s): by fd#%d\n",
                                   task_pid_nr(p), p->comm, i - 1);
 -                      force_sig(SIGKILL, p);
 +                      group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p, PIDTYPE_SID);
                }
                task_unlock(p);
        } while_each_thread(g, p);
index 73cdc0d633dd62c12e6ed1ca6545846b8dabaa09,82d38d7ba014610eb07092c4b5f6325a35b895e4..8b0ed139592f95bf4c42fdfbd88273dec874372e
@@@ -32,8 -32,6 +32,8 @@@
  #include <asm/io.h>
  #include <linux/uaccess.h>
  
 +#include <linux/nospec.h>
 +
  #include <linux/kbd_kern.h>
  #include <linux/vt_kern.h>
  #include <linux/kbd_diacr.h>
@@@ -702,8 -700,6 +702,8 @@@ int vt_ioctl(struct tty_struct *tty
                if (vsa.console == 0 || vsa.console > MAX_NR_CONSOLES)
                        ret = -ENXIO;
                else {
 +                      vsa.console = array_index_nospec(vsa.console,
 +                                                       MAX_NR_CONSOLES + 1);
                        vsa.console--;
                        console_lock();
                        ret = vc_allocate(vsa.console);
@@@ -1175,17 -1171,13 +1175,13 @@@ long vt_compat_ioctl(struct tty_struct 
  {
        struct vc_data *vc = tty->driver_data;
        struct console_font_op op;      /* used in multiple places here */
-       unsigned int console;
-       void __user *up = (void __user *)arg;
+       unsigned int console = vc->vc_num;
+       void __user *up = compat_ptr(arg);
        int perm;
-       int ret = 0;
  
-       console = vc->vc_num;
  
-       if (!vc_cons_allocated(console)) {      /* impossible? */
-               ret = -ENOIOCTLCMD;
-               goto out;
-       }
+       if (!vc_cons_allocated(console))        /* impossible? */
+               return -ENOIOCTLCMD;
  
        /*
         * To have permissions to do most of the vt ioctls, we either have
         */
        case PIO_FONTX:
        case GIO_FONTX:
-               ret = compat_fontx_ioctl(cmd, up, perm, &op);
-               break;
+               return compat_fontx_ioctl(cmd, up, perm, &op);
  
        case KDFONTOP:
-               ret = compat_kdfontop_ioctl(up, perm, &op, vc);
-               break;
+               return compat_kdfontop_ioctl(up, perm, &op, vc);
  
        case PIO_UNIMAP:
        case GIO_UNIMAP:
-               ret = compat_unimap_ioctl(cmd, up, perm, vc);
-               break;
+               return compat_unimap_ioctl(cmd, up, perm, vc);
  
        /*
         * all these treat 'arg' as an integer
        case VT_DISALLOCATE:
        case VT_RESIZE:
        case VT_RESIZEX:
-               goto fallback;
+               return vt_ioctl(tty, cmd, arg);
  
        /*
         * the rest has a compatible data structure behind arg,
         * but we have to convert it to a proper 64 bit pointer.
         */
        default:
-               arg = (unsigned long)compat_ptr(arg);
-               goto fallback;
+               return vt_ioctl(tty, cmd, (unsigned long)up);
        }
- out:
-       return ret;
- fallback:
-       return vt_ioctl(tty, cmd, arg);
  }
  
  
index 9ede35cecb1267be281ca9f3733f187a79f16d23,5fd59a58de929831a1880d5e0edba7bc3229ddde..47d75c20c211c6f70cad75fcae4ae6b75c458c09
@@@ -310,17 -310,17 +310,17 @@@ static void acm_process_notification(st
  
                if (difference & ACM_CTRL_DSR)
                        acm->iocount.dsr++;
 -              if (difference & ACM_CTRL_BRK)
 -                      acm->iocount.brk++;
 -              if (difference & ACM_CTRL_RI)
 -                      acm->iocount.rng++;
                if (difference & ACM_CTRL_DCD)
                        acm->iocount.dcd++;
 -              if (difference & ACM_CTRL_FRAMING)
 +              if (newctrl & ACM_CTRL_BRK)
 +                      acm->iocount.brk++;
 +              if (newctrl & ACM_CTRL_RI)
 +                      acm->iocount.rng++;
 +              if (newctrl & ACM_CTRL_FRAMING)
                        acm->iocount.frame++;
 -              if (difference & ACM_CTRL_PARITY)
 +              if (newctrl & ACM_CTRL_PARITY)
                        acm->iocount.parity++;
 -              if (difference & ACM_CTRL_OVERRUN)
 +              if (newctrl & ACM_CTRL_OVERRUN)
                        acm->iocount.overrun++;
                spin_unlock_irqrestore(&acm->read_lock, flags);
  
@@@ -355,6 -355,7 +355,6 @@@ static void acm_ctrl_irq(struct urb *ur
        case -ENOENT:
        case -ESHUTDOWN:
                /* this urb is terminated, clean up */
 -              acm->nb_index = 0;
                dev_dbg(&acm->control->dev,
                        "%s - urb shutting down with status: %d\n",
                        __func__, status);
@@@ -779,9 -780,20 +779,9 @@@ static int acm_tty_write(struct tty_str
        }
  
        if (acm->susp_count) {
 -              if (acm->putbuffer) {
 -                      /* now to preserve order */
 -                      usb_anchor_urb(acm->putbuffer->urb, &acm->delayed);
 -                      acm->putbuffer = NULL;
 -              }
                usb_anchor_urb(wb->urb, &acm->delayed);
                spin_unlock_irqrestore(&acm->write_lock, flags);
                return count;
 -      } else {
 -              if (acm->putbuffer) {
 -                      /* at this point there is no good way to handle errors */
 -                      acm_start_wb(acm, acm->putbuffer);
 -                      acm->putbuffer = NULL;
 -              }
        }
  
        stat = acm_start_wb(acm, wb);
        return count;
  }
  
 -static void acm_tty_flush_chars(struct tty_struct *tty)
 -{
 -      struct acm *acm = tty->driver_data;
 -      struct acm_wb *cur;
 -      int err;
 -      unsigned long flags;
 -
 -      spin_lock_irqsave(&acm->write_lock, flags);
 -
 -      cur = acm->putbuffer;
 -      if (!cur) /* nothing to do */
 -              goto out;
 -
 -      acm->putbuffer = NULL;
 -      err = usb_autopm_get_interface_async(acm->control);
 -      if (err < 0) {
 -              cur->use = 0;
 -              acm->putbuffer = cur;
 -              goto out;
 -      }
 -
 -      if (acm->susp_count)
 -              usb_anchor_urb(cur->urb, &acm->delayed);
 -      else
 -              acm_start_wb(acm, cur);
 -out:
 -      spin_unlock_irqrestore(&acm->write_lock, flags);
 -      return;
 -}
 -
 -static int acm_tty_put_char(struct tty_struct *tty, unsigned char ch)
 -{
 -      struct acm *acm = tty->driver_data;
 -      struct acm_wb *cur;
 -      int wbn;
 -      unsigned long flags;
 -
 -overflow:
 -      cur = acm->putbuffer;
 -      if (!cur) {
 -              spin_lock_irqsave(&acm->write_lock, flags);
 -              wbn = acm_wb_alloc(acm);
 -              if (wbn >= 0) {
 -                      cur = &acm->wb[wbn];
 -                      acm->putbuffer = cur;
 -              }
 -              spin_unlock_irqrestore(&acm->write_lock, flags);
 -              if (!cur)
 -                      return 0;
 -      }
 -
 -      if (cur->len == acm->writesize) {
 -              acm_tty_flush_chars(tty);
 -              goto overflow;
 -      }
 -
 -      cur->buf[cur->len++] = ch;
 -      return 1;
 -}
 -
  static int acm_tty_write_room(struct tty_struct *tty)
  {
        struct acm *acm = tty->driver_data;
@@@ -884,37 -956,28 +884,28 @@@ static int acm_tty_tiocmset(struct tty_
        return acm_set_control(acm, acm->ctrlout = newctrl);
  }
  
- static int get_serial_info(struct acm *acm, struct serial_struct __user *info)
+ static int get_serial_info(struct tty_struct *tty, struct serial_struct *ss)
  {
-       struct serial_struct tmp;
+       struct acm *acm = tty->driver_data;
  
-       memset(&tmp, 0, sizeof(tmp));
-       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 ?
+       ss->xmit_fifo_size = acm->writesize;
+       ss->baud_base = le32_to_cpu(acm->line.dwDTERate);
+       ss->close_delay = acm->port.close_delay / 10;
+       ss->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;
-       else
-               return 0;
+       return 0;
  }
  
- static int set_serial_info(struct acm *acm,
-                               struct serial_struct __user *newinfo)
+ static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss)
  {
-       struct serial_struct new_serial;
+       struct acm *acm = tty->driver_data;
        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;
+       close_delay = ss->close_delay * 10;
+       closing_wait = ss->closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+                       ASYNC_CLOSING_WAIT_NONE : ss->closing_wait * 10;
  
        mutex_lock(&acm->port.mutex);
  
@@@ -999,12 -1062,6 +990,6 @@@ static int acm_tty_ioctl(struct tty_str
        int rv = -ENOIOCTLCMD;
  
        switch (cmd) {
-       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;
        case TIOCMIWAIT:
                rv = usb_autopm_get_interface(acm->control);
                if (rv < 0) {
@@@ -1513,7 -1570,6 +1498,7 @@@ static void acm_disconnect(struct usb_i
  {
        struct acm *acm = usb_get_intfdata(intf);
        struct tty_struct *tty;
 +      int i;
  
        /* sibling interface is already cleaning up */
        if (!acm)
  
        tty_unregister_device(acm_tty_driver, acm->minor);
  
 +      usb_free_urb(acm->ctrlurb);
 +      for (i = 0; i < ACM_NW; i++)
 +              usb_free_urb(acm->wb[i].urb);
 +      for (i = 0; i < acm->rx_buflimit; i++)
 +              usb_free_urb(acm->read_urbs[i]);
        acm_write_buffers_free(acm);
        usb_free_coherent(acm->dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
        acm_read_buffers_free(acm);
@@@ -1641,7 -1692,6 +1626,7 @@@ static int acm_pre_reset(struct usb_int
        struct acm *acm = usb_get_intfdata(intf);
  
        clear_bit(EVENT_RX_STALL, &acm->flags);
 +      acm->nb_index = 0; /* pending control transfers are lost */
  
        return 0;
  }
@@@ -1922,6 -1972,8 +1907,6 @@@ static const struct tty_operations acm_
        .cleanup =              acm_tty_cleanup,
        .hangup =               acm_tty_hangup,
        .write =                acm_tty_write,
 -      .put_char =             acm_tty_put_char,
 -      .flush_chars =          acm_tty_flush_chars,
        .write_room =           acm_tty_write_room,
        .ioctl =                acm_tty_ioctl,
        .throttle =             acm_tty_throttle,
        .set_termios =          acm_tty_set_termios,
        .tiocmget =             acm_tty_tiocmget,
        .tiocmset =             acm_tty_tiocmset,
+       .get_serial =           get_serial_info,
+       .set_serial =           set_serial_info,
        .get_icount =           acm_tty_get_icount,
  };
  
index e72ad9f81c73973b86bd7992c0f331039a8192bf,e6e811ac23312ae85b11d911529bc814a8fcfd9f..e24ff16d4147754b382595dc401fa79bf815ac6b
@@@ -561,9 -561,6 +561,9 @@@ static void option_instat_callback(stru
  /* Interface is reserved */
  #define RSVD(ifnum)   ((BIT(ifnum) & 0xff) << 0)
  
 +/* Interface must have two endpoints */
 +#define NUMEP2                BIT(16)
 +
  
  static const struct usb_device_id option_ids[] = {
        { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
          .driver_info = RSVD(4) },
        { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_BG96),
          .driver_info = RSVD(4) },
 -      { USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06),
 -        .driver_info = RSVD(4) | RSVD(5) },
 +      { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff),
 +        .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 },
 +      { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0, 0) },
        { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) },
        { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) },
        { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6003),
@@@ -1966,7 -1962,8 +1966,8 @@@ static struct usb_serial_driver option_
        .chars_in_buffer   = usb_wwan_chars_in_buffer,
        .tiocmget          = usb_wwan_tiocmget,
        .tiocmset          = usb_wwan_tiocmset,
-       .ioctl             = usb_wwan_ioctl,
+       .get_serial        = usb_wwan_get_serial_info,
+       .set_serial        = usb_wwan_set_serial_info,
        .attach            = option_attach,
        .release           = option_release,
        .port_probe        = usb_wwan_port_probe,
@@@ -2003,13 -2000,6 +2004,13 @@@ static int option_probe(struct usb_seri
        if (device_flags & RSVD(iface_desc->bInterfaceNumber))
                return -ENODEV;
  
 +      /*
 +       * Allow matching on bNumEndpoints for devices whose interface numbers
 +       * can change (e.g. Quectel EP06).
 +       */
 +      if (device_flags & NUMEP2 && iface_desc->bNumEndpoints != 2)
 +              return -ENODEV;
 +
        /* Store the device flags so we can use them during attach. */
        usb_set_serial_data(serial, (void *)device_flags);
  
index e3c5832337e0bdbb83df78285a697ac5f5a8af3f,2657382280944ad4eef4b8144a648653040b870b..dd0ad67aa71e619907afdab6dc142a4335684e51
@@@ -313,8 -313,6 +313,6 @@@ static int ti_chars_in_buffer(struct tt
  static bool ti_tx_empty(struct usb_serial_port *port);
  static void ti_throttle(struct tty_struct *tty);
  static void ti_unthrottle(struct tty_struct *tty);
- static int ti_ioctl(struct tty_struct *tty,
-               unsigned int cmd, unsigned long arg);
  static void ti_set_termios(struct tty_struct *tty,
                struct usb_serial_port *port, struct ktermios *old_termios);
  static int ti_tiocmget(struct tty_struct *tty);
@@@ -330,10 -328,10 +328,10 @@@ static void ti_recv(struct usb_serial_p
  static void ti_send(struct ti_port *tport);
  static int ti_set_mcr(struct ti_port *tport, unsigned int mcr);
  static int ti_get_lsr(struct ti_port *tport, u8 *lsr);
- static int ti_get_serial_info(struct ti_port *tport,
-       struct serial_struct __user *ret_arg);
- static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport,
-       struct serial_struct __user *new_arg);
+ static int ti_get_serial_info(struct tty_struct *tty,
+       struct serial_struct *ss);
+ static int ti_set_serial_info(struct tty_struct *tty,
+       struct serial_struct *ss);
  static void ti_handle_new_msr(struct ti_port *tport, u8 msr);
  
  static void ti_stop_read(struct ti_port *tport, struct tty_struct *tty);
@@@ -436,7 -434,8 +434,8 @@@ static struct usb_serial_driver ti_1por
        .tx_empty               = ti_tx_empty,
        .throttle               = ti_throttle,
        .unthrottle             = ti_unthrottle,
-       .ioctl                  = ti_ioctl,
+       .get_serial             = ti_get_serial_info,
+       .set_serial             = ti_set_serial_info,
        .set_termios            = ti_set_termios,
        .tiocmget               = ti_tiocmget,
        .tiocmset               = ti_tiocmset,
@@@ -469,7 -468,8 +468,8 @@@ static struct usb_serial_driver ti_2por
        .tx_empty               = ti_tx_empty,
        .throttle               = ti_throttle,
        .unthrottle             = ti_unthrottle,
-       .ioctl                  = ti_ioctl,
+       .get_serial             = ti_get_serial_info,
+       .set_serial             = ti_set_serial_info,
        .set_termios            = ti_set_termios,
        .tiocmget               = ti_tiocmget,
        .tiocmset               = ti_tiocmset,
@@@ -902,24 -902,6 +902,6 @@@ static void ti_unthrottle(struct tty_st
        }
  }
  
- static int ti_ioctl(struct tty_struct *tty,
-       unsigned int cmd, unsigned long arg)
- {
-       struct usb_serial_port *port = tty->driver_data;
-       struct ti_port *tport = usb_get_serial_port_data(port);
-       switch (cmd) {
-       case TIOCGSERIAL:
-               return ti_get_serial_info(tport,
-                               (struct serial_struct __user *)arg);
-       case TIOCSSERIAL:
-               return ti_set_serial_info(tty, tport,
-                               (struct serial_struct __user *)arg);
-       }
-       return -ENOIOCTLCMD;
- }
  static void ti_set_termios(struct tty_struct *tty,
                struct usb_serial_port *port, struct ktermios *old_termios)
  {
@@@ -1119,7 -1101,7 +1101,7 @@@ static void ti_break(struct tty_struct 
  
  static int ti_get_port_from_code(unsigned char code)
  {
 -      return (code >> 4) - 3;
 +      return (code >> 6) & 0x01;
  }
  
  static int ti_get_func_from_code(unsigned char code)
@@@ -1417,45 -1399,37 +1399,37 @@@ free_data
  }
  
  
- static int ti_get_serial_info(struct ti_port *tport,
-       struct serial_struct __user *ret_arg)
+ static int ti_get_serial_info(struct tty_struct *tty,
+       struct serial_struct *ss)
  {
-       struct usb_serial_port *port = tport->tp_port;
-       struct serial_struct ret_serial;
+       struct usb_serial_port *port = tty->driver_data;
+       struct ti_port *tport = usb_get_serial_port_data(port);
        unsigned cwait;
  
        cwait = port->port.closing_wait;
        if (cwait != ASYNC_CLOSING_WAIT_NONE)
                cwait = jiffies_to_msecs(cwait) / 10;
  
-       memset(&ret_serial, 0, sizeof(ret_serial));
-       ret_serial.type = PORT_16550A;
-       ret_serial.line = port->minor;
-       ret_serial.port = port->port_number;
-       ret_serial.xmit_fifo_size = kfifo_size(&port->write_fifo);
-       ret_serial.baud_base = tport->tp_tdev->td_is_3410 ? 921600 : 460800;
-       ret_serial.closing_wait = cwait;
-       if (copy_to_user(ret_arg, &ret_serial, sizeof(*ret_arg)))
-               return -EFAULT;
+       ss->type = PORT_16550A;
+       ss->line = port->minor;
+       ss->port = port->port_number;
+       ss->xmit_fifo_size = kfifo_size(&port->write_fifo);
+       ss->baud_base = tport->tp_tdev->td_is_3410 ? 921600 : 460800;
+       ss->closing_wait = cwait;
        return 0;
  }
  
  
- static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport,
-       struct serial_struct __user *new_arg)
+ static int ti_set_serial_info(struct tty_struct *tty,
+       struct serial_struct *ss)
  {
-       struct serial_struct new_serial;
+       struct usb_serial_port *port = tty->driver_data;
+       struct ti_port *tport = usb_get_serial_port_data(port);
        unsigned cwait;
  
-       if (copy_from_user(&new_serial, new_arg, sizeof(new_serial)))
-               return -EFAULT;
-       cwait = new_serial.closing_wait;
+       cwait = ss->closing_wait;
        if (cwait != ASYNC_CLOSING_WAIT_NONE)
-               cwait = msecs_to_jiffies(10 * new_serial.closing_wait);
+               cwait = msecs_to_jiffies(10 * ss->closing_wait);
  
        tport->tp_port->port.closing_wait = cwait;
  
diff --combined net/nfc/nci/uart.c
index 4503937915add62e980d0111b44ee631ccd790d2,d1fa0f22c10c09b4398f505aab8ed1460c9e633f..78fe622eba6579979f392ec0b03fd8627638a755
@@@ -192,8 -192,10 +192,8 @@@ static void nci_uart_tty_close(struct t
        if (!nu)
                return;
  
 -      if (nu->tx_skb)
 -              kfree_skb(nu->tx_skb);
 -      if (nu->rx_skb)
 -              kfree_skb(nu->rx_skb);
 +      kfree_skb(nu->tx_skb);
 +      kfree_skb(nu->rx_skb);
  
        skb_queue_purge(&nu->tx_q);
  
@@@ -463,6 -465,7 +463,7 @@@ static struct tty_ldisc_ops nci_uart_ld
        .receive_buf    = nci_uart_tty_receive,
        .write_wakeup   = nci_uart_tty_wakeup,
        .ioctl          = nci_uart_tty_ioctl,
+       .compat_ioctl   = nci_uart_tty_ioctl,
  };
  
  static int __init nci_uart_init(void)