[PATCH 1/1] mct_u232: IOCTL implementation

From: Tsozik
Date: Sun Dec 26 2010 - 00:39:56 EST


From: Vadim Tsozik <tsozik@xxxxxxxxx>

Added mct_u232_ioctl function and implemented TIOCMIWAIT and TIOCGICOUNT commands. 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.

Patch below is based on linux-2.6.35.10-72.fc14.x86_64.

Signed-off-by: Vadim Tsozik <tsozik@xxxxxxxxx>

---
--- original/mct_u232.c 2010-12-25 21:31:13.744174626 -0500
+++ mct_u232.c 2010-12-25 21:44:57.714640343 -0500
@@ -24,6 +24,12 @@
* Basic tests have been performed with minicom/zmodem transfers and
* modem dialing under Linux 2.4.0-test10 (for me it works fine).
*
+ * 24-Apr-2010 Vadim Tsozik <tsozik@xxxxxxxxx>
+ * - Added implementation of 'TIOCMIWAIT' and 'TIOCGICOUNT' ioctls.
+ * This routines are necessary if you use mct u232 p9 as data
+ * acquisition interface. These routines were tested with RM-60 AWARE
+ * Electronics Radiation Monitor.
+ *
* 04-Nov-2003 Bill Marr <marr at flex dot com>
* - Mimic Windows driver by sending 2 USB 'device request' messages
* following normal 'baud rate change' message. This allows data to be
@@ -78,6 +84,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"

/*
@@ -104,6 +112,8 @@ 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 void mct_u232_throttle(struct tty_struct *tty);
static void mct_u232_unthrottle(struct tty_struct *tty);

@@ -150,6 +160,7 @@ static struct usb_serial_driver mct_u232
.tiocmset = mct_u232_tiocmset,
.attach = mct_u232_startup,
.release = mct_u232_release,
+ .ioctl = mct_u232_ioctl,
};


@@ -160,6 +171,8 @@ 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 +399,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)
{
- /* Translate Control Line states */
- if (msr & MCT_U232_MSR_DSR)
- *control_state |= TIOCM_DSR;
- else
- *control_state &= ~TIOCM_DSR;
- if (msr & MCT_U232_MSR_CTS)
- *control_state |= TIOCM_CTS;
- else
- *control_state &= ~TIOCM_CTS;
- if (msr & MCT_U232_MSR_RI)
- *control_state |= TIOCM_RI;
- else
- *control_state &= ~TIOCM_RI;
- if (msr & MCT_U232_MSR_CD)
- *control_state |= TIOCM_CD;
- else
- *control_state &= ~TIOCM_CD;
- dbg("msr_to_state: msr=0x%x ==> state=0x%x", msr, *control_state);
+ 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) {
+ *control_state |= TIOCM_DSR;
+ icount->dsr++;
+ } else {
+ *control_state &= ~TIOCM_DSR;
+ }
+ if (msr & MCT_U232_MSR_CTS) {
+ *control_state |= TIOCM_CTS;
+ icount->cts++;
+ } else {
+ *control_state &= ~TIOCM_CTS;
+ }
+ if (msr & MCT_U232_MSR_RI) {
+ *control_state |= TIOCM_RI;
+ icount->rng++;
+ } else {
+ *control_state &= ~TIOCM_RI;
+ }
+ if (msr & MCT_U232_MSR_CD) {
+ *control_state |= TIOCM_CD;
+ 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 +449,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 +526,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;
@@ -616,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 */
@@ -823,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;
@@ -844,6 +871,57 @@ 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;
+ }
+ /* NOTREACHED */
+ break;
+
+ case TIOCGICOUNT:
+ dbg("%s - (%d) TIOCGICOUNT RX=%d, TX=%d", __func__,
+ port->number, mct_u232_port->icount.rx, mct_u232_port->icount.tx);
+ if (copy_to_user((void __user *)arg, &mct_u232_port->icount,
+ sizeof(mct_u232_port->icount)))
+ return -EFAULT;
+ return 0;
+ }
+ return -ENOIOCTLCMD;
+}
+
static int __init mct_u232_init(void)
{
int retval;




--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/