USB: serial: mct_u232: added _ioctl, _msr_to_icount and _get_icount functions

Added mct_u232_ioctl (implements TIOCMIWAIT command),
mct_u232_get_icount (implements TIOCGICOUNT command) and
mct_u232_msr_to_icount 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 <tsozik@yahoo.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Vadim Tsozik 2011-01-09 01:00:11 -05:00 committed by Greg Kroah-Hartman
parent 0fe6f1d1f6
commit 7af75af242

View 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"
/*
@ -104,6 +106,10 @@ 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,
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);
@ -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;