Page MenuHomeFreeBSD

D46290.diff
No OneTemporary

D46290.diff

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 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 @@
.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 @@
.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
--- a/sys/dev/usb/serial/uchcom.c
+++ b/sys/dev/usb/serial/uchcom.c
@@ -59,8 +59,7 @@
#include <sys/cdefs.h>
/*
- * Driver for WinChipHead CH341/340, the worst USB-serial chip in the
- * world.
+ * Driver for WinChipHead CH9102/343/341/340.
*/
#include <sys/stdint.h>
@@ -102,17 +101,19 @@
&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
@@ -135,13 +136,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 +160,18 @@
#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,
};
@@ -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,39 +188,19 @@
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 */
@@ -226,8 +222,9 @@
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;
@@ -257,8 +254,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 +311,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 +331,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) {
+ 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;
}
@@ -450,7 +479,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,78 +587,69 @@
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);
-
- dp->dv_mod = (mod + 0xFF) / 0x100;
+ *factor = 256 - clk / rate;
- 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);
}
/* ----------------------------------------------------------------------
@@ -674,6 +696,14 @@
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);
}
@@ -681,53 +711,69 @@
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);
}
@@ -738,7 +784,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 +796,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 +826,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 +845,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

File Metadata

Mime Type
text/plain
Expires
Fri, Jan 10, 11:47 AM (13 h, 14 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15742301
Default Alt Text
D46290.diff (15 KB)

Event Timeline