git: 0cc03e473ed7 - stable/14 - uchcom: add support for CH9102 and CH343 uarts

From: Kevin Lo <kevlo_at_FreeBSD.org>
Date: Thu, 03 Jul 2025 01:27:53 UTC
The branch stable/14 has been updated by kevlo:

URL: https://cgit.FreeBSD.org/src/commit/?id=0cc03e473ed74fb495c4ebe21d3ba2d92c5c4968

commit 0cc03e473ed74fb495c4ebe21d3ba2d92c5c4968
Author:     Kevin Lo <kevlo@FreeBSD.org>
AuthorDate: 2025-06-25 01:33:35 +0000
Commit:     Kevin Lo <kevlo@FreeBSD.org>
CommitDate: 2025-07-03 01:26:06 +0000

    uchcom: add support for CH9102 and CH343 uarts
    
    The CH343 devices support any baud rate up to 6 Mbps.
    PR:     272803
    Reviewed by:    imp
    Tested by:      joerg, Tomasz "CeDeROM" CEDRO <tomek_AT_cedro_DOT_info>
    Differential Revision:  https://reviews.freebsd.org/D46290
    
    (cherry picked from commit 1395712cab8e95808064ba68c5a792b7cd0fe35f)
---
 share/man/man4/uchcom.4     |  27 +---
 sys/dev/usb/serial/uchcom.c | 353 +++++++++++++++++++++++++-------------------
 sys/dev/usb/usbdevs         |   4 +-
 3 files changed, 208 insertions(+), 176 deletions(-)

diff --git a/share/man/man4/uchcom.4 b/share/man/man4/uchcom.4
index d5efe83286ba..4d395573589f 100644
--- a/share/man/man4/uchcom.4
+++ b/share/man/man4/uchcom.4
@@ -27,12 +27,12 @@
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd April 26, 2017
+.Dd August 19, 2024
 .Dt UCHCOM 4
 .Os
 .Sh NAME
 .Nm uchcom
-.Nd WinChipHead CH341/CH340 serial adapter driver
+.Nd WinChipHead CH9102/CH343/CH341/CH340 serial adapter driver
 .Sh SYNOPSIS
 To compile this driver into the kernel,
 place the following lines in your
@@ -52,22 +52,12 @@ uchcom_load="YES"
 .Sh DESCRIPTION
 The
 .Nm
-driver provides support for the WinChipHead CH341/CH340 USB-to-RS-232
-Bridge chip.
+driver provides support for the WinChipHead CH9102/CH343/CH341/CH340
+USB-to-RS-232 Bridge chip.
 .Pp
-The device is accessed through the
-.Xr ucom 4
-driver which makes it behave like a
-.Xr tty 4 .
-.Sh HARDWARE
-The
-.Nm
-driver supports the following adapters:
-.Pp
-.Bl -bullet -compact
-.It
-HL USB-RS232
-.El
+The datasheets for the CH340/CH341 list the maximum
+supported baud rate as 2,000,000.
+CH9102/CH343 devices support any baud rate up to 6 Mbps.
 .Sh FILES
 .Bl -tag -width "/dev/ttyU*.init" -compact
 .It Pa /dev/ttyU*
@@ -95,6 +85,3 @@ The first
 .Fx
 release to include it was
 .Fx 8.0 .
-.Sh BUGS
-Actually, this chip seems unable to drive other than 8 data bits and
-1 stop bit line.
diff --git a/sys/dev/usb/serial/uchcom.c b/sys/dev/usb/serial/uchcom.c
index a886b25c89d7..fdc5515fa722 100644
--- a/sys/dev/usb/serial/uchcom.c
+++ b/sys/dev/usb/serial/uchcom.c
@@ -58,8 +58,7 @@
  */
 
 /*
- * Driver for WinChipHead CH341/340, the worst USB-serial chip in the
- * world.
+ * Driver for WinChipHead CH9102/343/341/340.
  */
 
 #include <sys/stdint.h>
@@ -101,17 +100,19 @@ SYSCTL_INT(_hw_usb_uchcom, OID_AUTO, debug, CTLFLAG_RWTUN,
     &uchcom_debug, 0, "uchcom debug level");
 #endif
 
