summaryrefslogtreecommitdiff
path: root/drivers/usb/serial/mct_u232.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/serial/mct_u232.c')
-rw-r--r--drivers/usb/serial/mct_u232.c199
1 files changed, 133 insertions, 66 deletions
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index 3db1adc25f84..e08c9bb403d8 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -81,7 +81,7 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "z2.0" /* Linux in-kernel version */
+#define DRIVER_VERSION "z2.1" /* Linux in-kernel version */
#define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>"
#define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver"
@@ -110,6 +110,10 @@ static int mct_u232_tiocmget (struct usb_serial_port *port,
static int mct_u232_tiocmset (struct usb_serial_port *port,
struct file *file, unsigned int set,
unsigned int clear);
+static void mct_u232_throttle (struct usb_serial_port *port);
+static void mct_u232_unthrottle (struct usb_serial_port *port);
+
+
/*
* All of the device info needed for the MCT USB-RS232 converter.
*/
@@ -145,6 +149,8 @@ static struct usb_serial_driver mct_u232_device = {
.num_ports = 1,
.open = mct_u232_open,
.close = mct_u232_close,
+ .throttle = mct_u232_throttle,
+ .unthrottle = mct_u232_unthrottle,
.read_int_callback = mct_u232_read_int_callback,
.ioctl = mct_u232_ioctl,
.set_termios = mct_u232_set_termios,
@@ -162,8 +168,11 @@ struct mct_u232_private {
unsigned char last_lcr; /* Line Control Register */
unsigned char last_lsr; /* Line Status Register */
unsigned char last_msr; /* Modem Status Register */
+ unsigned int rx_flags; /* Throttling flags */
};
+#define THROTTLED 0x01
+
/*
* Handle vendor specific USB requests
*/
@@ -175,21 +184,21 @@ struct mct_u232_private {
* we do not know how to support. We ignore them for the moment.
* XXX Rate-limit the error message, it's user triggerable.
*/
-static int mct_u232_calculate_baud_rate(struct usb_serial *serial, int value)
+static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value)
{
if (le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_SITECOM_PID
|| le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_BELKIN_F5U109_PID) {
switch (value) {
- case B300: return 0x01;
- case B600: return 0x02; /* this one not tested */
- case B1200: return 0x03;
- case B2400: return 0x04;
- case B4800: return 0x06;
- case B9600: return 0x08;
- case B19200: return 0x09;
- case B38400: return 0x0a;
- case B57600: return 0x0b;
- case B115200: return 0x0c;
+ case 300: return 0x01;
+ case 600: return 0x02; /* this one not tested */
+ case 1200: return 0x03;
+ case 2400: return 0x04;
+ case 4800: return 0x06;
+ case 9600: return 0x08;
+ case 19200: return 0x09;
+ case 38400: return 0x0a;
+ case 57600: return 0x0b;
+ case 115200: return 0x0c;
default:
err("MCT USB-RS232: unsupported baudrate request 0x%x,"
" using default of B9600", value);
@@ -197,30 +206,32 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, int value)
}
} else {
switch (value) {
- case B300: value = 300; break;
- case B600: value = 600; break;
- case B1200: value = 1200; break;
- case B2400: value = 2400; break;
- case B4800: value = 4800; break;
- case B9600: value = 9600; break;
- case B19200: value = 19200; break;
- case B38400: value = 38400; break;
- case B57600: value = 57600; break;
- case B115200: value = 115200; break;
- default:
- err("MCT USB-RS232: unsupported baudrate request 0x%x,"
- " using default of B9600", value);
- value = 9600;
+ case 300: break;
+ case 600: break;
+ case 1200: break;
+ case 2400: break;
+ case 4800: break;
+ case 9600: break;
+ case 19200: break;
+ case 38400: break;
+ case 57600: break;
+ case 115200: break;
+ default:
+ err("MCT USB-RS232: unsupported baudrate request 0x%x,"
+ " using default of B9600", value);
+ value = 9600;
}
return 115200/value;
}
}
-static int mct_u232_set_baud_rate(struct usb_serial *serial, int value)
+static int mct_u232_set_baud_rate(struct usb_serial *serial, struct usb_serial_port *port,
+ speed_t value)
{
__le32 divisor;
int rc;
unsigned char zero_byte = 0;
+ unsigned char cts_enable_byte = 0;
divisor = cpu_to_le32(mct_u232_calculate_baud_rate(serial, value));
@@ -238,10 +249,17 @@ static int mct_u232_set_baud_rate(struct usb_serial *serial, int value)
'baud rate change' message. The actual functionality of the
request codes in these messages is not fully understood but these
particular codes are never seen in any operation besides a baud
- rate change. Both of these messages send a single byte of data
- whose value is always zero. The second of these two extra messages
- is required in order for data to be properly written to an RS-232
- device which does not assert the 'CTS' signal. */
+ rate change. Both of these messages send a single byte of data.
+ In the first message, the value of this byte is always zero.
+
+ The second message has been determined experimentally to control
+ whether data will be transmitted to a device which is not asserting
+ the 'CTS' signal. If the second message's data byte is zero, data
+ will be transmitted even if 'CTS' is not asserted (i.e. no hardware
+ flow control). if the second message's data byte is nonzero (a value
+ of 1 is used by this driver), data will not be transmitted to a device
+ which is not asserting 'CTS'.
+ */
rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
MCT_U232_SET_UNKNOWN1_REQUEST,
@@ -252,14 +270,19 @@ static int mct_u232_set_baud_rate(struct usb_serial *serial, int value)
err("Sending USB device request code %d failed (error = %d)",
MCT_U232_SET_UNKNOWN1_REQUEST, rc);
+ if (port && C_CRTSCTS(port->tty)) {
+ cts_enable_byte = 1;
+ }
+
+ dbg("set_baud_rate: send second control message, data = %02X", cts_enable_byte);
rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- MCT_U232_SET_UNKNOWN2_REQUEST,
+ MCT_U232_SET_CTS_REQUEST,
MCT_U232_SET_REQUEST_TYPE,
- 0, 0, &zero_byte, MCT_U232_SET_UNKNOWN2_SIZE,
+ 0, 0, &cts_enable_byte, MCT_U232_SET_CTS_SIZE,
WDR_TIMEOUT);
if (rc < 0)
- err("Sending USB device request code %d failed (error = %d)",
- MCT_U232_SET_UNKNOWN2_REQUEST, rc);
+ err("Sending USB device request code %d failed (error = %d)",
+ MCT_U232_SET_CTS_REQUEST, rc);
return rc;
} /* mct_u232_set_baud_rate */
@@ -458,8 +481,25 @@ error:
static void mct_u232_close (struct usb_serial_port *port, struct file *filp)
{
+ unsigned int c_cflag;
+ unsigned long flags;
+ unsigned int control_state;
+ struct mct_u232_private *priv = usb_get_serial_port_data(port);
dbg("%s port %d", __FUNCTION__, port->number);
+ if (port->tty) {
+ c_cflag = port->tty->termios->c_cflag;
+ if (c_cflag & HUPCL) {
+ /* drop DTR and RTS */
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+ control_state = priv->control_state;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ mct_u232_set_modem_ctrl(port->serial, control_state);
+ }
+ }
+
+
if (port->serial->dev) {
/* shutdown our urbs */
usb_kill_urb(port->write_urb);
@@ -476,10 +516,11 @@ static void mct_u232_read_int_callback (struct urb *urb)
struct usb_serial *serial = port->serial;
struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
- int status;
+ int retval;
+ int status = urb->status;
unsigned long flags;
- switch (urb->status) {
+ switch (status) {
case 0:
/* success */
break;
@@ -487,10 +528,12 @@ static void mct_u232_read_int_callback (struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ dbg("%s - urb shutting down with status: %d",
+ __FUNCTION__, status);
return;
default:
- dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+ dbg("%s - nonzero urb status received: %d",
+ __FUNCTION__, status);
goto exit;
}
@@ -554,10 +597,10 @@ static void mct_u232_read_int_callback (struct urb *urb)
#endif
spin_unlock_irqrestore(&priv->lock, flags);
exit:
- status = usb_submit_urb (urb, GFP_ATOMIC);
- if (status)
+ retval = usb_submit_urb (urb, GFP_ATOMIC);
+ if (retval)
err ("%s - usb_submit_urb failed with result %d",
- __FUNCTION__, status);
+ __FUNCTION__, retval);
} /* mct_u232_read_int_callback */
static void mct_u232_set_termios (struct usb_serial_port *port,
@@ -565,11 +608,10 @@ static void mct_u232_set_termios (struct usb_serial_port *port,
{
struct usb_serial *serial = port->serial;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
- unsigned int iflag = port->tty->termios->c_iflag;
unsigned int cflag = port->tty->termios->c_cflag;
unsigned int old_cflag = old_termios->c_cflag;
unsigned long flags;
- unsigned int control_state, new_state;
+ unsigned int control_state;
unsigned char last_lcr;
/* get a local copy of the current port settings */
@@ -585,18 +627,14 @@ static void mct_u232_set_termios (struct usb_serial_port *port,
* Premature optimization is the root of all evil.
*/
- /* reassert DTR and (maybe) RTS on transition from B0 */
+ /* reassert DTR and RTS on transition from B0 */
if ((old_cflag & CBAUD) == B0) {
dbg("%s: baud was B0", __FUNCTION__);
- control_state |= TIOCM_DTR;
- /* don't set RTS if using hardware flow control */
- if (!(old_cflag & CRTSCTS)) {
- control_state |= TIOCM_RTS;
- }
+ control_state |= TIOCM_DTR | TIOCM_RTS;
mct_u232_set_modem_ctrl(serial, control_state);
}
- mct_u232_set_baud_rate(serial, cflag & CBAUD);
+ mct_u232_set_baud_rate(serial, port, tty_get_baud_rate(port->tty));
if ((cflag & CBAUD) == B0 ) {
dbg("%s: baud is B0", __FUNCTION__);
@@ -638,21 +676,6 @@ static void mct_u232_set_termios (struct usb_serial_port *port,
mct_u232_set_line_ctrl(serial, last_lcr);
- /*
- * Set flow control: well, I do not really now how to handle DTR/RTS.
- * Just do what we have seen with SniffUSB on Win98.
- */
- /* Drop DTR/RTS if no flow control otherwise assert */
- new_state = control_state;
- if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS))
- new_state |= TIOCM_DTR | TIOCM_RTS;
- else
- new_state &= ~(TIOCM_DTR | TIOCM_RTS);
- if (new_state != control_state) {
- mct_u232_set_modem_ctrl(serial, new_state);
- control_state = new_state;
- }
-
/* save off the modified port settings */
spin_lock_irqsave(&priv->lock, flags);
priv->control_state = control_state;
@@ -747,6 +770,50 @@ static int mct_u232_ioctl (struct usb_serial_port *port, struct file * file,
return 0;
} /* mct_u232_ioctl */
+static void mct_u232_throttle (struct usb_serial_port *port)
+{
+ struct mct_u232_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ unsigned int control_state;
+ struct tty_struct *tty;
+
+ tty = port->tty;
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->rx_flags |= THROTTLED;
+ if (C_CRTSCTS(tty)) {
+ priv->control_state &= ~TIOCM_RTS;
+ control_state = priv->control_state;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ (void) mct_u232_set_modem_ctrl(port->serial, control_state);
+ } else {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+}
+
+
+static void mct_u232_unthrottle (struct usb_serial_port *port)
+{
+ struct mct_u232_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ unsigned int control_state;
+ struct tty_struct *tty;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ tty = port->tty;
+ spin_lock_irqsave(&priv->lock, flags);
+ if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) {
+ priv->rx_flags &= ~THROTTLED;
+ priv->control_state |= TIOCM_RTS;
+ control_state = priv->control_state;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ (void) mct_u232_set_modem_ctrl(port->serial, control_state);
+ } else {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+}
static int __init mct_u232_init (void)
{