Problems reading out USB to serial converter

  • ATTENTION! As of November 1, 2020, you are not able to reply to threads 6 months after the thread is opened if there are more than 500 posts in the thread.
    Threads will not be locked, so posts may still be edited by their authors.
    Just start a new thread on the topic to post if you get an error message when trying to reply to a thread.

svollebregt

Occasional Visitor
I am trying to use my ASUS RT-AC68U router to read-out a device through the serial interface. The device send a 35 lines message every 20 seconds, of which I want to capture some data. I tested the USB convertor under Windows and Ubuntu 16.04.3 on my laptop, and I have no problems in receiving the messages.

However, things didn't go as expected on asuswrt.merlin 380.68 with entware installed. At first the interface kept disconnecting, which I fixed by mknod /dev/ttyUSB0 c 188 0 as suggested here: https://github.com/RMerl/asuswrt-me...ux/linux-2.6/Documentation/usb/usb-serial.txt

Now dmesg | grep tty shows this:

serial8250.0: ttyS1 at MMIO 0x18000400 (irq = 117) is a 16550
usb 2-2: GSM modem (1-port) converter now attached to ttyUSB0
option1 ttyUSB0: GSM modem (1-port) converter now disconnected from ttyUSB0
usb 2-2: GSM modem (1-port) converter now attached to ttyUSB0
option1 ttyUSB0: GSM modem (1-port) converter now disconnected from ttyUSB0
usb 2-2: GSM modem (1-port) converter now attached to ttyUSB0
option1 ttyUSB0: GSM modem (1-port) converter now disconnected from ttyUSB0
usb 2-2: GSM modem (1-port) converter now attached to ttyUSB0
option1 ttyUSB0: GSM modem (1-port) converter now disconnected from ttyUSB0
usb 2-2: GSM modem (1-port) converter now attached to ttyUSB0