-#define	UCHCOM_IFACE_INDEX	0
-#define	UCHCOM_CONFIG_INDEX	0
+#define	UCHCOM_IFACE_INDEX		0
+#define	UCHCOM_CONFIG_INDEX		0
+#define	UCHCOM_SECOND_IFACE_INDEX	1
 
 #define	UCHCOM_REV_CH340	0x0250
 #define	UCHCOM_INPUT_BUF_SIZE	8
 
-#define	UCHCOM_REQ_GET_VERSION	0x5F
-#define	UCHCOM_REQ_READ_REG	0x95
-#define	UCHCOM_REQ_WRITE_REG	0x9A
-#define	UCHCOM_REQ_RESET	0xA1
-#define	UCHCOM_REQ_SET_DTRRTS	0xA4
+#define	UCHCOM_REQ_GET_VERSION		0x5F
+#define	UCHCOM_REQ_READ_REG		0x95
+#define	UCHCOM_REQ_WRITE_REG		0x9A
+#define	UCHCOM_REQ_RESET		0xA1
+#define	UCHCOM_REQ_SET_DTRRTS		0xA4
+#define UCHCOM_REQ_CH343_WRITE_REG	0xA8
 
 #define	UCHCOM_REG_STAT1	0x06
 #define	UCHCOM_REG_STAT2	0x07
@@ -134,13 +135,21 @@ SYSCTL_INT(_hw_usb_uchcom, OID_AUTO, debug, CTLFLAG_RWTUN,
 #define	UCHCOM_RTS_MASK		0x40
 
 #define	UCHCOM_BRK_MASK		0x01
+#define	UCHCOM_ABRK_MASK	0x10
+#define	UCHCOM_CH343_BRK_MASK	0x80
 
 #define	UCHCOM_LCR1_MASK	0xAF
 #define	UCHCOM_LCR2_MASK	0x07
 #define	UCHCOM_LCR1_RX		0x80
 #define	UCHCOM_LCR1_TX		0x40
 #define	UCHCOM_LCR1_PARENB	0x08
+#define	UCHCOM_LCR1_CS5		0x00
+#define	UCHCOM_LCR1_CS6		0x01
+#define	UCHCOM_LCR1_CS7		0x02
 #define	UCHCOM_LCR1_CS8		0x03
+#define	UCHCOM_LCR1_STOPB	0x04
+#define	UCHCOM_LCR1_PARODD	0x00
+#define	UCHCOM_LCR1_PAREVEN	0x10
 #define	UCHCOM_LCR2_PAREVEN	0x07
 #define	UCHCOM_LCR2_PARODD	0x06
 #define	UCHCOM_LCR2_PARMARK	0x05
@@ -150,12 +159,18 @@ SYSCTL_INT(_hw_usb_uchcom, OID_AUTO, debug, CTLFLAG_RWTUN,
 #define	UCHCOM_INTR_STAT2	0x03
 #define	UCHCOM_INTR_LEAST	4
 
-#define	UCHCOM_BULK_BUF_SIZE 1024	/* bytes */
+#define	UCHCOM_T		0x08
+#define	UCHCOM_CL		0x04
+#define	UCHCOM_CH343_CT		0x80
+#define	UCHCOM_CT		0x90
+
+#define	UCHCOM_BULK_BUF_SIZE	1024	/* bytes */
+
+#define	TYPE_CH343		1
 
 enum {
 	UCHCOM_BULK_DT_WR,
 	UCHCOM_BULK_DT_RD,
-	UCHCOM_INTR_DT_RD,
 	UCHCOM_N_TRANSFER,
 };
 
@@ -164,6 +179,7 @@ struct uchcom_softc {
 	struct ucom_softc sc_ucom;
 
 	struct usb_xfer *sc_xfer[UCHCOM_N_TRANSFER];
+	struct usb_xfer *sc_intr_xfer;	/* Interrupt endpoint */
 	struct usb_device *sc_udev;
 	struct mtx sc_mtx;
 
@@ -171,39 +187,19 @@ struct uchcom_softc {
 	uint8_t	sc_rts;			/* local copy */
 	uint8_t	sc_version;
 	uint8_t	sc_msr;
-	uint8_t	sc_lsr;			/* local status register */
-};
-
-struct uchcom_divider {
-	uint8_t	dv_prescaler;
-	uint8_t	dv_div;
-	uint8_t	dv_mod;
-};
-
-struct uchcom_divider_record {
-	uint32_t dvr_high;
-	uint32_t dvr_low;
-	uint32_t dvr_base_clock;
-	struct uchcom_divider dvr_divider;
-};
-
-static const struct uchcom_divider_record dividers[] =
-{
-	{307200, 307200, UCHCOM_BASE_UNKNOWN, {7, 0xD9, 0}},
-	{921600, 921600, UCHCOM_BASE_UNKNOWN, {7, 0xF3, 0}},
-	{2999999, 23530, 6000000, {3, 0, 0}},
-	{23529, 2942, 750000, {2, 0, 0}},
-	{2941, 368, 93750, {1, 0, 0}},
-	{367, 1, 11719, {0, 0, 0}},
+	uint8_t sc_lsr;			/* local status register */
+	uint8_t sc_chiptype;		/* type of chip */
+	uint8_t sc_ctrl_iface_no;
+	uint8_t sc_iface_index;
 };
 
-#define	NUM_DIVIDERS	nitems(dividers)
-
 static const STRUCT_USB_HOST_ID uchcom_devs[] = {
 	{USB_VPI(USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341SER, 0)},
 	{USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341SER, 0)},
 	{USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341SER_2, 0)},
 	{USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341SER_3, 0)},
+	{USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH343SER, 0)},
+	{USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH9102SER, 0)},
 };
 
 /* protypes */
