diff --git a/drivers/usb/serial/belkin_sa.h b/drivers/usb/serial/belkin_sa.h index a13a98d284f2..89ec30c63cc6 100644 --- a/drivers/usb/serial/belkin_sa.h +++ b/drivers/usb/serial/belkin_sa.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Definitions for Belkin USB Serial Adapter Driver * diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index c5ecdcd51ffc..89675ee29645 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -73,6 +73,8 @@ #define CH341_LCR_CS6 0x01 #define CH341_LCR_CS5 0x00 +#define CH341_QUIRK_LIMITED_PRESCALER BIT(0) + static const struct usb_device_id id_table[] = { { USB_DEVICE(0x4348, 0x5523) }, { USB_DEVICE(0x1a86, 0x7523) }, @@ -87,6 +89,7 @@ struct ch341_private { u8 mcr; u8 msr; u8 lcr; + unsigned long quirks; }; static void ch341_set_termios(struct tty_struct *tty, @@ -159,9 +162,11 @@ static const speed_t ch341_min_rates[] = { * 2 <= div <= 256 if fact = 0, or * 9 <= div <= 256 if fact = 1 */ -static int ch341_get_divisor(speed_t speed) +static int ch341_get_divisor(struct ch341_private *priv) { unsigned int fact, div, clk_div; + speed_t speed = priv->baud_rate; + bool force_fact0 = false; int ps; /* @@ -187,8 +192,12 @@ static int ch341_get_divisor(speed_t speed) clk_div = CH341_CLK_DIV(ps, fact); div = CH341_CLKRATE / (clk_div * speed); + /* Some devices require a lower base clock if ps < 3. */ + if (ps < 3 && (priv->quirks & CH341_QUIRK_LIMITED_PRESCALER)) + force_fact0 = true; + /* Halve base clock (fact = 0) if required. */ - if (div < 9 || div > 255) { + if (div < 9 || div > 255 || force_fact0) { div /= 2; clk_div *= 2; fact = 0; @@ -227,7 +236,7 @@ static int ch341_set_baudrate_lcr(struct usb_device *dev, if (!priv->baud_rate) return -EINVAL; - val = ch341_get_divisor(priv->baud_rate); + val = ch341_get_divisor(priv); if (val < 0) return -EINVAL; @@ -308,6 +317,54 @@ out: kfree(buffer); return r; } +static int ch341_detect_quirks(struct usb_serial_port *port) +{ + struct ch341_private *priv = usb_get_serial_port_data(port); + struct usb_device *udev = port->serial->dev; + const unsigned int size = 2; + unsigned long quirks = 0; + char *buffer; + int r; + + buffer = kmalloc(size, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + /* + * A subset of CH34x devices does not support all features. The + * prescaler is limited and there is no support for sending a RS232 + * break condition. A read failure when trying to set up the latter is + * used to detect these devices. + */ + r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), CH341_REQ_READ_REG, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + CH341_REG_BREAK, 0, buffer, size, DEFAULT_TIMEOUT); + if (r == -EPIPE) { + dev_dbg(&port->dev, "break control not supported\n"); + quirks = CH341_QUIRK_LIMITED_PRESCALER; + r = 0; + goto out; + } + + if (r != size) { + if (r >= 0) + r = -EIO; + dev_err(&port->dev, "failed to read break control: %d\n", r); + goto out; + } + + r = 0; +out: + kfree(buffer); + + if (quirks) { + dev_dbg(&port->dev, "enabling quirk flags: 0x%02lx\n", quirks); + priv->quirks |= quirks; + } + + return r; +} + static int ch341_port_probe(struct usb_serial_port *port) { struct ch341_private *priv; @@ -330,6 +387,11 @@ static int ch341_port_probe(struct usb_serial_port *port) goto error; usb_set_serial_port_data(port, priv); + + r = ch341_detect_quirks(port); + if (r < 0) + goto error; + return 0; error: kfree(priv); diff --git a/drivers/usb/serial/io_16654.h b/drivers/usb/serial/io_16654.h index 4980f72dc56f..f18501f056cf 100644 --- a/drivers/usb/serial/io_16654.h +++ b/drivers/usb/serial/io_16654.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /************************************************************************ * * 16654.H Definitions for 16C654 UART used on EdgePorts diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h index 2e7fedbaf2ff..43ba53a3a6fa 100644 --- a/drivers/usb/serial/io_edgeport.h +++ b/drivers/usb/serial/io_edgeport.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /************************************************************************ * * io_edgeport.h Edgeport Linux Interface definitions diff --git a/drivers/usb/serial/io_ionsp.h b/drivers/usb/serial/io_ionsp.h index 4b8e4823bd45..db4fce815c97 100644 --- a/drivers/usb/serial/io_ionsp.h +++ b/drivers/usb/serial/io_ionsp.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /************************************************************************ * * IONSP.H Definitions for I/O Networks Serial Protocol diff --git a/drivers/usb/serial/io_ti.h b/drivers/usb/serial/io_ti.h index 9bbcee37524e..50b899d55ed0 100644 --- a/drivers/usb/serial/io_ti.h +++ b/drivers/usb/serial/io_ti.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /***************************************************************************** * * Copyright (C) 1997-2002 Inside Out Networks, Inc. diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h index 0d1a5bb4636e..52cbc353051f 100644 --- a/drivers/usb/serial/io_usbvend.h +++ b/drivers/usb/serial/io_usbvend.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /************************************************************************ * * USBVEND.H Vendor-specific USB definitions diff --git a/drivers/usb/serial/iuu_phoenix.h b/drivers/usb/serial/iuu_phoenix.h index b400b262f72e..87992b24d904 100644 --- a/drivers/usb/serial/iuu_phoenix.h +++ b/drivers/usb/serial/iuu_phoenix.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Infinity Unlimited USB Phoenix driver * diff --git a/drivers/usb/serial/mct_u232.h b/drivers/usb/serial/mct_u232.h index 0084edf518e8..e3d09a83cab1 100644 --- a/drivers/usb/serial/mct_u232.h +++ b/drivers/usb/serial/mct_u232.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Definitions for MCT (Magic Control Technology) USB-RS232 Converter Driver * diff --git a/drivers/usb/serial/oti6858.h b/drivers/usb/serial/oti6858.h index 1226bf2347eb..5c25836fdcd9 100644 --- a/drivers/usb/serial/oti6858.h +++ b/drivers/usb/serial/oti6858.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Ours Technology Inc. OTi-6858 USB to serial adapter driver. */ diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 52db5519aaf0..7d3090ee7e0c 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Prolific PL2303 USB to serial adaptor driver header file */ diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 13be21aad2f4..4b9845807bee 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -270,6 +270,10 @@ static void usb_wwan_indat_callback(struct urb *urb) if (status) { dev_dbg(dev, "%s: nonzero status: %d on endpoint %02x.\n", __func__, status, endpoint); + + /* don't resubmit on fatal errors */ + if (status == -ESHUTDOWN || status == -ENOENT) + return; } else { if (urb->actual_length) { tty_insert_flip_string(&port->port, data, diff --git a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h index 4bd69d047036..622d639ce74e 100644 --- a/drivers/usb/serial/visor.h +++ b/drivers/usb/serial/visor.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * USB HandSpring Visor driver * diff --git a/drivers/usb/serial/whiteheat.h b/drivers/usb/serial/whiteheat.h index 269e727a92f9..7e63074c9128 100644 --- a/drivers/usb/serial/whiteheat.h +++ b/drivers/usb/serial/whiteheat.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * USB ConnectTech WhiteHEAT driver *