Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 12 Nov 2011 08:40:52 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r227463 - in head/sys/dev/usb: . serial
Message-ID:  <201111120840.pAC8eqSI033808@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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)



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201111120840.pAC8eqSI033808>