@@ -225,8 +221,9 @@ static void	uchcom_update_version(struct uchcom_softc *);
 static void	uchcom_convert_status(struct uchcom_softc *, uint8_t);
 static void	uchcom_update_status(struct uchcom_softc *);
 static void	uchcom_set_dtr_rts(struct uchcom_softc *);
-static int	uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t);
-static void	uchcom_set_baudrate(struct uchcom_softc *, uint32_t);
+static void	uchcom_calc_baudrate(struct uchcom_softc *, uint32_t, uint8_t *,
+		    uint8_t *);
+static void	uchcom_set_baudrate(struct uchcom_softc *, uint32_t, uint16_t);
 static void	uchcom_poll(struct ucom_softc *ucom);
 
 static device_probe_t uchcom_probe;
@@ -244,7 +241,7 @@ static const struct usb_config uchcom_config_data[UCHCOM_N_TRANSFER] = {
 		.endpoint = UE_ADDR_ANY,
 		.direction = UE_DIR_OUT,
 		.bufsize = UCHCOM_BULK_BUF_SIZE,
-		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.flags = {.pipe_bof = 1,},
 		.callback = &uchcom_write_callback,
 	},
 
@@ -256,8 +253,10 @@ static const struct usb_config uchcom_config_data[UCHCOM_N_TRANSFER] = {
 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
 		.callback = &uchcom_read_callback,
 	},
+};
 
