i bought some random usb to rs485 serial adapters so i can talk
modbus to a thing, but then discovered i can't talk to the modbus
thing because uchcom doesn't support configuring parity.

this ports the functionality to support configuring parity and char size
masks from netbsd src/sys/dev/usb/uchcom.c r1.26. part of that change
including tweaks to uchcom_reset_chip, which was then changed in r1.28
back to what we already have, so i left that chunk out.

ive tested this talking to a device at 19200 with cs8 and even
parity. more tests would be appreciated to make sure i haven't
broken existing use functionality.

Index: uchcom.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/uchcom.c,v
retrieving revision 1.28
diff -u -p -r1.28 uchcom.c
--- uchcom.c    31 Jul 2020 10:49:33 -0000      1.28
+++ uchcom.c    26 Oct 2021 20:33:36 -0000
@@ -72,9 +72,8 @@ int   uchcomdebug = 0;
 #define UCHCOM_REG_BPS_DIV     0x13
 #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_BREAK       0x05
+#define UCHCOM_REG_LCR         0x18
 #define UCHCOM_REG_LCR2                0x25
 
 #define UCHCOM_VER_20          0x20
@@ -83,11 +82,25 @@ int uchcomdebug = 0;
 #define UCHCOM_BPS_MOD_BASE    20000000
 #define UCHCOM_BPS_MOD_BASE_OFS        1100
 
+#define UCHCOM_BPS_PRE_IMM     0x80    /* CH341: immediate RX forwarding */
+
 #define UCHCOM_DTR_MASK                0x20
 #define UCHCOM_RTS_MASK                0x40
 
-#define UCHCOM_BRK1_MASK       0x01
-#define UCHCOM_BRK2_MASK       0x40
+#define UCHCOM_BREAK_MASK      0x01
+
+#define UCHCOM_LCR_CS5         0x00
+#define UCHCOM_LCR_CS6         0x01
+#define UCHCOM_LCR_CS7         0x02
+#define UCHCOM_LCR_CS8         0x03
+#define UCHCOM_LCR_STOPB       0x04
+#define UCHCOM_LCR_PARENB      0x08
+#define UCHCOM_LCR_PARODD      0x00
+#define UCHCOM_LCR_PAREVEN     0x10
+#define UCHCOM_LCR_PARMARK     0x20
+#define UCHCOM_LCR_PARSPACE    0x30
+#define UCHCOM_LCR_TXE         0x40
+#define UCHCOM_LCR_RXE         0x80
 
 #define UCHCOM_INTR_STAT1      0x02
 #define UCHCOM_INTR_STAT2      0x03
@@ -577,23 +590,21 @@ int
 uchcom_set_break(struct uchcom_softc *sc, int onoff)
 {
        usbd_status err;
-       uint8_t brk1, brk2;
+       uint8_t brk, lcr;
 
-       err = uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_BREAK2,
-           &brk2);
+       err = uchcom_read_reg(sc, UCHCOM_REG_BREAK, &brk, UCHCOM_REG_LCR, &lcr);
        if (err)
                return EIO;
        if (onoff) {
                /* on - clear bits */
-               brk1 &= ~UCHCOM_BRK1_MASK;
-               brk2 &= ~UCHCOM_BRK2_MASK;
+               brk &= ~UCHCOM_BREAK_MASK;
+               lcr &= ~UCHCOM_LCR_TXE;
        } else {
                /* off - set bits */
-               brk1 |= UCHCOM_BRK1_MASK;
-               brk2 |= UCHCOM_BRK2_MASK;
+               brk |= UCHCOM_BREAK_MASK;
+               lcr |= UCHCOM_LCR_TXE;
        }
-       err = uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_BREAK2,
-           brk2);
+       err = uchcom_write_reg(sc, UCHCOM_REG_BREAK, brk, UCHCOM_REG_LCR, lcr);
        if (err)
                return EIO;
 
@@ -665,23 +676,50 @@ uchcom_set_dte_rate(struct uchcom_softc 
 int
 uchcom_set_line_control(struct uchcom_softc *sc, tcflag_t cflag)
 {
-       /*
-        * XXX: it is difficult to handle the line control appropriately:
-        *   work as chip default - CS8, no parity, !CSTOPB
-        *   other modes are not supported.
-        */
+       usbd_status err;
+       uint8_t lcr = 0, lcr2 = 0;
+
+       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)) {
        case CS5:
+               lcr |= UCHCOM_LCR_CS5;
+               break;
        case CS6:
+               lcr |= UCHCOM_LCR_CS6;
+               break;
        case CS7:
-               return EINVAL;
+               lcr |= UCHCOM_LCR_CS7;
+               break;
        case CS8:
+               lcr |= UCHCOM_LCR_CS8;
                break;
        }
 
-       if (ISSET(cflag, PARENB) || ISSET(cflag, CSTOPB))
-               return EINVAL;
+       if (ISSET(cflag, PARENB)) {
+               lcr |= UCHCOM_LCR_PARENB;
+               if (!ISSET(cflag, PARODD))
+                       lcr |= UCHCOM_LCR_PAREVEN;
+       }
+
+       if (ISSET(cflag, CSTOPB)) {
+               lcr |= UCHCOM_LCR_STOPB;
+       }
+
+       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;
+       }
 
        return 0;
 }

Reply via email to