Merge branch 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / drivers / usb / serial / mct_u232.c
index 2849f8c..d2c0196 100644 (file)
@@ -78,6 +78,8 @@
 #include <asm/unaligned.h>
 #include <linux/usb.h>
 #include <linux/usb/serial.h>
+#include <linux/serial.h>
+#include <linux/ioctl.h>
 #include "mct_u232.h"
 
 /*
@@ -101,9 +103,13 @@ static void mct_u232_read_int_callback(struct urb *urb);
 static void mct_u232_set_termios(struct tty_struct *tty,
                        struct usb_serial_port *port, struct ktermios *old);
 static void mct_u232_break_ctl(struct tty_struct *tty, int break_state);
-static int  mct_u232_tiocmget(struct tty_struct *tty, struct file *file);
-static int  mct_u232_tiocmset(struct tty_struct *tty, struct file *file,
+static int  mct_u232_tiocmget(struct tty_struct *tty);
+static int  mct_u232_tiocmset(struct tty_struct *tty,
                        unsigned int set, unsigned int clear);
+static int  mct_u232_ioctl(struct tty_struct *tty, struct file *file,
+                       unsigned int cmd, unsigned long arg);
+static int  mct_u232_get_icount(struct tty_struct *tty,
+                       struct serial_icounter_struct *icount);
 static void mct_u232_throttle(struct tty_struct *tty);
 static void mct_u232_unthrottle(struct tty_struct *tty);
 
@@ -150,9 +156,10 @@ static struct usb_serial_driver mct_u232_device = {
        .tiocmset =          mct_u232_tiocmset,
        .attach =            mct_u232_startup,
        .release =           mct_u232_release,
+       .ioctl =             mct_u232_ioctl,
+       .get_icount =        mct_u232_get_icount,
 };
 
-
 struct mct_u232_private {
        spinlock_t lock;
        unsigned int         control_state; /* Modem Line Setting (TIOCM) */
@@ -160,6 +167,9 @@ struct mct_u232_private {
        unsigned char        last_lsr;      /* Line Status Register */
        unsigned char        last_msr;      /* Modem Status Register */
        unsigned int         rx_flags;      /* Throttling flags */
+       struct async_icount  icount;
+       wait_queue_head_t    msr_wait;  /* for handling sleeping while waiting
+                                               for msr change to happen */
 };
 
 #define THROTTLED              0x01
@@ -386,6 +396,20 @@ static int mct_u232_get_modem_stat(struct usb_serial *serial,
        return rc;
 } /* mct_u232_get_modem_stat */
 
+static void mct_u232_msr_to_icount(struct async_icount *icount,
+                                               unsigned char msr)
+{
+       /* Translate Control Line states */
+       if (msr & MCT_U232_MSR_DDSR)
+               icount->dsr++;
+       if (msr & MCT_U232_MSR_DCTS)
+               icount->cts++;
+       if (msr & MCT_U232_MSR_DRI)
+               icount->rng++;
+       if (msr & MCT_U232_MSR_DCD)
+               icount->dcd++;
+} /* mct_u232_msr_to_icount */
+
 static void mct_u232_msr_to_state(unsigned int *control_state,
                                                unsigned char msr)
 {
@@ -422,6 +446,7 @@ static int mct_u232_startup(struct usb_serial *serial)
        if (!priv)
                return -ENOMEM;
        spin_lock_init(&priv->lock);
+       init_waitqueue_head(&priv->msr_wait);
        usb_set_serial_port_data(serial->port[0], priv);
 
        init_waitqueue_head(&serial->port[0]->write_wait);
@@ -621,6 +646,8 @@ static void mct_u232_read_int_callback(struct urb *urb)
        /* Record Control Line states */
        mct_u232_msr_to_state(&priv->control_state, priv->last_msr);
 
+       mct_u232_msr_to_icount(&priv->icount, priv->last_msr);
+
 #if 0
        /* Not yet handled. See belkin_sa.c for further information */
        /* Now to report any errors */
@@ -647,6 +674,7 @@ static void mct_u232_read_int_callback(struct urb *urb)
                tty_kref_put(tty);
        }
 #endif
