diff options
Diffstat (limited to 'drivers/usb/serial/mct_u232.c')
-rw-r--r-- | drivers/usb/serial/mct_u232.c | 149 |
1 files changed, 108 insertions, 41 deletions
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 3db1adc25f84..2a3fabcf5186 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c | |||
@@ -81,7 +81,7 @@ | |||
81 | /* | 81 | /* |
82 | * Version Information | 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 | #define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>" | 85 | #define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>" |
86 | #define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver" | 86 | #define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver" |
87 | 87 | ||
@@ -110,6 +110,10 @@ static int mct_u232_tiocmget (struct usb_serial_port *port, | |||
110 | static int mct_u232_tiocmset (struct usb_serial_port *port, | 110 | static int mct_u232_tiocmset (struct usb_serial_port *port, |
111 | struct file *file, unsigned int set, | 111 | struct file *file, unsigned int set, |
112 | unsigned int clear); | 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 | * All of the device info needed for the MCT USB-RS232 converter. | 118 | * All of the device info needed for the MCT USB-RS232 converter. |
115 | */ | 119 | */ |
@@ -145,6 +149,8 @@ static struct usb_serial_driver mct_u232_device = { | |||
145 | .num_ports = 1, | 149 | .num_ports = 1, |
146 | .open = mct_u232_open, | 150 | .open = mct_u232_open, |
147 | .close = mct_u232_close, | 151 | .close = mct_u232_close, |
152 | .throttle = mct_u232_throttle, | ||
153 | .unthrottle = mct_u232_unthrottle, | ||
148 | .read_int_callback = mct_u232_read_int_callback, | 154 | .read_int_callback = mct_u232_read_int_callback, |
149 | .ioctl = mct_u232_ioctl, | 155 | .ioctl = mct_u232_ioctl, |
150 | .set_termios = mct_u232_set_termios, | 156 | .set_termios = mct_u232_set_termios, |
@@ -162,8 +168,11 @@ struct mct_u232_private { | |||
162 | unsigned char last_lcr; /* Line Control Register */ | 168 | unsigned char last_lcr; /* Line Control Register */ |
163 | unsigned char last_lsr; /* Line Status Register */ | 169 | unsigned char last_lsr; /* Line Status Register */ |
164 | unsigned char last_msr; /* Modem Status Register */ | 170 | unsigned char last_msr; /* Modem Status Register */ |
171 | unsigned int rx_flags; /* Throttling flags */ | ||
165 | }; | 172 | }; |
166 | 173 | ||
174 | #define THROTTLED 0x01 | ||
175 | |||
167 | /* | 176 | /* |
168 | * Handle vendor specific USB requests | 177 | * Handle vendor specific USB requests |
169 | */ | 178 | */ |
@@ -216,11 +225,13 @@ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, int value) | |||
216 | } | 225 | } |
217 | } | 226 | } |
218 | 227 | ||
219 | static int mct_u232_set_baud_rate(struct usb_serial *serial, int value) | 228 | static int mct_u232_set_baud_rate(struct usb_serial *serial, struct usb_serial_port *port, |
229 | int value) | ||
220 | { | 230 | { |
221 | __le32 divisor; | 231 | __le32 divisor; |
222 | int rc; | 232 | int rc; |
223 | unsigned char zero_byte = 0; | 233 | unsigned char zero_byte = 0; |
234 | unsigned char cts_enable_byte = 0; | ||
224 | 235 | ||
225 | divisor = cpu_to_le32(mct_u232_calculate_baud_rate(serial, value)); | 236 | divisor = cpu_to_le32(mct_u232_calculate_baud_rate(serial, value)); |
226 | 237 | ||
@@ -238,10 +249,17 @@ static int mct_u232_set_baud_rate(struct usb_serial *serial, int value) | |||
238 | 'baud rate change' message. The actual functionality of the | 249 | 'baud rate change' message. The actual functionality of the |
239 | request codes in these messages is not fully understood but these | 250 | request codes in these messages is not fully understood but these |
240 | particular codes are never seen in any operation besides a baud | 251 | particular codes are never seen in any operation besides a baud |
241 | rate change. Both of these messages send a single byte of data | 252 | rate change. Both of these messages send a single byte of data. |
242 | whose value is always zero. The second of these two extra messages | 253 | In the first message, the value of this byte is always zero. |
243 | is required in order for data to be properly written to an RS-232 | 254 | |
244 | device which does not assert the 'CTS' signal. */ | 255 | The second message has been determined experimentally to control |
256 | whether data will be transmitted to a device which is not asserting | ||
257 | the 'CTS' signal. If the second message's data byte is zero, data | ||
258 | will be transmitted even if 'CTS' is not asserted (i.e. no hardware | ||
259 | flow control). if the second message's data byte is nonzero (a value | ||
260 | of 1 is used by this driver), data will not be transmitted to a device | ||
261 | which is not asserting 'CTS'. | ||
262 | */ | ||
245 | 263 | ||
246 | rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), | 264 | rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
247 | MCT_U232_SET_UNKNOWN1_REQUEST, | 265 | MCT_U232_SET_UNKNOWN1_REQUEST, |
@@ -252,14 +270,19 @@ static int mct_u232_set_baud_rate(struct usb_serial *serial, int value) | |||
252 | err("Sending USB device request code %d failed (error = %d)", | 270 | err("Sending USB device request code %d failed (error = %d)", |
253 | MCT_U232_SET_UNKNOWN1_REQUEST, rc); | 271 | MCT_U232_SET_UNKNOWN1_REQUEST, rc); |
254 | 272 | ||
273 | if (port && C_CRTSCTS(port->tty)) { | ||
274 | cts_enable_byte = 1; | ||
275 | } | ||
276 | |||
277 | dbg("set_baud_rate: send second control message, data = %02X", cts_enable_byte); | ||
255 | rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), | 278 | rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), |
256 | MCT_U232_SET_UNKNOWN2_REQUEST, | 279 | MCT_U232_SET_CTS_REQUEST, |
257 | MCT_U232_SET_REQUEST_TYPE, | 280 | MCT_U232_SET_REQUEST_TYPE, |
258 | 0, 0, &zero_byte, MCT_U232_SET_UNKNOWN2_SIZE, | 281 | 0, 0, &cts_enable_byte, MCT_U232_SET_CTS_SIZE, |
259 | WDR_TIMEOUT); | 282 | WDR_TIMEOUT); |
260 | if (rc < 0) | 283 | if (rc < 0) |
261 | err("Sending USB device request code %d failed (error = %d)", | 284 | err("Sending USB device request code %d failed (error = %d)", |
262 | MCT_U232_SET_UNKNOWN2_REQUEST, rc); | 285 | MCT_U232_SET_CTS_REQUEST, rc); |
263 | 286 | ||
264 | return rc; | 287 | return rc; |
265 | } /* mct_u232_set_baud_rate */ | 288 | } /* mct_u232_set_baud_rate */ |
@@ -458,8 +481,25 @@ error: | |||
458 | 481 | ||
459 | static void mct_u232_close (struct usb_serial_port *port, struct file *filp) | 482 | static void mct_u232_close (struct usb_serial_port *port, struct file *filp) |
460 | { | 483 | { |
484 | unsigned int c_cflag; | ||
485 | unsigned long flags; | ||
486 | unsigned int control_state; | ||
487 | struct mct_u232_private *priv = usb_get_serial_port_data(port); | ||
461 | dbg("%s port %d", __FUNCTION__, port->number); | 488 | dbg("%s port %d", __FUNCTION__, port->number); |
462 | 489 | ||
490 | if (port->tty) { | ||
491 | c_cflag = port->tty->termios->c_cflag; | ||
492 | if (c_cflag & HUPCL) { | ||
493 | /* drop DTR and RTS */ | ||
494 | spin_lock_irqsave(&priv->lock, flags); | ||
495 | priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); | ||
496 | control_state = priv->control_state; | ||
497 | spin_unlock_irqrestore(&priv->lock, flags); | ||
498 | mct_u232_set_modem_ctrl(port->serial, control_state); | ||
499 | } | ||
500 | } | ||
501 | |||
502 | |||
463 | if (port->serial->dev) { | 503 | if (port->serial->dev) { |
464 | /* shutdown our urbs */ | 504 | /* shutdown our urbs */ |
465 | usb_kill_urb(port->write_urb); | 505 | usb_kill_urb(port->write_urb); |
@@ -476,10 +516,11 @@ static void mct_u232_read_int_callback (struct urb *urb) | |||
476 | struct usb_serial *serial = port->serial; | 516 | struct usb_serial *serial = port->serial; |
477 | struct tty_struct *tty; | 517 | struct tty_struct *tty; |
478 | unsigned char *data = urb->transfer_buffer; | 518 | unsigned char *data = urb->transfer_buffer; |
479 | int status; | 519 | int retval; |
520 | int status = urb->status; | ||
480 | unsigned long flags; | 521 | unsigned long flags; |
481 | 522 | ||
482 | switch (urb->status) { | 523 | switch (status) { |
483 | case 0: | 524 | case 0: |
484 | /* success */ | 525 | /* success */ |
485 | break; | 526 | break; |
@@ -487,10 +528,12 @@ static void mct_u232_read_int_callback (struct urb *urb) | |||
487 | case -ENOENT: | 528 | case -ENOENT: |
488 | case -ESHUTDOWN: | 529 | case -ESHUTDOWN: |
489 | /* this urb is terminated, clean up */ | 530 | /* this urb is terminated, clean up */ |
490 | dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); | 531 | dbg("%s - urb shutting down with status: %d", |
532 | __FUNCTION__, status); | ||
491 | return; | 533 | return; |
492 | default: | 534 | default: |
493 | dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); | 535 | dbg("%s - nonzero urb status received: %d", |
536 | __FUNCTION__, status); | ||
494 | goto exit; | 537 | goto exit; |
495 | } | 538 | } |
496 | 539 | ||
@@ -554,10 +597,10 @@ static void mct_u232_read_int_callback (struct urb *urb) | |||
554 | #endif | 597 | #endif |
555 | spin_unlock_irqrestore(&priv->lock, flags); | 598 | spin_unlock_irqrestore(&priv->lock, flags); |
556 | exit: | 599 | exit: |
557 | status = usb_submit_urb (urb, GFP_ATOMIC); | 600 | retval = usb_submit_urb (urb, GFP_ATOMIC); |
558 | if (status) | 601 | if (retval) |
559 | err ("%s - usb_submit_urb failed with result %d", | 602 | err ("%s - usb_submit_urb failed with result %d", |
560 | __FUNCTION__, status); | 603 | __FUNCTION__, retval); |
561 | } /* mct_u232_read_int_callback */ | 604 | } /* mct_u232_read_int_callback */ |
562 | 605 | ||
563 | static void mct_u232_set_termios (struct usb_serial_port *port, | 606 | 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, | |||
565 | { | 608 | { |
566 | struct usb_serial *serial = port->serial; | 609 | struct usb_serial *serial = port->serial; |
567 | struct mct_u232_private *priv = usb_get_serial_port_data(port); | 610 | struct mct_u232_private *priv = usb_get_serial_port_data(port); |
568 | unsigned int iflag = port->tty->termios->c_iflag; | ||
569 | unsigned int cflag = port->tty->termios->c_cflag; | 611 | unsigned int cflag = port->tty->termios->c_cflag; |
570 | unsigned int old_cflag = old_termios->c_cflag; | 612 | unsigned int old_cflag = old_termios->c_cflag; |
571 | unsigned long flags; | 613 | unsigned long flags; |
572 | unsigned int control_state, new_state; | 614 | unsigned int control_state; |
573 | unsigned char last_lcr; | 615 | unsigned char last_lcr; |
574 | 616 | ||
575 | /* get a local copy of the current port settings */ | 617 | /* get a local copy of the current port settings */ |
@@ -585,18 +627,14 @@ static void mct_u232_set_termios (struct usb_serial_port *port, | |||
585 | * Premature optimization is the root of all evil. | 627 | * Premature optimization is the root of all evil. |
586 | */ | 628 | */ |
587 | 629 | ||
588 | /* reassert DTR and (maybe) RTS on transition from B0 */ | 630 | /* reassert DTR and RTS on transition from B0 */ |
589 | if ((old_cflag & CBAUD) == B0) { | 631 | if ((old_cflag & CBAUD) == B0) { |
590 | dbg("%s: baud was B0", __FUNCTION__); | 632 | dbg("%s: baud was B0", __FUNCTION__); |
591 | control_state |= TIOCM_DTR; | 633 | control_state |= TIOCM_DTR | TIOCM_RTS; |
592 | /* don't set RTS if using hardware flow control */ | ||
593 | if (!(old_cflag & CRTSCTS)) { | ||
594 | control_state |= TIOCM_RTS; | ||
595 | } | ||
596 | mct_u232_set_modem_ctrl(serial, control_state); | 634 | mct_u232_set_modem_ctrl(serial, control_state); |
597 | } | 635 | } |
598 | 636 | ||
599 | mct_u232_set_baud_rate(serial, cflag & CBAUD); | 637 | mct_u232_set_baud_rate(serial, port, cflag & CBAUD); |
600 | 638 | ||
601 | if ((cflag & CBAUD) == B0 ) { | 639 | if ((cflag & CBAUD) == B0 ) { |
602 | dbg("%s: baud is B0", __FUNCTION__); | 640 | dbg("%s: baud is B0", __FUNCTION__); |
@@ -638,21 +676,6 @@ static void mct_u232_set_termios (struct usb_serial_port *port, | |||
638 | 676 | ||
639 | mct_u232_set_line_ctrl(serial, last_lcr); | 677 | mct_u232_set_line_ctrl(serial, last_lcr); |
640 | 678 | ||
641 | /* | ||
642 | * Set flow control: well, I do not really now how to handle DTR/RTS. | ||
643 | * Just do what we have seen with SniffUSB on Win98. | ||
644 | */ | ||
645 | /* Drop DTR/RTS if no flow control otherwise assert */ | ||
646 | new_state = control_state; | ||
647 | if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS)) | ||
648 | new_state |= TIOCM_DTR | TIOCM_RTS; | ||
649 | else | ||
650 | new_state &= ~(TIOCM_DTR | TIOCM_RTS); | ||
651 | if (new_state != control_state) { | ||
652 | mct_u232_set_modem_ctrl(serial, new_state); | ||
653 | control_state = new_state; | ||
654 | } | ||
655 | |||
656 | /* save off the modified port settings */ | 679 | /* save off the modified port settings */ |
657 | spin_lock_irqsave(&priv->lock, flags); | 680 | spin_lock_irqsave(&priv->lock, flags); |
658 | priv->control_state = control_state; | 681 | priv->control_state = control_state; |
@@ -747,6 +770,50 @@ static int mct_u232_ioctl (struct usb_serial_port *port, struct file * file, | |||
747 | return 0; | 770 | return 0; |
748 | } /* mct_u232_ioctl */ | 771 | } /* mct_u232_ioctl */ |
749 | 772 | ||
773 | static void mct_u232_throttle (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 | tty = port->tty; | ||
781 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
782 | |||
783 | spin_lock_irqsave(&priv->lock, flags); | ||
784 | priv->rx_flags |= THROTTLED; | ||
785 | if (C_CRTSCTS(tty)) { | ||
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 | } | ||
794 | |||
795 | |||
796 | static void mct_u232_unthrottle (struct usb_serial_port *port) | ||
797 | { | ||
798 | struct mct_u232_private *priv = usb_get_serial_port_data(port); | ||
799 | unsigned long flags; | ||
800 | unsigned int control_state; | ||
801 | struct tty_struct *tty; | ||
802 | |||
803 | dbg("%s - port %d", __FUNCTION__, port->number); | ||
804 | |||
805 | tty = port->tty; | ||
806 | spin_lock_irqsave(&priv->lock, flags); | ||
807 | if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) { | ||
808 | priv->rx_flags &= ~THROTTLED; | ||
809 | priv->control_state |= TIOCM_RTS; | ||
810 | control_state = priv->control_state; | ||
811 | spin_unlock_irqrestore(&priv->lock, flags); | ||
812 | (void) mct_u232_set_modem_ctrl(port->serial, control_state); | ||
813 | } else { | ||
814 | spin_unlock_irqrestore(&priv->lock, flags); | ||
815 | } | ||
816 | } | ||
750 | 817 | ||
751 | static int __init mct_u232_init (void) | 818 | static int __init mct_u232_init (void) |
752 | { | 819 | { |