serialch340.class
=================
A Poseidon USB class for serial adapters built around the WCH CH340 and
CH341 chips - the cheap "USB-SERIAL CH340" cables and the USB port of many
Arduino, ESP8266 and ESP32 boards. It gives AmigaOS a serial.device
compatible device, serialch340.device, so terminal programs and anything
else that talks to a serial.device can use these adapters.
Requirements
------------
- Poseidon 6.x, the AROS-lineage backport of Poseidon for AmigaOS 3.x
(https://github.com/rondoval/poseidon-backport). The class uses the
Poseidon 6.x class interface; it does not work with the classic Poseidon
4.x/5.x releases.
- AmigaOS 3.1 or later.
- The binary is built for the 68040 (as are the backport's own 68040
builds). It was developed and tested on an A1200 with PiStorm/Emu68,
AmigaOS 3.2.3, xhci.device and poseidon.library 6.1/6.2. To build for
another CPU, see "Building" below.
Supported adapters
------------------
1A86:7523 WCH CH340 (the common "USB-SERIAL CH340")
1A86:7522 WCH CH340K
1A86:5523 WCH CH341 in serial mode
4348:5523 WCH CH341 (old vendor ID)
2184:0057 GW Instek GDM-834x multimeter
9986:7523 CH340 clone
Other CH34x adapters can be bound by hand in Trident (Devices, select the
adapter, force binding to serialch340.class).
Installation
------------
Double-click the Install icon in the Poseidon_ch340 drawer (it runs the
Install script through IconX), or from a Shell:
CD Poseidon_ch340
Execute Install
The script
- copies the class to SYS:Classes/USB/ (an existing one is kept as
serialch340.class.old),
- asks whether to copy the tools chtest, chxfer and psdlog to C:,
- if the class is not yet known to Poseidon: asks you to unplug all
CH340/CH341 adapters, then registers the class (C/chclass ADD) and saves
the Poseidon prefs (C/chclass SAVE; the old prefs are kept as
ENVARC:Sys/poseidon.prefs.bak),
- if the class is already registered: tells you to reboot to load the new
version.
Unplug the adapters before registering: Poseidon suspends USB devices that
stay without a driver, and the class cannot take over an adapter that is
already suspended (see "Known problems").
By hand: with the adapters unplugged, copy Classes/USB/serialch340.class
to SYS:Classes/USB/, add it in Trident (Classes, Add) and press Save.
Then plug in the adapter. Trident's message log shows
serialch340.class Another serial killer 'USB2.0-Serial' at
serialch340.device unit 0!
serialch340.class CH34x chip version 31.
The first adapter is unit 0, the second unit 1, and so on - also when two
adapters have the same USB IDs. In a terminal program select the device
"serialch340.device" and the unit number.
Install the class in SYS:Classes/USB, so that it is there when an adapter
is plugged in. See "Known problems" for what happens when it is loaded
later by hand.
Features
--------
- Baud rates from 50 to 2,000,000; any rate in between, computed with the
same divisor algorithm as the Linux ch341 driver.
- 5, 6, 7 or 8 data bits; parity none, odd, even, mark or space; 1 or 2
stop bits.
- DTR and RTS are switched on while the unit is open and off when the
adapter is set up.
- SDCMD_QUERY reports CTS, DSR, DCD and RI (read from the chip) and RTS,
DTR, and the number of bytes waiting in the read buffer.
- SDCMD_BREAK sends a break of io_BrkTime microseconds.
- CMD_READ, CMD_WRITE (io_Length -1 = NUL terminated), CMD_CLEAR,
CMD_RESET, CMD_FLUSH, CMD_START, CMD_STOP, SDCMD_SETPARAMS,
NSCMD_DEVICEQUERY; SERF_EOFMODE with the termination array (taken over
from the cp210x class, not tested with the CH340).
- Read-ahead buffering: data arriving while no read is pending is kept.
- Several adapters at the same time, one unit each.
- Unplugging the adapter while a unit is open does not crash anything:
pending reads end with error -2 (IOERR_ABORTED). When the adapter is
plugged in again, the same open unit continues to work.
Tested
------
All tests on an A1200 with PiStorm/Emu68 (68040), AmigaOS 3.2.3,
Poseidon 6.2 backport, with CH340 adapters (chip version 0x31).
Binding at boot and hot plug ............................ OK
Open/close, many times .................................. OK
SETPARAMS 9600, 115200, 921600, 2000000 baud ............ OK
Line status (QUERY), DTR/RTS on, BREAK .................. OK
Loopback (TX wired to RX) at 9600/115200/921600 ......... OK, byte exact
Reading an ESP32 that prints text at 9600 ............... OK
Two adapters at once (units 0 and 1) .................... OK
Unplug and replug while the unit is open and reading .... OK (see below)
Framing, with two adapters wired crosswise (TX to RX, both ways, GND):
Same settings on both ends, text arrives intact:
8N1, 7E1, 7O1, 8E1, 8O2, 8M1, 8S1, 7N2 ............... all OK
Proof that the settings reach the chip: unit 0 sends "ABC" with 7 data
bits, unit 1 receives as 8N1, so the parity bit appears as bit 7:
sender expected received
7M1 C1 C2 C3 C1 C2 C3
7S1 41 42 43 41 42 43
7E1 41 42 C3 41 42 C3
7O1 C1 C2 43 C1 C2 43
Throughput: 1 MB of a pattern from one adapter to the other, every byte
checked, 8N1, measured with chxfer:
baud result bytes/s line limit
115200 no loss 11533 11520
460800 no loss 45988 46080
921600 no loss (both directions) 83500 92160
1000000 no loss 90068 100000
1500000 no loss 122112 150000
2000000 ~180 bytes lost per MB 151000 200000
Up to 1.5 Mbaud nothing was lost. At 2 Mbaud the receive side tops out at
about 150 KB/s and, as the CH340 has no flow control, the chip drops what
it cannot deliver. Use 1.5 Mbaud or less for bulk transfers.
Known problems and limits
-------------------------
- Loading the class later onto a suspended adapter hangs.
Poseidon 6.2 suspends USB devices that stay without a driver for a
while. If serialch340.class is added by hand (e.g. from RAM:) after an
adapter has been plugged in long enough to be suspended, the class binds,
but its task hangs while the adapter is resumed, and only a reboot
clears it. Both the implicit resume (by the first transfer) and an
explicit psdResumeDevice() were tried; the cause lies on the resume path
and was not found. With the class in SYS:Classes/USB this does not
happen: the class binds when the adapter is enumerated, before Poseidon
would suspend it.
- No flush of old data on open. The read-ahead keeps receiving while the
unit is closed, and after a baud rate change the buffer still holds data
received with the old settings. A program gets that data first. Read and
discard what SDCMD_QUERY reports as waiting right after opening or after
SETPARAMS if you do not want it. (Flushing in the driver was tried twice
and stopped reception or hung on xhci.device, so it was left out.)
- Settings after replugging. When the adapter is unplugged and plugged in
again while a unit is open, the unit continues, but the chip starts again
at 9600 8N1 with DTR and RTS off. A program that notices a read error
-2 should send SDCMD_SETPARAMS again.
- No hardware flow control. The CH340 has none; with SERF_7WIRE the driver
only keeps DTR and RTS on. XON/XOFF is not handled by the driver either.
- DTR/RTS and development boards. Opening the unit switches DTR and RTS
on. Boards with an auto-reset circuit (ESP32, ESP8266, many Arduinos) may
reset when a unit is opened.
- A unit can be opened by one program at a time (SERF_SHARED is not
supported).
- Removing the class at run time (e.g. with a tool that calls
psdRemClass) and adding it again in the same session once led to a
reboot when the adapter was replugged afterwards. Use the normal install
and reboot to change versions.
Tools
-----
C/chtest - test a unit:
chtest [UNIT n] [BAUD n] [TEXT "..."] [SECS n] [BREAK] [LOOP]
[DATABITS 5-8] [PARITY N|E|O|M|S] [STOP 1|2] [HEX] [NOWRITE]
[DEVICE name]
Opens the unit, prints the line status, writes TEXT and prints what
arrives for SECS seconds. With TX wired to RX this is a loopback test.
LOOP keeps reading after errors (for unplug tests), HEX prints the
received bytes in hex, NOWRITE only listens.
chtest UNIT 0 BAUD 9600 SECS 5 NOWRITE
C/chxfer - bulk transfer test between two units wired TX to RX:
chxfer [FROM n] [TO n] [BAUD n] [SIZE n] [CHUNK n] [DEVICE name]
Sends SIZE bytes of a known pattern, checks every byte and prints the
throughput and PASS or FAIL. After a lost byte all following bytes count
as wrong; compare "sent" and "received" for the real loss.
chxfer FROM 0 TO 1 BAUD 921600 SIZE 1048576
C/chclass - used by the Install script:
chclass ADD <path> add a class to the running Poseidon
chclass LIST list the loaded classes
chclass SAVE save the Poseidon prefs (ENVARC: and ENV:)
There is no REMove on purpose; see "Known problems".
C/psdlog - Poseidon's message log without opening Trident:
psdlog [n] the last n messages (default 20)
psdlog DEV the USB devices, with their suspend state
Source/tests/ has the AmigaDOS scripts used for the framing tests
(match, mismatch).
Building
--------
The source is in Source/. The class is built inside the Poseidon backport
tree, which provides the class skeleton, the headers and the toolchain
container (ghcr.io/rondoval/amiga-build-container). build.sh clones the
backport (revision 0dc2b73), adds the class and builds it with docker:
./build.sh class and tools
./build.sh class class only
The class is built for 68040 with -DM68K_CPU=68040; change M68K_CPU in
build.sh for 68020 or 68060.
History
-------
1.0 (19.10.2026) First release.
Credits and license
-------------------
serialch340.class is derived from serialcp210x.class by Chris Hodges, part
of Poseidon, as found in the AROS backport by rondoval. The CH340 protocol
follows the Linux ch341 USB serial driver. Changes for the CH340, the
resume fix, the OpenDevice and stale unit fixes, the tools and the tests:
Serkan, 2026.
Distributed under the AROS Public License 1.1, see LICENSE. The source
code is included.
|