+       wake_up_interruptible(&priv->msr_wait);
        spin_unlock_irqrestore(&priv->lock, flags);
 exit:
        retval = usb_submit_urb(urb, GFP_ATOMIC);
@@ -762,7 +790,7 @@ static void mct_u232_break_ctl(struct tty_struct *tty, int break_state)
 } /* mct_u232_break_ctl */
 
 
-static int mct_u232_tiocmget(struct tty_struct *tty, struct file *file)
+static int mct_u232_tiocmget(struct tty_struct *tty)
 {
        struct usb_serial_port *port = tty->driver_data;
        struct mct_u232_private *priv = usb_get_serial_port_data(port);
@@ -778,7 +806,7 @@ static int mct_u232_tiocmget(struct tty_struct *tty, struct file *file)
        return control_state;
 }
 
-static int mct_u232_tiocmset(struct tty_struct *tty, struct file *file,
+static int mct_u232_tiocmset(struct tty_struct *tty,
                              unsigned int set, unsigned int clear)
 {
        struct usb_serial_port *port = tty->driver_data;
@@ -826,7 +854,6 @@ static void mct_u232_throttle(struct tty_struct *tty)
        }
 }
 
-
 static void mct_u232_unthrottle(struct tty_struct *tty)
 {
        struct usb_serial_port *port = tty->driver_data;
@@ -847,6 +874,82 @@ static void mct_u232_unthrottle(struct tty_struct *tty)
        }
 }
 
+static int  mct_u232_ioctl(struct tty_struct *tty, struct file *file,
+                       unsigned int cmd, unsigned long arg)
+{
+       DEFINE_WAIT(wait);
+       struct usb_serial_port *port = tty->driver_data;
+       struct mct_u232_private *mct_u232_port = usb_get_serial_port_data(port);
+       struct async_icount cnow, cprev;
+       unsigned long flags;
+
+       dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
+
+       switch (cmd) {
+
+       case TIOCMIWAIT:
+
+               dbg("%s (%d) TIOCMIWAIT", __func__,  port->number);
+
+               spin_lock_irqsave(&mct_u232_port->lock, flags);
+               cprev = mct_u232_port->icount;
+               spin_unlock_irqrestore(&mct_u232_port->lock, flags);
+               for ( ; ; ) {
+                       prepare_to_wait(&mct_u232_port->msr_wait,
+                                       &wait, TASK_INTERRUPTIBLE);
+                       schedule();
+                       finish_wait(&mct_u232_port->msr_wait, &wait);
+                       /* see if a signal did it */
+                       if (signal_pending(current))
+                               return -ERESTARTSYS;
+                       spin_lock_irqsave(&mct_u232_port->lock, flags);
+                       cnow = mct_u232_port->icount;
+                       spin_unlock_irqrestore(&mct_u232_port->lock, flags);
+                       if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+                           cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+                               return -EIO; /* no change => error */
+                       if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+                           ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+                           ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+                           ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
+                               return 0;
+                       }
+                       cprev = cnow;
+               }
+
+       }
+       return -ENOIOCTLCMD;
+}
+
+static int  mct_u232_get_icount(struct tty_struct *tty,
+                       struct serial_icounter_struct *icount)
+{
+       struct usb_serial_port *port = tty->driver_data;
+       struct mct_u232_private *mct_u232_port = usb_get_serial_port_data(port);
+       struct async_icount *ic = &mct_u232_port->icount;
+       unsigned long flags;
+
+       spin_lock_irqsave(&mct_u232_port->lock, flags);
+
+       icount->cts = ic->cts;
+       icount->dsr = ic->dsr;
+       icount->rng = ic->rng;
+       icount->dcd = ic->dcd;
+       icount->rx = ic->rx;
+       icount->tx = ic->tx;
+       icount->frame = ic->frame;
+       icount->overrun = ic->overrun;
+       icount->parity = ic->parity;
+       icount->brk = ic->brk;
+       icount->buf_overrun = ic->buf_overrun;
+
+       spin_unlock_irqrestore(&mct_u232_port->lock, flags);
+
+       dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d",
+               __func__,  port->number, icount->rx, icount->tx);
+       return 0;
+}
+
 static int __init mct_u232_init(void)
 {
        int retval;