Re: [Regression] CH341 USB-serial converter passes data in 32 byte chunks

Johan Hovold
2022-07-19T05:53:54Z

On Tue, Jul 19, 2022 at 01:29:17PM +0930, Jonathan Woithe wrote:
> On Mon, Jul 18, 2022 at 10:53:05AM +0200, Johan Hovold wrote:
> > [ +CC: Aidan, Grigori and Michael ]
> >
> > On Wed, Jul 13, 2022 at 09:52:18AM +0930, Jonathan Woithe wrote:
> > > On Tue, Jul 12, 2022 at 06:53:38PM +0200, Johan Hovold wrote:
> >
> > > > Simply reverting the commit blamed by the bisection should only makes
> > > > things worse, at least for some device types.
> > > >
> > > > Perhaps we need to set that bit 7 based on the type, even if the bit
> > > > meaning having been inverted seems a bit far-fetched.
> > > >
> > > > Jonathan, could you try simply commenting out the
> > > >
> > > > val |= BIT(7);
> > > >
> > > > statement in ch341_set_baudrate_lcr()?
> > >
> > > Commenting out the above line brought some improvement. In minicom with a
> > > loopback connector in place, the first byte sent does not get echoed
> > > back at all. However, all other bytes are echoed as soon as they are sent.
> >
> > Ok, so at least that addresses the disabled TX timer.
> >
> > What happens if you change the line speed? Does the first character
> > after changing speed also get dropped?
>
> Yes it does. Starting out at 115200 in minicom I confirmed the loss of the
> first character. I changed the line speed to 38400 in minicom and the next
> character to be pressed was dropped. All others after it were fine. I then
> returned to 115200 in minicom and the pattern repeated: the first character
> after the speed change was lost, but all others came through. All these
> tests were done in the same minicom session under kernel 672c0c5 with the
>
> val |= BIT(7);
>
> line still commented out.

Thanks for confirming.

I noticed one register write which was removed during 4.10 and we also
added support for setting the LCR register which wasn't there before.

Can you try the below patch which adds a register write that should
match what the old driver did at 115200 baud?

If that doesn't help, can you try just ifdeffing out the LCR update?

> > If that doesn't help, we'll keep digging. One more thing to try then
> > could be to comment out the above line on a 4.10 kernel to rule the
> > impact of any later changes on the first lost character.
>
> I assume the "above line" is the
>
> val |= BIT(7);
>
> statement in ch341_set_baudrate_lcr(). With this line commented out in the
> "v4.10" tag (c470abd), the first character is lost but all others are
> send/received as expected. As you suggested, this appears to rule out
> later changes as the source of the lost first character.

Ok, good.

Johan

From d384f68dfda4eaf5b0065bdab236800d65ffb71d Mon Sep 17 00:00:00 2001
From: Johan Hovold <johan@kernel.org>
Date: Mon, 18 Jul 2022 10:21:41 +0200
Subject: [PATCH] USB: serial: ch341: fix disabled rx timer on older devices

At least one older CH341 appears to have the RX timer enable bit
inverted so that setting it disables the RX timer and prevents the FIFO
from emptying until it is full.

Only set the RX timer enable bit for devices with version newer than
0x27.

Also try restoring a pre-4.10 register write when updating the line
settings to see if it has any effect on a lost first byte after
changing speed.

Another thing to try is to disable the LCR update, which wasn't
supported pre-4.10.

Reported-by: Jonathan Woithe <jwoithe@just42.net>
Link: https://lore.kernel.org/r/Ys1iPTfiZRWj2gXs@marvin.atrad.com.au
Not-Signed-off-yet-by: Johan Hovold <johan@kernel.org>
---
drivers/usb/serial/ch341.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index 2798fca71261..bd27847ece2c 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -97,7 +97,10 @@ struct ch341_private {
u8 mcr;
u8 msr;
u8 lcr;
+
unsigned long quirks;
+ u8 version;
+
unsigned long break_end;
};

@@ -250,8 +253,12 @@ static int ch341_set_baudrate_lcr(struct usb_device *dev,
/*
* CH341A buffers data until a full endpoint-size packet (32 bytes)
* has been received unless bit 7 is set.
+ *
+ * At least one device with version 0x27 appears to have this bit
+ * inverted.
*/
- val |= BIT(7);
+ if (priv->version > 0x27)
+ val |= BIT(7);

r = ch341_control_out(dev, CH341_REQ_WRITE_REG,
CH341_REG_DIVISOR << 8 | CH341_REG_PRESCALER,
@@ -259,6 +266,12 @@ static int ch341_set_baudrate_lcr(struct usb_device *dev,
if (r)
return r;

+#if 1
+ r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x0f2c, 0x08);
+ if (r)
+ return r;
+#endif
+#if 1
/*
* Chip versions before version 0x30 as read using
* CH341_REQ_READ_VERSION used separate registers for line control
@@ -269,7 +282,7 @@ static int ch341_set_baudrate_lcr(struct usb_device *dev,
CH341_REG_LCR2 << 8 | CH341_REG_LCR, lcr);
if (r)
return r;
-
+#endif
return r;
}

@@ -308,7 +321,9 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
r = ch341_control_in(dev, CH341_REQ_READ_VERSION, 0, 0, buffer, size);
if (r)
return r;
- dev_dbg(&dev->dev, "Chip version: 0x%02x\n", buffer[0]);
+
+ priv->version = buffer[0];
+ dev_dbg(&dev->dev, "Chip version: 0x%02x\n", priv->version);

r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT, 0, 0);
if (r < 0)
--
2.35.1