USB: RTS/CTS handshaking support, DTR fixes for MCT U232 serial adapter
Improvements and fixes to the MCT U232 USB/serial interface driver. Implement RTS/CTS hardware flow control. Implement HUPCL. Bring handling of DTR and RTS into conformance with other Linux serial port drivers - assert both signals when opening device, even if "crtscts" is not currently selected. Signed-off-by: Dave Platt <dplatt@radagast.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
01cd081920
commit
45b844df5a
|
@ -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
|
||||
*/
|
||||
|
@ -216,11 +225,13 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, int 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,
|
||||
int 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);
|
||||
|
@ -565,11 +605,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 +624,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, cflag & CBAUD);
|
||||
|
||||
if ((cflag & CBAUD) == B0 ) {
|
||||
dbg("%s: baud is B0", __FUNCTION__);
|
||||
|
@ -638,21 +673,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 +767,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)
|
||||
{
|
||||
|
|
|
@ -63,14 +63,15 @@
|
|||
#define MCT_U232_SET_UNKNOWN1_REQUEST 11 /* Unknown functionality */
|
||||
#define MCT_U232_SET_UNKNOWN1_SIZE 1
|
||||
|
||||
/* This USB device request code is not well understood. It is transmitted by
|
||||
the MCT-supplied Windows driver whenever the baud rate changes.
|
||||
/* This USB device request code appears to control whether CTS is required
|
||||
during transmission.
|
||||
|
||||
Without this USB device request, the USB/RS-232 adapter will not write to
|
||||
RS-232 devices which do not assert the 'CTS' signal.
|
||||
Sending a zero byte allows data transmission to a device which is not
|
||||
asserting CTS. Sending a '1' byte will cause transmission to be deferred
|
||||
until the device asserts CTS.
|
||||
*/
|
||||
#define MCT_U232_SET_UNKNOWN2_REQUEST 12 /* Unknown functionality */
|
||||
#define MCT_U232_SET_UNKNOWN2_SIZE 1
|
||||
#define MCT_U232_SET_CTS_REQUEST 12
|
||||
#define MCT_U232_SET_CTS_SIZE 1
|
||||
|
||||
/*
|
||||
* Baud rate (divisor)
|
||||
|
@ -439,7 +440,7 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, int value);
|
|||
* which says "U232-P9" ;-)
|
||||
*
|
||||
* The circuit board inside the adaptor contains a Philips PDIUSBD12
|
||||
* USB endpoint chip and a Phillips P87C52UBAA microcontroller with
|
||||
* USB endpoint chip and a Philips P87C52UBAA microcontroller with
|
||||
* embedded UART. Exhaustive documentation for these is available at:
|
||||
*
|
||||
* http://www.semiconductors.philips.com/pip/p87c52ubaa
|
||||
|
|
Loading…
Reference in a new issue