It is a bit puzzling to see the devices apparently connects and disconnects (or maybe it is because I plugged it in and out a few times since the last reboot). In contrast to Ubuntu the device also shows up as a GSM modem. The chipset, however, is CH340 and is also recognized as ch340 or ch341 (drivers should be the same, can't remember which) by Ubuntu.

lsusb on the router shows my device as:
Bus 002 Device 006: ID 1a86:7523 QinHeng Electronics HL-340 USB-Serial adapter
But as far as I know the ch341 driver is missing and the generic usbserial driver or some GSM driver is loaded instead. In any case, upon opening /dev/ttyUSB0 with screen or minicom the system just keeps waiting for input.

I don't seem to be the only one encountering this, as in the past patches seem to have been made which enabled the CH341 driver, e.g.: https://www.snbforums.com/threads/ntp-daemon-for-asuswrt-merlin.28041/page-4

Unfortunately, I have no clue how to add the required driver support to my running 380.68 and was hoping someone could point me in the right direction. My guess is I probably have to patch the source and build it myself using this nice guide: https://github.com/RMerl/asuswrt-merlin/wiki/Compile-Firmware-from-source-using-Ubuntu. Patching the source is probably the biggest difficulty, no clue how to do that. I'm kinda rusty with Linux, and I think it has been at least 15 years since I compiled my last kernel or anything in Linux, so please be gentle ;-)
 

Fitz Mutch

Senior Member
... In contrast to Ubuntu the device also shows up as a GSM modem. The chipset, however, is CH340 and is also recognized as ch340 or ch341 (drivers should be the same, can't remember which) by Ubuntu. ...
Have you tried?
modprobe cdc-acm

I do not think mknod is necessary.

If it helps, disable automatic loading of GSM modem drivers and programs.

/jffs/scripts/init-start
Code:
#!/bin/sh
deny_access() {
  local FILEPATH="$1"
  local FILENAME="$(/usr/bin/basename $FILEPATH)"
  local FILEEXT="${FILENAME##*.}"
  if [ "$FILEEXT" == "ko" ]; then
    local MODULENAME="${FILENAME%.*}"
    local FILEPATH="/lib/modules/$(/bin/uname -r)/$(/sbin/modprobe -l $MODULENAME)"
    if [ -f "$FILEPATH" ] && [ ! -h "$FILEPATH" ]; then
      /sbin/lsmod | /bin/grep -qF $MODULENAME && /sbin/modprobe -r $MODULENAME && /bin/usleep 250000
      /bin/mount -o bind /dev/null "$FILEPATH"
    fi
  else
    if [ -f "$FILEPATH" ] && [ ! -h "$FILEPATH" ]; then
      [ -n "$(/bin/pidof $FILENAME)" ] && /usr/bin/killall $FILENAME && /bin/usleep 250000
      /bin/mount -o bind /dev/null "$FILEPATH"
    fi
  fi
}

# disable automatic loading of GSM modem drivers and programs
deny_access option.ko
deny_access usb_wwan.ko
deny_access drxvi314.ko
deny_access /usr/sbin/find_modem_node.sh
deny_access /usr/sbin/find_modem_type.sh
deny_access /usr/sbin/gobi_update.sh
deny_access /usr/sbin/modem_at.sh
deny_access /usr/sbin/modem_autoapn.sh
deny_access /usr/sbin/modem_enable.sh
deny_access /usr/sbin/modem_status.sh
deny_access /usr/sbin/modem_stop.sh
deny_access /usr/sbin/chat


Last resort would be to extract the ch341.ko driver from a firmware image, here:
https://github.com/blackfuel/asuswrt-merlin/releases

I use the ch341.ko driver to connect my Chinese Arduino to the RT-AC68U router.
 
Last edited:

Fitz Mutch

Senior Member
One last thing. Without Hotplug or Udev rules, here's how I automatically start my USB device. If you unplug the USB device and plug it back in, the router sometimes makes a new device and things get messy.

/jffs/scripts/init-start
Code:
#!/bin/sh
/jffs/scripts/mydevice-start.sh &

/jffs/scripts/mydevice-start.sh
Code:
#!/bin/sh
DEVICE="/dev/ttyACM0"
SPEED=9600
PRODUCT="1546:01a7"  # u-blox GPS receiver

# wait for device to become ready
modprobe cdc-acm
while : ; do
  lsusb | grep -qi "$PRODUCT"
  if [ $? -eq 0 ]; then
    break
  else
    sleep 5
  fi
done

stty -F $DEVICE $SPEED raw -clocal -echo icrnl

# TODO: the USB device is now ready to communicate with me
 

svollebregt

Occasional Visitor
Thanks a lot for the great suggestions! Will try them out as soon a possible and let you know how it turns out.
 

svollebregt

Occasional Visitor
The init-start script indeed nicely blocked loading of the gsm driver. Unfortunately, with modprobe cdc-acm I cannot get the device to be recognised as USB to serial converter. I am now trying to figure out how to get it to load the ch341.ko from the blackfuel build.

Edit: well I guess I should be able to do that with modprobe, once I got the file on the right location ;-)
 

svollebregt

Occasional Visitor
I managed to load the driver by putting it in the Jffs partition (to allow loading during boot once everything works) and by using insmod /jffs/ch341.ko.

dmesg | grep tty now shows the devices correctly:
Kernel command line: root=/dev/mtdblock2 console=ttyS0,115200 init=/sbin/preinit earlyprintk debug
serial8250.0: ttyS0 at MMIO 0x18000300 (irq = 117) is a 16550
console [ttyS0] enabled, bootconsole disabled
serial8250.0: ttyS1 at MMIO 0x18000400 (irq = 117) is a 16550
usb 2-2: ch341-uart converter now attached to ttyUSB0

And lsmod | grep ch341 shows
ch341 6606 1 - Live 0xbf007000
usbserial 23658 3 ch341, Live 0xbf730000
usbcore 101810 16 ch341,cdc_acm,usbserial,cdc_mbim,qmi_wwan,cdc_wdm,cdc_ncm,rndis_host,cdc_ether,asix,usbnet,usblp,ohci_hcd,ehci_hcd,usb_storage, Live 0xbf48b000

So it looks like the driver loaded correctly. However, when I try to see if data is received correctly by using screen or minicom I see that data is being recieved as the cursor moves, but the received characters are not decoded (what it sends should be ASCI). I configure minicom for 115200 8N1, which works under Ubuntu. I am suspecting that either the device is not configured 100% correctly by the driver, or that it is not inverting the input. The data is send inverted by the device (don't ask me why...). This is correctly managed by the USB-to-serial converter without the need of any adjustment from my side in both Windows and Ubuntu (in fact I just plug it in, set the access rights in Ubuntu the first time, open any serial program and it works). Would it be possible to check the settings of the usb to serial converter in both Asuswrt and Ubuntu and compare them, or am I missing something else?
 

svollebregt

Occasional Visitor
After more investigation on how Ubuntu and the Asuswrt handle the device, and by comparing the source of the ch341.ko drivers between the kernel in Ubuntu 16.04.3 and the Asuswrt Blackfuel build it appears the latter uses different drivers which omit certain functionality (like different parity's and a different number of stop bits). This seems to be a recurring issue with the CH341 driver which was patched at some point: https://github.com/karlp/ch341-linux/blob/master/0001-usb-serial-ch341-Add-parity-support.patch As I am receiving HEX data on my router but it cannot be interpreted right (data is ASCI, everything works fine under Ubuntu), I have the feeling that the driver is the culprit. The alternative is that insmod loads the module wrongly, but I checked the modules.dep from Ubuntu and there seem to be no dependencies for the ch341.ko module.

I have absolutely zero experience in building drivers, so I was wondering if someone can point me in the right direction or perhaps even build the driver is they have a running build environment for the RT-AC68U. I put both source files in the following zip (just remove the first part of the name which indicates where I took it from): https://www.dropbox.com/s/wwgjzn16yjlcxpd/ch341.zip?dl=0
 

Fitz Mutch

Senior Member
... seems to be a recurring issue with the CH341 driver which was patched at some point ...
I think it would take nothing less than Linux kernel version 4.12 to fully satisfy this? That's where I see complete implementation of data bits, parity and stop bits in CH341.ko driver.

OR, you could use a tool to tweak the CH341 chip's firmware default settings to your liking?

Lastly, I've seen a loose or unconnected ground (GND) wire cause garbled text on the serial port. This is if you're wiring it yourself.
 

svollebregt

Occasional Visitor
You are absolutely right about the Ubuntu implantation requiring a newer kernel. I tried building it using these guides: https://www.hqt.ro/how-to-compile-modules-for-asuswrt-routers/ but part of the functionality is unsupported. I did find another driver, though, from the manufacturer: http://www.wch.cn/download/CH341SER_LINUX_ZIP.html

I renamed it to ch341.c, replaced the old ch341.c from the Asuswrt source and build ch341.ko. It loads on the router, but now I don't even seem to receive anything anymore. dmesg gives many 'ch34x ttyUSB0: ch34x_read_bulk_callback - length = 1, data = 08' errors...
Not sure if I build it correctly, though... or if renaming it could have caused issues. Maybe someone else can give it a go in their build environment?

The wire is home made, but works in Ubuntu and Windows. What kind of utility could I use to tweak the firmware?
 

Fitz Mutch

Senior Member
... Not sure if I build it correctly, though... or if renaming it could have caused issues.
I think you should add this as new kernel driver. See my example patch. It will update all three ARM kernels in one shot. Then you can say CONFIG_USB_SERIAL_CH34x=m in the kernel config.

You could also turn off debugging in ch34x.c. Example:
Code:
...
// For debug
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,1))
static int debug = 0;
#endif
...

... What kind of utility could I use to tweak the firmware?
Sorry, I was thinking of the configuration tool for FTDI chips.


FYI, the AsusWRT ch341 driver works just fine with my Chinese Arduino. You may need both newly compiled usbserial.ko and ch341.ko Example:

# insmod usbserial.ko
# insmod ch341.ko
# dmesg

Code:
USB Serial support registered for ch341-uart
ch341 2-2.1.2:1.0: ch341-uart converter detected
usb 2-2.1.2: ch341-uart converter now attached to ttyUSB1
usbcore: registered new interface driver ch341

# cat /dev/ttyUSB1
Code:
arduino test 0

arduino test 1

arduino test 2

arduino test 3

arduino test 4

...


sketch_serial_test.ino
Code:
int n = 0;

void setup() {
 //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB
  }
}

