Re: fix uchcom(4) handling of parity and character size config
Am Wed, Oct 27, 2021 at 06:46:38AM +1000 schrieb David Gwynne: > 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. ok patrick@ > 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 - 1.28 > +++ uchcom.c 26 Oct 2021 20:33:36 - > @@ -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_BREAK10x05 > -#define UCHCOM_REG_BREAK20x18 > -#define UCHCOM_REG_LCR1 0x18 > +#define UCHCOM_REG_BREAK 0x05 > +#define UCHCOM_REG_LCR 0x18 > #define UCHCOM_REG_LCR2 0x25 > > #define UCHCOM_VER_200x20 > @@ -83,11 +82,25 @@ int uchcomdebug = 0; > #define UCHCOM_BPS_MOD_BASE 2000 > #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_MASK0x01 > + > +#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_PARENB0x08 > +#define UCHCOM_LCR_PARODD0x00 > +#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_STAT10x02 > #define UCHCOM_INTR_STAT20x03 > @@ -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, , UCHCOM_REG_BREAK2, > - ); > + err = uchcom_read_reg(sc, UCHCOM_REG_BREAK, , UCHCOM_REG_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, , > + UCHCOM_REG_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; > + } >
Re: fix uchcom(4) handling of parity and character size config
On Wed, Oct 27, 2021 at 06:46:38AM +1000, David Gwynne wrote: Hi David, > 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. I use uchcom(4) to connect with 115200 to the pinebook pro. I see no regressions with your diff, works fine. felix
fix uchcom(4) handling of parity and character size config
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.c31 Jul 2020 10:49:33 - 1.28 +++ uchcom.c26 Oct 2021 20:33:36 - @@ -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_LCR10x18 +#define UCHCOM_REG_BREAK 0x05 +#define UCHCOM_REG_LCR 0x18 #define UCHCOM_REG_LCR20x25 #define UCHCOM_VER_20 0x20 @@ -83,11 +82,25 @@ int uchcomdebug = 0; #define UCHCOM_BPS_MOD_BASE2000 #define UCHCOM_BPS_MOD_BASE_OFS1100 +#define UCHCOM_BPS_PRE_IMM 0x80/* CH341: immediate RX forwarding */ + #define UCHCOM_DTR_MASK0x20 #define UCHCOM_RTS_MASK0x40 -#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_PARSPACE0x30 +#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, , UCHCOM_REG_BREAK2, - ); + err = uchcom_read_reg(sc, UCHCOM_REG_BREAK, , UCHCOM_REG_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, , + UCHCOM_REG_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)); +