diff --git a/share/man/man4/uchcom.4 b/share/man/man4/uchcom.4 --- 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 16, 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,8 +52,9 @@ .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. +CH9102/CH343 devices support any baud rate up to 6 Mbps. .Pp The device is accessed through the .Xr ucom 4 @@ -96,5 +97,5 @@ release to include it was .Fx 8.0 . .Sh BUGS -Actually, this chip seems unable to drive other than 8 data bits and +CH341/CH340 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 --- a/sys/dev/usb/serial/uchcom.c +++ b/sys/dev/usb/serial/uchcom.c @@ -59,8 +59,7 @@ #include /* - * Driver for WinChipHead CH341/340, the worst USB-serial chip in the - * world. + * Driver for WinChipHead CH9102/343/341/340. */ #include @@ -102,17 +101,20 @@ &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_REQ_SET_BAUDRATE UCHCOM_REQ_RESET #define UCHCOM_REG_STAT1 0x06 #define UCHCOM_REG_STAT2 0x07 @@ -135,13 +137,21 @@ #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 @@ -151,12 +161,17 @@ #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_CT 0x80 + +#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, }; @@ -165,6 +180,7 @@ 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; @@ -172,7 +188,10 @@ uint8_t sc_rts; /* local copy */ uint8_t sc_version; uint8_t sc_msr; - uint8_t sc_lsr; /* local status register */ + 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; }; struct uchcom_divider { @@ -205,6 +224,8 @@ {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 */ @@ -226,8 +247,11 @@ 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 void uchcom_calc_baudrate_ch343(uint32_t, uint8_t *, uint8_t *); static int uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t); static void uchcom_set_baudrate(struct uchcom_softc *, uint32_t); +static void uchcom_set_baudrate_ch343(struct uchcom_softc *, uint32_t, + uint16_t); static void uchcom_poll(struct ucom_softc *ucom); static device_probe_t uchcom_probe; @@ -257,8 +281,10 @@ .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, @@ -312,8 +338,9 @@ { 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"); @@ -331,20 +358,49 @@ 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) { - DPRINTF("one or more missing USB endpoints, " - "error=%s\n", usbd_errstr(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) { + device_printf(dev, "allocating USB transfers failed for " + "interrupt\n"); goto detach; } @@ -450,7 +506,9 @@ (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)); } @@ -556,17 +614,50 @@ 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 { + 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); + } +} + +static void +uchcom_calc_baudrate_ch343(uint32_t rate, uint8_t *divisor, uint8_t *factor) +{ + uint32_t clk = 12000000; + + if (rate >= 256000) + *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 { - /* off - set bits */ - brk1 |= UCHCOM_BRK_MASK; - brk2 |= UCHCOM_LCR1_TX; + clk = 11719; + *divisor = 0; } - uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_LCR1, brk2); + + *factor = 256 - clk / rate; } static int @@ -630,6 +721,18 @@ UCHCOM_REG_BPS_PAD, 0); } +static void +uchcom_set_baudrate_ch343(struct uchcom_softc *sc, uint32_t rate, uint16_t lcr) +{ + uint16_t idx; + uint8_t factor, div; + + uchcom_calc_baudrate_ch343(rate, &div, &factor); + idx = (factor << 8) | div; + + uchcom_ctrl_write(sc, UCHCOM_REQ_SET_BAUDRATE, lcr, idx); +} + /* ---------------------------------------------------------------------- * methods for ucom */ @@ -681,8 +784,18 @@ static int uchcom_pre_param(struct ucom_softc *ucom, struct termios *t) { + struct uchcom_softc *sc = ucom->sc_parent; struct uchcom_divider dv; + if (sc->sc_chiptype == TYPE_CH343) { + /* + * Check requested baud rate. + * The CH9102/CH343 can set any baud rate up to 6Mb. + */ + if (t->c_ospeed <= 6000000) + return (0); + } + switch (t->c_cflag & CSIZE) { case CS8: break; @@ -704,6 +817,45 @@ uchcom_cfg_param(struct ucom_softc *ucom, struct termios *t) { struct uchcom_softc *sc = ucom->sc_parent; + uint8_t lcr = 0; + + if (sc->sc_chiptype == TYPE_CH343) { + 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; + } + + 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; + } + + uchcom_set_baudrate_ch343(sc, t->c_ospeed, + UCHCOM_T | UCHCOM_CL | UCHCOM_CT | lcr << 8); + + uchcom_set_dtr_rts(sc); + uchcom_update_status(sc); + return; + } uchcom_get_version(sc, NULL); uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0); @@ -738,7 +890,7 @@ 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]); @@ -750,7 +902,7 @@ 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]); @@ -780,6 +932,7 @@ { struct uchcom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; + uint32_t intrstat; uint8_t buf[UCHCOM_INTR_LEAST]; int actlen; @@ -798,7 +951,10 @@ (unsigned)buf[0], (unsigned)buf[1], (unsigned)buf[2], (unsigned)buf[3]); - uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]); + intrstat = (sc->sc_chiptype != TYPE_CH343) ? + actlen - 1 : 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 --- a/sys/dev/usb/usbdevs +++ b/sys/dev/usb/usbdevs @@ -4903,9 +4903,11 @@ /* WCH products */ product WCH CH341SER 0x5523 CH341/CH340 USB-Serial Bridge 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