svn commit: r264149 - in head: share/man/man4 sys/dev/usb sys/dev/usb/serial

Ian Lepore ian at FreeBSD.org
Sat Apr 5 16:08:14 UTC 2014


Author: ian
Date: Sat Apr  5 16:08:13 2014
New Revision: 264149
URL: http://svnweb.freebsd.org/changeset/base/264149

Log:
  Add ioctl(2) calls to uftdi(4) to access bitbang, MPSSE, CPU_FIFO, and
  other modes supported by the FTDI serial adapter chips.
  
  In addition to adding the new ioctls, this change removes all the code
  that reset the chip at attach and open/close time, and also the code
  that turned on RTS/CTS flow control on open without any permission to do
  so (that was just always a bug in the driver).
  
  When FTDI chips are configured as GPIO or MPSSE or other special-purpose
  uses by an attached serial eeprom, the chip will power on with certain
  pins driven or floating, and it's important that the driver not do
  anything to the chip to perturb that unless it receives a specific
  command to do so.  When used for "plain old serial comms" the chip
  powers on into the right mode and never needs to be reset while it's
  running to operate properly, so this change is transparent to most users.

Added:
  head/sys/dev/usb/uftdiio.h   (contents, props changed)
Modified:
  head/share/man/man4/uftdi.4
  head/sys/dev/usb/serial/uftdi.c
  head/sys/dev/usb/serial/uftdi_reg.h

Modified: head/share/man/man4/uftdi.4
==============================================================================
--- head/share/man/man4/uftdi.4	Sat Apr  5 14:24:45 2014	(r264148)
+++ head/share/man/man4/uftdi.4	Sat Apr  5 16:08:13 2014	(r264149)
@@ -83,6 +83,87 @@ The device is accessed through the
 .Xr ucom 4
 driver which makes it behave like a
 .Xr tty 4 .
+.Pp
+Many of the supported chips provide additional functionality
+such as bitbang mode and the MPSSE engine for serial bus emulation.
+The
+.Nm
+driver provides access to that functionality with the following
+.Xr ioctl 2
+calls, defined in
+.In dev/usb/uftdiio.h :
+.Bl -tag -width indent
+.It Dv UFTDIIOC_RESET_IO Pq Vt int
+Reset the channel to its default configuration, flush RX and TX FIFOs.
+.It Dv UFTDIIOC_RESET_RX Pq Vt int
+Flush the RX FIFO.
+.It Dv UFTDIIOC_RESET_TX Pq Vt int
+Flush the TX FIFO.
+.It Dv UFTDIIOC_SET_BITMODE Pq Vt "struct uftdi_bitmode"
+Put the channel into the operating mode specified in
+.Va mode ,
+and set the pins indicated by ones in
+.Va iomask
+to output mode.
+The
+.Va mode
+must be one of the
+.Va uftdi_bitmodes
+values.
+.Bd -literal
+enum uftdi_bitmodes
+{
+	UFTDI_BITMODE_ASYNC = 0,
+	UFTDI_BITMODE_MPSSE = 1,
+	UFTDI_BITMODE_SYNC = 2,
+	UFTDI_BITMODE_CPU_EMUL = 3,
+	UFTDI_BITMODE_FAST_SERIAL = 4,
+	UFTDI_BITMODE_CBUS = 5,
+	UFTDI_BITMODE_NONE = 0xff,
+};
+
+struct uftdi_bitmode
+{
+	uint8_t mode;
+	uint8_t iomask;
+};
+.Ed
+.Pp
+Manuals and application notes published by FTDI describe these
+modes in detail.
+To use most of these modes, you first put the channel into
+the desired mode, then you
+.Xr read 2
+and
+.Xr write 2
+data which either reflects pin state or is interpreted 
+as MPSSE commands and parameters, depending on the mode.
+.It Dv UFTDIIOC_GET_BITMODE Pq Vt "struct uftdi_bitmode"
+Return the state of the bitbang pins at the time of the call in the
+.Va iomask
+member.
+The
+.Va mode
+member is unused.
+.It Dv UFTDIIOC_SET_ERROR_CHAR Pq Vt int
+Set the character which is inserted into the buffer to mark
+the point of an error such as FIFO overflow.
+.It Dv UFTDIIOC_SET_EVENT_CHAR Pq Vt int
+Set the character which causes a partial FIFO full of data 
+to be returned immediately even if the FIFO is not full.
+.It Dv UFTDIIOC_SET_LATENCY Pq Vt int
+Set the amount of time to wait for a full FIFO,
+in milliseconds.
+If more than this much time elapses without receiving a new
+character, any characters in the FIFO are returned.
+.It Dv UFTDIIOC_GET_LATENCY Pq Vt int
+Get the current value of the latency timer.
+.It Dv UFTDIIOC_GET_HWREV Pq Vt int
+Get the hardware revision number.
+This is the
+.Va bcdDevice
+value from the
+.Va usb_device_descriptor .
 .Sh HARDWARE
 The
 .Nm

