Index | Thread | Search

From:
Kevin Lo <kevlo@kevlo.org>
Subject:
uchcom: move the common parts to allow for sharing code
To:
tech@openbsd.org
Date:
Thu, 31 Jul 2025 14:07:55 +0800

Download raw body.

Thread
  • Kevin Lo:

    uchcom: move the common parts to allow for sharing code

Hi,

Since the ch340/ch341 shares many common parts with the ch343,
move the common parts to allow for sharing code.
Diff also fixes baudrate divisor calculation for ch343.

Tested on ch341/ch343 uart adapters.
Flashing a binary file to an esp32-c6 development board [1]
using devel/py-esptool works without issues.

[1] https://www.aliexpress.com/item/1005007086080362.html

ok?

Index: sys/dev/usb/uchcom.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/uchcom.c,v
diff -u -p -u -p -r1.37 uchcom.c
--- sys/dev/usb/uchcom.c	22 Oct 2024 21:50:02 -0000	1.37
+++ sys/dev/usb/uchcom.c	30 Jul 2025 08:17:03 -0000
@@ -116,14 +116,8 @@ int	uchcomdebug = 0;
 #define UCHCOM_T		0x08
 #define UCHCOM_CL		0x04
 #define UCHCOM_CT		0x80
-/*
- * XXX - these magic numbers come from Linux (drivers/usb/serial/ch341.c).
- * The manufacturer was unresponsive when asked for documentation.
- */
-#define UCHCOM_RESET_VALUE	0x501F	/* line mode? */
-#define UCHCOM_RESET_INDEX	0xD90A	/* baud rate? */
 
-#define UCHCOMOBUFSIZE 256
+#define UCHCOMOBUFSIZE	256
 
 #define UCHCOM_TYPE_CH343	1
 
@@ -158,29 +152,6 @@ struct uchcom_endpoints {
 	int		ep_intr_size;
 };
 
-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 } },
-};
-
 void		uchcom_get_status(void *, int, u_char *, u_char *);
 void		uchcom_set(void *, int, int, int);
 int		uchcom_param(void *, int, struct termios *);
@@ -211,16 +182,12 @@ int		uchcom_update_status(struct uchcom_
 int		uchcom_set_dtrrts(struct uchcom_softc *, int, int);
 int		uchcom_set_break(struct uchcom_softc *, int);
 int		uchcom_set_break_ch343(struct uchcom_softc *, int);
-void		uchcom_calc_baudrate_ch343(uint32_t, uint8_t *, uint8_t *);
-int		uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t);
-int		uchcom_set_dte_rate_ch343(struct uchcom_softc *, uint32_t,
-		    uint16_t);
-int		uchcom_set_dte_rate(struct uchcom_softc *, uint32_t);
+void		uchcom_calc_baudrate(struct uchcom_softc *, uint32_t, uint8_t *,
+		    uint8_t *);
+int		uchcom_set_dte_rate(struct uchcom_softc *, uint32_t, uint16_t);
 uint16_t	uchcom_set_line_control(struct uchcom_softc *, tcflag_t,
 		    uint16_t *);
 int		uchcom_clear_chip(struct uchcom_softc *);
-int		uchcom_reset_chip(struct uchcom_softc *);
-int		uchcom_setup_comm(struct uchcom_softc *);
 int		uchcom_setup_intr_pipe(struct uchcom_softc *);
 
 
@@ -576,9 +543,6 @@ uchcom_update_version(struct uchcom_soft
 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);
 }
@@ -595,6 +559,10 @@ uchcom_update_status(struct uchcom_softc
 		       sc->sc_dev.dv_xname, usbd_errstr(err));
 		return EIO;
 	}
+
+	sc->sc_dtr = !(cur & UCHCOM_DTR_MASK);
+	sc->sc_rts = !(cur & UCHCOM_RTS_MASK);
+
 	uchcom_convert_status(sc, cur);
 
 	return 0;
@@ -666,11 +634,12 @@ uchcom_set_break_ch343(struct uchcom_sof
 }
 
 void
