Skip to content

Commit c404bf4

Browse files
hansmijhovold
authored andcommitted
USB: serial: ch341: add basis for quirk detection
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 <[email protected]> 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 <[email protected]> # 5.5 Signed-off-by: Johan Hovold <[email protected]>
1 parent 986c174 commit c404bf4

File tree

1 file changed

+53
-0
lines changed

1 file changed

+53
-0
lines changed

drivers/usb/serial/ch341.c

+53
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ struct ch341_private {
8787
u8 mcr;
8888
u8 msr;
8989
u8 lcr;
90+
unsigned long quirks;
9091
};
9192

9293
static void ch341_set_termios(struct tty_struct *tty,
@@ -308,6 +309,53 @@ out: kfree(buffer);
308309
return r;
309310
}
310311

312+
static int ch341_detect_quirks(struct usb_serial_port *port)
313+
{
314+
struct ch341_private *priv = usb_get_serial_port_data(port);
315+
struct usb_device *udev = port->serial->dev;
316+
const unsigned int size = 2;
317+
unsigned long quirks = 0;
318+
char *buffer;
319+
int r;
320+
321+
buffer = kmalloc(size, GFP_KERNEL);
322+
if (!buffer)
323+
return -ENOMEM;
324+
325+
/*
326+
* A subset of CH34x devices does not support all features. The
327+
* prescaler is limited and there is no support for sending a RS232
328+
* break condition. A read failure when trying to set up the latter is
329+
* used to detect these devices.
330+
*/
331+
r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), CH341_REQ_READ_REG,
332+
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
333+
CH341_REG_BREAK, 0, buffer, size, DEFAULT_TIMEOUT);
334+
if (r == -EPIPE) {
335+
dev_dbg(&port->dev, "break control not supported\n");
336+
r = 0;
337+
goto out;
338+
}
339+
340+
if (r != size) {
341+
if (r >= 0)
342+
r = -EIO;
343+
dev_err(&port->dev, "failed to read break control: %d\n", r);
344+
goto out;
345+
}
346+
347+
r = 0;
348+
out:
349+
kfree(buffer);
350+
351+
if (quirks) {
352+
dev_dbg(&port->dev, "enabling quirk flags: 0x%02lx\n", quirks);
353+
priv->quirks |= quirks;
354+
}
355+
356+
return r;
357+
}
358+
311359
static int ch341_port_probe(struct usb_serial_port *port)
312360
{
313361
struct ch341_private *priv;
@@ -330,6 +378,11 @@ static int ch341_port_probe(struct usb_serial_port *port)
330378
goto error;
331379

332380
usb_set_serial_port_data(port, priv);
381+
382+
r = ch341_detect_quirks(port);
383+
if (r < 0)
384+
goto error;
385+
333386
return 0;
334387

335388
error: kfree(priv);

0 commit comments

Comments
 (0)