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.
This commit is contained in:
parent
e852cd6938
commit
cb4d7c77b6
@ -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
|
||||
|
@ -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;
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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 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("");
|
||||
}
|
||||
|
||||
static void
|
||||
uftdi_cfg_close(struct ucom_softc *ucom)
|
||||
{
|
||||
|
||||
/*
|
||||
* 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)) {
|
||||
case USB_ST_SETUP:
|
||||
case USB_ST_TRANSFERRED:
|
||||
tr_setup:
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
return;
|
||||
/* FALLTHROUGH */
|
||||
case USB_ST_SETUP:
|
||||
case USB_ST_TRANSFERRED:
|
||||
/*
|
||||
* 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 (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;
|
||||
}
|
||||
}
|
||||
if (buflen != 0) {
|
||||
usbd_xfer_set_frame_len(xfer, 0, buflen);
|
||||
usbd_transfer_submit(xfer);
|
||||
}
|
||||
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);
|
||||
|
||||
ftdi_msr = FTDI_GET_MSR(buf);
|
||||
lsr = FTDI_GET_LSR(buf);
|
||||
|
||||
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);
|
||||
|
||||
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,57 +1330,161 @@ 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));
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
return (ERANGE);
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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);
|
||||
}
|
||||
break;
|
||||
|
||||
case UFTDI_TYPE_8U232AM:
|
||||
if (uftdi_8u232am_getrate(t->c_ospeed, &cfg->rate)) {
|
||||
return (EINVAL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
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;
|
||||
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
75
sys/dev/usb/uftdiio.h
Normal file
75
sys/dev/usb/uftdiio.h
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user