-	[UCHCOM_INTR_DT_RD] = {
+static const struct usb_config uchcom_intr_config_data[1] = {
+	[0] = {
 		.type = UE_INTERRUPT,
 		.endpoint = UE_ADDR_ANY,
 		.direction = UE_DIR_IN,
@@ -311,8 +310,9 @@ uchcom_attach(device_t dev)
 {
 	struct uchcom_softc *sc = device_get_softc(dev);
 	struct usb_attach_arg *uaa = device_get_ivars(dev);
+	struct usb_interface *iface;
+	struct usb_interface_descriptor *id;
 	int error;
-	uint8_t iface_index;
 
 	DPRINTFN(11, "\n");
 
@@ -330,20 +330,49 @@ uchcom_attach(device_t dev)
 	case USB_PRODUCT_WCH2_CH341SER_3:
 		device_printf(dev, "CH341 detected\n");
 		break;
+	case USB_PRODUCT_WCH2_CH343SER:
+		device_printf(dev, "CH343 detected\n");
+		break;
+	case USB_PRODUCT_WCH2_CH9102SER:
+		device_printf(dev, "CH9102 detected\n");
+		break;
 	default:
-		device_printf(dev, "New CH340/CH341 product 0x%04x detected\n",
-		    uaa->info.idProduct);
+		device_printf(dev, "New CH340/CH341/CH343/CH9102 product "
+		    "0x%04x detected\n", uaa->info.idProduct);
 		break;
 	}
 
-	iface_index = UCHCOM_IFACE_INDEX;
-	error = usbd_transfer_setup(uaa->device,
-	    &iface_index, sc->sc_xfer, uchcom_config_data,
-	    UCHCOM_N_TRANSFER, sc, &sc->sc_mtx);
+	/* CH343/CH9102 has two interfaces. */
+	sc->sc_ctrl_iface_no = uaa->info.bIfaceNum;
 
+	iface = usbd_get_iface(uaa->device, UCHCOM_SECOND_IFACE_INDEX);
+	if (iface) {
+		id = usbd_get_interface_descriptor(iface);
+		if (id == NULL) {
+			device_printf(dev, "no interface descriptor\n");
+			goto detach;
+		}
+		sc->sc_iface_index = UCHCOM_SECOND_IFACE_INDEX;
+		usbd_set_parent_iface(uaa->device, UCHCOM_SECOND_IFACE_INDEX,
+		    uaa->info.bIfaceIndex);
+		sc->sc_chiptype = TYPE_CH343;
+	} else {
+		sc->sc_iface_index = UCHCOM_IFACE_INDEX;
+	}
+
+	/* Setup all transfers. */
+	error = usbd_transfer_setup(uaa->device, &sc->sc_iface_index,
+	    sc->sc_xfer, uchcom_config_data, UCHCOM_N_TRANSFER, sc,
+	    &sc->sc_mtx);
+	if (error) {
+		device_printf(dev, "could not allocate all pipes\n");
+		goto detach;
+	}
+	error = usbd_transfer_setup(uaa->device, &sc->sc_ctrl_iface_no,
+	    &sc->sc_intr_xfer, uchcom_intr_config_data, 1, sc, &sc->sc_mtx);
 	if (error) {
-		DPRINTF("one or more missing USB endpoints, "
-		    "error=%s\n", usbd_errstr(error));
+		device_printf(dev, "allocating USB transfers failed for "
+		    "interrupt\n");
 		goto detach;
 	}
 
@@ -449,7 +478,9 @@ uchcom_write_reg(struct uchcom_softc *sc,
 	    (unsigned)reg1, (unsigned)val1,
 	    (unsigned)reg2, (unsigned)val2);
 	uchcom_ctrl_write(
-	    sc, UCHCOM_REQ_WRITE_REG,
+	    sc, 
+	    (sc->sc_chiptype != TYPE_CH343) ?
+	    UCHCOM_REQ_WRITE_REG : UCHCOM_REQ_CH343_WRITE_REG,
 	    reg1 | ((uint16_t)reg2 << 8), val1 | ((uint16_t)val2 << 8));
 }
 
@@ -516,9 +547,6 @@ uchcom_update_version(struct uchcom_softc *sc)
 static void
 uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur)
 {
-	sc->sc_dtr = !(cur & UCHCOM_DTR_MASK);
-	sc->sc_rts = !(cur & UCHCOM_RTS_MASK);
-
 	cur = ~cur & 0x0F;
 	sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur);
 }
@@ -555,78 +583,69 @@ uchcom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
 	uint8_t brk1;
 	uint8_t brk2;
 
