tty: Make tiocgicount a handler
authorAlan Cox <alan@linux.intel.com>
Thu, 16 Sep 2010 17:21:24 +0000 (18:21 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 22 Oct 2010 17:20:04 +0000 (10:20 -0700)
Dan Rosenberg noted that various drivers return the struct with uncleared
fields. Instead of spending forever trying to stomp all the drivers that
get it wrong (and every new driver) do the job in one place.

This first patch adds the needed operations and hooks them up, including
the needed USB midlayer and serial core plumbing.

Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/char/tty_io.c
drivers/serial/serial_core.c
drivers/usb/serial/usb-serial.c
include/linux/tty_driver.h
include/linux/usb/serial.h

index e185db36f8ad1e33830f8521f1731f65d6a8c80c..c05c5af5aa044029561e38de9683e12a26c0c0c4 100644 (file)
@@ -96,6 +96,7 @@
 #include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/seq_file.h>
+#include <linux/serial.h>
 
 #include <linux/uaccess.h>
 #include <asm/system.h>
@@ -2511,6 +2512,20 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int
        return tty->ops->tiocmset(tty, file, set, clear);
 }
 
+static int tty_tiocgicount(struct tty_struct *tty, void __user *arg)
+{
+       int retval = -EINVAL;
+       struct serial_icounter_struct icount;
+       memset(&icount, 0, sizeof(icount));
+       if (tty->ops->get_icount)
+               retval = tty->ops->get_icount(tty, &icount);
+       if (retval != 0)
+               return retval;
+       if (copy_to_user(arg, &icount, sizeof(icount)))
+               return -EFAULT;
+       return 0;
+}
+
 struct tty_struct *tty_pair_get_tty(struct tty_struct *tty)
 {
        if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
@@ -2631,6 +2646,12 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        case TIOCMBIC:
        case TIOCMBIS:
                return tty_tiocmset(tty, file, 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;
        case TCFLSH:
                switch (arg) {
                case TCIFLUSH:
index bc6cddd102944f9b0074fa70fbfa8d9b026ff6f2..c4ea14670d4486fc142df31d806c528d2752c238 100644 (file)
@@ -1074,10 +1074,10 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg)
  * NB: both 1->0 and 0->1 transitions are counted except for
  *     RI where only 0->1 is counted.
  */
-static int uart_get_count(struct uart_state *state,
-                         struct serial_icounter_struct __user *icnt)
+static int uart_get_icount(struct tty_struct *tty,
+                         struct serial_icounter_struct *icount)
 {
-       struct serial_icounter_struct icount;
+       struct uart_state *state = tty->driver_data;
        struct uart_icount cnow;
        struct uart_port *uport = state->uart_port;
 
@@ -1085,19 +1085,19 @@ static int uart_get_count(struct uart_state *state,
        memcpy(&cnow, &uport->icount, sizeof(struct uart_icount));
        spin_unlock_irq(&uport->lock);
 
-       icount.cts         = cnow.cts;
-       icount.dsr         = cnow.dsr;
-       icount.rng         = cnow.rng;
-       icount.dcd         = cnow.dcd;
-       icount.rx          = cnow.rx;
-       icount.tx          = cnow.tx;
-       icount.frame       = cnow.frame;
-       icount.overrun     = cnow.overrun;
-       icount.parity      = cnow.parity;
-       icount.brk         = cnow.brk;
-       icount.buf_overrun = cnow.buf_overrun;
+       icount->cts         = cnow.cts;
+       icount->dsr         = cnow.dsr;
+       icount->rng         = cnow.rng;
+       icount->dcd         = cnow.dcd;
+       icount->rx          = cnow.rx;
+       icount->tx          = cnow.tx;
+       icount->frame       = cnow.frame;
+       icount->overrun     = cnow.overrun;
+       icount->parity      = cnow.parity;
+       icount->brk         = cnow.brk;
+       icount->buf_overrun = cnow.buf_overrun;
 
-       return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0;
+       return 0;
 }
 
 /*
@@ -1150,10 +1150,6 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,
        case TIOCMIWAIT:
                ret = uart_wait_modem_status(state, arg);
                break;
-
-       case TIOCGICOUNT:
-               ret = uart_get_count(state, uarg);
-               break;
        }
 
        if (ret != -ENOIOCTLCMD)
@@ -2295,6 +2291,7 @@ static const struct tty_operations uart_ops = {
 #endif
        .tiocmget       = uart_tiocmget,
        .tiocmset       = uart_tiocmset,
+       .get_icount     = uart_get_icount,
 #ifdef CONFIG_CONSOLE_POLL
        .poll_init      = uart_poll_init,
        .poll_get_char  = uart_poll_get_char,
index 7a2177c79bdefb05fe88d6c67ee5b19cd7fae0f5..e64da74bdcc5d112ff20cf46e0c074115ca3ee67 100644 (file)
@@ -519,6 +519,18 @@ static int serial_tiocmset(struct tty_struct *tty, struct file *file,
        return -EINVAL;
 }
 
+static int serial_get_icount(struct tty_struct *tty,
+                               struct serial_icounter_struct *icount)
+{
+       struct usb_serial_port *port = tty->driver_data;
+
+       dbg("%s - port %d", __func__, port->number);
+
+       if (port->serial->type->get_icount)
+               return port->serial->type->get_icount(tty, icount);
+       return -EINVAL;
+}
+
 /*
  * We would be calling tty_wakeup here, but unfortunately some line
  * disciplines have an annoying habit of calling tty->write from
@@ -1195,6 +1207,7 @@ static const struct tty_operations serial_ops = {
        .chars_in_buffer =      serial_chars_in_buffer,
        .tiocmget =             serial_tiocmget,
        .tiocmset =             serial_tiocmset,
+       .get_icount =           serial_get_icount,
        .cleanup =              serial_cleanup,
        .install =              serial_install,
        .proc_fops =            &serial_proc_fops,
index b086779825252b40612062391dad9c67c3912ddf..db2d227694da664f2e789767927da0126de7c64d 100644 (file)
  *     unless the tty also has a valid tty->termiox pointer.
  *
  *     Optional: Called under the termios lock
+ *
+ * int (*get_icount)(struct tty_struct *tty, struct serial_icounter *icount);
+ *
+ *     Called when the device receives a TIOCGICOUNT ioctl. Passed a kernel
+ *     structure to complete. This method is optional and will only be called
+ *     if provided (otherwise EINVAL will be returned).
  */
 
 #include <linux/fs.h>
 
 struct tty_struct;
 struct tty_driver;
+struct serial_icounter_struct;
 
 struct tty_operations {
        struct tty_struct * (*lookup)(struct tty_driver *driver,
@@ -268,6 +275,8 @@ struct tty_operations {
                        unsigned int set, unsigned int clear);
        int (*resize)(struct tty_struct *tty, struct winsize *ws);
        int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
+       int (*get_icount)(struct tty_struct *tty,
+                               struct serial_icounter_struct *icount);
 #ifdef CONFIG_CONSOLE_POLL
        int (*poll_init)(struct tty_driver *driver, int line, char *options);
        int (*poll_get_char)(struct tty_driver *driver, int line);
index 55675b1efb28659b37ff7b165fc6aa6ab0835fd7..16d682f4f7c3572fd946d736af644cd4ef9c0b27 100644 (file)
@@ -271,6 +271,8 @@ struct usb_serial_driver {
        int  (*tiocmget)(struct tty_struct *tty, struct file *file);
        int  (*tiocmset)(struct tty_struct *tty, struct file *file,
                         unsigned int set, unsigned int clear);
+       int  (*get_icount)(struct tty_struct *tty,
+                       struct serial_icounter_struct *icount);
        /* Called by the tty layer for port level work. There may or may not
           be an attached tty at this point */
        void (*dtr_rts)(struct usb_serial_port *port, int on);