Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7af75af2 authored by Vadim Tsozik's avatar Vadim Tsozik Committed by Greg Kroah-Hartman
Browse files

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: default avatarVadim Tsozik <tsozik@yahoo.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 0fe6f1d1
Loading
Loading
Loading
Loading
+105 −2
Original line number Diff line number Diff line
@@ -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;