Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 2 Apr 2014 01:58:54 +0000 (UTC)
From:      Ian Lepore <ian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r264031 - head/sys/dev/usb/serial
Message-ID:  <201404020158.s321wsB0002050@svn.freebsd.org>

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



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