void loop() {
  Serial.print("arduino test ");
  Serial.print(n++);
  Serial.println();
  delay(1000);
}
 

Attachments

  • asuswrt-arm-ch34x.patch.txt
    4.3 KB · Views: 454
Last edited:

svollebregt

Occasional Visitor
Thanks! Will give this a go tomorrow. Just to be sure I do the right thing, how do I apply this patch? Can I change the running system (stock Asuswrt-merlin, so far I didn't manage to write anything outside the jffs and netware partition as everything else is marked read-only). Or do I have to use it to change the kernel of the original or home-build ROM image in my Ubuntu build environment and load that into the device?

I think the old driver works for most regular cases, but fails when the data is formatted in certain ways. The data is coming in with 115200 baud 8N1, which is quite normal, but the TxD signal is inverted. Apparantly this is no issue for the CH341 chip with the Windows or Ubuntu 16.04 driver, as it is automatically inverted by the dongle which results in regular ASCI encoding being received by any software I used so far to read-out the port. Alternatively, I could use an npn transistor in the cable to invert the data before it reaches the CH341.
 

Fitz Mutch

Senior Member
Just to be sure I do the right thing, how do I apply this patch? Can I change the running system (stock Asuswrt-merlin, so far I didn't manage to write anything outside the jffs and netware partition as everything else is marked read-only). Or do I have to use it to change the kernel of the original or home-build ROM image in my Ubuntu build environment and load that into the device?
I updated asuswrt-arm-ch34x.patch so that it includes usbserial.ko in the firmware, just in case you try it that way.

Compiling the kernel modules

Add the ch34x entry to Kconfig and Makefile, for ARM kernels only
cd $HOME/asuswrt-merlin
patch -p1 -i ~/path/to/patches/asuswrt-arm-ch34x.patch


Update the kernel config for your ARM router with "CONFIG_USB_SERIAL_CH34x=m":
$HOME/asuswrt-merlin/release/src-rt-6.x.4708/linux/linux-2.6.36/config_base.6a
$HOME/asuswrt-merlin/release/src-rt-7.14.114.x/src/linux/linux-2.6.36/config_base.6a
$HOME/asuswrt-merlin/release/src-rt-7.x.main/src/linux/linux-2.6.36/config_base.6a


Turn off debugging in ch34x.c
static int debug = 0;

Put ch34x.c here:
$HOME/asuswrt-merlin/release/src-rt-6.x.4708/linux/linux-2.6.36/drivers/usb/serial/ch34x.c
$HOME/asuswrt-merlin/release/src-rt-7.14.114.x/src/linux/linux-2.6.36/drivers/usb/serial/ch34x.c
$HOME/asuswrt-merlin/release/src-rt-7.x.main/src/linux/linux-2.6.36/drivers/usb/serial/ch34x.c



Testing kernel modules on AsusWRT

To play with a running AsusWRT system, try it this way
insmod /jffs/modules/usbserial.ko
insmod /jffs/modules/ch34x.ko


If the modules are included with router firmware, do it this way
modprobe usbserial
modprobe ch34x
 

svollebregt

Occasional Visitor
Succeeded in building the driver, thanks for the guide, although I saw some warnings flashing by that certain variables were not used in ch34x.c. Unfortunately, I don't receive anything with the driver from the manufacturer... this is getting quite annoying. As I at least receive some hex data with the original ch341.ko driver I am going to see if inverting the input with a transistor does any good. Why oh why did a buy a cheap USB to serial converter instead of a good one :p
 

Fitz Mutch

Senior Member
Succeeded in building the driver, thanks for the guide, although I saw some warnings flashing by that certain variables were not used in ch34x.c. Unfortunately, I don't receive anything with the driver from the manufacturer... this is getting quite annoying. As I at least receive some hex data with the original ch341.ko driver I am going to see if inverting the input with a transistor does any good. Why oh why did a buy a cheap USB to serial converter instead of a good one :p
I would try turning debugging back on and re-compile the driver. Now you can see the hex bytes in the dmesg log and tell if it's inverted or not.

Lastly, if the driver is not in the firmware, I don't think there's a way to unload it by doing modprobe -r, so a reboot is necessary if you want to switch between ch341.ko and ch34x.ko, for testing purposes.
 

svollebregt

Occasional Visitor
I can unload it with rmmod, so that gives me some room to play. I think I give up on the manufacturer driver as hardly anyone seems to use it (not part of any kernel). I did find the git of the ch341.c driver though (https://git.kernel.org/pub/scm/linu...nux-stable.git/log/drivers/usb/serial/ch341.c), and the character corruption is an issue reported by others (it receives the newlines correctly, data is not inverted from the looks of it, but characters are all printed as spaces by mincom or screen). There are a few possible fixes I can try and see if will build, e.g. to add parity support and there appears to be a registry callback in the driver which can cause issues. Too bad the latest version seems not to build with the 2.6 kernel.
 

svollebregt

Occasional Visitor
I had to give up with the ch341 driver. I suspect the baud rate is set wrong and cannot be corrected, which is fixed at some point in the new driver which is part of the modern kernel. Unfortunately, the code which seems to do fix this uses a tty construct which is not supported in the 2.6.36 kernel. Setting the baud rate correctly in the driver doesn't work either.

Now I got a Keyspan USA-19i USB serial converter from a colleague, which again works flawlessly under Ubuntu 16.04. Drivers of this device haven't changed the past decade, so the version which is in the Asuswrt release should work. Building keyspan.ko and loading it with isnmod is no issue... but after that the new challenge emerges. This driver requires a firmware file, and I cannot get the driver to load it. I obtained the .fw file from Ubuntu, but also the .HEX is part of the kernel source. It seems that /lib/firmware/ points to /tmp/Beceem_driver, so I made that dir and put the required driver in it so I get the path /lib/firmware/keyspan/usa19qi.fw... but no luck: the system doesn't want to load the driver. Any idea how I can tell the driver where to find the firmware? Does the standard Asuswrt build has the capability to load firmware from the userspace? I also tried putting it in a subdir of the /jffs/modules which holds keyspan.ko, but that also doesn't work.

Below the log from the router when I load the driver with insmod, followed by pluging in the device
Code:
Oct  8 19:29:57 kernel: USB Serial support registered for Keyspan - (without firmware)
Oct  8 19:29:57 kernel: USB Serial support registered for Keyspan 1 port adapter
Oct  8 19:29:57 kernel: USB Serial support registered for Keyspan 2 port adapter
Oct  8 19:29:57 kernel: USB Serial support registered for Keyspan 4 port adapter
Oct  8 19:29:57 kernel: usbcore: registered new interface driver keyspan
Oct  8 19:29:57 kernel: keyspan: v1.1.5:Keyspan USB to Serial Converter Driver
Oct  8 19:30:18 kernel: usb 2-2: new full speed USB device using ohci_hcd and address 4
Oct  8 19:30:18 kernel: keyspan 2-2:1.0: Keyspan - (without firmware) converter detected
Oct  8 19:30:18 kernel: usb 2-2: Required keyspan firmware image (keyspan/usa19qi.fw) unavailable.
 

Fitz Mutch

Senior Member
I got a Keyspan USA-19i USB serial converter from a colleague, which again works flawlessly under Ubuntu 16.04.
You may need to compile your driver firmware blob into the Linux kernel, then flash the router. Example:

~/asuswrt-merlin/release/src-rt-6.x.4708/linux/linux-2.6.36/config_base.6a
Code:
CONFIG_FW_LOADER=y
CONFIG_FIRMWARE_IN_KERNEL=y
CONFIG_USB_SERIAL_KEYSPAN=m
CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y
CONFIG_EXTRA_FIRMWARE="keyspan/usa19qi.fw"
CONFIG_EXTRA_FIRMWARE_DIR="/lib/firmware/"
 

svollebregt

Occasional Visitor
I managed to get the Keyspan device working without building the entire firmware for the router as I didn't like this option. I added user space firmware loading as a module in my Ubuntu build environment to give me firmware_class.ko, I also build usbserial.ko and keyspan.ko and moved these all to my router. By using isnmod I first load the firmware_class, followed by usbserial and keyspan. The device now registers, but still cannot find the firmware.

The biggest challenge was to get it to load the firmware. While it now looks for firmware (there is a delay between registering device and reporting it cannot find the firmware), I am unsure which path is being used. For sure it is not /lib/firmware/ as that path is redirecting to a directory with keyspan/usa19qi.fw in it. To circumvent this, I managed to build fxload under entware, which allows me to load the .HEX firmware from the kernel source into the device. This actually works and now the device is receiving data!
 

svollebregt

Occasional Visitor
Ok... the problems are not over yet. While it receives most of the data fine, it misses a character now and then. This doesn't happen with the same device under Ubuntu 16.04. Could this be a hardware limitations of the AC68u router? It is only a serial port at 115200 baud...
 

Similar threads

Latest threads

Sign Up For SNBForums Daily Digest

Get an update of what's new every day delivered to your mailbox. Sign up here!
Top