#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>
        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 &&
        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:
 
  * 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;
 
        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;
 }
 
 /*
        case TIOCMIWAIT:
                ret = uart_wait_modem_status(state, arg);
                break;
-
-       case TIOCGICOUNT:
-               ret = uart_get_count(state, uarg);
-               break;
        }
 
        if (ret != -ENOIOCTLCMD)
 #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,
 
        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
        .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,
 
  *     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,
                        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);
 
        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);