Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Dave Platt and committed by
Greg Kroah-Hartman
45b844df 01cd0819

+106 -41
+98 -34
drivers/usb/serial/mct_u232.c
··· 81 81 /* 82 82 * Version Information 83 83 */ 84 - #define DRIVER_VERSION "z2.0" /* Linux in-kernel version */ 84 + #define DRIVER_VERSION "z2.1" /* Linux in-kernel version */ 85 85 #define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>" 86 86 #define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver" 87 87 ··· 110 110 static int mct_u232_tiocmset (struct usb_serial_port *port, 111 111 struct file *file, unsigned int set, 112 112 unsigned int clear); 113 + static void mct_u232_throttle (struct usb_serial_port *port); 114 + static void mct_u232_unthrottle (struct usb_serial_port *port); 115 + 116 + 113 117 /* 114 118 * All of the device info needed for the MCT USB-RS232 converter. 115 119 */ ··· 149 145 .num_ports = 1, 150 146 .open = mct_u232_open, 151 147 .close = mct_u232_close, 148 + .throttle = mct_u232_throttle, 149 + .unthrottle = mct_u232_unthrottle, 152 150 .read_int_callback = mct_u232_read_int_callback, 153 151 .ioctl = mct_u232_ioctl, 154 152 .set_termios = mct_u232_set_termios, ··· 168 162 unsigned char last_lcr; /* Line Control Register */ 169 163 unsigned char last_lsr; /* Line Status Register */ 170 164 unsigned char last_msr; /* Modem Status Register */ 165 + unsigned int rx_flags; /* Throttling flags */ 171 166 }; 167 + 168 + #define THROTTLED 0x01 172 169 173 170 /* 174 171 * Handle vendor specific USB requests ··· 225 216 } 226 217 } 227 218 228 - static int mct_u232_set_baud_rate(struct usb_serial *serial, int value) 219 + static int mct_u232_set_baud_rate(struct usb_serial *serial, struct usb_serial_port *port, 220 + int value) 229 221 { 230 222 __le32 divisor; 231 223 int rc; 232 224 unsigned char zero_byte = 0; 225 + unsigned char cts_enable_byte = 0; 233 226 234 227 divisor = cpu_to_le32(mct_u232_calculate_baud_rate(serial, value)); 235 228 ··· 249 238 'baud rate change' message. The actual functionality of the 250 239 request codes in these messages is not fully understood but these 251 240 particular codes are never seen in any operation besides a baud 252 - rate change. Both of these messages send a single byte of data 253 - whose value is always zero. The second of these two extra messages 254 - is required in order for data to be properly written to an RS-232 255 - device which does not assert the 'CTS' signal. */ 241 + rate change. Both of these messages send a single byte of data. 242 + In the first message, the value of this byte is always zero. 243 + 244 + The second message has been determined experimentally to control 245 + whether data will be transmitted to a device which is not asserting 246 + the 'CTS' signal. If the second message's data byte is zero, data 247 + will be transmitted even if 'CTS' is not asserted (i.e. no hardware 248 + flow control). if the second message's data byte is nonzero (a value 249 + of 1 is used by this driver), data will not be transmitted to a device 250 + which is not asserting 'CTS'. 251 + */ 256 252 257 253 rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 258 254 MCT_U232_SET_UNKNOWN1_REQUEST, ··· 270 252 err("Sending USB device request code %d failed (error = %d)", 271 253 MCT_U232_SET_UNKNOWN1_REQUEST, rc); 272 254 255 + if (port && C_CRTSCTS(port->tty)) { 256 + cts_enable_byte = 1; 257 + } 258 + 259 + dbg("set_baud_rate: send second control message, data = %02X", cts_enable_byte); 273 260 rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 274 - MCT_U232_SET_UNKNOWN2_REQUEST, 261 + MCT_U232_SET_CTS_REQUEST, 275 262 MCT_U232_SET_REQUEST_TYPE, 276 - 0, 0, &zero_byte, MCT_U232_SET_UNKNOWN2_SIZE, 263 + 0, 0, &cts_enable_byte, MCT_U232_SET_CTS_SIZE, 277 264 WDR_TIMEOUT); 278 265 if (rc < 0) 279 - err("Sending USB device request code %d failed (error = %d)", 280 - MCT_U232_SET_UNKNOWN2_REQUEST, rc); 266 + err("Sending USB device request code %d failed (error = %d)", 267 + MCT_U232_SET_CTS_REQUEST, rc); 281 268 282 269 return rc; 283 270 } /* mct_u232_set_baud_rate */ ··· 481 458 482 459 static void mct_u232_close (struct usb_serial_port *port, struct file *filp) 483 460 { 461 + unsigned int c_cflag; 462 + unsigned long flags; 463 + unsigned int control_state; 464 + struct mct_u232_private *priv = usb_get_serial_port_data(port); 484 465 dbg("%s port %d", __FUNCTION__, port->number); 466 + 467 + if (port->tty) { 468 + c_cflag = port->tty->termios->c_cflag; 469 + if (c_cflag & HUPCL) { 470 + /* drop DTR and RTS */ 471 + spin_lock_irqsave(&priv->lock, flags); 472 + priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); 473 + control_state = priv->control_state; 474 + spin_unlock_irqrestore(&priv->lock, flags); 475 + mct_u232_set_modem_ctrl(port->serial, control_state); 476 + } 477 + } 478 + 485 479 486 480 if (port->serial->dev) { 487 481 /* shutdown our urbs */ ··· 605 565 { 606 566 struct usb_serial *serial = port->serial; 607 567 struct mct_u232_private *priv = usb_get_serial_port_data(port); 608 - unsigned int iflag = port->tty->termios->c_iflag; 609 568 unsigned int cflag = port->tty->termios->c_cflag; 610 569 unsigned int old_cflag = old_termios->c_cflag; 611 570 unsigned long flags; 612 - unsigned int control_state, new_state; 571 + unsigned int control_state; 613 572 unsigned char last_lcr; 614 573 615 574 /* get a local copy of the current port settings */ ··· 624 585 * Premature optimization is the root of all evil. 625 586 */ 626 587 627 - /* reassert DTR and (maybe) RTS on transition from B0 */ 588 + /* reassert DTR and RTS on transition from B0 */ 628 589 if ((old_cflag & CBAUD) == B0) { 629 590 dbg("%s: baud was B0", __FUNCTION__); 630 - control_state |= TIOCM_DTR; 631 - /* don't set RTS if using hardware flow control */ 632 - if (!(old_cflag & CRTSCTS)) { 633 - control_state |= TIOCM_RTS; 634 - } 591 + control_state |= TIOCM_DTR | TIOCM_RTS; 635 592 mct_u232_set_modem_ctrl(serial, control_state); 636 593 } 637 594 638 - mct_u232_set_baud_rate(serial, cflag & CBAUD); 595 + mct_u232_set_baud_rate(serial, port, cflag & CBAUD); 639 596 640 597 if ((cflag & CBAUD) == B0 ) { 641 598 dbg("%s: baud is B0", __FUNCTION__); ··· 672 637 MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1; 673 638 674 639 mct_u232_set_line_ctrl(serial, last_lcr); 675 - 676 - /* 677 - * Set flow control: well, I do not really now how to handle DTR/RTS. 678 - * Just do what we have seen with SniffUSB on Win98. 679 - */ 680 - /* Drop DTR/RTS if no flow control otherwise assert */ 681 - new_state = control_state; 682 - if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS)) 683 - new_state |= TIOCM_DTR | TIOCM_RTS; 684 - else 685 - new_state &= ~(TIOCM_DTR | TIOCM_RTS); 686 - if (new_state != control_state) { 687 - mct_u232_set_modem_ctrl(serial, new_state); 688 - control_state = new_state; 689 - } 690 640 691 641 /* save off the modified port settings */ 692 642 spin_lock_irqsave(&priv->lock, flags); ··· 767 747 return 0; 768 748 } /* mct_u232_ioctl */ 769 749 750 + static void mct_u232_throttle (struct usb_serial_port *port) 751 + { 752 + struct mct_u232_private *priv = usb_get_serial_port_data(port); 753 + unsigned long flags; 754 + unsigned int control_state; 755 + struct tty_struct *tty; 756 + 757 + tty = port->tty; 758 + dbg("%s - port %d", __FUNCTION__, port->number); 759 + 760 + spin_lock_irqsave(&priv->lock, flags); 761 + priv->rx_flags |= THROTTLED; 762 + if (C_CRTSCTS(tty)) { 763 + priv->control_state &= ~TIOCM_RTS; 764 + control_state = priv->control_state; 765 + spin_unlock_irqrestore(&priv->lock, flags); 766 + (void) mct_u232_set_modem_ctrl(port->serial, control_state); 767 + } else { 768 + spin_unlock_irqrestore(&priv->lock, flags); 769 + } 770 + } 771 + 772 + 773 + static void mct_u232_unthrottle (struct usb_serial_port *port) 774 + { 775 + struct mct_u232_private *priv = usb_get_serial_port_data(port); 776 + unsigned long flags; 777 + unsigned int control_state; 778 + struct tty_struct *tty; 779 + 780 + dbg("%s - port %d", __FUNCTION__, port->number); 781 + 782 + tty = port->tty; 783 + spin_lock_irqsave(&priv->lock, flags); 784 + if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) { 785 + priv->rx_flags &= ~THROTTLED; 786 + priv->control_state |= TIOCM_RTS; 787 + control_state = priv->control_state; 788 + spin_unlock_irqrestore(&priv->lock, flags); 789 + (void) mct_u232_set_modem_ctrl(port->serial, control_state); 790 + } else { 791 + spin_unlock_irqrestore(&priv->lock, flags); 792 + } 793 + } 770 794 771 795 static int __init mct_u232_init (void) 772 796 {
+8 -7
drivers/usb/serial/mct_u232.h
··· 63 63 #define MCT_U232_SET_UNKNOWN1_REQUEST 11 /* Unknown functionality */ 64 64 #define MCT_U232_SET_UNKNOWN1_SIZE 1 65 65 66 - /* This USB device request code is not well understood. It is transmitted by 67 - the MCT-supplied Windows driver whenever the baud rate changes. 66 + /* This USB device request code appears to control whether CTS is required 67 + during transmission. 68 68 69 - Without this USB device request, the USB/RS-232 adapter will not write to 70 - RS-232 devices which do not assert the 'CTS' signal. 69 + Sending a zero byte allows data transmission to a device which is not 70 + asserting CTS. Sending a '1' byte will cause transmission to be deferred 71 + until the device asserts CTS. 71 72 */ 72 - #define MCT_U232_SET_UNKNOWN2_REQUEST 12 /* Unknown functionality */ 73 - #define MCT_U232_SET_UNKNOWN2_SIZE 1 73 + #define MCT_U232_SET_CTS_REQUEST 12 74 + #define MCT_U232_SET_CTS_SIZE 1 74 75 75 76 /* 76 77 * Baud rate (divisor) ··· 440 439 * which says "U232-P9" ;-) 441 440 * 442 441 * The circuit board inside the adaptor contains a Philips PDIUSBD12 443 - * USB endpoint chip and a Phillips P87C52UBAA microcontroller with 442 + * USB endpoint chip and a Philips P87C52UBAA microcontroller with 444 443 * embedded UART. Exhaustive documentation for these is available at: 445 444 * 446 445 * http://www.semiconductors.philips.com/pip/p87c52ubaa