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