summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorian <ian@FreeBSD.org>2014-04-28 12:46:23 +0000
committerian <ian@FreeBSD.org>2014-04-28 12:46:23 +0000
commitcb4d7c77b61877ede0f30cc96e4a61514819ba2a (patch)
tree7e28eff5570cf7bfe3e20ab95357c424589f6198
parente852cd69383058c931f6f569329eed128eb9d109 (diff)
downloadFreeBSD-src-cb4d7c77b61877ede0f30cc96e4a61514819ba2a.zip
FreeBSD-src-cb4d7c77b61877ede0f30cc96e4a61514819ba2a.tar.gz
MFC uftdi(4) driver changes...
r264010: Support speeds up to 12mbaud on newer chips. r264031: Use 2K IO buffers for improved throughput. r264149: Add ioctl(2) calls to access bitbang, MPSSE, CPU_FIFO, and other modes.
-rw-r--r--share/man/man4/uftdi.481
-rw-r--r--sys/dev/usb/serial/uftdi.c781
-rw-r--r--sys/dev/usb/serial/uftdi_reg.h26
-rw-r--r--sys/dev/usb/uftdiio.h75
4 files changed, 728 insertions, 235 deletions
diff --git a/share/man/man4/uftdi.4 b/share/man/man4/uftdi.4
index db75c68..bfaa780 100644
--- a/share/man/man4/uftdi.4
+++ b/share/man/man4/uftdi.4
@@ -61,6 +61,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
diff --git a/sys/dev/usb/serial/uftdi.c b/sys/dev/usb/serial/uftdi.c
index fa4dcde..c670b6d 100644
--- a/sys/dev/usb/serial/uftdi.c
+++ b/sys/dev/usb/serial/uftdi.c
@@ -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>
@@ -63,6 +70,8 @@ __FBSDID("$FreeBSD$");
#include <dev/usb/usb.h>
#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
@@ -71,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;
@@ -83,8 +93,34 @@ SYSCTL_INT(_hw_usb_uftdi, OID_AUTO, debug, CTLFLAG_RW,
#define UFTDI_CONFIG_INDEX 0
#define UFTDI_IFACE_INDEX_JTAG 0
-#define UFTDI_OBUFSIZE 64 /* bytes, cannot be increased due to
- * do size encoding */
+/*
+ * IO buffer sizes and FTDI device procotol sizes.
+ *
+ * Note that the output packet size in the following defines is not the usb
+ * protocol packet size based on bus speed, it is the size dictated by the FTDI
+ * device itself, and is used only on older chips.
+ *
+ * We allocate buffers bigger than the hardware's packet size, and process
+ * multiple packets within each buffer. This allows the controller to make
+ * optimal use of the usb bus by conducting multiple transfers with the device
+ * during a single bus timeslice to fill or drain the chip's fifos.
+ *
+ * The output data on newer chips has no packet header, and we are able to pack
+ * any number of output bytes into a buffer. On some older chips, each output
+ * packet contains a 1-byte header and up to 63 bytes of payload. The size is
+ * encoded in 6 bits of the header, hence the 64-byte limit on packet size. We
+ * loop to fill the buffer with many of these header+payload packets.
+ *
+ * The input data on all chips consists of packets which contain a 2-byte header
+ * followed by data payload. The total size of the packet is wMaxPacketSize
+ * which can change based on the bus speed (e.g., 64 for full speed, 512 for
+ * high speed). We loop to extract the headers and payloads from the packets
+ * packed into an input buffer.
+ */
+#define UFTDI_IBUFSIZE 2048
+#define UFTDI_IHDRSIZE 2
+#define UFTDI_OBUFSIZE 2048
+#define UFTDI_OPKTSIZE 64
enum {
UFTDI_BULK_DT_WR,
@@ -92,6 +128,21 @@ enum {
UFTDI_N_TRANSFER,
};
+enum {
+ DEVT_SIO,
+ DEVT_232A,
+ DEVT_232B,
+ DEVT_2232D, /* Includes 2232C */
+ DEVT_232R,
+ DEVT_2232H,
+ DEVT_4232H,
+ DEVT_232H,
+ DEVT_230X,
+};
+
+#define DEVF_BAUDBITS_HINDEX 0x01 /* Baud bits in high byte of index. */
+#define DEVF_BAUDCLK_12M 0X02 /* Base baud clock is 12MHz. */
+
struct uftdi_softc {
struct ucom_super_softc sc_super_ucom;
struct ucom_softc sc_ucom;
@@ -104,16 +155,18 @@ struct uftdi_softc {
uint32_t sc_unit;
uint16_t sc_last_lcr;
+ uint16_t sc_bcdDevice;
- uint8_t sc_type;
- uint8_t sc_iface_index;
+ uint8_t sc_devtype;
+ uint8_t sc_devflags;
uint8_t sc_hdrlen;
uint8_t sc_msr;
uint8_t sc_lsr;
};
struct uftdi_param_config {
- uint16_t rate;
+ uint16_t baud_lobits;
+ uint16_t baud_hibits;
uint16_t lcr;
uint8_t v_start;
uint8_t v_stop;
@@ -132,20 +185,29 @@ static usb_callback_t uftdi_read_callback;
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);
-static int uftdi_set_parm_soft(struct termios *,
- struct uftdi_param_config *, uint8_t);
+static int uftdi_set_parm_soft(struct ucom_softc *, struct termios *,
+ struct uftdi_param_config *);
static int uftdi_pre_param(struct ucom_softc *, struct termios *);
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 *);
static void uftdi_stop_write(struct ucom_softc *);
-static uint8_t uftdi_8u232am_getrate(uint32_t, uint16_t *);
static void uftdi_poll(struct ucom_softc *ucom);
static const struct usb_config uftdi_config[UFTDI_N_TRANSFER] = {
@@ -163,7 +225,7 @@ static const struct usb_config uftdi_config[UFTDI_N_TRANSFER] = {
.type = UE_BULK,
.endpoint = UE_ADDR_ANY,
.direction = UE_DIR_IN,
- .bufsize = 0, /* use wMaxPacketSize */
+ .bufsize = UFTDI_IBUFSIZE,
.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
.callback = &uftdi_read_callback,
},
@@ -176,7 +238,9 @@ static const struct ucom_callback uftdi_callback = {
.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,
@@ -847,6 +911,82 @@ static const STRUCT_USB_HOST_ID uftdi_devs[] = {
#undef UFTDI_DEV
};
+/*
+ * Set up softc fields whose value depends on the device type.
+ *
+ * Note that the 2232C and 2232D devices are the same for our purposes. In the
+ * silicon the difference is that the D series has CPU FIFO mode and C doesn't.
+ * I haven't found any way of determining the C/D difference from info provided
+ * by the chip other than trying to set CPU FIFO mode and having it work or not.
+ *
+ * Due to a hardware bug, a 232B chip without an eeprom reports itself as a
+ * 232A, but if the serial number is also zero we know it's really a 232B.
+ */
+static void
+uftdi_devtype_setup(struct uftdi_softc *sc, struct usb_attach_arg *uaa)
+{
+ 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);
+ if (dd->iSerialNumber == 0) {
+ sc->sc_devtype = DEVT_232B;
+ } else {
+ sc->sc_devtype = DEVT_232A;
+ }
+ sc->sc_ucom.sc_portno = 0;
+ break;
+ case 0x400:
+ sc->sc_devtype = DEVT_232B;
+ sc->sc_ucom.sc_portno = 0;
+ break;
+ case 0x500:
+ sc->sc_devtype = DEVT_2232D;
+ sc->sc_devflags |= DEVF_BAUDBITS_HINDEX;
+ sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
+ break;
+ case 0x600:
+ sc->sc_devtype = DEVT_232R;
+ sc->sc_ucom.sc_portno = 0;
+ break;
+ case 0x700:
+ sc->sc_devtype = DEVT_2232H;
+ sc->sc_devflags |= DEVF_BAUDBITS_HINDEX | DEVF_BAUDCLK_12M;
+ sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
+ break;
+ case 0x800:
+ sc->sc_devtype = DEVT_4232H;
+ sc->sc_devflags |= DEVF_BAUDBITS_HINDEX | DEVF_BAUDCLK_12M;
+ sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
+ break;
+ case 0x900:
+ sc->sc_devtype = DEVT_232H;
+ sc->sc_devflags |= DEVF_BAUDBITS_HINDEX | DEVF_BAUDCLK_12M;
+ sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
+ break;
+ case 0x1000:
+ sc->sc_devtype = DEVT_230X;
+ sc->sc_devflags |= DEVF_BAUDBITS_HINDEX;
+ sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
+ break;
+ default:
+ if (uaa->info.bcdDevice < 0x200) {
+ sc->sc_devtype = DEVT_SIO;
+ sc->sc_hdrlen = 1;
+ } else {
+ sc->sc_devtype = DEVT_232R;
+ device_printf(sc->sc_dev, "Warning: unknown FTDI "
+ "device type, bcdDevice=0x%04x, assuming 232R",
+ uaa->info.bcdDevice);
+ }
+ sc->sc_ucom.sc_portno = 0;
+ break;
+ }
+}
+
static int
uftdi_probe(device_t dev)
{
@@ -886,6 +1026,8 @@ uftdi_attach(device_t dev)
struct uftdi_softc *sc = device_get_softc(dev);
int error;
+ DPRINTF("\n");
+
sc->sc_udev = uaa->device;
sc->sc_dev = dev;
sc->sc_unit = device_get_unit(dev);
@@ -894,34 +1036,11 @@ uftdi_attach(device_t dev)
mtx_init(&sc->sc_mtx, "uftdi", NULL, MTX_DEF);
ucom_ref(&sc->sc_super_ucom);
- DPRINTF("\n");
-
- sc->sc_iface_index = uaa->info.bIfaceIndex;
- sc->sc_type = USB_GET_DRIVER_INFO(uaa) & UFTDI_TYPE_MASK;
- switch (sc->sc_type) {
- case UFTDI_TYPE_AUTO:
- /* simplified type check */
- if (uaa->info.bcdDevice >= 0x0200 ||
- usbd_get_iface(uaa->device, 1) != NULL) {
- sc->sc_type = UFTDI_TYPE_8U232AM;
- sc->sc_hdrlen = 0;
- } else {
- sc->sc_type = UFTDI_TYPE_SIO;
- sc->sc_hdrlen = 1;
- }
- break;
- case UFTDI_TYPE_SIO:
- sc->sc_hdrlen = 1;
- break;
- case UFTDI_TYPE_8U232AM:
- default:
- sc->sc_hdrlen = 0;
- break;
- }
+ uftdi_devtype_setup(sc, uaa);
error = usbd_transfer_setup(uaa->device,
- &sc->sc_iface_index, sc->sc_xfer, uftdi_config,
+ &uaa->info.bIfaceIndex, sc->sc_xfer, uftdi_config,
UFTDI_N_TRANSFER, sc, &sc->sc_mtx);
if (error) {
@@ -929,8 +1048,6 @@ uftdi_attach(device_t dev)
"transfers failed\n");
goto detach;
}
- sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum;
-
/* clear stall at first run */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_WR]);
@@ -993,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
@@ -1031,35 +1136,47 @@ uftdi_write_callback(struct usb_xfer *xfer, usb_error_t error)
{
struct uftdi_softc *sc = usbd_xfer_softc(xfer);
struct usb_page_cache *pc;
- uint32_t actlen;
+ uint32_t pktlen;
+ uint32_t buflen;
uint8_t buf[1];
switch (USB_GET_STATE(xfer)) {
+ default: /* Error */
+ if (error != USB_ERR_CANCELLED) {
+ /* try to clear stall first */
+ usbd_xfer_set_stall(xfer);
+ }
+ /* FALLTHROUGH */
case USB_ST_SETUP:
case USB_ST_TRANSFERRED:
-tr_setup:
+ /*
+ * If output packets don't require headers (the common case) we
+ * can just load the buffer up with payload bytes all at once.
+ * Otherwise, loop to format packets into the buffer while there
+ * is data available, and room for a packet header and at least
+ * one byte of payload.
+ */
pc = usbd_xfer_get_frame(xfer, 0);
- if (ucom_get_data(&sc->sc_ucom, pc,
- sc->sc_hdrlen, UFTDI_OBUFSIZE - sc->sc_hdrlen,
- &actlen)) {
-
- if (sc->sc_hdrlen > 0) {
- buf[0] =
- FTDI_OUT_TAG(actlen, sc->sc_ucom.sc_portno);
- usbd_copy_in(pc, 0, buf, 1);
+ if (sc->sc_hdrlen == 0) {
+ ucom_get_data(&sc->sc_ucom, pc, 0, UFTDI_OBUFSIZE,
+ &buflen);
+ } else {
+ buflen = 0;
+ while (buflen < UFTDI_OBUFSIZE - sc->sc_hdrlen - 1 &&
+ ucom_get_data(&sc->sc_ucom, pc, buflen +
+ sc->sc_hdrlen, UFTDI_OPKTSIZE - sc->sc_hdrlen,
+ &pktlen) != 0) {
+ buf[0] = FTDI_OUT_TAG(pktlen,
+ sc->sc_ucom.sc_portno);
+ usbd_copy_in(pc, buflen, buf, 1);
+ buflen += pktlen + sc->sc_hdrlen;
}
- usbd_xfer_set_frame_len(xfer, 0, actlen + sc->sc_hdrlen);
- usbd_transfer_submit(xfer);
}
- return;
-
- default: /* Error */
- if (error != USB_ERR_CANCELLED) {
- /* try to clear stall first */
- usbd_xfer_set_stall(xfer);
- goto tr_setup;
+ if (buflen != 0) {
+ usbd_xfer_set_frame_len(xfer, 0, buflen);
+ usbd_transfer_submit(xfer);
}
- return;
+ break;
}
}
@@ -1072,23 +1189,47 @@ uftdi_read_callback(struct usb_xfer *xfer, usb_error_t error)
uint8_t ftdi_msr;
uint8_t msr;
uint8_t lsr;
- int actlen;
+ int buflen;
+ int pktlen;
+ int pktmax;
+ int offset;
- usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
+ usbd_xfer_status(xfer, &buflen, NULL, NULL, NULL);
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
-
- if (actlen < 2) {
+ if (buflen < UFTDI_IHDRSIZE)
goto tr_setup;
- }
pc = usbd_xfer_get_frame(xfer, 0);
- usbd_copy_out(pc, 0, buf, 2);
-
+ pktmax = xfer->max_packet_size - UFTDI_IHDRSIZE;
+ lsr = 0;
+ msr = 0;
+ offset = 0;
+ /*
+ * Extract packet headers and payload bytes from the buffer.
+ * Feed payload bytes to ucom/tty layer; OR-accumulate header
+ * status bits which are transient and could toggle with each
+ * packet. After processing all packets in the buffer, process
+ * the accumulated transient MSR and LSR values along with the
+ * non-transient bits from the last packet header.
+ */
+ while (buflen >= UFTDI_IHDRSIZE) {
+ usbd_copy_out(pc, offset, buf, UFTDI_IHDRSIZE);
+ offset += UFTDI_IHDRSIZE;
+ buflen -= UFTDI_IHDRSIZE;
+ lsr |= FTDI_GET_LSR(buf);
+ if (FTDI_GET_MSR(buf) & FTDI_SIO_RI_MASK)
+ msr |= SER_RI;
+ pktlen = min(buflen, pktmax);
+ if (pktlen != 0) {
+ ucom_put_data(&sc->sc_ucom, pc, offset,
+ pktlen);
+ offset += pktlen;
+ buflen -= pktlen;
+ }
+ }
ftdi_msr = FTDI_GET_MSR(buf);
- lsr = FTDI_GET_LSR(buf);
- msr = 0;
if (ftdi_msr & FTDI_SIO_CTS_MASK)
msr |= SER_CTS;
if (ftdi_msr & FTDI_SIO_DSR_MASK)
@@ -1109,11 +1250,7 @@ uftdi_read_callback(struct usb_xfer *xfer, usb_error_t error)
ucom_status_change(&sc->sc_ucom);
}
- actlen -= 2;
-
- if (actlen > 0) {
- ucom_put_data(&sc->sc_ucom, pc, 2, actlen);
- }
+ /* FALLTHROUGH */
case USB_ST_SETUP:
tr_setup:
usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
@@ -1193,58 +1330,162 @@ uftdi_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff)
&req, NULL, 0, 1000);
}
-static int
-uftdi_set_parm_soft(struct termios *t,
- struct uftdi_param_config *cfg, uint8_t type)
+/*
+ * Return true if the given speed is within operational tolerance of the target
+ * speed. FTDI recommends that the hardware speed be within 3% of nominal.
+ */
+static inline boolean_t
+uftdi_baud_within_tolerance(uint64_t speed, uint64_t target)
{
+ return ((speed >= (target * 100) / 103) &&
+ (speed <= (target * 100) / 97));
+}
- memset(cfg, 0, sizeof(*cfg));
+static int
+uftdi_sio_encode_baudrate(struct uftdi_softc *sc, speed_t speed,
+ struct uftdi_param_config *cfg)
+{
+ u_int i;
+ const speed_t sio_speeds[] = {
+ 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200
+ };
- switch (type) {
- case UFTDI_TYPE_SIO:
- switch (t->c_ospeed) {
- case 300:
- cfg->rate = ftdi_sio_b300;
- break;
- case 600:
- cfg->rate = ftdi_sio_b600;
- break;
- case 1200:
- cfg->rate = ftdi_sio_b1200;
- break;
- case 2400:
- cfg->rate = ftdi_sio_b2400;
- break;
- case 4800:
- cfg->rate = ftdi_sio_b4800;
- break;
- case 9600:
- cfg->rate = ftdi_sio_b9600;
- break;
- case 19200:
- cfg->rate = ftdi_sio_b19200;
- break;
- case 38400:
- cfg->rate = ftdi_sio_b38400;
- break;
- case 57600:
- cfg->rate = ftdi_sio_b57600;
- break;
- case 115200:
- cfg->rate = ftdi_sio_b115200;
- break;
- default:
- return (EINVAL);
+ /*
+ * The original SIO chips were limited to a small choice of speeds
+ * listed in an internal table of speeds chosen by an index value.
+ */
+ for (i = 0; i < nitems(sio_speeds); ++i) {
+ if (speed == sio_speeds[i]) {
+ cfg->baud_lobits = i;
+ cfg->baud_hibits = 0;
+ return (0);
}
- break;
+ }
+ return (ERANGE);
+}
- case UFTDI_TYPE_8U232AM:
- if (uftdi_8u232am_getrate(t->c_ospeed, &cfg->rate)) {
- return (EINVAL);
- }
- break;
+static int
+uftdi_encode_baudrate(struct uftdi_softc *sc, speed_t speed,
+ struct uftdi_param_config *cfg)
+{
+ static const uint8_t encoded_fraction[8] = {0, 3, 2, 4, 1, 5, 6, 7};
+ static const uint8_t roundoff_232a[16] = {
+ 0, 1, 0, 1, 0, -1, 2, 1,
+ 0, -1, -2, -3, 4, 3, 2, 1,
+ };
+ uint32_t clk, divisor, fastclk_flag, frac, hwspeed;
+
+ /*
+ * If this chip has the fast clock capability and the speed is within
+ * range, use the 12MHz clock, otherwise the standard clock is 3MHz.
+ */
+ if ((sc->sc_devflags & DEVF_BAUDCLK_12M) && speed >= 1200) {
+ clk = 12000000;
+ fastclk_flag = (1 << 17);
+ } else {
+ clk = 3000000;
+ fastclk_flag = 0;
}
+ /*
+ * Make sure the requested speed is reachable with the available clock
+ * and a 14-bit divisor.
+ */
+ if (speed < (clk >> 14) || speed > clk)
+ return (ERANGE);
+
+ /*
+ * Calculate the divisor, initially yielding a fixed point number with a
+ * 4-bit (1/16ths) fraction, then round it to the nearest fraction the
+ * hardware can handle. When the integral part of the divisor is
+ * greater than one, the fractional part is in 1/8ths of the base clock.
+ * The FT8U232AM chips can handle only 0.125, 0.250, and 0.5 fractions.
+ * Later chips can handle all 1/8th fractions.
+ *
+ * If the integral part of the divisor is 1, a special rule applies: the
+ * fractional part can only be .0 or .5 (this is a limitation of the
+ * hardware). We handle this by truncating the fraction rather than
+ * rounding, because this only applies to the two fastest speeds the
+ * chip can achieve and rounding doesn't matter, either you've asked for
+ * that exact speed or you've asked for something the chip can't do.
+ *
+ * For the FT8U232AM chips, use a roundoff table to adjust the result
+ * to the nearest 1/8th fraction that is supported by the hardware,
+ * leaving a fixed-point number with a 3-bit fraction which exactly
+ * represents the math the hardware divider will do. For later-series
+ * chips that support all 8 fractional divisors, just round 16ths to
+ * 8ths by adding 1 and dividing by 2.
+ */
+ divisor = (clk << 4) / speed;
+ if ((divisor & 0xfffffff0) == 1)
+ divisor &= 0xfffffff8;
+ else if (sc->sc_devtype == DEVT_232A)
+ divisor += roundoff_232a[divisor & 0x0f];
+ else
+ divisor += 1; /* Rounds odd 16ths up to next 8th. */
+ divisor >>= 1;
+
+ /*
+ * Ensure the resulting hardware speed will be within operational
+ * tolerance (within 3% of nominal).
+ */
+ hwspeed = (clk << 3) / divisor;
+ if (!uftdi_baud_within_tolerance(hwspeed, speed))
+ return (ERANGE);
+
+ /*
+ * Re-pack the divisor into hardware format. The lower 14-bits hold the
+ * integral part, while the upper bits specify the fraction by indexing
+ * a table of fractions within the hardware which is laid out as:
+ * {0.0, 0.5, 0.25, 0.125, 0.325, 0.625, 0.725, 0.875}
+ * The A-series chips only have the first four table entries; the
+ * roundoff table logic above ensures that the fractional part for those
+ * chips will be one of the first four values.
+ *
+ * When the divisor is 1 a special encoding applies: 1.0 is encoded as
+ * 0.0, and 1.5 is encoded as 1.0. The rounding logic above has already
+ * ensured that the fraction is either .0 or .5 if the integral is 1.
+ */
+ frac = divisor & 0x07;
+ divisor >>= 3;
+ if (divisor == 1) {
+ if (frac == 0)
+ divisor = 0; /* 1.0 becomes 0.0 */
+ else
+ frac = 0; /* 1.5 becomes 1.0 */
+ }
+ divisor |= (encoded_fraction[frac] << 14) | fastclk_flag;
+
+ cfg->baud_lobits = (uint16_t)divisor;
+ cfg->baud_hibits = (uint16_t)(divisor >> 16);
+
+ /*
+ * If this chip requires the baud bits to be in the high byte of the
+ * index word, move the bits up to that location.
+ */
+ if (sc->sc_devflags & DEVF_BAUDBITS_HINDEX) {
+ cfg->baud_hibits <<= 8;
+ }
+
+ return (0);
+}
+
+static int
+uftdi_set_parm_soft(struct ucom_softc *ucom, struct termios *t,
+ struct uftdi_param_config *cfg)
+{
+ struct uftdi_softc *sc = ucom->sc_parent;
+ int err;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ if (sc->sc_devtype == DEVT_SIO)
+ err = uftdi_sio_encode_baudrate(sc, t->c_ospeed, cfg);
+ else
+ err = uftdi_encode_baudrate(sc, t->c_ospeed, cfg);
+ if (err != 0)
+ return (err);
+
if (t->c_cflag & CSTOPB)
cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_2;
else
@@ -1294,12 +1535,11 @@ uftdi_set_parm_soft(struct termios *t,
static int
uftdi_pre_param(struct ucom_softc *ucom, struct termios *t)
{
- struct uftdi_softc *sc = ucom->sc_parent;
struct uftdi_param_config cfg;
DPRINTF("\n");
- return (uftdi_set_parm_soft(t, &cfg, sc->sc_type));
+ return (uftdi_set_parm_soft(ucom, t, &cfg));
}
static void
@@ -1310,7 +1550,7 @@ uftdi_cfg_param(struct ucom_softc *ucom, struct termios *t)
struct uftdi_param_config cfg;
struct usb_device_request req;
- if (uftdi_set_parm_soft(t, &cfg, sc->sc_type)) {
+ if (uftdi_set_parm_soft(ucom, t, &cfg)) {
/* should not happen */
return;
}
@@ -1320,8 +1560,8 @@ uftdi_cfg_param(struct ucom_softc *ucom, struct termios *t)
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
req.bRequest = FTDI_SIO_SET_BAUD_RATE;
- USETW(req.wValue, cfg.rate);
- USETW(req.wIndex, wIndex);
+ USETW(req.wValue, cfg.baud_lobits);
+ USETW(req.wIndex, cfg.baud_hibits | wIndex);
USETW(req.wLength, 0);
ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom,
&req, NULL, 0, 1000);
@@ -1355,6 +1595,186 @@ uftdi_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr)
*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)
{
@@ -1387,75 +1807,6 @@ uftdi_stop_write(struct ucom_softc *ucom)
usbd_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_WR]);
}
-/*------------------------------------------------------------------------*
- * uftdi_8u232am_getrate
- *
- * Return values:
- * 0: Success
- * Else: Failure
- *------------------------------------------------------------------------*/
-static uint8_t
-uftdi_8u232am_getrate(uint32_t speed, uint16_t *rate)
-{
- /* Table of the nearest even powers-of-2 for values 0..15. */
- static const uint8_t roundoff[16] = {
- 0, 2, 2, 4, 4, 4, 8, 8,
- 8, 8, 8, 8, 16, 16, 16, 16,
- };
- uint32_t d;
- uint32_t freq;
- uint16_t result;
-
- if ((speed < 178) || (speed > ((3000000 * 100) / 97)))
- return (1); /* prevent numerical overflow */
-
- /* Special cases for 2M and 3M. */
- if ((speed >= ((3000000 * 100) / 103)) &&
- (speed <= ((3000000 * 100) / 97))) {
- result = 0;
- goto done;
- }
- if ((speed >= ((2000000 * 100) / 103)) &&
- (speed <= ((2000000 * 100) / 97))) {
- result = 1;
- goto done;
- }
- d = (FTDI_8U232AM_FREQ << 4) / speed;
- d = (d & ~15) + roundoff[d & 15];
-
- if (d < FTDI_8U232AM_MIN_DIV)
- d = FTDI_8U232AM_MIN_DIV;
- else if (d > FTDI_8U232AM_MAX_DIV)
- d = FTDI_8U232AM_MAX_DIV;
-
- /*
- * Calculate the frequency needed for "d" to exactly divide down to
- * our target "speed", and check that the actual frequency is within
- * 3% of this.
- */
- freq = (speed * d);
- if ((freq < ((FTDI_8U232AM_FREQ * 1600ULL) / 103)) ||
- (freq > ((FTDI_8U232AM_FREQ * 1600ULL) / 97)))
- return (1);
-
- /*
- * Pack the divisor into the resultant value. The lower 14-bits
- * hold the integral part, while the upper 2 bits encode the
- * fractional component: either 0, 0.5, 0.25, or 0.125.
- */
- result = (d >> 4);
- if (d & 8)
- result |= 0x4000;
- else if (d & 4)
- result |= 0x8000;
- else if (d & 2)
- result |= 0xc000;
-
-done:
- *rate = result;
- return (0);
-}
-
static void
uftdi_poll(struct ucom_softc *ucom)
{
diff --git a/sys/dev/usb/serial/uftdi_reg.h b/sys/dev/usb/serial/uftdi_reg.h
index 929e40a..21ce9a1 100644
--- a/sys/dev/usb/serial/uftdi_reg.h
+++ b/sys/dev/usb/serial/uftdi_reg.h
@@ -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 */
@@ -75,30 +79,12 @@
/*
* BmRequestType: 0100 0000B
* bRequest: FTDI_SIO_SET_BAUDRATE
- * wValue: BaudRate value - see below
- * wIndex: Port
+ * wValue: BaudRate low bits
+ * wIndex: Port and BaudRate high bits
* wLength: 0
* Data: None
*/
/* FTDI_SIO_SET_BAUDRATE */
-enum {
- ftdi_sio_b300 = 0,
- ftdi_sio_b600 = 1,
- ftdi_sio_b1200 = 2,
- ftdi_sio_b2400 = 3,
- ftdi_sio_b4800 = 4,
- ftdi_sio_b9600 = 5,
- ftdi_sio_b19200 = 6,
- ftdi_sio_b38400 = 7,
- ftdi_sio_b57600 = 8,
- ftdi_sio_b115200 = 9
-};
-
-#define FTDI_8U232AM_FREQ 3000000
-
-/* Bounds for normal divisors as 4-bit fixed precision ints. */
-#define FTDI_8U232AM_MIN_DIV 0x20
-#define FTDI_8U232AM_MAX_DIV 0x3fff8
/*
* BmRequestType: 0100 0000B
diff --git a/sys/dev/usb/uftdiio.h b/sys/dev/usb/uftdiio.h
new file mode 100644
index 0000000..0db2f38
--- /dev/null
+++ b/sys/dev/usb/uftdiio.h
@@ -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
OpenPOWER on IntegriCloud