Modified: head/sys/dev/usb/serial/uftdi.c
==============================================================================
--- head/sys/dev/usb/serial/uftdi.c	Sat Apr  5 14:24:45 2014	(r264148)
+++ head/sys/dev/usb/serial/uftdi.c	Sat Apr  5 16:08:13 2014	(r264149)
@@ -38,7 +38,14 @@ __FBSDID("$FreeBSD$");
  */
 
 /*
- * FTDI FT2232x, FT8U100AX and FT8U232AM serial adapter driver
+ * FTDI FT232x, FT2232x, FT4232x, FT8U100AX and FT8U232xM serial adapters.
+ *
+ * Note that we specifically do not do a reset or otherwise alter the state of
+ * the chip during attach, detach, open, and close, because it could be
+ * pre-initialized (via an attached serial eeprom) to power-on into a mode such
+ * as bitbang in which the pins are being driven to a specific state which we
+ * must not perturb.  The device gets reset at power-on, and doesn't need to be
+ * reset again after that to function, except as directed by ioctl() calls.
  */
 
 #include <sys/stdint.h>
@@ -64,6 +71,7 @@ __FBSDID("$FreeBSD$");
 #include <dev/usb/usbdi.h>
 #include <dev/usb/usbdi_util.h>
 #include <dev/usb/usb_core.h>
+#include <dev/usb/usb_ioctl.h>
 #include "usbdevs.h"
 
 #define	USB_DEBUG_VAR uftdi_debug
@@ -72,6 +80,7 @@ __FBSDID("$FreeBSD$");
 
 #include <dev/usb/serial/usb_serial.h>
 #include <dev/usb/serial/uftdi_reg.h>
+#include <dev/usb/uftdiio.h>
 
 #ifdef USB_DEBUG
 static int uftdi_debug = 0;