-uchcom_calc_baudrate_ch343(uint32_t rate, uint8_t *divisor, uint8_t *factor)
+uchcom_calc_baudrate(struct uchcom_softc *sc, uint32_t rate, uint8_t *divisor,
+    uint8_t *factor)
 {
 	uint32_t clk = 12000000;
 
-	if (rate >= 256000)
+	if (rate == 921600 || rate == 4000000)
 		*divisor = 7;
 	else if (rate > 23529) {
 		clk /= 2;
@@ -686,56 +655,21 @@ uchcom_calc_baudrate_ch343(uint32_t rate
 		*divisor = 0;
 	}
 
-	*factor = 256 - clk / rate;
-}
-
-int
-uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate)
-{
-	int i;
-	const struct uchcom_divider_record *rp;
-	uint32_t div, rem, mod;
-
-	/* find record */
-	for (i=0; i<nitems(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;
-	}
-
-	mod = UCHCOM_BPS_MOD_BASE/rate + UCHCOM_BPS_MOD_BASE_OFS;
-	mod = mod + mod/2;
-
-	dp->dv_mod = mod / 0x100;
-
-	return 0;
+	if (rate == 921600 && sc->sc_type != UCHCOM_TYPE_CH343)
+		*factor = 243;
+	else
+		*factor = 256 - clk / rate;
 }
 
 int
-uchcom_set_dte_rate_ch343(struct uchcom_softc *sc, uint32_t rate, uint16_t val)
+uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate, uint16_t val)
 {
 	usbd_status err;
 	uint16_t idx;
 	uint8_t factor, div;
 
-	uchcom_calc_baudrate_ch343(rate, &div, &factor);
+	uchcom_calc_baudrate(sc, rate, &div, &factor);
+	div |= (sc->sc_type != UCHCOM_TYPE_CH343) ? 0x80 : 0;
 	idx = (factor << 8) | div;
 
 	err = uchcom_generic_control_out(sc, UCHCOM_REQ_SET_BAUDRATE, val, idx);
@@ -748,34 +682,10 @@ uchcom_set_dte_rate_ch343(struct uchcom_
 	return 0;
 }
 
-int
-uchcom_set_dte_rate(struct uchcom_softc *sc, uint32_t rate)
-{
-	usbd_status err;
-	struct uchcom_divider dv;
-
-	if (uchcom_calc_divider_settings(&dv, rate))
-		return EINVAL;
-
-	if ((err = uchcom_write_reg(sc,
-			     UCHCOM_REG_BPS_PRE, dv.dv_prescaler,
-			     UCHCOM_REG_BPS_DIV, dv.dv_div)) ||
-	    (err = uchcom_write_reg(sc,
-			     UCHCOM_REG_BPS_MOD, dv.dv_mod,
-			     UCHCOM_REG_BPS_PAD, 0))) {
-		printf("%s: cannot set DTE rate: %s\n",
-		       sc->sc_dev.dv_xname, usbd_errstr(err));
-		return EIO;
-	}
-
-	return 0;
-}
-
 uint16_t
 uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag, uint16_t *val)
 {
-	usbd_status err;
-	uint8_t lcr = 0, lcr2 = 0;
+	uint8_t lcr = 0;
 
 	if (sc->sc_release == UCHCOM_REV_CH340) {
 		/*
@@ -797,16 +707,6 @@ uchcom_set_line_control(struct uchcom_so
 		return 0;
 	}
 
-	if (sc->sc_type != UCHCOM_TYPE_CH343) {
-		err = uchcom_read_reg(sc, UCHCOM_REG_LCR, &lcr, UCHCOM_REG_LCR2,
-		    &lcr2);
-		if (err) {
-			printf("%s: cannot get LCR: %s\n",
-			    sc->sc_dev.dv_xname, usbd_errstr(err));
-			return EIO;
-		}
-	}
-
 	lcr = UCHCOM_LCR_RXE | UCHCOM_LCR_TXE;
 
 	switch (ISSET(cflag, CSIZE)) {
@@ -834,16 +734,8 @@ uchcom_set_line_control(struct uchcom_so
 		lcr |= UCHCOM_LCR_STOPB;
 	}
 
-	if (sc->sc_type != UCHCOM_TYPE_CH343) {
-		err = uchcom_write_reg(sc, UCHCOM_REG_LCR, lcr, UCHCOM_REG_LCR2,
-		    lcr2);
-		if (err) {
-			printf("%s: cannot set LCR: %s\n",
-			    sc->sc_dev.dv_xname, usbd_errstr(err));
-			return EIO;
-		}
-	} else
-		*val = UCHCOM_T | UCHCOM_CL | UCHCOM_CT | lcr << 8;
+	*val = UCHCOM_T | UCHCOM_CL | UCHCOM_CT | lcr << 8;
+	*val |= (sc->sc_type != UCHCOM_TYPE_CH343) ? 0x10 : 0;
 
 	return 0;
 }
@@ -865,59 +757,6 @@ uchcom_clear_chip(struct uchcom_softc *s
 }
 
 int
-uchcom_reset_chip(struct uchcom_softc *sc)
-{
-	usbd_status err;
-
-	DPRINTF(("%s: reset\n", sc->sc_dev.dv_xname));
-
-	err = uchcom_generic_control_out(sc, UCHCOM_REQ_RESET,
-					 UCHCOM_RESET_VALUE,
-					 UCHCOM_RESET_INDEX);
-	if (err)
-		goto failed;
-
-	return 0;
-
-failed:
-	printf("%s: cannot reset: %s\n",
-	       sc->sc_dev.dv_xname, usbd_errstr(err));
-	return EIO;
-}
-
-int
-uchcom_setup_comm(struct uchcom_softc *sc)
-{
-	int ret;
-
-	ret = uchcom_clear_chip(sc);
-	if (ret)
-		return ret;
-
-	ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED);
-	if (ret)
-		return ret;
-
-	ret = uchcom_set_line_control(sc, CS8, 0);
-	if (ret)
-		return ret;
-
-	ret = uchcom_update_status(sc);
-	if (ret)
-		return ret;
-
-	ret = uchcom_reset_chip(sc);
-	if (ret)
-		return ret;
-
-	ret = uchcom_set_dte_rate(sc, TTYDEF_SPEED); /* XXX */
-	if (ret)
-		return ret;
-
-	return 0;
-}
-
-int
 uchcom_setup_intr_pipe(struct uchcom_softc *sc)
 {
 	usbd_status err;
@@ -1008,14 +847,16 @@ uchcom_param(void *arg, int portno, stru
 	if (usbd_is_dying(sc->sc_udev))
 		return 0;
 
+	if (t->c_ospeed <= 0 ||
+	    (t->c_ospeed > 921600 && sc->sc_type != UCHCOM_TYPE_CH343) ||
+	    (t->c_ospeed > 6000000 && sc->sc_type == UCHCOM_TYPE_CH343))
+		return EINVAL;
+
 	ret = uchcom_set_line_control(sc, t->c_cflag, &val);
 	if (ret)
 		return ret;
 
-	if (sc->sc_type == UCHCOM_TYPE_CH343)
-		ret = uchcom_set_dte_rate_ch343(sc, t->c_ospeed, val);
-	else
-		ret = uchcom_set_dte_rate(sc, t->c_ospeed);
+	ret = uchcom_set_dte_rate(sc, t->c_ospeed, val);
 	if (ret)
 		return ret;
 
@@ -1025,8 +866,8 @@ uchcom_param(void *arg, int portno, stru
 int
 uchcom_open(void *arg, int portno)
 {
-	int ret;
 	struct uchcom_softc *sc = arg;
+	int ret;
 
 	if (usbd_is_dying(sc->sc_udev))
 		return EIO;
@@ -1039,14 +880,17 @@ uchcom_open(void *arg, int portno)
 	if (ret)
 		return ret;
 
-	if (sc->sc_type == UCHCOM_TYPE_CH343)
-		ret = uchcom_update_status(sc);
-	else
-		ret = uchcom_setup_comm(sc);
+	if (sc->sc_type != UCHCOM_TYPE_CH343) {
+		ret = uchcom_clear_chip(sc);
+		if (ret)
+			return ret;
+	}
+
+	ret = uchcom_update_status(sc);
 	if (ret)
 		return ret;
 
-	sc->sc_dtr = sc->sc_rts = 1;
+	sc->sc_dtr = sc->sc_rts = 0;
 	ret = uchcom_set_dtrrts(sc, sc->sc_dtr, sc->sc_rts);
 	if (ret)
 		return ret;
@@ -1085,6 +929,10 @@ uchcom_intr(struct usbd_xfer *xfer, void
 		usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
 		return;
 	}
+
+	if (xfer->actlen < 4)
+		return;
+
 	DPRINTF(("%s: intr: 0x%02X 0x%02X 0x%02X 0x%02X "
 		 "0x%02X 0x%02X 0x%02X 0x%02X\n",
 		 sc->sc_dev.dv_xname,