-	uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_LCR1, &brk2);
-	if (onoff) {
-		/* on - clear bits */
-		brk1 &= ~UCHCOM_BRK_MASK;
-		brk2 &= ~UCHCOM_LCR1_TX;
+	if (sc->sc_chiptype == TYPE_CH343) {
+		brk1 = UCHCOM_CH343_BRK_MASK;
+		if (!onoff)
+			brk1 |= UCHCOM_ABRK_MASK;
+		uchcom_write_reg(sc, brk1, 0, 0, 0);
 	} else {
-		/* off - set bits */
-		brk1 |= UCHCOM_BRK_MASK;
-		brk2 |= UCHCOM_LCR1_TX;
+		uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_LCR1,
+		    &brk2);
+		if (onoff) {
+			/* on - clear bits */
+			brk1 &= ~UCHCOM_BRK_MASK;
+			brk2 &= ~UCHCOM_LCR1_TX;
+		} else {
+			/* off - set bits */
+			brk1 |= UCHCOM_BRK_MASK;
+			brk2 |= UCHCOM_LCR1_TX;
+		}
+		uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_LCR1,
+		    brk2);
 	}
-	uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_LCR1, brk2);
 }
 
-static int
-uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate)
-{
-	const struct uchcom_divider_record *rp;
-	uint32_t div;
-	uint32_t rem;
-	uint32_t mod;
-	uint8_t i;
-
-	/* find record */
-	for (i = 0; i != NUM_DIVIDERS; i++) {
-		if (dividers[i].dvr_high >= rate &&
-		    dividers[i].dvr_low <= rate) {
-			rp = &dividers[i];
-			goto found;
-		}
-	}
-	return (-1);
-
-found:
-	dp->dv_prescaler = rp->dvr_divider.dv_prescaler;
-	if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN)
-		dp->dv_div = rp->dvr_divider.dv_div;
-	else {
-		div = rp->dvr_base_clock / rate;
-		rem = rp->dvr_base_clock % rate;
-		if (div == 0 || div >= 0xFF)
-			return (-1);
-		if ((rem << 1) >= rate)
-			div += 1;
-		dp->dv_div = (uint8_t)-div;
+static void
+uchcom_calc_baudrate(struct uchcom_softc *sc, uint32_t rate, uint8_t *divisor,
+    uint8_t *factor)
+{
+	uint32_t clk = 12000000;
+
+	if (rate >= 256000 && sc->sc_chiptype == TYPE_CH343)
+		*divisor = 7;
+	else if (rate > 23529) {
+		clk /= 2;
+		*divisor = 3;
+	} else if (rate > 2941) {
+		clk /=  16;
+		*divisor = 2;
+	} else if (rate > 367) {
+		clk /= 128;
+		*divisor = 1;
+	} else {
+		clk = 11719;
+		*divisor = 0;
 	}
 
-	mod = (UCHCOM_BPS_MOD_BASE / rate) + UCHCOM_BPS_MOD_BASE_OFS;
-	mod = mod + (mod / 2);
+	*factor = 256 - clk / rate;
 
-	dp->dv_mod = (mod + 0xFF) / 0x100;
-
-	return (0);
+	if (rate == 921600 && sc->sc_chiptype != TYPE_CH343) {
+		*divisor = 7;
+		*factor = 243;
+	}
 }
 
 static void
-uchcom_set_baudrate(struct uchcom_softc *sc, uint32_t rate)
+uchcom_set_baudrate(struct uchcom_softc *sc, uint32_t rate, uint16_t lcr)
 {
-	struct uchcom_divider dv;
+	uint16_t idx;
+	uint8_t factor, div;
 
-	if (uchcom_calc_divider_settings(&dv, rate))
-		return;
+	uchcom_calc_baudrate(sc, rate, &div, &factor);
+	div |= (sc->sc_chiptype != TYPE_CH343) ? 0x80 : 0x00;
+	idx = (factor << 8) | div;
 
-	/*
-	 * According to linux code we need to set bit 7 of UCHCOM_REG_BPS_PRE,
-	 * otherwise the chip will buffer data.
-	 */
-	uchcom_write_reg(sc,
-	    UCHCOM_REG_BPS_PRE, dv.dv_prescaler | 0x80,
-	    UCHCOM_REG_BPS_DIV, dv.dv_div);
-	uchcom_write_reg(sc,
-	    UCHCOM_REG_BPS_MOD, dv.dv_mod,
-	    UCHCOM_REG_BPS_PAD, 0);
+	uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, lcr, idx);
 }
 
 /* ----------------------------------------------------------------------
@@ -673,6 +692,14 @@ uchcom_cfg_open(struct ucom_softc *ucom)
 
 	DPRINTF("\n");
 
+	if (sc->sc_chiptype != TYPE_CH343) {
+		/* Set default configuration. */
+		uchcom_get_version(sc, NULL);
+		uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0);
+		uchcom_write_reg(sc, UCHCOM_REG_BPS_PRE, 0x82,
+		    UCHCOM_REG_BPS_DIV, 0xd9);
+		uchcom_write_reg(sc, 0x2c, 0x07, UCHCOM_REG_BPS_PAD, 0);
+	}
 	uchcom_update_version(sc);
 	uchcom_update_status(sc);
 }