@@ -146,6 +155,7 @@ struct uftdi_softc {
 	uint32_t sc_unit;
 
 	uint16_t sc_last_lcr;
+	uint16_t sc_bcdDevice;
 
 	uint8_t sc_devtype;
 	uint8_t sc_devflags;
@@ -175,6 +185,7 @@ static usb_callback_t uftdi_read_callbac
 
 static void	uftdi_free(struct ucom_softc *);
 static void	uftdi_cfg_open(struct ucom_softc *);
+static void	uftdi_cfg_close(struct ucom_softc *);
 static void	uftdi_cfg_set_dtr(struct ucom_softc *, uint8_t);
 static void	uftdi_cfg_set_rts(struct ucom_softc *, uint8_t);
 static void	uftdi_cfg_set_break(struct ucom_softc *, uint8_t);
@@ -184,6 +195,15 @@ static int	uftdi_pre_param(struct ucom_s
 static void	uftdi_cfg_param(struct ucom_softc *, struct termios *);
 static void	uftdi_cfg_get_status(struct ucom_softc *, uint8_t *,
 		    uint8_t *);
+static int	uftdi_reset(struct ucom_softc *, int);
+static int	uftdi_set_bitmode(struct ucom_softc *, uint8_t, uint8_t);
+static int	uftdi_get_bitmode(struct ucom_softc *, uint8_t *);
+static int	uftdi_set_latency(struct ucom_softc *, int);
+static int	uftdi_get_latency(struct ucom_softc *, int *);
+static int	uftdi_set_event_char(struct ucom_softc *, int);
+static int	uftdi_set_error_char(struct ucom_softc *, int);
+static int	uftdi_ioctl(struct ucom_softc *, uint32_t, caddr_t, int,
+		    struct thread *);
 static void	uftdi_start_read(struct ucom_softc *);
 static void	uftdi_stop_read(struct ucom_softc *);
 static void	uftdi_start_write(struct ucom_softc *);
@@ -218,7 +238,9 @@ static const struct ucom_callback uftdi_
 	.ucom_cfg_set_break = &uftdi_cfg_set_break,
 	.ucom_cfg_param = &uftdi_cfg_param,
 	.ucom_cfg_open = &uftdi_cfg_open,
+	.ucom_cfg_close = &uftdi_cfg_close,
 	.ucom_pre_param = &uftdi_pre_param,
+	.ucom_ioctl = &uftdi_ioctl,
 	.ucom_start_read = &uftdi_start_read,
 	.ucom_stop_read = &uftdi_stop_read,
 	.ucom_start_write = &uftdi_start_write,
@@ -905,6 +927,8 @@ uftdi_devtype_setup(struct uftdi_softc *
 {
 	struct usb_device_descriptor *dd;
 
+	sc->sc_bcdDevice = uaa->info.bcdDevice;
+
 	switch (uaa->info.bcdDevice) {
 	case 0x200:
 		dd = usbd_get_device_descriptor(sc->sc_udev);
@@ -1086,37 +1110,25 @@ uftdi_free(struct ucom_softc *ucom)
 static void
 uftdi_cfg_open(struct ucom_softc *ucom)
 {
-	struct uftdi_softc *sc = ucom->sc_parent;
-	uint16_t wIndex = ucom->sc_portno;
-	struct usb_device_request req;
 
+	/*
+	 * This do-nothing open routine exists for the sole purpose of this
+	 * DPRINTF() so that you can see the point at which open gets called
+	 * when debugging is enabled.
+	 */
 	DPRINTF("");
+}
 
-	/* perform a full reset on the device */
-
-	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
-	req.bRequest = FTDI_SIO_RESET;
-	USETW(req.wValue, FTDI_SIO_RESET_SIO);
-	USETW(req.wIndex, wIndex);
-	USETW(req.wLength, 0);
-	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
-	    &req, NULL, 0, 1000);
-
-	/* turn on RTS/CTS flow control */
-
-	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
-	req.bRequest = FTDI_SIO_SET_FLOW_CTRL;
-	USETW(req.wValue, 0);
-	USETW2(req.wIndex, FTDI_SIO_RTS_CTS_HS, wIndex);
-	USETW(req.wLength, 0);
-	ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
-	    &req, NULL, 0, 1000);
+static void
+uftdi_cfg_close(struct ucom_softc *ucom)
+{
 
 	/*
-	 * NOTE: with the new UCOM layer there will always be a
-	 * "uftdi_cfg_param()" call after "open()", so there is no need for
-	 * "open()" to configure anything
+	 * This do-nothing close routine exists for the sole purpose of this
+	 * DPRINTF() so that you can see the point at which close gets called
+	 * when debugging is enabled.
 	 */
+	DPRINTF("");
 }
 
 static void
@@ -1583,6 +1595,186 @@ uftdi_cfg_get_status(struct ucom_softc *
 	*lsr = sc->sc_lsr;
 }
 
+static int
+uftdi_reset(struct ucom_softc *ucom, int reset_type)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_RESET;
+
+	USETW(req.wIndex, sc->sc_ucom.sc_portno);
+	USETW(req.wLength, 0);
+	USETW(req.wValue, reset_type);
+
+	return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL));
+}
+
+static int
+uftdi_set_bitmode(struct ucom_softc *ucom, uint8_t bitmode, uint8_t iomask)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_SET_BITMODE;
+
+	USETW(req.wIndex, sc->sc_ucom.sc_portno);
+	USETW(req.wLength, 0);
+
+	if (bitmode == UFTDI_BITMODE_NONE)
+	    USETW2(req.wValue, 0, 0);
+	else
+	    USETW2(req.wValue, (1 << bitmode), iomask);
+
+	return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL));
+}
+
+static int
+uftdi_get_bitmode(struct ucom_softc *ucom, uint8_t *iomask)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_GET_BITMODE;
+
+	USETW(req.wIndex, sc->sc_ucom.sc_portno);
+	USETW(req.wLength, 1);
+	USETW(req.wValue,  0);
+
+	return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, iomask));
+}
+
+static int
+uftdi_set_latency(struct ucom_softc *ucom, int latency)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+
+	if (latency < 0 || latency > 255)
+		return (USB_ERR_INVAL);
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_SET_LATENCY;
+
+	USETW(req.wIndex, sc->sc_ucom.sc_portno);
+	USETW(req.wLength, 0);
+	USETW2(req.wValue, 0, latency);
+
+	return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL));
+}
+
+static int
+uftdi_get_latency(struct ucom_softc *ucom, int *latency)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+	usb_error_t err;
+	uint8_t buf;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_GET_LATENCY;
+
+	USETW(req.wIndex, sc->sc_ucom.sc_portno);
+	USETW(req.wLength, 1);
+	USETW(req.wValue, 0);
+
+	err = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, &buf);
+	*latency = buf;
+
+	return (err);
+}
+
+static int
+uftdi_set_event_char(struct ucom_softc *ucom, int echar)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+	uint8_t enable;
+
+	enable = (echar == -1) ? 0 : 1;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_SET_EVENT_CHAR;
+
+	USETW(req.wIndex, sc->sc_ucom.sc_portno);
+	USETW(req.wLength, 0);
+	USETW2(req.wValue, enable, echar & 0xff);
+
+	return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL));
+}
+
+static int
+uftdi_set_error_char(struct ucom_softc *ucom, int echar)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	usb_device_request_t req;
+	uint8_t enable;
+
+	enable = (echar == -1) ? 0 : 1;
+
+	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+	req.bRequest = FTDI_SIO_SET_ERROR_CHAR;
+
+	USETW(req.wIndex, sc->sc_ucom.sc_portno);
+	USETW(req.wLength, 0);
+	USETW2(req.wValue, enable, echar & 0xff);
+
+	return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL));
+}
+
+static int
+uftdi_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data,
+    int flag, struct thread *td)
+{
+	struct uftdi_softc *sc = ucom->sc_parent;
+	int err;
+	struct uftdi_bitmode * mode;
+
+	DPRINTF("portno: %d cmd: %#x\n", ucom->sc_portno, cmd);
+
+	switch (cmd) {
+	case UFTDIIOC_RESET_IO:
+	case UFTDIIOC_RESET_RX:
+	case UFTDIIOC_RESET_TX:
+		err = uftdi_reset(ucom, 
+		    cmd == UFTDIIOC_RESET_IO ? FTDI_SIO_RESET_SIO :
+		    (cmd == UFTDIIOC_RESET_RX ? FTDI_SIO_RESET_PURGE_RX :
+		    FTDI_SIO_RESET_PURGE_TX));
+		break;
+	case UFTDIIOC_SET_BITMODE:
+		mode = (struct uftdi_bitmode *)data;
+		err = uftdi_set_bitmode(ucom, mode->mode, mode->iomask);
+		break;
+	case UFTDIIOC_GET_BITMODE:
+		mode = (struct uftdi_bitmode *)data;
+		err = uftdi_get_bitmode(ucom, &mode->iomask);
+		break;
+	case UFTDIIOC_SET_LATENCY:
+		err = uftdi_set_latency(ucom, *((int *)data));
+		break;
+	case UFTDIIOC_GET_LATENCY:
+		err = uftdi_get_latency(ucom, (int *)data);
+		break;
+	case UFTDIIOC_SET_ERROR_CHAR:
+		err = uftdi_set_event_char(ucom, *(int *)data);
+		break;
+	case UFTDIIOC_SET_EVENT_CHAR:
+		err = uftdi_set_error_char(ucom, *(int *)data);
+	case UFTDIIOC_GET_HWREV:
+		*(int *)data = sc->sc_bcdDevice;
+		err = 0;
+		break;
+	default:
+		return (ENOIOCTL);
+	}
+	if (err != USB_ERR_NORMAL_COMPLETION)
+		return (EIO);
+	return (0);
+}
+
 static void
 uftdi_start_read(struct ucom_softc *ucom)
 {

Modified: head/sys/dev/usb/serial/uftdi_reg.h
==============================================================================
--- head/sys/dev/usb/serial/uftdi_reg.h	Sat Apr  5 14:24:45 2014	(r264148)
+++ head/sys/dev/usb/serial/uftdi_reg.h	Sat Apr  5 16:08:13 2014	(r264149)
@@ -28,6 +28,10 @@
 					 * reg */
 #define	FTDI_SIO_SET_EVENT_CHAR	6	/* Set the event character */
 #define	FTDI_SIO_SET_ERROR_CHAR	7	/* Set the error character */
+#define	FTDI_SIO_SET_LATENCY	9	/* Set the latency timer */
+#define	FTDI_SIO_GET_LATENCY	10	/* Read the latency timer */
+#define	FTDI_SIO_SET_BITMODE	11	/* Set the bit bang I/O mode */
+#define	FTDI_SIO_GET_BITMODE	12	/* Read pin states in bit bang mode */
 
 /* Port Identifier Table */
 #define	FTDI_PIT_DEFAULT 	0	/* SIOA */

Added: head/sys/dev/usb/uftdiio.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/usb/uftdiio.h	Sat Apr  5 16:08:13 2014	(r264149)
@@ -0,0 +1,75 @@
+/*-
+ * Copyright 2008-2012 - Symmetricom, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *  $FreeBSD$ 
+ */
+
+/*
+ * FTDI USB serial converter chip ioctl commands.
+ */
+
+#ifndef _USB_UFTDIIO_H_
+#define _USB_UFTDIIO_H_
+
+#include <sys/ioccom.h>
+
+enum uftdi_bitmodes
+{
+	UFTDI_BITMODE_ASYNC = 0,
+	UFTDI_BITMODE_MPSSE = 1,
+	UFTDI_BITMODE_SYNC = 2,
+	UFTDI_BITMODE_CPU_EMUL = 3,
+	UFTDI_BITMODE_FAST_SERIAL = 4,
+	UFTDI_BITMODE_CBUS = 5,
+	UFTDI_BITMODE_NONE = 0xff,
+};
+
+/*
+ * For UFTDIIOC_SET_BITMODE:
+ *   mode   = One of the uftdi_bitmodes enum values.
+ *   iomask = Mask of bits enabled for bitbang output.
+ *
+ * For UFTDIIOC_GET_BITMODE:
+ *   mode   = Unused.
+ *   iomask = Returned snapshot of bitbang pin states at time of call.
+ */
+struct uftdi_bitmode
+{
+	uint8_t mode;
+	uint8_t iomask;
+};
+
+#define	UFTDIIOC_RESET_IO	_IO('c', 0)	/* Reset config, flush fifos.*/
+#define	UFTDIIOC_RESET_RX	_IO('c', 1)	/* Flush input fifo. */
+#define	UFTDIIOC_RESET_TX	_IO('c', 2)	/* Flush output fifo. */
+#define	UFTDIIOC_SET_BITMODE	_IOW('c', 3, struct uftdi_bitmode)
+#define	UFTDIIOC_GET_BITMODE	_IOR('c', 4, struct uftdi_bitmode)
+#define	UFTDIIOC_SET_ERROR_CHAR	_IOW('c', 5, int)	/* -1 to disable */
+#define	UFTDIIOC_SET_EVENT_CHAR	_IOW('c', 6, int)	/* -1 to disable */
+#define	UFTDIIOC_SET_LATENCY	_IOW('c', 7, int)	/* 1-255 ms */
+#define	UFTDIIOC_GET_LATENCY	_IOR('c', 8, int)
+#define	UFTDIIOC_GET_HWREV	_IOR('c', 9, int)
+
+#endif


More information about the svn-src-head mailing list