A CH9102 or a CH343 needs nothing on a current Mac or Linux box. Both chips enumerate as a standard USB CDC-ACM serial device, so Linux binds cdc_acm and gives you /dev/ttyACM0, and macOS binds Apple’s own CDC driver and gives you /dev/cu.usbmodemXXXX. What goes wrong is more specific than “missing driver”: on Linux a kernel that refuses to send a BREAK on the CH343, a braille daemon that since late 2024 lists the CH343 as a braille display, and ModemManager poking the port; on the Mac a usbmodem port that runs a serial monitor fine but fails every esptool upload with Failed to write to target RAM (result was 01070000). Below: what binds 1A86:55D4 and 1A86:55D3 on each system, the kernel versions that matter, WCH’s Linux driver mirrored with its checksum, and the source behind every row. The Windows package is on the CH9102 and CH343 driver page; the CH340 has its own macOS and Linux guide.
⬇ WCH ch343 Linux driver, V2.2 (official source, mirrored)
Download ch343ser_linux_V2.2.zip (371 KB)
| System | Driver | What you install | Port name |
|---|---|---|---|
| Linux, any distro | cdc_acm, in every kernel |
nothing | /dev/ttyACM0 |
| Linux, WCH vendor module | ch343 V2.2, kernel ≥ 3.2 | build the module above + the udev rule | /dev/ttyCH343USB0 |
| macOS 11 to 26 | Apple’s CDC-ACM driver | nothing | /dev/cu.usbmodemXXXX |
| macOS 11 to 26, WCH dext | CH34xVCPDriver 2.1 (dext 1.0, 26 USB IDs) | pkg or dmg, then approve the extension | /dev/cu.wchusbserialXXXX |
| OS X 10.9 to 10.15 | CH34xVCPDriver.kext 1.8 (25 USB IDs, same package) | pkg, restart | /dev/cu.wchusbserialXXXX |
What binds the chip on each system
The in-tree ch341 driver has nothing to do with these chips: its ID table in the current kernel tree lists six devices (1a86:5523, 7522, 7523, 2184:0057, 4348:5523, 9986:7523) and not one 55xx PID. The CH9102 and CH343 are claimed by cdc_acm because they present a CDC communications interface, which is also why they show up as ttyACM and not ttyUSB. WCH’s README says it in its first paragraph: the chips “are fully compliant to the Communications Device Class (CDC) standard” and “will work with a standard CDC-ACM driver”.

On the Mac the logic is the same: from Big Sur on, Apple’s CDC-ACM driver binds the chip and a /dev/cu.usbmodemXXXX node appears with no download and no approval. The WCH package adds a second driver that takes the device away from Apple’s and renames the port cu.wchusbserialXXXX; the Info.plist of its dext matches 26 USB IDs, including 55D4, 55D3 and 7523, and the legacy kext 1.8 in the same package matches 25 of them for OS X 10.9 to 10.15.
The kernel timeline that matters: BREAK on the CH343
The CH343 can send a BREAK but its CDC descriptor does not advertise the capability (CAP_BRK). Until Linux 6.7 cdc_acm sent the request anyway; Oliver Neukum’s commit 66aad7d8d3ec (7 Dec 2023) made the driver respect the descriptor, so from Linux 6.8 tcsendbreak() on a CH343 returned EOPNOTSUPP. Marc Zyngier’s fix, commit 14ae24cba291 (1 Mar 2026), adds a quirk line for 1a86:55d3 and was marked for stable. The table was read from cdc-acm.c at each tag and from the stable branches.
| Kernel | BREAK on CH343 (1a86:55d3) |
Source |
|---|---|---|
| up to 6.7 | works (capability not checked) | acm_tty_break_ctl() in v6.7 has no CAP_BRK test |
| 6.8 to 6.19 as released | refused, EOPNOTSUPP |
66aad7d8d3ec, first in v6.8 |
| 7.0 and newer | works again (MISSING_CAP_BRK quirk) |
14ae24cba291, committed 11 Mar 2026 |
| 6.19.9 · 6.18.19 | works (backport of 19 Mar 2026) | stable branches linux-6.19.y, linux-6.18.y |
| 6.12.78 · 6.6.130 · 6.1.167 | works (backport of 25 Mar 2026) | stable branches linux-6.12.y, linux-6.6.y, linux-6.1.y |
CH9102 (1a86:55d4), any kernel |
no quirk line | only 55d3 is in acm_ids[]; if your CH9102 does not advertise CAP_BRK either, BREAK stays refused from 6.8 on |

