Sync the uplcom(4) driver with FreeBSD.
authorSascha Wildner <swildner@dragonflybsd.org>
Mon, 13 Nov 2006 21:39:19 +0000 (21:39 +0000)
committerSascha Wildner <swildner@dragonflybsd.org>
Mon, 13 Nov 2006 21:39:19 +0000 (21:39 +0000)
Submitted-by: Johannes Hofmann <Johannes.Hofmann@gmx.de>
Reviewed-by: dillon
I did some minor fixes and synced the manpage, too.

share/man/man4/uplcom.4
sys/dev/usbmisc/uplcom/uplcom.c

index de97020..9dc1ff6 100644 (file)
 .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $FreeBSD: src/share/man/man4/uplcom.4,v 1.3.2.1 2002/08/08 17:59:26 joe Exp $
-.\" $DragonFly: src/share/man/man4/uplcom.4,v 1.3 2003/11/06 14:36:39 asmodai Exp $
+.\" $FreeBSD: src/share/man/man4/uplcom.4,v 1.15 2006/08/20 05:56:00 imp Exp $
+.\" $DragonFly: src/share/man/man4/uplcom.4,v 1.4 2006/11/13 21:39:19 swildner Exp $
 .\"
-.Dd May 21, 2001
+.Dd November 13, 2006
 .Dt UPLCOM 4
 .Os
 .Sh NAME
 .Nm uplcom
-.Nd USB support for Prolific PL-2303 serial adapters driver
+.Nd USB support for Prolific PL-2303/2303X/2303HX serial adapters driver
 .Sh SYNOPSIS
-.Cd "uplcom* at uhub?"
-.Cd "ucom*   at uplcom?"
+.Cd "device uplcom"
+.Cd "device ucom"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for various serial adapters based on the Prolific
+PL-2303, PL-2303X and PL-2303HX USB-to-RS232 Bridge chips.
+.Pp
+The device is accessed through the
+.Xr ucom 4
+driver which makes it behave like a
+.Xr tty 4 .
 .Sh HARDWARE
 The
 .Nm
 driver supports the following adapters:
 .Pp
-.Bl -item -offset indent -compact
+.Bl -bullet -compact
 .It
 ATEN UC-232A
 .It
@@ -59,27 +69,36 @@ BAFO BF-800
 .It
 BAFO BF-810
 .It
-Bandridge CPL-4201
-.It
 ELECOM UC-SGT
 .It
+HAL Corporation Crossam2+USB IR commander
+.It
+Hama USB RS-232 Serial Adapter
+.It
+Hamlet exagerate XURS232
+.It
 IOGEAR UC-232A
 .It
 I/O DATA USB-RSAQ
 .It
 I/O DATA USB-RSAQ2
 .It
+I/O DATA USB-RSAQ3
+.It
 PLANEX USB-RS232 URS-03
+.It
+RATOC REX-USB60
+.It
+Radio Shack USB Serial Cable
+.It
+Sandberg USB to Serial Link (model number 133-08)
+.It
+SOURCENEXT KeikaiDenwa 8 (with and without charger)
+.It
+Sony Ericsson USB Cable (Susteen USB Data Cable)
+.It
+Willcom W-SIM DD PHS terminal.(WS002IN)
 .El
-.Sh DESCRIPTION
-The
-.Nm
-driver provides support for Prolific PL-2303 USB-to-RS232 Bridge chip.
-.Pp
-The device is accessed through the
-.Xr ucom 4
-driver which makes it behave like a
-.Xr tty 4 .
 .Sh SEE ALSO
 .Xr tty 4 ,
 .Xr ucom 4 ,
index 025a62d..34eaa89 100644 (file)
@@ -1,11 +1,11 @@
 /*
  * $NetBSD: uplcom.c,v 1.21 2001/11/13 06:24:56 lukem Exp $
- * $FreeBSD: src/sys/dev/usb/uplcom.c,v 1.17 2003/11/16 13:13:16 akiyama Exp $
- * $DragonFly: src/sys/dev/usbmisc/uplcom/uplcom.c,v 1.8 2006/09/05 00:55:44 dillon Exp $
+ * $FreeBSD: src/sys/dev/usb/uplcom.c,v 1.39 2006/09/07 00:06:42 imp Exp $
+ * $DragonFly: src/sys/dev/usbmisc/uplcom/uplcom.c,v 1.9 2006/11/13 21:39:19 swildner Exp $
  */
 
 /*-
- * Copyright (c) 2001-2002, Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  */
 
 /*
- * 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 <sys/param.h>
 #include <sys/vnode.h>
 #include <sys/poll.h>
 #include <sys/sysctl.h>
+#include <sys/taskqueue.h>
 
 #include <bus/usb/usb.h>
 #include <bus/usb/usbcdc.h>
@@ -131,9 +143,14 @@ SYSCTL_INT(_hw_usb_uplcom, OID_AUTO, debug, CTLFLAG_RW,
 
 #define        UPLCOM_SET_REQUEST              0x01
 #define        UPLCOM_SET_CRTSCTS              0x41
+#define        UPLCOM_SET_CRTSCTS_PL2303X      0x61
+#define RSAQ_STATUS_CTS                        0x80
 #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 +169,10 @@ struct     uplcom_softc {
 
        u_char                  sc_lsr;         /* Local status register */
        u_char                  sc_msr;         /* uplcom status register */
