uplcom.c (PL2303X usb-to-serial) fix
This patch fixes the uplcom driver to work with newer PL2303X-based
devices. It works for me. (Taken from FreeBSD)
- Rahul
--- sys/dev/usbmisc/uplcom/uplcom.c.orig 2005-03-21 19:04:18.000000000 +0530
+++ sys/dev/usbmisc/uplcom/uplcom.c 2005-03-27 19:11:14.000000000 +0530
@@ -67,13 +67,26 @@
*/
/*
- * Simple datasheet
- * http://www.prolific.com.tw/download/DataSheet/pl2303_ds11.PDF
- * http://www.nisseisg.co.jp/jyouhou/_cp/@gif/2303.pdf
- * (english)
+ * This driver supports several USB-to-RS232 serial adapters driven by
+ * Prolific PL-2303, PL-2303X and probably PL-2303HX USB-to-RS232
+ * bridge chip. The adapters are sold under many different brand
+ * names.
*
+ * Datasheets are available at Prolific www site at
+ * http://www.prolific.com.tw. The datasheets don't contain full
+ * programming information for the chip.
+ *
+ * PL-2303HX is probably programmed the same as PL-2303X.
+ *
+ * There are several differences between PL-2303 and PL-2303(H)X.
+ * PL-2303(H)X can do higher bitrate in bulk mode, has _probably_
+ * different command for controlling CRTSCTS and needs special
+ * sequence of commands for initialization which aren't also
+ * documented in the datasheet.
*/
+/* #include "opt_uplcom.h" */
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
@@ -94,10 +107,13 @@
#include <sys/poll.h>
#include <sys/sysctl.h>
+#include <machine/bus.h>
+
#include <bus/usb/usb.h>
#include <bus/usb/usbcdc.h>
#include <bus/usb/usbdi.h>
+#include <bus/usb/usbdivar.h>
#include <bus/usb/usbdi_util.h>
#include <bus/usb/usbdevs.h>
#include <bus/usb/usb_quirks.h>
@@ -131,9 +147,13 @@
#define UPLCOM_SET_REQUEST 0x01
#define UPLCOM_SET_CRTSCTS 0x41
+#define UPLCOM_SET_CRTSCTS_PL2303X 0x61
#define RSAQ_STATUS_DSR 0x02
#define RSAQ_STATUS_DCD 0x01
+#define TYPE_PL2303 0
+#define TYPE_PL2303X 1
+
struct uplcom_softc {
struct ucom_softc sc_ucom;
@@ -152,6 +172,8 @@
u_char sc_lsr; /* Local status register */
u_char sc_msr; /* uplcom status register */
+
+ int sc_chiptype; /* Type of chip */
};
/*
@@ -194,27 +216,40 @@
static const struct uplcom_product {
uint16_t vendor;
uint16_t product;
+ int32_t release; /* release is a 16bit entity,
+ * if -1 is specified we "don't care"
+ */
+ char chiptype;
} uplcom_products [] = {
/* I/O DATA USB-RSAQ */
- { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ },
+ { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ, -1, TYPE_PL2303 },
/* I/O DATA USB-RSAQ2 */
- { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2 },
+ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ2, -1, TYPE_PL2303 },
/* PLANEX USB-RS232 URS-03 */
- { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A },
- /* IOGEAR/ATEN UC-232A */
- { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303 },
+ { USB_VENDOR_ATEN, USB_PRODUCT_ATEN_UC232A, -1, TYPE_PL2303 },
+ /* ST Lab USB-SERIAL-4 */
+ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303,
+ 0x300, TYPE_PL2303X },
+ /* IOGEAR/ATEN UC-232A (also ST Lab USB-SERIAL-1) */
+ { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, -1, TYPE_PL2303 },
/* TDK USB-PHS Adapter UHA6400 */
- { USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400 },
+ { USB_VENDOR_TDK, USB_PRODUCT_TDK_UHA6400, -1, TYPE_PL2303 },
/* RATOC REX-USB60 */
- { USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60 },
+ { USB_VENDOR_RATOC, USB_PRODUCT_RATOC_REXUSB60, -1, TYPE_PL2303 },
/* ELECOM UC-SGT */
- { USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT },
+ { USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT, -1, TYPE_PL2303 },
+ { USB_VENDOR_ELECOM, USB_PRODUCT_ELECOM_UCSGT0, -1, TYPE_PL2303 },
+ /* Sony Ericsson USB Cable */
+ { USB_VENDOR_SUSTEEN, USB_PRODUCT_SUSTEEN_DCU10,
+ -1,TYPE_PL2303 },
/* SOURCENEXT KeikaiDenwa 8 */
- { USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8 },
+ { USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8,
+ -1, TYPE_PL2303 },
/* SOURCENEXT KeikaiDenwa 8 with charger */
- { USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG },
+ { USB_VENDOR_SOURCENEXT, USB_PRODUCT_SOURCENEXT_KEIKAI8_CHG,
+ -1, TYPE_PL2303 },
/* HAL Corporation Crossam2+USB */
- { USB_VENDOR_HAL, USB_PRODUCT_HAL_IMR001 },
+ { USB_VENDOR_HAL, USB_PRODUCT_HAL_IMR001, -1, TYPE_PL2303 },
{ 0, 0 }
};
@@ -241,7 +276,7 @@
MODULE_DEPEND(uplcom, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
MODULE_VERSION(uplcom, UPLCOM_MODVER);
-static int uplcominterval = UPLCOM_INTR_INTERVAL;
+static int uplcominterval = UPLCOM_INTR_INTERVAL;
static int
sysctl_hw_usb_uplcom_interval(SYSCTL_HANDLER_ARGS)
@@ -261,8 +296,8 @@
}
SYSCTL_PROC(_hw_usb_uplcom, OID_AUTO, interval, CTLTYPE_INT | CTLFLAG_RW,
- 0, sizeof(int), sysctl_hw_usb_uplcom_interval,
- "I", "uplcom interrpt pipe interval");
+ 0, sizeof(int), sysctl_hw_usb_uplcom_interval,
+ "I", "uplcom interrupt pipe interval");
USB_MATCH(uplcom)
{
@@ -274,7 +309,9 @@
for (i = 0; uplcom_products[i].vendor != 0; i++) {
if (uplcom_products[i].vendor == uaa->vendor &&
- uplcom_products[i].product == uaa->product) {
+ uplcom_products[i].product == uaa->product &&
+ (uplcom_products[i].release == uaa->release ||
+ uplcom_products[i].release == -1)) {
return (UMATCH_VENDOR_PRODUCT);
}
}
@@ -294,7 +331,7 @@
usbd_status err;
int i;
- devinfo = malloc(1024, M_USBDEV, M_INTWAIT);
+ devinfo = malloc(1024, M_USBDEV, M_WAITOK);
ucom = &sc->sc_ucom;
bzero(sc, sizeof (struct uplcom_softc));
@@ -313,6 +350,36 @@
DPRINTF(("uplcom attach: sc = %p\n", sc));
+ /* determine chip type */
+ for (i = 0; uplcom_products[i].vendor != 0; i++) {
+ if (uplcom_products[i].vendor == uaa->vendor &&
+ uplcom_products[i].product == uaa->product &&
+ (uplcom_products[i].release == uaa->release ||
+ uplcom_products[i].release == -1)) {
+ sc->sc_chiptype = uplcom_products[i].chiptype;
+ break;
+ }
+ }
+
+ /*
+ * check we found the device - attach should have ensured we
+ * don't get here without matching device
+ */
+ if (uplcom_products[i].vendor == 0) {
+ printf("%s: didn't match\n", devname);
+ ucom->sc_dying = 1;
+ goto error;
+ }
+
+#ifdef USB_DEBUG
+ /* print the chip type */
+ if (sc->sc_chiptype == TYPE_PL2303X) {
+ DPRINTF(("uplcom_attach: chiptype 2303X\n"));
+ } else {
+ DPRINTF(("uplcom_attach: chiptype 2303\n"));
+ }
+#endif
+
/* initialize endpoints */
ucom->sc_bulkin_no = ucom->sc_bulkout_no = -1;
sc->sc_intr_number = -1;
@@ -514,6 +581,55 @@
return (0);
}
+struct pl2303x_init {
+ uint8_t req_type;
+ uint8_t request;
+ uint16_t value;
+ uint16_t index;
+ uint16_t length;
+};
+
+Static const struct pl2303x_init pl2303x[] = {
+ { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 0 },
+ { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 0, 0 },
+ { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 0 },
+ { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 0 },
+ { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 0 },
+ { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 1, 0 },
+ { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 0 },
+ { UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 0 },
+ { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0, 1, 0 },
+ { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 1, 0, 0 },
+ { UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x44, 0 }
+};
+#define N_PL2302X_INIT (sizeof(pl2303x)/sizeof(pl2303x[0]))
+
+Static usbd_status
+uplcom_pl2303x_init(struct uplcom_softc *sc)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ int i;
+
+ for (i = 0; i < N_PL2302X_INIT; i++) {
+ req.bmRequestType = pl2303x[i].req_type;
+ req.bRequest = pl2303x[i].request;
+ USETW(req.wValue, pl2303x[i].value);
+ USETW(req.wIndex, pl2303x[i].index);
+ USETW(req.wLength, pl2303x[i].length);
+
+ err = usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
+ if (err) {
+ printf("%s: uplcom_pl2303x_init: %d: %s\n",
+ USBDEVNAME(sc->sc_ucom.sc_dev), i,
+ usbd_errstr(err));
+ return (EIO);
+ }
+ }
+
+ return (0);
+}
+
Static void
uplcom_set_line_state(struct uplcom_softc *sc)
{
@@ -610,7 +726,10 @@
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
req.bRequest = UPLCOM_SET_REQUEST;
USETW(req.wValue, 0);
- USETW(req.wIndex, UPLCOM_SET_CRTSCTS);
+ if (sc->sc_chiptype == TYPE_PL2303X)
+ USETW(req.wIndex, UPLCOM_SET_CRTSCTS_PL2303X);
+ else
+ USETW(req.wIndex, UPLCOM_SET_CRTSCTS);
USETW(req.wLength, 0);
err = usbd_do_request(sc->sc_ucom.sc_udev, &req, 0);
@@ -657,15 +776,36 @@
return (USBD_NORMAL_COMPLETION);
}
+Static const int uplcom_rates[] = {
+ 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400,
+ 19200, 28800, 38400, 57600, 115200,
+ /*
+ * Higher speeds are probably possible. PL2303X supports up to
+ * 6Mb and can set any rate
+ */
+ 230400, 460800, 614400, 921600, 1228800
+};
+#define N_UPLCOM_RATES (sizeof(uplcom_rates)/sizeof(uplcom_rates[0]))
+
Static int
uplcom_param(void *addr, int portno, struct termios *t)
{
struct uplcom_softc *sc = addr;
usbd_status err;
usb_cdc_line_state_t ls;
+ int i;
DPRINTF(("uplcom_param: sc = %p\n", sc));
+ /* Check requested baud rate */
+ for (i = 0; i < N_UPLCOM_RATES; i++)
+ if (uplcom_rates[i] == t->c_ospeed)
+ break;
+ if (i == N_UPLCOM_RATES) {
+ DPRINTF(("uplcom_param: bad baud rate (%d)\n", t->c_ospeed));
+ return (EIO);
+ }
+
USETDW(ls.dwDTERate, t->c_ospeed);
if (ISSET(t->c_cflag, CSTOPB))
ls.bCharFormat = UCDC_STOP_BIT_2;
@@ -737,6 +877,9 @@
}
}
+ if (sc->sc_chiptype == TYPE_PL2303X)
+ return (uplcom_pl2303x_init(sc));
+
return (0);
}