svn commit: r264031 - head/sys/dev/usb/serial
Ian Lepore
ian at FreeBSD.org
Wed Apr 2 01:58:55 UTC 2014
Author: ian
Date: Wed Apr 2 01:58:54 2014
New Revision: 264031
URL: http://svnweb.freebsd.org/changeset/base/264031
Log:
Use 2K buffers for IO to help achieve full device speed, rather than the
default wMaxPacketSize (64 or 512 bytes). This actually helps older FTDI
devices (which were USB 1/full speed) more than the new H-series high
speed, but even for the new chips it helps cut the number of interrupts
when doing very high speed (3-12mbaud).
Modified:
head/sys/dev/usb/serial/uftdi.c
Modified: head/sys/dev/usb/serial/uftdi.c
==============================================================================
--- head/sys/dev/usb/serial/uftdi.c Tue Apr 1 22:54:54 2014 (r264030)
+++ head/sys/dev/usb/serial/uftdi.c Wed Apr 2 01:58:54 2014 (r264031)
@@ -63,6 +63,7 @@ __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 "usbdevs.h"
#define USB_DEBUG_VAR uftdi_debug
@@ -83,8 +84,34 @@ SYSCTL_INT(_hw_usb_uftdi, OID_AUTO, debu
#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,
@@ -178,7 +205,7 @@ static const struct usb_config uftdi_con
.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,
},
@@ -1096,35 +1123,47 @@ uftdi_write_callback(struct usb_xfer *xf
{
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;
}
}
@@ -1137,23 +1176,47 @@ uftdi_read_callback(struct usb_xfer *xfe
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)
@@ -1174,11 +1237,7 @@ uftdi_read_callback(struct usb_xfer *xfe
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));
More information about the svn-src-all
mailing list