Index: sys/dev/usb/serial/uchcom.c =================================================================== --- sys/dev/usb/serial/uchcom.c +++ sys/dev/usb/serial/uchcom.c @@ -122,25 +122,31 @@ #define UCHCOM_REG_BPS_MOD 0x14 #define UCHCOM_REG_BPS_PAD 0x0F #define UCHCOM_REG_BREAK1 0x05 -#define UCHCOM_REG_BREAK2 0x18 #define UCHCOM_REG_LCR1 0x18 #define UCHCOM_REG_LCR2 0x25 #define UCHCOM_VER_20 0x20 +#define UCHCOM_VER_30 0x30 #define UCHCOM_BASE_UNKNOWN 0 #define UCHCOM_BPS_MOD_BASE 20000000 #define UCHCOM_BPS_MOD_BASE_OFS 1100 +#define UCHCOM_CTS_MASK 0x01 +#define UCHCOM_DSR_MASK 0x02 +#define UCHCOM_RI_MASK 0x04 +#define UCHCOM_DCD_MASK 0x08 #define UCHCOM_DTR_MASK 0x20 #define UCHCOM_RTS_MASK 0x40 -#define UCHCOM_BRK1_MASK 0x01 -#define UCHCOM_BRK2_MASK 0x40 +#define UCHCOM_BRK_MASK 0x01 #define UCHCOM_LCR1_MASK 0xAF #define UCHCOM_LCR2_MASK 0x07 -#define UCHCOM_LCR1_PARENB 0x80 +#define UCHCOM_LCR1_RX 0x80 +#define UCHCOM_LCR1_TX 0x40 +#define UCHCOM_LCR1_PARENB 0x08 +#define UCHCOM_LCR1_CS8 0x03 #define UCHCOM_LCR2_PAREVEN 0x07 #define UCHCOM_LCR2_PARODD 0x06 #define UCHCOM_LCR2_PARMARK 0x05 @@ -322,13 +328,17 @@ sc->sc_udev = uaa->device; - switch (uaa->info.bcdDevice) { - case UCHCOM_REV_CH340: + switch (uaa->info.idProduct) { + case USB_PRODUCT_WCH2_CH341SER: device_printf(dev, "CH340 detected\n"); break; - default: + case USB_PRODUCT_WCH2_CH341SER_2: device_printf(dev, "CH341 detected\n"); break; + default: + device_printf(dev, "New CH340/CH341 product 0x%04x detected\n", + uaa->info.idProduct); + break; } iface_index = UCHCOM_IFACE_INDEX; @@ -344,7 +354,10 @@ /* clear stall at first run */ mtx_lock(&sc->sc_mtx); +#if 1 + /* This resets my particular CH340-based device. */ usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_WR]); +#endif usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_RD]); mtx_unlock(&sc->sc_mtx); @@ -412,6 +425,8 @@ USETW(req.wIndex, index); USETW(req.wLength, 0); + DPRINTF("WR REQ 0x%02X VAL 0x%04X IDX 0x%04X\n", + reqno, value, index); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } @@ -428,6 +443,8 @@ USETW(req.wIndex, index); USETW(req.wLength, buflen); + DPRINTF("RD REQ 0x%02X VAL 0x%04X IDX 0x%04X LEN %d\n", + reqno, value, index, buflen); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, buf, USB_SHORT_XFER_OK, 1000); } @@ -502,6 +519,7 @@ uchcom_update_version(struct uchcom_softc *sc) { uchcom_get_version(sc, &sc->sc_version); + DPRINTF("Chip version: 0x%02x\n", sc->sc_version); } static void @@ -547,17 +565,17 @@ uint8_t brk1; uint8_t brk2; - uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2, &brk2); + uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_LCR1, &brk2); if (onoff) { /* on - clear bits */ - brk1 &= ~UCHCOM_BRK1_MASK; - brk2 &= ~UCHCOM_BRK2_MASK; + brk1 &= ~UCHCOM_BRK_MASK; + brk2 &= ~UCHCOM_LCR1_TX; } else { /* off - set bits */ - brk1 |= UCHCOM_BRK1_MASK; - brk2 |= UCHCOM_BRK2_MASK; + brk1 |= UCHCOM_BRK_MASK; + brk2 |= UCHCOM_LCR1_TX; } - uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2, brk2); + uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_LCR1, brk2); } static int @@ -609,8 +627,12 @@ if (uchcom_calc_divider_settings(&dv, rate)) return; + /* + * 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, + 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, @@ -676,6 +698,10 @@ default: return (EIO); } + 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); @@ -688,11 +714,26 @@ { struct uchcom_softc *sc = ucom->sc_parent; - uchcom_get_version(sc, 0); + uchcom_get_version(sc, NULL); uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0); uchcom_set_baudrate(sc, t->c_ospeed); - uchcom_read_reg(sc, 0x18, 0, 0x25, 0); - uchcom_write_reg(sc, 0x18, 0x50, 0x25, 0x00); + 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); + } uchcom_update_status(sc); uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0x501f, 0xd90a); uchcom_set_baudrate(sc, t->c_ospeed);