On 26.10.2020 09:30, Patrick Wildt wrote:
On Mon, Oct 26, 2020 at 09:12:17AM +0100, Stefan Huber wrote:
Hey all,

I recently got hands on an USB-TTY converter that is not (yet)
supported by
OpenBSD:

> addr 03: 067b:23a3 Prolific Technology Inc., USB-Serial Controller

Simply adding the device ID to usbdevs and the uplcom driver did not
do the
trick, as apparently it is what the linux driver calls a "HXN" chip,
which
is (as of now) not yet supported by the uplcom driver (also, not in
NetBSD
where the uplcom driver seems to come from). Some hours (days) later,
I have
produced the following diff, butchering both the uplcom and the linux
driver. I know it looks like shit, but it works (for now and for me).
I will
try to "streamline" the driver, and am happy for any feedback (since
you may
see watching the code I am not a good kernel hacker yet).

I was also thinking of adding all the other IDs from the linux driver
to
uplcom, but then I can't test them since I only have the two chips
067b:2303
and 067b:23a3. So I'd rather not merge the other IDs to prevent the
kernel
from doing nasty things to them (or was it the other way around?)

The diff's below. Works for me. Not looking for okay or a merge,
mostly
feedback and maybe a merge for a future version of this diff.

First of all, we need unified diffs.  Usually you have a CVS checkout
and then run cvs diff -u.  Yours don't look like you used cvs, was that
a diff -r?  Well, even then you should add -u, otherwise diffs become
unreadable.

Second, only OpenBSD account holders can ask for OKs. :)

So, please resend with diff -u, otherwise no one will have a look at it
since the diff is hard to follow.

Thanks,
Patrick

Thanks Patrick!

The unified diff is below:


diff -u /home/src_orig/sys/dev/usb/uplcom.c
/usr/src/sys/dev/usb/uplcom.c
--- /home/src_orig/sys/dev/usb/uplcom.c Fri Jul 31 12:49:33 2020
+++ /usr/src/sys/dev/usb/uplcom.c       Mon Oct 26 09:26:13 2020
@@ -69,12 +69,26 @@
  #define       UPLCOM_SECOND_IFACE_INDEX       1

  #define       UPLCOM_SET_REQUEST      0x01
+#define UPLCOM_SET_NREQUEST    0x80
+#define UPLCOM_READ_REQUEST    0x01
+#define UPLCOM_READ_NREQUEST   0x81
  #define       UPLCOM_SET_CRTSCTS      0x41
  #define       UPLCOM_HX_SET_CRTSCTS   0x61
  #define RSAQ_STATUS_CTS               0x80
  #define RSAQ_STATUS_DSR               0x02
  #define RSAQ_STATUS_DCD               0x01

+#define UPLCOM_READ_HX_STATUS                  0x8080
+#define UPLCOM_HXN_RESET_REG                   0x07
+#define UPLCOM_HXN_RESET_UPSTREAM_PIPE         0x02
+#define UPLCOM_HXN_RESET_DOWNSTREAM_PIPE       0x01
+
+#define UPLCOM_HXN_FLOWCTRL_REG                        0x0a
+#define UPLCOM_HXN_FLOWCTRL_MASK               0x1c
+#define UPLCOM_HXN_FLOWCTRL_NONE               0x1c
+#define UPLCOM_HXN_FLOWCTRL_RTS_CTS            0x18
+#define UPLCOM_HXN_FLOWCTRL_XON_XOFF           0x0c
+
  struct        uplcom_softc {
        struct device            sc_dev;        /* base device */
        struct usbd_device      *sc_udev;       /* USB device */
@@ -96,6 +110,7 @@
        u_char                   sc_lsr;        /* Local status register */
        u_char                   sc_msr;        /* uplcom status register */
        int                      sc_type_hx;    /* HX variant */
+       int                      sc_type_hxn;   /* HXN variant */
  };

  /*
@@ -106,6 +121,10 @@
  #define UPLCOMOBUFSIZE 256

  usbd_status uplcom_reset(struct uplcom_softc *);
+usbd_status uplcom_update_reg(struct uplcom_softc *sc, char reg, char
mask, char val);
+usbd_status uplcom_vendor_read(struct uplcom_softc *sc, char val, char
buf[1]);
+usbd_status uplcom_vendor_write(struct uplcom_softc *sc, char val, char
index);
+int uplcom_test_hxn(struct uplcom_softc *);
  usbd_status uplcom_set_line_coding(struct uplcom_softc *sc,
      struct usb_cdc_line_state *state);
  usbd_status uplcom_set_crtscts(struct uplcom_softc *);
@@ -151,6 +170,7 @@
        { USB_VENDOR_PLX, USB_PRODUCT_PLX_CA42 },
        { USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_TYTP50P6S },
        { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303 },
+       { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL23a3 },
        { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303X },
        { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303X2 },
        { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2 },
@@ -247,16 +267,34 @@
         * The Linux driver suggest this will only be true for the HX
         * variants. The datasheets disagree.
         */