+
+       int                     sc_chiptype;    /* Type of chip */
+
+       struct task             sc_task;
 };
 
 /*
@@ -173,12 +194,13 @@ Static    void uplcom_rts(struct uplcom_softc *, int);
 Static void uplcom_break(struct uplcom_softc *, int);
 Static void uplcom_set_line_state(struct uplcom_softc *);
 Static void uplcom_get_status(void *, int, u_char *, u_char *);
-#if TODO
+#if 0 /* TODO */
 Static int  uplcom_ioctl(void *, int, u_long, caddr_t, int, usb_proc_ptr);
 #endif
 Static int  uplcom_param(void *, int, struct termios *);
 Static int  uplcom_open(void *, int);
 Static void uplcom_close(void *, int);
+Static void uplcom_notify(void *, int);
 
 struct ucom_callback uplcom_callback = {
        uplcom_get_status,
@@ -194,27 +216,57 @@ struct ucom_callback uplcom_callback = {
 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"
+                                         * This is a floor value.  The table
+                                         * must have newer revs before older
+                                         * revs (and -1 last).
+                                         */
+       char            chiptype;
 } uplcom_products [] = {
+       { USB_VENDOR_RADIOSHACK, USB_PRODUCT_RADIOSHACK_USBCABLE, -1, TYPE_PL2303 },
+
        /* I/O DATA USB-RSAQ */
-       { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ },
+       { USB_VENDOR_IODATA, USB_PRODUCT_IODATA_USBRSAQ, -1, TYPE_PL2303 },
+       /* Prolific Pharos */
+       { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303X, -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 },
+       /* I/O DATA USB-RSAQ3 */
+       { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_RSAQ3, -1, TYPE_PL2303X },
+       /* Willcom W-SIM */
+       { USB_VENDOR_PROLIFIC2, USB_PRODUCT_PROLIFIC2_WSIM, -1, TYPE_PL2303X},
        /* 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 },
+       /* HAMLET exagerate XURS232 */
+       { USB_VENDOR_PROLIFIC, USB_PRODUCT_PROLIFIC_PL2303, -1, TYPE_PL2303X },
        /* 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 },
+       /* Sitecom USB to Serial */
+       { USB_VENDOR_MCT, USB_PRODUCT_MCT_SITECOM_USB232, -1, TYPE_PL2303 },
+       /* Tripp-Lite U209-000-R */
+       { USB_VENDOR_TRIPPLITE, USB_PRODUCT_TRIPPLITE_U209, -1, TYPE_PL2303X },
        { 0, 0 }
 };
 
@@ -241,7 +293,7 @@ MODULE_DEPEND(uplcom, usb, 1, 1, 1);
 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 +313,8 @@ sysctl_hw_usb_uplcom_interval(SYSCTL_HANDLER_ARGS)
 }
 
 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 +326,9 @@ USB_MATCH(uplcom)
 
        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);
                }
        }
@@ -313,6 +367,36 @@ USB_ATTACH(uplcom)
 
        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;
@@ -461,6 +545,7 @@ USB_ATTACH(uplcom)
        DPRINTF(("uplcom: in = 0x%x, out = 0x%x, intr = 0x%x\n",
                 ucom->sc_bulkin_no, ucom->sc_bulkout_no, sc->sc_intr_number));
 
+       TASK_INIT(&sc->sc_task, 0, uplcom_notify, sc);
        ucom_attach(&sc->sc_ucom);
 
        kfree(devinfo, M_USBDEV);
@@ -514,7 +599,56 @@ uplcom_reset(struct uplcom_softc *sc)
        return (0);
 }
 
-Static void
+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",
+                               device_get_nameunit(sc->sc_ucom.sc_dev), i,
+                               usbd_errstr(err));
+                       return (EIO);
+               }
+       }
+
+       return (0);
+}
+
+static void
 uplcom_set_line_state(struct uplcom_softc *sc)
 {
        usb_device_request_t req;
@@ -610,7 +744,10 @@ uplcom_set_crtscts(struct uplcom_softc *sc)
        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 +794,36 @@ uplcom_set_line_coding(struct uplcom_softc *sc, usb_cdc_line_state_t *state)
        return (USBD_NORMAL_COMPLETION);
 }
 
-Static int
+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 +895,9 @@ uplcom_open(void *addr, int portno)
                }
        }
 
+       if (sc->sc_chiptype == TYPE_PL2303X)
+               return (uplcom_pl2303x_init(sc));
+
        return (0);
 }
 
@@ -793,10 +954,31 @@ uplcom_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
 
        sc->sc_lsr = sc->sc_msr = 0;
        pstatus = buf[8];
+       if (ISSET(pstatus, RSAQ_STATUS_CTS))
+               sc->sc_msr |= UMSR_CTS;
+       else
+               sc->sc_msr &= ~UMSR_CTS;
        if (ISSET(pstatus, RSAQ_STATUS_DSR))
                sc->sc_msr |= UMSR_DSR;
+       else
+               sc->sc_msr &= ~UMSR_DSR;
        if (ISSET(pstatus, RSAQ_STATUS_DCD))
                sc->sc_msr |= UMSR_DCD;
+       else
+               sc->sc_msr &= ~UMSR_DCD;
+
+       /* Deferred notifying to the ucom layer */
+       taskqueue_enqueue(taskqueue_swi, &sc->sc_task);
+}
+
+static void
+uplcom_notify(void *arg, int count)
+{
+       struct uplcom_softc *sc;
+
+       sc = (struct uplcom_softc *)arg;
+       if (sc->sc_ucom.sc_dying)
+               return;
        ucom_status_change(&sc->sc_ucom);
 }
 
@@ -813,7 +995,7 @@ uplcom_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
                *msr = sc->sc_msr;
 }
 
-#if TODO
+#if 0 /* TODO */
 Static int
 uplcom_ioctl(void *addr, int portno, u_long cmd, caddr_t data, int flag,
             usb_proc_ptr p)