From: From: Vadim Tsozik Added mct_u232_ioctl (implements TIOCMIWAIT command) and mct_u232_get_icount (implements TIOCGICOUNT command) functions. MCT u232 p9 is one of a few usb to serail adapters which converts USB +/-5v voltage levels to COM +/-15 voltages. So it can also power COM interfaced devices. This makes it very usable for legacy COM interfaced data-acquisition hardware. I tested new implementation with AWARE Electronics RM-60 radiation meter, which sends pulse via RNG COM line whenever new particle is registered. Signed-off-by: Vadim Tsozik --- Patch below is based on linux-2.6.37-rc7 --- usb/serial/mct_u232.c.orig 2010-12-26 16:54:16.660294924 -0500 +++ usb/serial/mct_u232.c 2010-12-26 20:45:41.711682127 -0500 @@ -78,6 +78,8 @@ #include #include #include +#include +#include #include "mct_u232.h" /* @@ -104,6 +106,10 @@ static void mct_u232_break_ctl(struct tt static int mct_u232_tiocmget(struct tty_struct *tty, struct file *file); static int mct_u232_tiocmset(struct tty_struct *tty, struct file *file, 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 .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,27 +396,41 @@ static int mct_u232_get_modem_stat(struc return rc; } /* mct_u232_get_modem_stat */ -static void mct_u232_msr_to_state(unsigned int *control_state, - unsigned char msr) +static void mct_u232_msr_to_state(struct mct_u232_private *priv) { + unsigned char msr = priv->last_msr; + unsigned int *control_state = &priv->control_state; + struct async_icount *icount = &priv->icount; + /* Translate Control Line states */ - if (msr & MCT_U232_MSR_DSR) + if (msr & MCT_U232_MSR_DSR) { *control_state |= TIOCM_DSR; - else + icount->dsr++; + } else { *control_state &= ~TIOCM_DSR; - if (msr & MCT_U232_MSR_CTS) + } + if (msr & MCT_U232_MSR_CTS) { *control_state |= TIOCM_CTS; - else + icount->cts++; + } else { *control_state &= ~TIOCM_CTS; - if (msr & MCT_U232_MSR_RI) + } + if (msr & MCT_U232_MSR_RI) { *control_state |= TIOCM_RI; - else + icount->rng++; + } else { *control_state &= ~TIOCM_RI; - if (msr & MCT_U232_MSR_CD) + } + if (msr & MCT_U232_MSR_CD) { *control_state |= TIOCM_CD; - else + icount->dcd++; + } else { *control_state &= ~TIOCM_CD; + } + dbg("msr_to_state: msr=0x%x ==> state=0x%x", msr, *control_state); + + wake_up_interruptible(&priv->msr_wait); } /* mct_u232_msr_to_state */ /* @@ -422,6 +446,7 @@ static int mct_u232_startup(struct usb_s 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); @@ -498,7 +523,7 @@ static int mct_u232_open(struct tty_str mct_u232_get_modem_stat(serial, &last_msr); spin_lock_irqsave(&priv->lock, flags); priv->last_msr = last_msr; - mct_u232_msr_to_state(&priv->control_state, priv->last_msr); + mct_u232_msr_to_state(priv); spin_unlock_irqrestore(&priv->lock, flags); port->read_urb->dev = port->serial->dev; @@ -619,7 +644,7 @@ static void mct_u232_read_int_callback(s priv->last_msr = data[MCT_U232_MSR_INDEX]; /* Record Control Line states */ - mct_u232_msr_to_state(&priv->control_state, priv->last_msr); + mct_u232_msr_to_state(priv); #if 0 /* Not yet handled. See belkin_sa.c for further information */ @@ -826,7 +851,6 @@ static void mct_u232_throttle(struct tty } } - static void mct_u232_unthrottle(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; @@ -847,6 +871,72 @@ static void mct_u232_unthrottle(struct t } } +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; + + dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd); + + switch (cmd) { + + case TIOCMIWAIT: + + dbg("%s (%d) TIOCMIWAIT", __func__, port->number); + + cprev = mct_u232_port->icount; + 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; + cnow = mct_u232_port->icount; + 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; + + 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; + + 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;