RE: linux/drivers/usb/serial/ch341.c calculates some baud rates wrong

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Johan,

Thanks for the info. I followed
https://nickdesaulniers.github.io/blog/2017/05/16/submitting-your-first-patc
h-to-the-linux-kernel-and-responding-to-feedback/ and made a proposal patch
("[PATCH] USB: serial: ch341: fix wrong baud rate setting calculation"). The
Get_maintainers.pl file didn't work for me I got...

../../../scripts/get_maintainer.pl
0001-USB-serial-ch341-fix-wrong-baud-rate-setting-calcula.patch 
../../../scripts/get_maintainer.pl: The current directory does not appear to
be a linux kernel source tree.

I've measured the actual baud rates for a lot of given baud rates and I
think I have deduced the formulas for calculating the "a" value. "a" is a
uint16 and split up in two, a LSB and a MSB. The current driver only uses
LSB from the set {0,1,2,3}. There is another valid LSB of 7 that the current
driver doesn't use. The formula for LSB from the set {0,1,2,3} is...

Actual baud rate == 2^(3*LSB-10)*12000000/(256-MSB), if LSB is in {0,1,2,3}
and 0<MSB<255

When LSB == 7 then things are a bit different. LSB==7 seems to switch off
the clock divider that the LSB usually does but only if MSB<248; when
MSB>=248 the clock divider is turned back on and LSB is effectively 3 again.
So we can also use the following formula...

Actual baud rate == 12000000/(256-MSB), if LSB == 7 and 0<MSB<248

So the trick is to use these formulas to find a MSB and a LSB that produce
and actual baud rate that are as close as possible to the desired baud rate.
With errors greater than say 2 to 3% hardware will start to fail to
communicate.

Looking at some common baud rates only the higher rates are affected by not
using a LSB of 7. Of the typical rates only 256000 and 921600 are really
affected. However other unusual frequencies are affected too such as 1333333
and I think you could calculate a lot more unusual affected frequencies.
That being the case I think calculating the MSB when LSB == 7 for a given
wanted baud rate could be a better solution than special cases for each
affected baud rate. Something like this seems to work and what I used for
testing...

    // chip frequency is 12Mhz. not quite the same as
(CH341_BAUDBASE_FACTOR>>7)
    #define CH341_OSC_FREQUENCY 12000000
    //
    //this block of code is for deciding when LSB==7 of "a" is beter than
LSB=={0,1,2,3} of "a".
    //frequencies such as 921600, 256000, 1333333 etc have a large baud rate
error when using LSB=={0,1,2,3}
    //such as the above code uses. 921600 when using LSB=={0,1,2,3} I
measured 7.4% error but when
    //using LSB==7 I measured 0.4% error.
    //
    //The chip I tested this on was a CH340G and came back with "Chip
version: 0x31".
    //I assume this will work for CH341 and other chip versions but I don't
think I
    //have any of those chips lying around.
    //
    //calc baud error using the 0,1,2,3 LSB and also the error without the
divisor (LSB==7)
    uint32_t msB=(a>>8)&0xFF;
    uint32_t lsB=a&0xFF;
    int32_t baud_wanted=(priv->baud_rate);
    uint32_t denom=((1<<(10-3*lsB))*(256-msB));
 
if(denom&&((baud_wanted+100)>=(((uint32_t)CH341_OSC_FREQUENCY)/256)))//as
baud_wanted==(CH341_OSC_FREQUENCY/256) implies MSB==0 for no divisor, the
100 is for rounding.
    {
        //calc error for divisor
        int32_t baud_expected=((uint32_t)CH341_OSC_FREQUENCY)/denom;
        uint32_t baud_error_difference=abs(baud_expected-baud_wanted);
    
        //calc a for no divisor
        uint32_t
a_no_divisor=(((256<<8)-(((uint32_t)CH341_OSC_FREQUENCY)<<8)/baud_wanted+128
)&0xFF00)|0x07;
        
        //a_no_divisor is only valid for MSB<248.
        if((a_no_divisor>>8)<248)
        {

            //calc error for no divisor
            int32_t
baud_expected_no_divisor=((uint32_t)CH341_OSC_FREQUENCY)/(256-(a_no_divisor>
>8));
            uint32_t
baud_error_difference_no_divisor=abs(baud_expected_no_divisor-baud_wanted);

            //if error using no divisor is less than using a divisor then
use it instead for the "a" word.
            if(baud_error_difference_no_divisor<baud_error_difference)
            {
                a=a_no_divisor;
            }
            
            printk("ch341_set_baudrate_lcr: using LSB==%u.
org_expected_baud=%u org_error=%u org_a=0x%4X no_divisor_expected_baud=%u
no_divisor_error=%u
no_divisor_a=0x%4X\n",a&0xFF,baud_expected,baud_error_difference,(msB<<8|lsB
),baud_expected_no_divisor,baud_error_difference_no_divisor,a_no_divisor);

        }
    }
  

