Author: hselasky
Date: Sat Nov 12 08:40:52 2011
New Revision: 227463
URL: http://svn.freebsd.org/changeset/base/227463

Log:
  - This patch adds custom IOCTLs to read and write the 4 GPIO pins on the
  cp2103 usb-to-serial chip.
  - This patch also makes the line status polling asynchronous, to reduce
  the time needed to change the GPIO pins.
  
  Submitted by: JD Louw
  MFC after:    1 week

Modified:
  head/sys/dev/usb/serial/uslcom.c
  head/sys/dev/usb/usb_ioctl.h

Modified: head/sys/dev/usb/serial/uslcom.c
==============================================================================
--- head/sys/dev/usb/serial/uslcom.c    Sat Nov 12 08:19:36 2011        
(r227462)
+++ head/sys/dev/usb/serial/uslcom.c    Sat Nov 12 08:40:52 2011        
(r227463)
@@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$");
 #include <dev/usb/usb.h>
 #include <dev/usb/usbdi.h>
 #include <dev/usb/usbdi_util.h>
+#include <dev/usb/usb_ioctl.h>
 #include "usbdevs.h"
 
 #define        USB_DEBUG_VAR uslcom_debug
@@ -75,6 +76,7 @@ SYSCTL_INT(_hw_usb_uslcom, OID_AUTO, deb
 #define        USLCOM_CTRL             0x07
 #define        USLCOM_RCTRL            0x08
 #define        USLCOM_SET_FLOWCTRL     0x13
+#define        USLCOM_VENDOR_SPECIFIC  0xff
 
 /* USLCOM_UART values */
 #define        USLCOM_UART_DISABLE     0x00
@@ -113,6 +115,10 @@ SYSCTL_INT(_hw_usb_uslcom, OID_AUTO, deb
 #define        USLCOM_FLOW_RTS_ON      0x00000040 /* RTS static active */
 #define        USLCOM_FLOW_RTS_HS      0x00000080 /* RTS handshake */
 
+/* USLCOM_VENDOR_SPECIFIC values */
+#define        USLCOM_WRITE_LATCH      0x37E1
+#define        USLCOM_READ_LATCH       0x00C2
+
 enum {
        USLCOM_BULK_DT_WR,
        USLCOM_BULK_DT_RD,
@@ -123,6 +129,7 @@ enum {
 struct uslcom_softc {
        struct ucom_super_softc sc_super_ucom;
        struct ucom_softc sc_ucom;
+       struct usb_callout sc_watchdog;
 
        struct usb_xfer *sc_xfer[USLCOM_N_TRANSFER];
        struct usb_device *sc_udev;
@@ -145,6 +152,8 @@ static void uslcom_close(struct ucom_sof
 static void uslcom_set_dtr(struct ucom_softc *, uint8_t);
 static void uslcom_set_rts(struct ucom_softc *, uint8_t);
 static void uslcom_set_break(struct ucom_softc *, uint8_t);
+static int uslcom_ioctl(struct ucom_softc *, uint32_t, caddr_t, int,
+               struct thread *);
 static int uslcom_pre_param(struct ucom_softc *, struct termios *);
 static void uslcom_param(struct ucom_softc *, struct termios *);
 static void uslcom_get_status(struct ucom_softc *, uint8_t *, uint8_t *);
@@ -177,7 +186,6 @@ static const struct usb_config uslcom_co
                .type = UE_CONTROL,
                .endpoint = 0x00,
                .direction = UE_DIR_ANY,
-               .interval = 150,        /* poll status every 150 ms */
                .bufsize = sizeof(struct usb_device_request) + 8,
                .flags = {.pipe_bof = 1,},
                .callback = &uslcom_control_callback,
@@ -192,6 +200,7 @@ static struct ucom_callback uslcom_callb
        .ucom_cfg_set_dtr = &uslcom_set_dtr,
        .ucom_cfg_set_rts = &uslcom_set_rts,
        .ucom_cfg_set_break = &uslcom_set_break,
+       .ucom_ioctl = &uslcom_ioctl,
        .ucom_cfg_param = &uslcom_param,
        .ucom_pre_param = &uslcom_pre_param,
        .ucom_start_read = &uslcom_start_read,
@@ -308,6 +317,19 @@ MODULE_DEPEND(uslcom, ucom, 1, 1, 1);
 MODULE_DEPEND(uslcom, usb, 1, 1, 1);
 MODULE_VERSION(uslcom, 1);
 
+static void
+uslcom_watchdog(void *arg)
+{
+       struct uslcom_softc *sc = arg;
+
+       mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+       usbd_transfer_start(sc->sc_xfer[USLCOM_CTRL_DT_RD]);
+
+       usb_callout_reset(&sc->sc_watchdog,
+           hz / 4, &uslcom_watchdog, sc);
+}
+
 static int
 uslcom_probe(device_t dev)
 {
@@ -338,6 +360,7 @@ uslcom_attach(device_t dev)
 
        device_set_usb_desc(dev);
        mtx_init(&sc->sc_mtx, "uslcom", NULL, MTX_DEF);
+       usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0);
 
        sc->sc_udev = uaa->device;
 
@@ -378,6 +401,8 @@ uslcom_detach(device_t dev)
 
        ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
        usbd_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER);
+
+       usb_callout_drain(&sc->sc_watchdog);
        mtx_destroy(&sc->sc_mtx);
 
        return (0);
@@ -399,8 +424,9 @@ uslcom_open(struct ucom_softc *ucom)
            &req, NULL, 0, 1000)) {
                DPRINTF("UART enable failed (ignored)\n");
        }
-       /* Start polling status */
-       usbd_transfer_start(sc->sc_xfer[USLCOM_CTRL_DT_RD]);
+
+       /* start polling status */
+       uslcom_watchdog(sc);
 }
 
 static void
@@ -409,8 +435,8 @@ uslcom_close(struct ucom_softc *ucom)
        struct uslcom_softc *sc = ucom->sc_parent;
        struct usb_device_request req;
 
-       /* Stop polling status */
-       usbd_transfer_stop(sc->sc_xfer[USLCOM_CTRL_DT_RD]);
+       /* stop polling status */
+       usb_callout_stop(&sc->sc_watchdog);
 
        req.bmRequestType = USLCOM_WRITE;
        req.bRequest = USLCOM_UART;
@@ -591,6 +617,55 @@ uslcom_set_break(struct ucom_softc *ucom
        }
 }
 
+static int
+uslcom_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data,
+    int flag, struct thread *td)
+{
+       struct uslcom_softc *sc = ucom->sc_parent;
+       struct usb_device_request req;
+       int error = 0;
+       uint8_t latch;
+
+       DPRINTF("cmd=0x%08x\n", cmd);
+
+       switch (cmd) {
+       case USB_GET_GPIO:
+               req.bmRequestType = USLCOM_READ;
+               req.bRequest = USLCOM_VENDOR_SPECIFIC;
+               USETW(req.wValue, USLCOM_READ_LATCH);
+               USETW(req.wIndex, 0);
+               USETW(req.wLength, sizeof(latch));
+
+               if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+                   &req, &latch, 0, 1000)) {
+                       DPRINTF("Get LATCH failed\n");
+                       error = EIO;
+               }
+               *(int *)data = latch;
+               break;
+
+       case USB_SET_GPIO:
+               req.bmRequestType = USLCOM_WRITE;
+               req.bRequest = USLCOM_VENDOR_SPECIFIC;
+               USETW(req.wValue, USLCOM_WRITE_LATCH);
+               USETW(req.wIndex, (*(int *)data));
+               USETW(req.wLength, 0);
+               
+               if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+                   &req, NULL, 0, 1000)) {
+                       DPRINTF("Set LATCH failed\n");
+                       error = EIO;
+               }
+               break;
+
+       default:
+               DPRINTF("Unknown IOCTL\n");
+               error = ENOIOCTL;
+               break;
+       }
+       return (error);
+}
+
 static void
 uslcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
 {
@@ -681,11 +756,9 @@ uslcom_control_callback(struct usb_xfer 
                        sc->sc_msr = msr;
                        ucom_status_change(&sc->sc_ucom);
                }
-
-               /* FALLTHROUGH */
+               break;
 
        case USB_ST_SETUP:
-tr_setup:              
                req.bmRequestType = USLCOM_READ;
                req.bRequest = USLCOM_RCTRL;
                USETW(req.wValue, 0);
@@ -702,10 +775,8 @@ tr_setup:              
                break;
 
        default:                /* error */
-               if (error != USB_ERR_CANCELLED) {
+               if (error != USB_ERR_CANCELLED)
                        DPRINTF("error=%s\n", usbd_errstr(error));
-                       goto tr_setup;
-               }
                break;
        }
 }

Modified: head/sys/dev/usb/usb_ioctl.h
==============================================================================
--- head/sys/dev/usb/usb_ioctl.h        Sat Nov 12 08:19:36 2011        
(r227462)
+++ head/sys/dev/usb/usb_ioctl.h        Sat Nov 12 08:40:52 2011        
(r227463)
@@ -289,6 +289,10 @@ struct usb_gen_quirk {
 #define        USB_GET_CM_OVER_DATA    _IOR ('U', 180, int)
 #define        USB_SET_CM_OVER_DATA    _IOW ('U', 181, int)
 
+/* GPIO control */
+#define        USB_GET_GPIO            _IOR ('U', 182, int)
+#define        USB_SET_GPIO            _IOW ('U', 183, int)
+
 /* USB file system interface */
 #define        USB_FS_START            _IOW ('U', 192, struct usb_fs_start)
 #define        USB_FS_STOP             _IOW ('U', 193, struct usb_fs_stop)
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to