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.
Happy Hacking,
Stefan
Common subdirectories: /home/src_orig/sys/dev/usb/CVS and
/usr/src/sys/dev/usb/CVS
Common subdirectories: /home/src_orig/sys/dev/usb/dwc2 and
/usr/src/sys/dev/usb/dwc2
diff /home/src_orig/sys/dev/usb/uplcom.c /usr/src/sys/dev/usb/uplcom.c
66c66
< #define DPRINTF(x) DPRINTFN(0, x)
---
#define DPRINTF(x) printf x;
71a72,74
#define UPLCOM_SET_NREQUEST 0x80
#define UPLCOM_READ_REQUEST 0x01
#define UPLCOM_READ_NREQUEST 0x81
77a81,91
#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
98a113
int sc_type_hxn; /* HXN variant */
108a124,127
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 *);
153a173
{ USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL23a3 },
250c270,272
< if (ddesc->bDeviceClass == 0x02)
---
printf("[DEBUG] DeviceClass: %x\n", ddesc->bDeviceClass);
printf("[DEBUG] MaxPacketSize: %x\n", ddesc->bMaxPacketSize);
if (ddesc->bDeviceClass == 0x02) { /* type 0 */
252c274,276
< else if (ddesc->bMaxPacketSize == 0x40)
---
sc->sc_type_hxn = 0;
}
else if (ddesc->bMaxPacketSize == 0x40) { /* type hx */
254c278,280
< else
---
sc->sc_type_hxn = 0;
}
else { /* probably type 1 */
255a282,283
sc->sc_type_hxn = 0;
}
256a285,298
/*
* The Linux driver also distinguishes HXN variant...
* Let's see if that is needed for the 23a3 variant.
*/
if (sc->sc_type_hx == 1) {
printf("[DEBUG] seems to be HX type. Checking for HXN type.\n");
if (uplcom_test_hxn(sc)) {
printf("[DEBUG] Probably HXN.\n");
sc->sc_type_hxn = 1;
} else {
printf("[DEBUG] Probably not HXN.\n");
}
}
259c301,303
< if (sc->sc_type_hx) {
---
if (sc->sc_type_hxn) {
DPRINTF(("uplcom_attach: chiptype 2303XN\n"));
} else if (sc->sc_type_hx) {
419a464,471
if (sc->sc_type_hxn) {
DPRINTF(("[DEBUG] trying to reset hxn\n"));
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);
421,425c473,484
< 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(("[DEBUG] uplcom_reset_hxn err=%d\n", err));
return (err);
}
} else {
DPRINTF(("[DEBUG] trying to reset non-hxn device\n"));
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
req.bRequest = UPLCOM_SET_REQUEST;
USETW(req.wValue, 8);
USETW(req.wIndex, 0);
USETW(req.wLength, 0);
427,429c486,495
< 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(("[DEBUG] uplcom_reset_8 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);
430a497,503
err = usbd_do_request(sc->sc_udev, &req, 0);
if (err) {
DPRINTF(("[DEBUG] uplcom_reset_9 err=%s\n",
usbd_errstr(err)));
return (err);
}
}
528a602,610
if (sc->sc_type_hxn) {
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);
530,541c612,617
< 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);
}
658c734,738
< if (sc->sc_type_hx == 1) {
---
if (sc->sc_type_hxn) {
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) {
768a849,984
}
/*
* 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) {
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) {
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 /home/src_orig/sys/dev/usb/usbdevs /usr/src/sys/dev/usb/usbdevs
3552a3553
product PROLIFIC PL23a3 0x23a3 PL2303 Serial
diff /home/src_orig/sys/dev/usb/usbdevs.h /usr/src/sys/dev/usb/usbdevs.h
1c1
< /* $OpenBSD: usbdevs.h,v 1.732 2020/08/03 14:25:56 deraadt Exp $ */
---
/* $OpenBSD$ */
3559a3560
#define USB_PRODUCT_PROLIFIC_PL23a3 0x23a3 /* PL2303 Serial */
diff /home/src_orig/sys/dev/usb/usbdevs_data.h
/usr/src/sys/dev/usb/usbdevs_data.h
1c1
< /* $OpenBSD: usbdevs_data.h,v 1.726 2020/08/03 14:25:56 deraadt Exp
$ */
---
/* $OpenBSD$ */
8777a8778,8781
"PL2303 Serial",
},
{
USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL23a3,