@@ -680,53 +707,69 @@ uchcom_cfg_open(struct ucom_softc *ucom)
 static int
 uchcom_pre_param(struct ucom_softc *ucom, struct termios *t)
 {
-	struct uchcom_divider dv;
+	struct uchcom_softc *sc = ucom->sc_parent;
 
-	switch (t->c_cflag & CSIZE) {
-	case CS8:
+	/* 
+	 * Check requested baud rate.
+	 * The CH340/CH341 can set any baud rate up to 2Mb.
+	 * The CH9102/CH343 can set any baud rate up to 6Mb.
+	 */
+	switch (sc->sc_chiptype) {
+	case TYPE_CH343:
+		if (t->c_ospeed <= 6000000)
+			return (0);
 		break;
 	default:
-		return (EIO);
+		if (t->c_ospeed <= 2000000)
+			return (0);
+		break;
 	}
-	if ((t->c_cflag & CSTOPB) != 0)
-		return (EIO);
-	if ((t->c_cflag & PARENB) != 0)
-		return (EIO);
 
-	if (uchcom_calc_divider_settings(&dv, t->c_ospeed)) {
-		return (EIO);
-	}
-	return (0);			/* success */
+	return (EIO);
 }
 
 static void
 uchcom_cfg_param(struct ucom_softc *ucom, struct termios *t)
 {
 	struct uchcom_softc *sc = ucom->sc_parent;
+	uint8_t lcr;
 
-	uchcom_get_version(sc, NULL);
-	uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0);
-	uchcom_set_baudrate(sc, t->c_ospeed);
-	if (sc->sc_version < UCHCOM_VER_30) {
-		uchcom_read_reg(sc, UCHCOM_REG_LCR1, NULL,
-		    UCHCOM_REG_LCR2, NULL);
-		uchcom_write_reg(sc, UCHCOM_REG_LCR1, 0x50,
-		    UCHCOM_REG_LCR2, 0x00);
-	} else {
-		/*
-		 * Set up line control:
-		 * - enable transmit and receive
-		 * - set 8n1 mode
-		 * To do: support other sizes, parity, stop bits.
-		 */
-		uchcom_write_reg(sc,
-		    UCHCOM_REG_LCR1,
-		    UCHCOM_LCR1_RX | UCHCOM_LCR1_TX | UCHCOM_LCR1_CS8,
-		    UCHCOM_REG_LCR2, 0x00);
+	lcr = UCHCOM_LCR1_RX | UCHCOM_LCR1_TX;
+
+	if (t->c_cflag & CSTOPB)
+		lcr |= UCHCOM_LCR1_STOPB;
+
+	if (t->c_cflag & PARENB) {
+		lcr |= UCHCOM_LCR1_PARENB;
+		if (t->c_cflag & PARODD)
+			lcr |= UCHCOM_LCR1_PARODD;
+		else
+			lcr |= UCHCOM_LCR1_PAREVEN;
 	}
-	uchcom_update_status(sc);
-	uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0x501f, 0xd90a);
-	uchcom_set_baudrate(sc, t->c_ospeed);
+
+	switch (t->c_cflag & CSIZE) {
+	case CS5:
+		lcr |= UCHCOM_LCR1_CS5;
+		break;
+	case CS6:
+		lcr |= UCHCOM_LCR1_CS6;
+		break;
+	case CS7:
+		lcr |= UCHCOM_LCR1_CS7;
+		break;
+	case CS8:
+	default:
+		lcr |= UCHCOM_LCR1_CS8;
+		break;
+	}
+
+	if (sc->sc_chiptype == TYPE_CH343)
+		uchcom_set_baudrate(sc, t->c_ospeed,
+		    UCHCOM_T | UCHCOM_CL | UCHCOM_CH343_CT | lcr << 8);
+	else
+		uchcom_set_baudrate(sc, t->c_ospeed,
+		    UCHCOM_T | UCHCOM_CL | UCHCOM_CT | lcr << 8);
+
 	uchcom_set_dtr_rts(sc);
 	uchcom_update_status(sc);
 }
