diff options
author | Ian Abbott <abbotti@mev.co.uk> | 2005-07-29 15:16:41 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-07-29 16:12:52 -0400 |
commit | 74ede0ff59fb18787213ed979641624a2f234821 (patch) | |
tree | f3edc4df478c45684a856bbca6f90047ab457862 /drivers/usb/serial/ftdi_sio.c | |
parent | 9b1513d91e195af46b8e59626f74d3d41a7565af (diff) |
[PATCH] USB: ftdi_sio: Update RTS and DTR simultaneously
ftdi_sio: Update RTS and DTR simultaneously, using a single control URB
instead of separate control URBs for RTS and DTR. Reinhard Bergmann
observed time differences of up to 680 ms with his application on a
2.4.22 kernel when RTS and DTR were updated using separate control
URBs, which is unacceptable.
Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/usb/serial/ftdi_sio.c')
-rw-r--r-- | drivers/usb/serial/ftdi_sio.c | 138 |
1 files changed, 43 insertions, 95 deletions
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index cfb77ceced58..d1d2aeebd20a 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c | |||
@@ -596,62 +596,59 @@ static __u32 ftdi_232bm_baud_to_divisor(int baud) | |||
596 | return(ftdi_232bm_baud_base_to_divisor(baud, 48000000)); | 596 | return(ftdi_232bm_baud_base_to_divisor(baud, 48000000)); |
597 | } | 597 | } |
598 | 598 | ||
599 | static int set_rts(struct usb_serial_port *port, int high_or_low) | 599 | #define set_mctrl(port, set) update_mctrl((port), (set), 0) |
600 | #define clear_mctrl(port, clear) update_mctrl((port), 0, (clear)) | ||
601 | |||
602 | static int update_mctrl(struct usb_serial_port *port, unsigned int set, unsigned int clear) | ||
600 | { | 603 | { |
601 | struct ftdi_private *priv = usb_get_serial_port_data(port); | 604 | struct ftdi_private *priv = usb_get_serial_port_data(port); |
602 | char *buf; | 605 | char *buf; |
603 | unsigned ftdi_high_or_low; | 606 | unsigned urb_value; |
604 | int rv; | 607 | int rv; |
605 | |||
606 | buf = kmalloc(1, GFP_NOIO); | ||
607 | if (!buf) | ||
608 | return -ENOMEM; | ||
609 | |||
610 | if (high_or_low) { | ||
611 | ftdi_high_or_low = FTDI_SIO_SET_RTS_HIGH; | ||
612 | priv->last_dtr_rts |= TIOCM_RTS; | ||
613 | } else { | ||
614 | ftdi_high_or_low = FTDI_SIO_SET_RTS_LOW; | ||
615 | priv->last_dtr_rts &= ~TIOCM_RTS; | ||
616 | } | ||
617 | rv = usb_control_msg(port->serial->dev, | ||
618 | usb_sndctrlpipe(port->serial->dev, 0), | ||
619 | FTDI_SIO_SET_MODEM_CTRL_REQUEST, | ||
620 | FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, | ||
621 | ftdi_high_or_low, priv->interface, | ||
622 | buf, 0, WDR_TIMEOUT); | ||
623 | |||
624 | kfree(buf); | ||
625 | return rv; | ||
626 | } | ||
627 | 608 | ||
609 | if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) { | ||
610 | dbg("%s - DTR|RTS not being set|cleared", __FUNCTION__); | ||
611 | return 0; /* no change */ | ||
612 | } | ||
628 | 613 | ||
629 | static int set_dtr(struct usb_serial_port *port, int high_or_low) | ||
630 | { | ||
631 | struct ftdi_private *priv = usb_get_serial_port_data(port); | ||
632 | char *buf; | ||
633 | unsigned ftdi_high_or_low; | ||
634 | int rv; | ||
635 | |||
636 | buf = kmalloc(1, GFP_NOIO); | 614 | buf = kmalloc(1, GFP_NOIO); |
637 | if (!buf) | 615 | if (!buf) { |
638 | return -ENOMEM; | 616 | return -ENOMEM; |
639 | |||
640 | if (high_or_low) { | ||
641 | ftdi_high_or_low = FTDI_SIO_SET_DTR_HIGH; | ||
642 | priv->last_dtr_rts |= TIOCM_DTR; | ||
643 | } else { | ||
644 | ftdi_high_or_low = FTDI_SIO_SET_DTR_LOW; | ||
645 | priv->last_dtr_rts &= ~TIOCM_DTR; | ||
646 | } | 617 | } |
618 | |||
619 | clear &= ~set; /* 'set' takes precedence over 'clear' */ | ||
620 | urb_value = 0; | ||
621 | if (clear & TIOCM_DTR) | ||
622 | urb_value |= FTDI_SIO_SET_DTR_LOW; | ||
623 | if (clear & TIOCM_RTS) | ||
624 | urb_value |= FTDI_SIO_SET_RTS_LOW; | ||
625 | if (set & TIOCM_DTR) | ||
626 | urb_value |= FTDI_SIO_SET_DTR_HIGH; | ||
627 | if (set & TIOCM_RTS) | ||
628 | urb_value |= FTDI_SIO_SET_RTS_HIGH; | ||
647 | rv = usb_control_msg(port->serial->dev, | 629 | rv = usb_control_msg(port->serial->dev, |
648 | usb_sndctrlpipe(port->serial->dev, 0), | 630 | usb_sndctrlpipe(port->serial->dev, 0), |
649 | FTDI_SIO_SET_MODEM_CTRL_REQUEST, | 631 | FTDI_SIO_SET_MODEM_CTRL_REQUEST, |
650 | FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, | 632 | FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, |
651 | ftdi_high_or_low, priv->interface, | 633 | urb_value, priv->interface, |
652 | buf, 0, WDR_TIMEOUT); | 634 | buf, 0, WDR_TIMEOUT); |
653 | 635 | ||
654 | kfree(buf); | 636 | kfree(buf); |
637 | if (rv < 0) { | ||
638 | err("%s Error from MODEM_CTRL urb: DTR %s, RTS %s", | ||
639 | __FUNCTION__, | ||
640 | (set & TIOCM_DTR) ? "HIGH" : | ||
641 | (clear & TIOCM_DTR) ? "LOW" : "unchanged", | ||
642 | (set & TIOCM_RTS) ? "HIGH" : | ||
643 | (clear & TIOCM_RTS) ? "LOW" : "unchanged"); | ||
644 | } else { | ||
645 | dbg("%s - DTR %s, RTS %s", __FUNCTION__, | ||
646 | (set & TIOCM_DTR) ? "HIGH" : | ||
647 | (clear & TIOCM_DTR) ? "LOW" : "unchanged", | ||
648 | (set & TIOCM_RTS) ? "HIGH" : | ||
649 | (clear & TIOCM_RTS) ? "LOW" : "unchanged"); | ||
650 | priv->last_dtr_rts = (priv->last_dtr_rts & ~clear) | set; | ||
651 | } | ||
655 | return rv; | 652 | return rv; |
656 | } | 653 | } |
657 | 654 | ||
@@ -1222,12 +1219,7 @@ static int ftdi_open (struct usb_serial_port *port, struct file *filp) | |||
1222 | /* FIXME: Flow control might be enabled, so it should be checked - | 1219 | /* FIXME: Flow control might be enabled, so it should be checked - |
1223 | we have no control of defaults! */ | 1220 | we have no control of defaults! */ |
1224 | /* Turn on RTS and DTR since we are not flow controlling by default */ | 1221 | /* Turn on RTS and DTR since we are not flow controlling by default */ |
1225 | if (set_dtr(port, HIGH) < 0) { | 1222 | set_mctrl(port, TIOCM_DTR | TIOCM_RTS); |
1226 | err("%s Error from DTR HIGH urb", __FUNCTION__); | ||
1227 | } | ||
1228 | if (set_rts(port, HIGH) < 0){ | ||
1229 | err("%s Error from RTS HIGH urb", __FUNCTION__); | ||
1230 | } | ||
1231 | 1223 | ||
1232 | /* Not throttled */ | 1224 | /* Not throttled */ |
1233 | spin_lock_irqsave(&priv->rx_lock, flags); | 1225 | spin_lock_irqsave(&priv->rx_lock, flags); |
@@ -1277,14 +1269,8 @@ static void ftdi_close (struct usb_serial_port *port, struct file *filp) | |||
1277 | err("error from flowcontrol urb"); | 1269 | err("error from flowcontrol urb"); |
1278 | } | 1270 | } |
1279 | 1271 | ||
1280 | /* drop DTR */ | 1272 | /* drop RTS and DTR */ |
1281 | if (set_dtr(port, LOW) < 0){ | 1273 | clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); |
1282 | err("Error from DTR LOW urb"); | ||
1283 | } | ||
1284 | /* drop RTS */ | ||
1285 | if (set_rts(port, LOW) < 0) { | ||
1286 | err("Error from RTS LOW urb"); | ||
1287 | } | ||
1288 | } /* Note change no line if hupcl is off */ | 1274 | } /* Note change no line if hupcl is off */ |
1289 | 1275 | ||
1290 | /* cancel any scheduled reading */ | 1276 | /* cancel any scheduled reading */ |
@@ -1815,25 +1801,14 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_ | |||
1815 | err("%s error from disable flowcontrol urb", __FUNCTION__); | 1801 | err("%s error from disable flowcontrol urb", __FUNCTION__); |
1816 | } | 1802 | } |
1817 | /* Drop RTS and DTR */ | 1803 | /* Drop RTS and DTR */ |
1818 | if (set_dtr(port, LOW) < 0){ | 1804 | clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); |
1819 | err("%s Error from DTR LOW urb", __FUNCTION__); | ||
1820 | } | ||
1821 | if (set_rts(port, LOW) < 0){ | ||
1822 | err("%s Error from RTS LOW urb", __FUNCTION__); | ||
1823 | } | ||
1824 | |||
1825 | } else { | 1805 | } else { |
1826 | /* set the baudrate determined before */ | 1806 | /* set the baudrate determined before */ |
1827 | if (change_speed(port)) { | 1807 | if (change_speed(port)) { |
1828 | err("%s urb failed to set baurdrate", __FUNCTION__); | 1808 | err("%s urb failed to set baurdrate", __FUNCTION__); |
1829 | } | 1809 | } |
1830 | /* Ensure RTS and DTR are raised */ | 1810 | /* Ensure RTS and DTR are raised */ |
1831 | else if (set_dtr(port, HIGH) < 0){ | 1811 | set_mctrl(port, TIOCM_DTR | TIOCM_RTS); |
1832 | err("%s Error from DTR HIGH urb", __FUNCTION__); | ||
1833 | } | ||
1834 | else if (set_rts(port, HIGH) < 0){ | ||
1835 | err("%s Error from RTS HIGH urb", __FUNCTION__); | ||
1836 | } | ||
1837 | } | 1812 | } |
1838 | 1813 | ||
1839 | /* Set flow control */ | 1814 | /* Set flow control */ |
@@ -1945,35 +1920,8 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file) | |||
1945 | 1920 | ||
1946 | static int ftdi_tiocmset(struct usb_serial_port *port, struct file * file, unsigned int set, unsigned int clear) | 1921 | static int ftdi_tiocmset(struct usb_serial_port *port, struct file * file, unsigned int set, unsigned int clear) |
1947 | { | 1922 | { |
1948 | int ret; | ||
1949 | |||
1950 | dbg("%s TIOCMSET", __FUNCTION__); | 1923 | dbg("%s TIOCMSET", __FUNCTION__); |
1951 | if (set & TIOCM_DTR){ | 1924 | return update_mctrl(port, set, clear); |
1952 | if ((ret = set_dtr(port, HIGH)) < 0) { | ||
1953 | err("Urb to set DTR failed"); | ||
1954 | return(ret); | ||
1955 | } | ||
1956 | } | ||
1957 | if (set & TIOCM_RTS) { | ||
1958 | if ((ret = set_rts(port, HIGH)) < 0){ | ||
1959 | err("Urb to set RTS failed"); | ||
1960 | return(ret); | ||
1961 | } | ||
1962 | } | ||
1963 | |||
1964 | if (clear & TIOCM_DTR){ | ||
1965 | if ((ret = set_dtr(port, LOW)) < 0){ | ||
1966 | err("Urb to unset DTR failed"); | ||
1967 | return(ret); | ||
1968 | } | ||
1969 | } | ||
1970 | if (clear & TIOCM_RTS) { | ||
1971 | if ((ret = set_rts(port, LOW)) < 0){ | ||
1972 | err("Urb to unset RTS failed"); | ||
1973 | return(ret); | ||
1974 | } | ||
1975 | } | ||
1976 | return(0); | ||
1977 | } | 1925 | } |
1978 | 1926 | ||
1979 | 1927 | ||