Download raw body.
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 = ÷rs[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,
uchcom: move the common parts to allow for sharing code