From 4d304a6fe93538ce356b4593dc43476b50c023e7 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Mon, 23 Apr 2018 09:03:06 +0200 Subject: USB: serial: option: blacklist unused dwm-158 interfaces The dwm-158 interface 4 and 5 doesn't answer to the AT commands and doesn't appears a option interface. Tested on openwrt distribution (kernel 4.14 using the old blacklist definitions). Lars Melin also writes: Blacklisting interface 4 and 5 is correct because: MI_00 D-Link Mobile Broadband Device (cdc_ether) MI_02 D-Link HSPA+DataCard Diagnostics Interface (also ppp modem) MI_03 D-Link HSPA+DataCard NMEA Device MI_04 D-Link HSPA+DataCard Speech Port MI_05 D-Link HSPA+DataCard Debug Port MI_06 USB Mass Storage Device Signed-off-by: Giuseppe Lippolis [ johan: add Lars's comment on the interface layout and reword summary ] Cc: Lars Melin Cc: Dan Williams Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index c3f252283ab9..f0c3612467a3 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1911,7 +1911,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d01, 0xff) }, /* D-Link DWM-156 (variant) */ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d02, 0xff) }, { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d03, 0xff) }, - { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) }, /* D-Link DWM-158 */ + { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff), /* D-Link DWM-158 */ + .driver_info = RSVD(4) | RSVD(5) }, { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d0e, 0xff) }, /* D-Link DWM-157 C1 */ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */ .driver_info = RSVD(4) }, -- cgit v1.2.3 From 79ef51894a8dfbd7c4db1d2a820c235b42499e2e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 16 May 2018 11:42:07 +0200 Subject: USB: serial: use tty_port_register_device() We already have the tty port when probing a usb-serial port so use tty_port_register_device() directly instead of tty_port_install() later to set up the port link. This is a step towards enabling serdev for usb-serial (but we need to determine how to handle hotplugging first). Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/bus.c | 3 ++- drivers/usb/serial/usb-serial.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 9e265eb92611..eb0195cf37dd 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -60,7 +60,8 @@ static int usb_serial_device_probe(struct device *dev) } minor = port->minor; - tty_dev = tty_register_device(usb_serial_tty_driver, minor, dev); + tty_dev = tty_port_register_device(&port->port, usb_serial_tty_driver, + minor, dev); if (IS_ERR(tty_dev)) { retval = PTR_ERR(tty_dev); goto err_port_remove; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 790e0cbe3da9..44ecf0e2be9d 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -192,7 +192,7 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty) if (retval) goto error_get_interface; - retval = tty_port_install(&port->port, driver, tty); + retval = tty_standard_install(driver, tty); if (retval) goto error_init_termios; -- cgit v1.2.3 From 5ada98427f12f5c19d6f93bfe02f23fe4531b978 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 18 May 2018 15:25:47 +0200 Subject: USB: serial: ftdi_sio: fix IXON/IXOFF mixup Since forever this driver has had IXON and IXOFF mixed up, and has used the latter rather than the former to enable hardware-assisted software flow control on output. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 7ea221d42dba..62c99871863c 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -2342,12 +2342,8 @@ no_c_cflag_changes: } else { /* * Xon/Xoff code - * - * Check the IXOFF status in the iflag component of the - * termios structure. If IXOFF is not set, the pre-xon/xoff - * code is executed. */ - if (iflag & IXOFF) { + if (iflag & IXON) { dev_dbg(ddev, "%s request to enable xonxoff iflag=%04x\n", __func__, iflag); /* Try to enable the XON/XOFF on the ftdi_sio @@ -2372,7 +2368,7 @@ no_c_cflag_changes: } } else { /* else clause to only run if cflag ! CRTSCTS and iflag - * ! XOFF. CHECKME Assuming XON/XOFF handled by tty + * ! XON. CHECKME Assuming XON/XOFF handled by tty * stack - not by device */ dev_dbg(ddev, "%s Turning off hardware flow control\n", __func__); if (usb_control_msg(dev, -- cgit v1.2.3 From fd54a99aec18fe47c3035a38815bcbeba09100cc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 18 May 2018 15:25:48 +0200 Subject: USB: serial: ftdi_sio: use non-underscore fixed types Replace all __u types with their u counterparts throughout the driver (whose structures are not exported to user space). Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 50 +++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 62c99871863c..0d24da03a4a3 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -54,7 +54,7 @@ struct ftdi_private { int custom_divisor; /* custom_divisor kludge, this is for baud_base (different from what goes to the chip!) */ - __u16 last_set_data_urb_value ; + u16 last_set_data_urb_value; /* the last data state set - needed for doing * a break */ @@ -62,7 +62,7 @@ struct ftdi_private { unsigned long last_dtr_rts; /* saved modem control outputs */ char prev_status; /* Used for TIOCMIWAIT */ char transmit_empty; /* If transmitter is empty or not */ - __u16 interface; /* FT2232C, FT2232H or FT4232H port interface + u16 interface; /* FT2232C, FT2232H or FT4232H port interface (0 for FT232/245) */ speed_t force_baud; /* if non-zero, force the baud rate to @@ -1063,10 +1063,10 @@ static int ftdi_get_modem_status(struct usb_serial_port *port, static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base); static unsigned short int ftdi_232am_baud_to_divisor(int baud); -static __u32 ftdi_232bm_baud_base_to_divisor(int baud, int base); -static __u32 ftdi_232bm_baud_to_divisor(int baud); -static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base); -static __u32 ftdi_2232h_baud_to_divisor(int baud); +static u32 ftdi_232bm_baud_base_to_divisor(int baud, int base); +static u32 ftdi_232bm_baud_to_divisor(int baud); +static u32 ftdi_2232h_baud_base_to_divisor(int baud, int base); +static u32 ftdi_2232h_baud_to_divisor(int baud); static struct usb_serial_driver ftdi_sio_device = { .driver = { @@ -1136,14 +1136,14 @@ static unsigned short int ftdi_232am_baud_to_divisor(int baud) return ftdi_232am_baud_base_to_divisor(baud, 48000000); } -static __u32 ftdi_232bm_baud_base_to_divisor(int baud, int base) +static u32 ftdi_232bm_baud_base_to_divisor(int baud, int base) { static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 }; - __u32 divisor; + u32 divisor; /* divisor shifted 3 bits to the left */ int divisor3 = base / 2 / baud; divisor = divisor3 >> 3; - divisor |= (__u32)divfrac[divisor3 & 0x7] << 14; + divisor |= (u32)divfrac[divisor3 & 0x7] << 14; /* Deal with special cases for highest baud rates. */ if (divisor == 1) divisor = 0; @@ -1152,22 +1152,22 @@ static __u32 ftdi_232bm_baud_base_to_divisor(int baud, int base) return divisor; } -static __u32 ftdi_232bm_baud_to_divisor(int baud) +static u32 ftdi_232bm_baud_to_divisor(int baud) { return ftdi_232bm_baud_base_to_divisor(baud, 48000000); } -static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base) +static u32 ftdi_2232h_baud_base_to_divisor(int baud, int base) { static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 }; - __u32 divisor; + u32 divisor; int divisor3; /* hi-speed baud rate is 10-bit sampling instead of 16-bit */ divisor3 = base * 8 / (baud * 10); divisor = divisor3 >> 3; - divisor |= (__u32)divfrac[divisor3 & 0x7] << 14; + divisor |= (u32)divfrac[divisor3 & 0x7] << 14; /* Deal with special cases for highest baud rates. */ if (divisor == 1) divisor = 0; @@ -1182,7 +1182,7 @@ static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base) return divisor; } -static __u32 ftdi_2232h_baud_to_divisor(int baud) +static u32 ftdi_2232h_baud_to_divisor(int baud) { return ftdi_2232h_baud_base_to_divisor(baud, 120000000); } @@ -1236,12 +1236,12 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set, } -static __u32 get_ftdi_divisor(struct tty_struct *tty, +static u32 get_ftdi_divisor(struct tty_struct *tty, struct usb_serial_port *port) { struct ftdi_private *priv = usb_get_serial_port_data(port); struct device *dev = &port->dev; - __u32 div_value = 0; + u32 div_value = 0; int div_okay = 1; int baud; @@ -1299,7 +1299,7 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty, case FT232RL: /* FT232RL chip */ case FTX: /* FT-X series */ if (baud <= 3000000) { - __u16 product_id = le16_to_cpu( + u16 product_id = le16_to_cpu( port->serial->dev->descriptor.idProduct); if (((product_id == FTDI_NDI_HUC_PID) || (product_id == FTDI_NDI_SPECTRA_SCU_PID) || @@ -1346,19 +1346,19 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty, static int change_speed(struct tty_struct *tty, struct usb_serial_port *port) { struct ftdi_private *priv = usb_get_serial_port_data(port); - __u16 urb_value; - __u16 urb_index; - __u32 urb_index_value; + u16 urb_value; + u16 urb_index; + u32 urb_index_value; int rv; urb_index_value = get_ftdi_divisor(tty, port); - urb_value = (__u16)urb_index_value; - urb_index = (__u16)(urb_index_value >> 16); + urb_value = (u16)urb_index_value; + urb_index = (u16)(urb_index_value >> 16); if ((priv->chip_type == FT2232C) || (priv->chip_type == FT2232H) || (priv->chip_type == FT4232H) || (priv->chip_type == FT232H)) { /* Probably the BM type needs the MSB of the encoded fractional * divider also moved like for the chips above. Any infos? */ - urb_index = (__u16)((urb_index << 8) | priv->interface); + urb_index = (u16)((urb_index << 8) | priv->interface); } rv = usb_control_msg(port->serial->dev, @@ -2140,7 +2140,7 @@ static void ftdi_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); - __u16 urb_value; + u16 urb_value; /* break_state = -1 to turn on break, and 0 to turn off break */ /* see drivers/char/tty_io.c to see it used */ @@ -2192,7 +2192,7 @@ static void ftdi_set_termios(struct tty_struct *tty, struct ftdi_private *priv = usb_get_serial_port_data(port); struct ktermios *termios = &tty->termios; unsigned int cflag = termios->c_cflag; - __u16 urb_value; /* will hold the new flags */ + u16 urb_value; /* will hold the new flags */ /* Added for xon/xoff support */ unsigned int iflag = termios->c_iflag; -- cgit v1.2.3 From 1641011549784699e013c7ccfebbd0d5a3dc41e7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 18 May 2018 15:25:49 +0200 Subject: USB: serial: ftdi_sio: drop unnecessary urb_ variable prefixes Drop urb_ prefixes from value and index variables used in control requests. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 83 +++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 42 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 0d24da03a4a3..3d7f181e7e2a 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -54,10 +54,9 @@ struct ftdi_private { int custom_divisor; /* custom_divisor kludge, this is for baud_base (different from what goes to the chip!) */ - u16 last_set_data_urb_value; - /* the last data state set - needed for doing - * a break - */ + u16 last_set_data_value; /* the last data state set - needed for doing + * a break + */ int flags; /* some ASYNC_xxxx flags are supported */ unsigned long last_dtr_rts; /* saved modem control outputs */ char prev_status; /* Used for TIOCMIWAIT */ @@ -1195,7 +1194,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set, { struct ftdi_private *priv = usb_get_serial_port_data(port); struct device *dev = &port->dev; - unsigned urb_value; + unsigned value; int rv; if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) { @@ -1204,20 +1203,20 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set, } clear &= ~set; /* 'set' takes precedence over 'clear' */ - urb_value = 0; + value = 0; if (clear & TIOCM_DTR) - urb_value |= FTDI_SIO_SET_DTR_LOW; + value |= FTDI_SIO_SET_DTR_LOW; if (clear & TIOCM_RTS) - urb_value |= FTDI_SIO_SET_RTS_LOW; + value |= FTDI_SIO_SET_RTS_LOW; if (set & TIOCM_DTR) - urb_value |= FTDI_SIO_SET_DTR_HIGH; + value |= FTDI_SIO_SET_DTR_HIGH; if (set & TIOCM_RTS) - urb_value |= FTDI_SIO_SET_RTS_HIGH; + value |= FTDI_SIO_SET_RTS_HIGH; rv = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0), FTDI_SIO_SET_MODEM_CTRL_REQUEST, FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, - urb_value, priv->interface, + value, priv->interface, NULL, 0, WDR_TIMEOUT); if (rv < 0) { dev_dbg(dev, "%s Error from MODEM_CTRL urb: DTR %s, RTS %s\n", @@ -1346,26 +1345,26 @@ static u32 get_ftdi_divisor(struct tty_struct *tty, static int change_speed(struct tty_struct *tty, struct usb_serial_port *port) { struct ftdi_private *priv = usb_get_serial_port_data(port); - u16 urb_value; - u16 urb_index; - u32 urb_index_value; + u16 value; + u16 index; + u32 index_value; int rv; - urb_index_value = get_ftdi_divisor(tty, port); - urb_value = (u16)urb_index_value; - urb_index = (u16)(urb_index_value >> 16); + index_value = get_ftdi_divisor(tty, port); + value = (u16)index_value; + index = (u16)(index_value >> 16); if ((priv->chip_type == FT2232C) || (priv->chip_type == FT2232H) || (priv->chip_type == FT4232H) || (priv->chip_type == FT232H)) { /* Probably the BM type needs the MSB of the encoded fractional * divider also moved like for the chips above. Any infos? */ - urb_index = (u16)((urb_index << 8) | priv->interface); + index = (u16)((index << 8) | priv->interface); } rv = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0), FTDI_SIO_SET_BAUDRATE_REQUEST, FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE, - urb_value, urb_index, + value, index, NULL, 0, WDR_SHORT_TIMEOUT); return rv; } @@ -2140,29 +2139,29 @@ static void ftdi_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); - u16 urb_value; + u16 value; /* break_state = -1 to turn on break, and 0 to turn off break */ /* see drivers/char/tty_io.c to see it used */ - /* last_set_data_urb_value NEVER has the break bit set in it */ + /* last_set_data_value NEVER has the break bit set in it */ if (break_state) - urb_value = priv->last_set_data_urb_value | FTDI_SIO_SET_BREAK; + value = priv->last_set_data_value | FTDI_SIO_SET_BREAK; else - urb_value = priv->last_set_data_urb_value; + value = priv->last_set_data_value; if (usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0), FTDI_SIO_SET_DATA_REQUEST, FTDI_SIO_SET_DATA_REQUEST_TYPE, - urb_value , priv->interface, + value , priv->interface, NULL, 0, WDR_TIMEOUT) < 0) { dev_err(&port->dev, "%s FAILED to enable/disable break state (state was %d)\n", __func__, break_state); } dev_dbg(&port->dev, "%s break state is %d - urb is %d\n", __func__, - break_state, urb_value); + break_state, value); } @@ -2192,7 +2191,7 @@ static void ftdi_set_termios(struct tty_struct *tty, struct ftdi_private *priv = usb_get_serial_port_data(port); struct ktermios *termios = &tty->termios; unsigned int cflag = termios->c_cflag; - u16 urb_value; /* will hold the new flags */ + u16 value; /* Added for xon/xoff support */ unsigned int iflag = termios->c_iflag; @@ -2258,44 +2257,44 @@ static void ftdi_set_termios(struct tty_struct *tty, no_skip: /* Set number of data bits, parity, stop bits */ - urb_value = 0; - urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 : - FTDI_SIO_SET_DATA_STOP_BITS_1); + value = 0; + value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 : + FTDI_SIO_SET_DATA_STOP_BITS_1); if (cflag & PARENB) { if (cflag & CMSPAR) - urb_value |= cflag & PARODD ? - FTDI_SIO_SET_DATA_PARITY_MARK : - FTDI_SIO_SET_DATA_PARITY_SPACE; + value |= cflag & PARODD ? + FTDI_SIO_SET_DATA_PARITY_MARK : + FTDI_SIO_SET_DATA_PARITY_SPACE; else - urb_value |= cflag & PARODD ? - FTDI_SIO_SET_DATA_PARITY_ODD : - FTDI_SIO_SET_DATA_PARITY_EVEN; + value |= cflag & PARODD ? + FTDI_SIO_SET_DATA_PARITY_ODD : + FTDI_SIO_SET_DATA_PARITY_EVEN; } else { - urb_value |= FTDI_SIO_SET_DATA_PARITY_NONE; + value |= FTDI_SIO_SET_DATA_PARITY_NONE; } switch (cflag & CSIZE) { case CS5: dev_dbg(ddev, "Setting CS5 quirk\n"); break; case CS7: - urb_value |= 7; + value |= 7; dev_dbg(ddev, "Setting CS7\n"); break; default: case CS8: - urb_value |= 8; + value |= 8; dev_dbg(ddev, "Setting CS8\n"); break; } /* This is needed by the break command since it uses the same command - but is or'ed with this value */ - priv->last_set_data_urb_value = urb_value; + priv->last_set_data_value = value; if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), FTDI_SIO_SET_DATA_REQUEST, FTDI_SIO_SET_DATA_REQUEST_TYPE, - urb_value , priv->interface, + value , priv->interface, NULL, 0, WDR_SHORT_TIMEOUT) < 0) { dev_err(ddev, "%s FAILED to set databits/stopbits/parity\n", __func__); @@ -2354,13 +2353,13 @@ no_c_cflag_changes: */ vstart = termios->c_cc[VSTART]; vstop = termios->c_cc[VSTOP]; - urb_value = (vstop << 8) | (vstart); + value = (vstop << 8) | (vstart); if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), FTDI_SIO_SET_FLOW_CTRL_REQUEST, FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, - urb_value , (FTDI_SIO_XON_XOFF_HS + value , (FTDI_SIO_XON_XOFF_HS | priv->interface), NULL, 0, WDR_TIMEOUT) < 0) { dev_err(&port->dev, "urb failed to set to " -- cgit v1.2.3 From df1cd63d6d112e4a1402166dacc411ccca9b09ac Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 18 May 2018 15:25:50 +0200 Subject: USB: serial: ftdi_sio: clean up flow control management Clean up the somewhat convoluted hardware-assisted flow control handling. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 81 ++++++++++++------------------------------- 1 file changed, 23 insertions(+), 58 deletions(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 3d7f181e7e2a..b5cef322826f 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -2191,12 +2191,8 @@ static void ftdi_set_termios(struct tty_struct *tty, struct ftdi_private *priv = usb_get_serial_port_data(port); struct ktermios *termios = &tty->termios; unsigned int cflag = termios->c_cflag; - u16 value; - - /* Added for xon/xoff support */ - unsigned int iflag = termios->c_iflag; - unsigned char vstop; - unsigned char vstart; + u16 value, index; + int ret; /* Force baud rate if this device requires it, unless it is set to B0. */ @@ -2325,61 +2321,30 @@ no_data_parity_stop_changes: set_mctrl(port, TIOCM_DTR | TIOCM_RTS); } - /* Set flow control */ - /* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */ no_c_cflag_changes: - if (cflag & CRTSCTS) { - dev_dbg(ddev, "%s Setting to CRTSCTS flow control\n", __func__); - if (usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), - FTDI_SIO_SET_FLOW_CTRL_REQUEST, - FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, - 0 , (FTDI_SIO_RTS_CTS_HS | priv->interface), - NULL, 0, WDR_TIMEOUT) < 0) { - dev_err(ddev, "urb failed to set to rts/cts flow control\n"); - } + /* Set hardware-assisted flow control */ + value = 0; + + if (C_CRTSCTS(tty)) { + dev_dbg(&port->dev, "enabling rts/cts flow control\n"); + index = FTDI_SIO_RTS_CTS_HS; + } else if (I_IXON(tty)) { + dev_dbg(&port->dev, "enabling xon/xoff flow control\n"); + index = FTDI_SIO_XON_XOFF_HS; + value = STOP_CHAR(tty) << 8 | START_CHAR(tty); } else { - /* - * Xon/Xoff code - */ - if (iflag & IXON) { - dev_dbg(ddev, "%s request to enable xonxoff iflag=%04x\n", - __func__, iflag); - /* Try to enable the XON/XOFF on the ftdi_sio - * Set the vstart and vstop -- could have been done up - * above where a lot of other dereferencing is done but - * that would be very inefficient as vstart and vstop - * are not always needed. - */ - vstart = termios->c_cc[VSTART]; - vstop = termios->c_cc[VSTOP]; - value = (vstop << 8) | (vstart); - - if (usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), - FTDI_SIO_SET_FLOW_CTRL_REQUEST, - FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, - value , (FTDI_SIO_XON_XOFF_HS - | priv->interface), - NULL, 0, WDR_TIMEOUT) < 0) { - dev_err(&port->dev, "urb failed to set to " - "xon/xoff flow control\n"); - } - } else { - /* else clause to only run if cflag ! CRTSCTS and iflag - * ! XON. CHECKME Assuming XON/XOFF handled by tty - * stack - not by device */ - dev_dbg(ddev, "%s Turning off hardware flow control\n", __func__); - if (usb_control_msg(dev, - usb_sndctrlpipe(dev, 0), - FTDI_SIO_SET_FLOW_CTRL_REQUEST, - FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, - 0, priv->interface, - NULL, 0, WDR_TIMEOUT) < 0) { - dev_err(ddev, "urb failed to clear flow control\n"); - } - } + dev_dbg(&port->dev, "disabling flow control\n"); + index = FTDI_SIO_DISABLE_FLOW_CTRL; } + + index |= priv->interface; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + FTDI_SIO_SET_FLOW_CTRL_REQUEST, + FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, + value, index, NULL, 0, WDR_TIMEOUT); + if (ret < 0) + dev_err(&port->dev, "failed to set flow control: %d\n", ret); } /* -- cgit v1.2.3 From 7041d9c3f01b365daa50340a5d2dce84a09ea813 Mon Sep 17 00:00:00 2001 From: Florian Zumbiehl Date: Sun, 20 May 2018 02:23:12 +0200 Subject: USB: serial: pl2303: add support for tx xon/xoff flow control Support hardware-level Xon/Xoff flow control in transmit direction with pl2303. I only know how to get the hardware to do IXON/!IXANY with ^S/^Q as control characters, so I preserve the old behaviour for all other cases. Signed-off-by: Florian Zumbiehl [ johan: rewrite logic using pl2303_termios_change() helper ] Signed-off-by: Johan Hovold --- drivers/usb/serial/pl2303.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/usb') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 46dd09da2434..5d1a1931967e 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -533,6 +533,17 @@ static int pl2303_set_line_request(struct usb_serial_port *port, return 0; } +static bool pl2303_termios_change(const struct ktermios *a, const struct ktermios *b) +{ + bool ixon_change; + + ixon_change = ((a->c_iflag ^ b->c_iflag) & (IXON | IXANY)) || + a->c_cc[VSTART] != b->c_cc[VSTART] || + a->c_cc[VSTOP] != b->c_cc[VSTOP]; + + return tty_termios_hw_change(a, b) || ixon_change; +} + static void pl2303_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { @@ -544,7 +555,7 @@ static void pl2303_set_termios(struct tty_struct *tty, int ret; u8 control; - if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios)) + if (old_termios && !pl2303_termios_change(&tty->termios, old_termios)) return; buf = kzalloc(7, GFP_KERNEL); @@ -662,6 +673,9 @@ static void pl2303_set_termios(struct tty_struct *tty, pl2303_vendor_write(serial, 0x0, 0x41); else pl2303_vendor_write(serial, 0x0, 0x61); + } else if (I_IXON(tty) && !I_IXANY(tty) && START_CHAR(tty) == 0x11 && + STOP_CHAR(tty) == 0x13) { + pl2303_vendor_write(serial, 0x0, 0xc0); } else { pl2303_vendor_write(serial, 0x0, 0x0); } -- cgit v1.2.3