| From fb6181ad1bedb35702911f14827bf207fb47523f Mon Sep 17 00:00:00 2001 |
| From: Michael Hanselmann <public@hansmi.ch> |
| Date: Tue, 31 Mar 2020 23:37:18 +0000 |
| Subject: [PATCH] USB: serial: ch341: add basis for quirk detection |
| |
| commit c404bf4aa9236cb4d1068e499ae42acf48a6ff97 upstream. |
| |
| A subset of CH341 devices does not support all features, namely the |
| prescaler is limited to a reduced precision and there is no support for |
| sending a RS232 break condition. This patch adds a detection function |
| which will be extended to set quirk flags as they're implemented. |
| |
| The author's affected device has an imprint of "340" on the |
| turquoise-colored plug, but not all such devices appear to be affected. |
| |
| Signed-off-by: Michael Hanselmann <public@hansmi.ch> |
| Link: https://lore.kernel.org/r/1e1ae0da6082bb528a44ef323d4e1d3733d38858.1585697281.git.public@hansmi.ch |
| [ johan: use long type for quirks; rephrase and use port device for |
| messages; handle short reads; set quirk flags directly in |
| helper function ] |
| Cc: stable <stable@vger.kernel.org> # 5.5 |
| Signed-off-by: Johan Hovold <johan@kernel.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c |
| index 955ab97b9b22..dcdd541b3291 100644 |
| --- a/drivers/usb/serial/ch341.c |
| +++ b/drivers/usb/serial/ch341.c |
| @@ -93,6 +93,7 @@ struct ch341_private { |
| u8 mcr; |
| u8 msr; |
| u8 lcr; |
| + unsigned long quirks; |
| }; |
| |
| static void ch341_set_termios(struct tty_struct *tty, |
| @@ -245,6 +246,53 @@ 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"); |
| + 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; |
| @@ -267,6 +315,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); |
| -- |
| 2.27.0 |
| |