I've tested that with my hardware and it seems to work fine with every
setting I could throw at it. I am aware that I've only tried it on my
hardware with a CH340G chip. So trying with different chips and computers
would be a good idea (I've tested it on the CH340G chip with two computers).

My measurements/workings as a libre/open office calc file can be downloaded
from https://jontio.github.io/linux_kernel_work/ch43x_tests.ods .

I measured the following with the current driver...

Baud wanted	Baud measured	Error as % of wanted
50	50	0.0%
75	75.2	0.3%
110	109.5	0.5%
135	134.6	0.3%
150	150.4	0.3%
300	300.8	0.3%
600	601.3	0.2%
1200	1201.9	0.2%
1800	1801.8	0.1%
2400	2403.8	0.2%
4800	4807.7	0.2%
7200	7215	0.2%
9600	9615.4	0.2%
14400	14430	0.2%
19200	19231	0.2%
38400	38462	0.2%
56000	56054	0.1%
57600	57837	0.4%
115200	115207	0.0%
128000	127551	0.4%
230400	230415	0.0%
256000	250000	2.3%
460800	460617	0.0%
921600	853242	7.4%
1000000	999001	0.1%
1333333	1204819	9.6%
1843200	1496334	18.8%
2000000	1984127	0.8%
5000000	2985075	40.3%

The patch will fix 256000, 1333333 and 921600 but not 1843200 and 5000000.

The driver even with my patch doesn't give an error to the user when the
error is beyond a certain threshold. With the formulas I deduced it would be
possible to estimate the percentage error and if it was beyond a certain
threshold send something to dmesg. For example the ch34x can't produce
1843200 baud with an acceptable accuracy but the driver will happily
calculate the best possible "a". The user could read dmesg and adjust their
hardware to better match the ch34x baud rate.

Cheers,
Jonti

-----Original Message-----
From: Johan Hovold [mailto:johan@xxxxxxxxxx] 
Sent: Monday, 3 June 2019 7:24 p.m.
To: Jonathan Olds
Cc: johan@xxxxxxxxxx; frank@xxxxxxxxxxxxxxxxxxxxxxxxxx;
werner@xxxxxxxxxxxxxxxxxxxx; boris@xxxxxxxxxx
Subject: Re: linux/drivers/usb/serial/ch341.c calculates some baud rates
wrong

Hi Jonathan,

and sorry about the late reply.

On Mon, May 20, 2019 at 10:37:48AM +1200, Jonathan Olds wrote:
> Hi,
> 
> Sorry for sending this email to so many but I've never contributed to 
> the Linux kernel before and I'm not sure how this is done or who to talk
to.

You can use scripts/get_maintainer.pl to figure out which maintainers and
lists to CC.

For USB serial that would be me and we always keep the linux-usb list on CC.

It never hurts to CC the original authors as well, just like you did here.

Note that you need to send mails to list as plain text, not html.

> With a CH340 chip the ch341 driver is loaded. My chip id is 0x31 
> according to the driver debug info.
> 
> In "static int ch341_set_baudrate_lcr(struct usb_device *dev, struct 
> ch341_private *priv, u8 lcr)"
> 
> 
> Currently we have...

...

>                a = (factor & 0xff00) | divisor;

> This "a" thing is called index when used in this function...
> 
> "ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x1312, a);"
> 
> This "a" is wrong for some baud rates. (I've only tried 921600 so far)
> 
> For example at 921600 baud "a" is calculated as "0xF303". This creates 
> a baud rate of about 857000 baud. This can be seen in the following
figures...

> I saw this is another driver...

...

> So this person used cases for various baud rates that have incorrect 
> rates using the method that the current Linux kernel driver implements.
> 
> I then use the 0xF307 that this calculates for 921600 baud and put an 
> if statement into the current Linux driver like so...

...

> This produced the correct baud rate as can be seen in the following 
> figure...

> I found this bug out of necessity. I made a board ( 
> http://jontio.zapto.org/jpic ) and programmed initially in Windows 
> without any baud rate issues. Then moving to Linux the board wasn't 
> able to connect as the baud rate where incorrect. So it is a real bug 
> and one that needs addressing. Fortunately the solution looks like 
> using the code with the cases in it. I have no idea where the 
> programming documentation is for this chip so this index number for 
> setting the baud rate is just a magic number to me at the moment.
> 
> What is the process to get this fix incorporated into new updates to 
> Linux kernel? If you want I can create a list of what standard baud 
> rates are incorrect and by how much with an oscilloscope; I think it 
> will only be for the very high baud rates and may only affect 921600 and
above.

Such a list would be very useful to have.

And I agree that if you can't infer the algorithm used from your
experimental data we may need to start special casing.

Please do gather the data, and send it to the list and propose a patch for
fixing the broken rates.

Thanks,
Johan




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux