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:
parent
0fe6f1d1f6
commit
7af75af242
@ -78,6 +78,8 @@
|
|||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
#include <linux/usb/serial.h>
|
#include <linux/usb/serial.h>
|
||||||
|
#include <linux/serial.h>
|
||||||
|
#include <linux/ioctl.h>
|
||||||
#include "mct_u232.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_tiocmget(struct tty_struct *tty, struct file *file);
|
||||||
static int mct_u232_tiocmset(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);
|
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_throttle(struct tty_struct *tty);
|
||||||
static void mct_u232_unthrottle(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,
|
.tiocmset = mct_u232_tiocmset,
|
||||||
.attach = mct_u232_startup,
|
.attach = mct_u232_startup,
|
||||||
.release = mct_u232_release,
|
.release = mct_u232_release,
|
||||||
|
.ioctl = mct_u232_ioctl,
|
||||||
|
.get_icount = mct_u232_get_icount,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct mct_u232_private {
|
struct mct_u232_private {
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
unsigned int control_state; /* Modem Line Setting (TIOCM) */
|
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_lsr; /* Line Status Register */
|
||||||
unsigned char last_msr; /* Modem Status Register */
|
unsigned char last_msr; /* Modem Status Register */
|
||||||
unsigned int rx_flags; /* Throttling flags */
|
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
|
#define THROTTLED 0x01
|
||||||
@ -386,6 +396,20 @@ static int mct_u232_get_modem_stat(struct usb_serial *serial,
|
|||||||
return rc;
|
return rc;
|
||||||
} /* mct_u232_get_modem_stat */
|
} /* 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,
|
static void mct_u232_msr_to_state(unsigned int *control_state,
|
||||||
unsigned char msr)
|
unsigned char msr)
|
||||||
{
|
{
|
||||||
@ -422,6 +446,7 @@ static int mct_u232_startup(struct usb_serial *serial)
|
|||||||
if (!priv)
|
if (!priv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
spin_lock_init(&priv->lock);
|
spin_lock_init(&priv->lock);
|
||||||
|
init_waitqueue_head(&priv->msr_wait);
|
||||||
usb_set_serial_port_data(serial->port[0], priv);
|
usb_set_serial_port_data(serial->port[0], priv);
|
||||||
|
|
||||||
init_waitqueue_head(&serial->port[0]->write_wait);
|
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 */
|
/* Record Control Line states */
|
||||||
mct_u232_msr_to_state(&priv->control_state, priv->last_msr);
|
mct_u232_msr_to_state(&priv->control_state, priv->last_msr);
|
||||||
|
|
||||||
|
mct_u232_msr_to_icount(&priv->icount, priv->last_msr);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/* Not yet handled. See belkin_sa.c for further information */
|
/* Not yet handled. See belkin_sa.c for further information */
|
||||||
/* Now to report any errors */
|
/* Now to report any errors */
|
||||||
@ -647,6 +674,7 @@ static void mct_u232_read_int_callback(struct urb *urb)
|
|||||||
tty_kref_put(tty);
|
tty_kref_put(tty);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
wake_up_interruptible(&priv->msr_wait);
|
||||||
spin_unlock_irqrestore(&priv->lock, flags);
|
spin_unlock_irqrestore(&priv->lock, flags);
|
||||||
exit:
|
exit:
|
||||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
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)
|
static void mct_u232_unthrottle(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
struct usb_serial_port *port = tty->driver_data;
|
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)
|
static int __init mct_u232_init(void)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
Loading…
Reference in New Issue
Block a user