@@ -737,7 +780,7 @@ uchcom_start_read(struct ucom_softc *ucom)
 	struct uchcom_softc *sc = ucom->sc_parent;
 
 	/* start interrupt endpoint */
-	usbd_transfer_start(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
+	usbd_transfer_start(sc->sc_intr_xfer);
 
 	/* start read endpoint */
 	usbd_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
@@ -749,7 +792,7 @@ uchcom_stop_read(struct ucom_softc *ucom)
 	struct uchcom_softc *sc = ucom->sc_parent;
 
 	/* stop interrupt endpoint */
-	usbd_transfer_stop(sc->sc_xfer[UCHCOM_INTR_DT_RD]);
+	usbd_transfer_stop(sc->sc_intr_xfer);
 
 	/* stop read endpoint */
 	usbd_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
@@ -779,7 +822,8 @@ uchcom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
 {
 	struct uchcom_softc *sc = usbd_xfer_softc(xfer);
 	struct usb_page_cache *pc;
-	uint8_t buf[UCHCOM_INTR_LEAST];
+	uint32_t intrstat;
+	uint8_t buf[16];
 	int actlen;
 
 	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
@@ -791,13 +835,12 @@ uchcom_intr_callback(struct usb_xfer *xfer, usb_error_t error)
 
 		if (actlen >= UCHCOM_INTR_LEAST) {
 			pc = usbd_xfer_get_frame(xfer, 0);
-			usbd_copy_out(pc, 0, buf, UCHCOM_INTR_LEAST);
+			usbd_copy_out(pc, 0, buf, sizeof(buf));
 
-			DPRINTF("data = 0x%02X 0x%02X 0x%02X 0x%02X\n",
-			    (unsigned)buf[0], (unsigned)buf[1],
-			    (unsigned)buf[2], (unsigned)buf[3]);
+			intrstat = (sc->sc_chiptype == TYPE_CH343) ?
+			    actlen - 1 : UCHCOM_INTR_STAT1;
 
-			uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]);
+			uchcom_convert_status(sc, buf[intrstat]);
 			ucom_status_change(&sc->sc_ucom);
 		}
 	case USB_ST_SETUP:
diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs
index 856ff526fa45..a7519c1e7fe5 100644
--- a/sys/dev/usb/usbdevs
+++ b/sys/dev/usb/usbdevs
@@ -4976,9 +4976,11 @@ product WAVESENSE JAZZ		0xaaaa	Jazz blood glucose meter
 product WCH CH341SER		0x5523	CH341/CH340 USB-Serial Bridge
 product WCH2 DUMMY		0x0000	Dummy product
 product WCH2 CH341SER_2		0x5523	CH341/CH340 USB-Serial Bridge
+product WCH2 CH343SER		0x55d3	CH343 USB Serial
+product WCH2 CH9102SER		0x55d4	CH9102 USB Serial
 product WCH2 CH341SER_3		0x7522	CH341/CH340 USB-Serial Bridge
 product WCH2 CH341SER		0x7523	CH341/CH340 USB-Serial Bridge
-product WCH2 U2M		0X752d	CH345 USB2.0-MIDI
+product WCH2 U2M		0x752d	CH345 USB2.0-MIDI
 
 /* West Mountain Radio products */
 product WESTMOUNTAIN RIGBLASTER_ADVANTAGE	0x0003	RIGblaster Advantage