-       if (ddesc->bDeviceClass == 0x02)
+       if (ddesc->bDeviceClass == 0x02) { /* type 0 */
                sc->sc_type_hx = 0;
-       else if (ddesc->bMaxPacketSize == 0x40)
+               sc->sc_type_hxn = 0;
+       }
+       else if (ddesc->bMaxPacketSize == 0x40) { /* type hx */
                sc->sc_type_hx = 1;
-       else
+               sc->sc_type_hxn = 0;
+       }
+       else { /* probably type 1 */
                sc->sc_type_hx = 0;
+               sc->sc_type_hxn = 0;
+       }

+       /*
+        * The Linux driver also distinguishes HXN variant...
+        * Let's see if that is needed for the 23a3 variant.
+        */
+       if (sc->sc_type_hx == 1) {
+               if (uplcom_test_hxn(sc)) {
+                       sc->sc_type_hxn = 1;
+               }
+       }
+
  #ifdef UPLCOM_DEBUG
        /* print the chip type */
-       if (sc->sc_type_hx) {
+       if (sc->sc_type_hxn == 1) {
+               DPRINTF(("uplcom_attach: chiptype 2303XN\n"));
+       } else if (sc->sc_type_hx == 1) {
                DPRINTF(("uplcom_attach: chiptype 2303X\n"));
        } else {
                DPRINTF(("uplcom_attach: chiptype 2303\n"));
@@ -417,17 +455,44 @@
  {
        usb_device_request_t req;
        usbd_status err;
+
+       if (sc->sc_type_hxn == 1) {
+               req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+               req.bRequest = UPLCOM_SET_NREQUEST;
+               USETW(req.wValue, 0x07);
+               USETW(req.wIndex, UPLCOM_HXN_RESET_UPSTREAM_PIPE |
UPLCOM_HXN_RESET_DOWNSTREAM_PIPE);
+               USETW(req.wLength, 0);

-       req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
-       req.bRequest = UPLCOM_SET_REQUEST;
-       USETW(req.wValue, 0);
-       USETW(req.wIndex, sc->sc_iface_number);
-       USETW(req.wLength, 0);
+               err = usbd_do_request(sc->sc_udev, &req, 0);
+               if (err) {
+                       DPRINTF(("uplcom_reset err=%s\n", usbd_errstr(err)));
+                       return (err);
+               }
+       } else {
+               req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+               req.bRequest = UPLCOM_SET_REQUEST;
+               USETW(req.wValue, 8);
+               USETW(req.wIndex, 0);
+               USETW(req.wLength, 0);

-       err = usbd_do_request(sc->sc_udev, &req, 0);
-       if (err)
-               return (EIO);
+               err = usbd_do_request(sc->sc_udev, &req, 0);
+               if (err) {
+                       DPRINTF(("uplcom_reset err=%s\n", usbd_errstr(err)));
+                       return (err);
+               }
+               req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+               req.bRequest = UPLCOM_SET_REQUEST;
+               USETW(req.wValue, 9);
+               USETW(req.wIndex, 0);
+               USETW(req.wLength, 0);

+               err = usbd_do_request(sc->sc_udev, &req, 0);
+               if (err) {
+                       DPRINTF(("uplcom_reset err=%s\n", usbd_errstr(err)));
+                       return (err);
+               }
+       }
+
        return (0);
  }

@@ -526,19 +591,22 @@
        usbd_status err;

        DPRINTF(("uplcom_set_crtscts: on\n"));
+       if (sc->sc_type_hxn == 1) {
+               uplcom_update_reg(sc, UPLCOM_HXN_FLOWCTRL_REG,
UPLCOM_HXN_FLOWCTRL_MASK, UPLCOM_HXN_FLOWCTRL_XON_XOFF);
+       } else {
+               req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+               req.bRequest = UPLCOM_SET_REQUEST;
+               USETW(req.wValue, 0);
+               USETW(req.wIndex,
+                   (sc->sc_type_hx ? UPLCOM_HX_SET_CRTSCTS : 
UPLCOM_SET_CRTSCTS));
+               USETW(req.wLength, 0);

-       req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
-       req.bRequest = UPLCOM_SET_REQUEST;
-       USETW(req.wValue, 0);
-       USETW(req.wIndex,
-           (sc->sc_type_hx ? UPLCOM_HX_SET_CRTSCTS : UPLCOM_SET_CRTSCTS));
-       USETW(req.wLength, 0);
-
-       err = usbd_do_request(sc->sc_udev, &req, 0);
-       if (err) {
-               DPRINTF(("uplcom_set_crtscts: failed, err=%s\n",
-                       usbd_errstr(err)));
-               return (err);
+               err = usbd_do_request(sc->sc_udev, &req, 0);
+               if (err) {
+                       DPRINTF(("uplcom_set_crtscts: failed, err=%s\n",
+                               usbd_errstr(err)));
+                       return (err);
+               }
        }

        return (USBD_NORMAL_COMPLETION);
@@ -655,7 +723,11 @@
                }
        }

-       if (sc->sc_type_hx == 1) {
+       if (sc->sc_type_hxn == 1) {
+               uplcom_vendor_write(sc, UPLCOM_HXN_RESET_REG,
+                       UPLCOM_HXN_RESET_UPSTREAM_PIPE |
+                       UPLCOM_HXN_RESET_DOWNSTREAM_PIPE);
+       } else if (sc->sc_type_hx == 1) {
                /*
                 * Undocumented (vendor unresponsive) - possibly changes
                 * flow control semantics. It is needed for HX variant devices.
@@ -766,4 +838,140 @@
                *lsr = sc->sc_lsr;
        if (msr != NULL)
                *msr = sc->sc_msr;
+}
+
+/*
+ * uplcom_test_hxn()
+ * will return 1 if attached device is HXN device,
+ * will return 0 else.
+ */
+int
+uplcom_test_hxn(struct uplcom_softc *sc)
+{
+       /*
+        * res = usb_control_msg(serial->dev,
+        *      usb_rcvctrlpipe(serial->dev, 0),
+        *      VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
+        *      PL2303_READ_TYPE_HX_STATUS, 0, buf, 1, 100);
+        * if (res != 1)
+        *      type = TYPE_HXN;
+        *
+        * int usb_control_msg(
+        *      struct usb_device * dev,
+        *      unsigned int pipe,
+        *      __u8 request,
+        *      __u8 requesttype,
+        *      __u16 value,
+        *      __u16 index,
+        *      void * data,
+        *      __u16 size,
+        *      int timeout);
+        *
+        * VENDOR_READ_REQUEST          0x01
+        * VENDOR_READ_REQUEST_TYPE     0xc0
+        * PL2303_READ_TYPE_HX_STATUS   0x8080
+        */
+       usb_device_request_t req;
+       usbd_status err;
+
+       req.bmRequestType = 0xc0;
+       req.bRequest = 0x01;
+       USETW(req.wValue, 0x8080);
+       USETW(req.wIndex, 0);
+       USETW(req.wLength, 1);
+
+       void* buf = malloc(1, M_USBDEV, M_WAITOK);
+       err = usbd_do_request(sc->sc_udev, &req, buf);
+       DPRINTF(("uplcom_test_hxn buf: %02x\n", (*(uint8_t*) buf)));
+       if (err) {
+               /* If it stalls, it's HXN */
+               DPRINTF(("uplcom_test_hxn err=%s\n", usbd_errstr(err)));
+               return (1);
+       }
+
+       return (0);
+}
+
+usbd_status
+uplcom_update_reg(struct uplcom_softc *sc, char reg, char mask, char
val)
+{
+       char* buf = malloc(1, M_USBDEV, M_WAITOK);
+       usbd_status err;
+       /* read value */
+       if (sc->sc_type_hxn == 1) {
+               err = uplcom_vendor_read(sc, reg, buf);
+               if (err) {
+                       DPRINTF(("%s: err=%s\n", __func__, usbd_errstr(err)));
+                       return (err);
+               }
+       } else {
+               err = uplcom_vendor_read(sc, reg | 0x80, buf);
+               if (err) {
+                       DPRINTF(("%s: err=%s\n", __func__, usbd_errstr(err)));
+                       return (err);
+               }
+       }
+
+       /* update value */
+       *buf &= ~mask;
+       *buf |= val & mask;
+
+       /* write new value */
+       err = uplcom_vendor_write(sc, reg, *buf);
+       if (err) {
+               DPRINTF(("%s: err=%s\n", __func__, usbd_errstr(err)));
+               return (err);
+       }
+
+       return USBD_NORMAL_COMPLETION;
+}
+
+usbd_status
+uplcom_vendor_read(struct uplcom_softc *sc, char val, char buf[1])
+{
+       usbd_status err;
+       usb_device_request_t req;
+       req.bmRequestType = UT_READ_VENDOR_DEVICE;
+       if (sc->sc_type_hxn == 1) {
+               req.bRequest = UPLCOM_READ_NREQUEST;
+       } else {
+               req.bRequest = UPLCOM_READ_REQUEST;
+       }
+       USETW(req.wValue, val);
+       USETW(req.wIndex, 0); /* read: index is always zero */
+       USETW(req.wLength, 1); /* read: read always one byte */
+
+       err = usbd_do_request(sc->sc_udev, &req, &buf);
+       if (err) {
+               DPRINTF(("%s: failed to read: %s\n",
+                       __func__, usbd_errstr(err)));
+               return (err);
+       }
+
+       return USBD_NORMAL_COMPLETION; /* should return 0 if everything worked
*/
+}
+
+usbd_status
+uplcom_vendor_write(struct uplcom_softc *sc, char val, char index)
+{
+       usbd_status err;
+       usb_device_request_t req;
+       req.bmRequestType = UT_READ_VENDOR_DEVICE;
+       if (sc->sc_type_hxn) {
+               req.bRequest = UPLCOM_SET_NREQUEST;
+       } else {
+               req.bRequest = UPLCOM_SET_REQUEST;
+       }
+       USETW(req.wValue, val);
+       USETW(req.wIndex, index);
+       USETW(req.wLength, 0);
+
+       err = usbd_do_request(sc->sc_udev, &req, 0);
+       if (err) {
+               DPRINTF(("%s: failed to write: %s\n",
+                       __func__, usbd_errstr(err)));
+               return (err);
+       }
+
+       return USBD_NORMAL_COMPLETION; /* should return 0 if everything worked
*/
  }
diff -u /home/src_orig/sys/dev/usb/usbdevs /usr/src/sys/dev/usb/usbdevs
--- /home/src_orig/sys/dev/usb/usbdevs  Mon Aug  3 16:25:44 2020
+++ /usr/src/sys/dev/usb/usbdevs        Fri Oct 23 09:27:49 2020
@@ -3550,6 +3550,7 @@
  product PROLIFIC RSAQ2                0x04bb  PL2303 Serial
  product PROLIFIC PL2303BENQ   0x0609  PL2303 Serial
  product PROLIFIC PL2303               0x2303  PL2303 Serial
+product PROLIFIC PL23a3                0x23a3  PL2303 Serial
  product PROLIFIC PL2305               0x2305  Parallel printer
  product PROLIFIC ATAPI4               0x2307  ATAPI-4 Bridge Controller
  product PROLIFIC PL2501               0x2501  PL2501 Host-Host

Reply via email to