1a86:55d3 entry with the comment “CH343 supports CAP_BRK, but doesn’t advertise it”.This only matters to software that calls tcsendbreak(), such as console servers (the use case in the commit message) and RS-485 tooling (issue 74, 9 Oct 2026, asks for BREAK reception in the vendor module too). esptool, PlatformIO, the Arduino IDE and serial monitors work on every kernel in the table.
Linux: the two commands, and when the vendor module is worth it
lsusb | grep -i 1a86 # ID 1a86:55d4 QinHeng Electronics USB Single Serial
dmesg | tail # cdc_acm 1-2:1.0: ttyACM0: USB ACM device
ls -l /dev/ttyACM* # crw-rw---- 1 root dialout ... (uucp on Arch)
If the node is there and you cannot open it, you are not in the group that owns it: sudo usermod -aG dialout $USER (Debian, Ubuntu, Fedora) or uucp (Arch), then log out and in. That was the whole story in Arduino forum thread 1334478 (Arch, CH343 on an ESP32-S3 board).
The vendor module is worth building only for what cdc_acm cannot do, which WCH lists as hardware flow control, the RS-485 TNOW direction signal, the GPIO pins of the CH9102 and CH344, and the USB parameter configuration tool in demo/param_config. From the README in the archive:
unzip ch343ser_linux_V2.2.zip && cd ch343ser_linux-*/driver
make # needs the headers for your kernel
sudo make install # insmod + copy to /lib/modules/.../usb/serial + depmod
sudo cp ../udev/99-ch34x.rules /etc/udev/rules.d/
sudo udevadm control -R # then unplug and replug the board
ls /dev/ttyCH343USB*
The udev rule is not optional: both drivers match the device and cdc_acm is already loaded, so without it you keep getting ttyACM0 and never ttyCH343USB0 (issue 11). The rule from pull request 27 (2 Aug 2024) writes the device to /sys/bus/usb/drivers/cdc_acm/unbind and then to usb_ch343/bind, and sets the node to MODE="0660", GROUP="plugdev", so you need that group too. Under Secure Boot the unsigned module will not load; issue 70 has a DKMS plus MOK recipe. And the nodes are named ttyCH343USB* on purpose (issue 14, open since 2022), so tools that scan only ttyUSB* and ttyACM* will not list them.
The two Linux interceptors: brltty 6.7 and ModemManager
If the port appears in dmesg and vanishes a second later, or is busy for a few seconds after plugging, look at these two before touching drivers.
| Daemon | What it does to the chip | Where that comes from | Fix |
|---|---|---|---|
| brltty 6.7 and newer | lists 1A86:55D3 (CH343) as a braille display and grabs the device; 55D4 (CH9102) is not listed |
Programs/usb_devices.c: entry “HIMS [eMotion (legacy)]”, added 4 Oct 2024, first release 6.7 (15 Oct 2024), absent in 6.6; generic IDs go into usb-generic.rules (Autostart/Udev/Makefile.in) |
grep -rl 55d3 /usr/lib/udev/rules.d/ /lib/udev/rules.d/; if it hits, sudo apt remove brltty or mask that rule |
| ModemManager | probes a cdc_acm port for AT commands when the interface is class 2 / subclass 2 / protocol 1 |
ModemManager filter documentation, rule MM_FILTER_RULE_TTY_ACM_INTERFACE |
udev rule ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55d4", ENV{ID_MM_DEVICE_IGNORE}="1", then udevadm control --reload; check the byte with lsusb -v -d 1a86: | grep bInterfaceProtocol |
Distributions with the brltty that knows the CH343: Ubuntu 25.04, 25.10 and 26.04 (6.7), Ubuntu 26.10 (6.9.1), Debian 13 (6.7) and Debian 14 (6.9.1). Ubuntu 24.04 (6.6), 22.04 (6.4) and Debian 12 (6.5) do not have the entry. Whether the rule fires also depends on which generated rules file your package installs; the grep above answers that for your machine.
macOS: two ports, two behaviours
With nothing installed, every macOS from Big Sur on gives a CH9102 a cu.usbmodem port, and a serial monitor on it works. Uploading firmware to an ESP32 through that port is where the reports pile up, all with the same esptool line:
A fatal error occurred: Failed to write to target RAM (result was 01070000: Operation timed out)
M5Stack’s M5EPD issue 47 (Big Sur, Nov 2021) traced it to a CH9102 on a board whose case says CP2104; the same user hit it on an M1 with Monterey (STAMP-C3 issue 1); PlatformIO threads 24575 and 26229 (macOS 12.2.1) show the whole pattern: cu.usbmodem52D60049421 fails, and after installing WCH’s package the new cu.wchusbserial52D60049421 uploads. As a PlatformIO regular put it, the CDC interface “is supported out-of-the-box on macOS. But for reasons unknown, it works for the serial monitor but does not work for uploading firmware.” Rule on a Mac: monitor on either port, flash on wchusbserial.
Installing 2.1: run the pkg (or drag the app from the dmg on a Mac without Rosetta), open the CH34xVCPDriver app, click Install, approve the extension, replug. On macOS 15.6 and newer the approval lives in System Settings → General → Login Items & Extensions → Driver Extensions, which is what worked in ch34xser_macos issue 47 (macOS 26). The message “system extension(s) … that need to be updated by the developer” (issue 38, macOS 15.6.1) means an older package; 2.1 is current, and the Homebrew cask wch-ch34x-usb-serial-driver installs 2.1 too. Uninstall from the same app and the port returns to cu.usbmodem.
Common problems, with the thread behind each
| What you see | Cause | Fix | Thread |
|---|---|---|---|
Linux: ttyACM0 appears, ttyCH343USB0 never does, after building the module |
cdc_acm claimed the interface first; udev rule missing or not reloaded |
copy 99-ch34x.rules, udevadm control -R, replug; rmmod cdc_acm for a quick test |
ch343ser_linux 11 |
| Linux: port exists, Arduino IDE 2 or PlatformIO cannot open it | group ownership (dialout, uucp, or plugdev with the WCH rule) |
add yourself to the group, log out and in | Arduino forum 1334478 |
Linux: tcsendbreak on a CH343 fails with “Operation not supported” |
kernel 6.8 to 6.19 without the backport | update to 7.0, 6.19.9, 6.18.19, 6.12.78, 6.6.130 or 6.1.167; or use the WCH module, which has its own break_ctl |
commit 14ae24cba291 |
macOS: monitor works on cu.usbmodem, esptool says Failed to write to target RAM (01070000) |
Apple’s CDC-ACM driver and the ESP32 bootloader handshake | install CH34xVCPDriver 2.1, flash on cu.wchusbserial |
PlatformIO 26229 |
macOS, Apple Silicon: RTS/DTR do not move on the wchusbserial port, ioctl(TIOCMSET) returns ENOTTY |
reported against dext 2.0 on macOS 26.5.1; tools that drive auto-reset through RTS/DTR are affected | open since 18 Jun 2026; WCH points to its WCHSerialPort app; press BOOT by hand, or monitor on Apple’s port | ch34xser_macos 50 |
| macOS: CH343 board shows no device at all in System Information | cable or board power, not the driver; a dual-port board also exposes a native “USB JTAG/serial debug unit” | another cable, hold BOOT at power-up, check System Information before installing anything | ch34xser_macos 41 |
FAQ
Do I need the WCH driver on Linux for an ESP32 board with a CH9102? No. cdc_acm handles upload and monitor, including the DTR/RTS reset. Build the module only for RS-485 direction control, GPIO or hardware flow control.
Why is my CH9102 /dev/ttyACM0 on one machine and /dev/ttyCH343USB0 on another? The second machine has the WCH module and its udev rule; the first has the kernel default. Both work for serial, only the node name differs.
Related guides
- CH9102 and CH343 driver: the official Windows download and every chip’s USB ID
- CH340 driver on macOS and Linux
- CP210x driver on macOS and Linux
- FTDI driver on macOS and Linux
- ESP32 board USB driver hub: which chip is on your board
- USB VID/PID lookup for bench tools
- esptool step by step
Sources
- WCHSoftGroup/ch343ser_linux: README,
driver/ch343.c(SPDX GPL-2.0+, “V2.2 On 2026.04”, kernel ≥ 3.2, ID table),udev/99-ch34x.rules; issues 11, 14, 70, 74, pull request 27 - WCHSoftGroup/ch34xser_macos: README, V2.1 package (Info.plist of the dext and the two kexts); issues 38, 41, 47, 50
- Linux kernel: drivers/usb/class/cdc-acm.c, drivers/usb/serial/ch341.c; commits 66aad7d8d3ec and 14ae24cba291; stable branches at gregkh/linux
- brltty: Programs/usb_devices.c at BRLTTY-6.7, commit 81482da831, Autostart/Udev/Makefile.in; versions from Launchpad and tracker.debian.org
- ModemManager: the modem filter · Homebrew cask wch-ch34x-usb-serial-driver · Apple: Login Items & Extensions
- Threads: Arduino forum 1334478, PlatformIO 24575, PlatformIO 26229, M5EPD 47, STAMP-C3 1
