usb/162307: [uslcom][patch] cp2103 usb-to-serial driver does not support modem control lines

JD Louw jl at nanoteq.com
Sat Nov 5 11:00:26 UTC 2011


>Number:         162307
>Category:       usb
>Synopsis:       [uslcom][patch] cp2103 usb-to-serial driver does not support modem control lines
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-usb
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sat Nov 05 11:00:21 UTC 2011
>Closed-Date:
>Last-Modified:
>Originator:     JD Louw
>Release:        FreeBSD 8.1-RELEASE i386
>Organization:
Nanoteq
>Environment:
FreeBSD jdl-desktop.lan 8.1-RELEASE FreeBSD 8.1-RELEASE #0: Thu Dec 30 10:20:16 SAST 2010     jdl at jdl-desktop.lan:/usr/obj/usr/src/sys/MYKERNEL2  i386
>Description:
The current cp2103 driver lacks modem control line status updates (CTS, DTS, RI, DCD). Also missing is hardware flow control mode.
>How-To-Repeat:
Connect a cp2103 adapter to any modem-like device and notice the modem status lines are not updated.
>Fix:
The attached patch adds hardware flow control as well as a polling usb transfer mechanism to update the modem status lines.

Patch attached with submission follows:

--- /usr/src/sys/dev/usb/serial/uslcom.c	2010-06-14 04:09:06.000000000 +0200
+++ uslcom.c	2011-11-05 09:33:04.000000000 +0200
@@ -64,43 +64,61 @@
 
 #define	USLCOM_SET_DATA_BITS(x)	((x) << 8)
 
+/* Request types */
 #define	USLCOM_WRITE		0x41
 #define	USLCOM_READ		0xc1
 
+/* Request codes */
 #define	USLCOM_UART		0x00
 #define	USLCOM_BAUD_RATE	0x01	
 #define	USLCOM_DATA		0x03
 #define	USLCOM_BREAK		0x05
 #define	USLCOM_CTRL		0x07
+#define	USLCOM_RCTRL		0x08
+#define	USLCOM_SET_FLOWCTRL	0x13
 
+/* USLCOM_UART values */
 #define	USLCOM_UART_DISABLE	0x00
 #define	USLCOM_UART_ENABLE	0x01
 
+/* USLCOM_CTRL/USLCOM_RCTRL values */
 #define	USLCOM_CTRL_DTR_ON	0x0001	
 #define	USLCOM_CTRL_DTR_SET	0x0100
 #define	USLCOM_CTRL_RTS_ON	0x0002
 #define	USLCOM_CTRL_RTS_SET	0x0200
 #define	USLCOM_CTRL_CTS		0x0010
 #define	USLCOM_CTRL_DSR		0x0020
+#define	USLCOM_CTRL_RI		0x0040
 #define	USLCOM_CTRL_DCD		0x0080
 
+/* USLCOM_BAUD_RATE values */
 #define	USLCOM_BAUD_REF		0x384000
 
+/* USLCOM_DATA values */
 #define	USLCOM_STOP_BITS_1	0x00
 #define	USLCOM_STOP_BITS_2	0x02
-
 #define	USLCOM_PARITY_NONE	0x00
 #define	USLCOM_PARITY_ODD	0x10
 #define	USLCOM_PARITY_EVEN	0x20
 
 #define	USLCOM_PORT_NO		0xFFFF /* XXX think this should be 0 --hps */
 
+/* USLCOM_BREAK values */
 #define	USLCOM_BREAK_OFF	0x00
 #define	USLCOM_BREAK_ON		0x01
 
+/* USLCOM_SET_FLOWCTRL values - 1st word */
+#define	USLCOM_FLOW_DTR_ON	0x00000001
+#define	USLCOM_FLOW_CTS_HS	0x00000008 /* CTS handshake */
+#define	USLCOM_FLOW_RESERVED	0xFFFFFF80
+/* USLCOM_SET_FLOWCTRL values - 2nd word */
+#define	USLCOM_FLOW_RTS_ON	0x00000040
+#define	USLCOM_FLOW_RTS_HS	0x00000080 /* RTS handshake */
+
 enum {
 	USLCOM_BULK_DT_WR,
 	USLCOM_BULK_DT_RD,
+	USLCOM_CTRL_DT_RD,
 	USLCOM_N_TRANSFER,
 };
 
@@ -122,6 +140,7 @@
 
 static usb_callback_t uslcom_write_callback;
 static usb_callback_t uslcom_read_callback;
+static usb_callback_t uslcom_control_callback;
 
 static void uslcom_open(struct ucom_softc *);
 static void uslcom_close(struct ucom_softc *);
@@ -144,7 +163,7 @@
 		.endpoint = UE_ADDR_ANY,
 		.direction = UE_DIR_OUT,
 		.bufsize = USLCOM_BULK_BUF_SIZE,
-		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
+		.flags = {.pipe_bof = 1,/*.force_short_xfer = 1,*/},
 		.callback = &uslcom_write_callback,
 	},
 
@@ -156,6 +175,15 @@
 		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
 		.callback = &uslcom_read_callback,
 	},
+	[USLCOM_CTRL_DT_RD] = {
+		.type = UE_CONTROL,
+		.endpoint = 0x00,
+		.direction = UE_DIR_ANY,
+		.interval = 250, /* poll status every 250 ms */
+		.bufsize = USLCOM_BULK_BUF_SIZE,
+		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
+		.callback = &uslcom_control_callback,
+	},
 };
 
 static struct ucom_callback uslcom_callback = {
@@ -262,6 +290,7 @@
 	mtx_lock(&sc->sc_mtx);
 	usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]);
 	usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]);
+	usbd_xfer_set_stall(sc->sc_xfer[USLCOM_CTRL_DT_RD]);
 	mtx_unlock(&sc->sc_mtx);
 
 	error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
@@ -306,6 +335,8 @@
 	    &req, NULL, 0, 1000)) {
 		DPRINTF("UART enable failed (ignored)\n");
 	}
+	/* Start polling status */
+	usbd_transfer_start(sc->sc_xfer[USLCOM_CTRL_DT_RD]);
 }
 
 static void
@@ -314,6 +345,9 @@
 	struct uslcom_softc *sc = ucom->sc_parent;
 	struct usb_device_request req;
 
+	/* Stop polling status */
+	usbd_transfer_stop(sc->sc_xfer[USLCOM_CTRL_DT_RD]);
+
 	req.bmRequestType = USLCOM_WRITE;
 	req.bRequest = USLCOM_UART;
 	USETW(req.wValue, USLCOM_UART_DISABLE);
@@ -388,6 +422,7 @@
 	struct uslcom_softc *sc = ucom->sc_parent;
 	struct usb_device_request req;
 	uint16_t data;
+	uint32_t flowctrl[4];
 
 	DPRINTF("\n");
 
@@ -438,6 +473,26 @@
 	    &req, NULL, 0, 1000)) {
 		DPRINTF("Set format failed (ignored)\n");
 	}
+	
+	if (t->c_cflag & CRTSCTS) {
+		flowctrl[0] = USLCOM_FLOW_RESERVED | USLCOM_FLOW_DTR_ON | 
+			USLCOM_FLOW_CTS_HS;
+		flowctrl[1] = USLCOM_FLOW_RTS_HS;
+	} else {
+		flowctrl[0] = USLCOM_FLOW_RESERVED | USLCOM_FLOW_DTR_ON;
+		flowctrl[1] = USLCOM_FLOW_RTS_ON;
+	}
+	req.bmRequestType = USLCOM_WRITE;
+	req.bRequest = USLCOM_SET_FLOWCTRL;
+	USETW(req.wValue, 0);
+	USETW(req.wIndex, USLCOM_PORT_NO);
+	USETW(req.wLength, sizeof(flowctrl));
+
+        if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, 
+	    &req, flowctrl, 0, 1000)) {
+		DPRINTF("Set flowcontrol failed (ignored)\n");
+	}
+	
 	return;
 }
 
@@ -534,6 +589,61 @@
 }
 
 static void
+uslcom_control_callback(struct usb_xfer *xfer, usb_error_t error)
+{
+	struct uslcom_softc *sc = usbd_xfer_softc(xfer);
+	struct usb_page_cache *pc;
+	uint8_t buf;
+	struct usb_device_request req;
+	uint8_t msr = 0;
+
+	switch (USB_GET_STATE(xfer)) {
+	case USB_ST_TRANSFERRED:
+		pc = usbd_xfer_get_frame(xfer, 1);
+		usbd_copy_out(pc, 0, &buf, sizeof(buf));
+		if (buf & USLCOM_CTRL_CTS)
+			msr |= SER_CTS;
+		if (buf & USLCOM_CTRL_DSR)
+			msr |= SER_DSR;
+		if (buf & USLCOM_CTRL_RI)
+			msr |= SER_RI;
+		if (buf & USLCOM_CTRL_DCD)
+			msr |= SER_DCD;
+
+		if (msr != sc->sc_msr) {
+			DPRINTF("status change msr=0x%02x (was 0x%02x)\n", msr, sc->sc_msr);
+			sc->sc_msr = msr;
+			ucom_status_change(&sc->sc_ucom);
+		}
+		/* no break */
+	case USB_ST_SETUP:
+tr_setup:		
+		req.bmRequestType = USLCOM_READ;
+		req.bRequest = USLCOM_RCTRL;
+		USETW(req.wValue, 0);
+		USETW(req.wIndex, 0);
+		USETW(req.wLength, sizeof(buf));
+		
+		usbd_xfer_set_frames(xfer, 2);
+		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
+		usbd_xfer_set_frame_len(xfer, 1, sizeof(buf));
+
+		pc = usbd_xfer_get_frame(xfer, 0);
+		usbd_copy_in(pc, 0, &req, sizeof(req));
+		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;
+	}
+}
+
+static void
 uslcom_start_read(struct ucom_softc *ucom)
 {
 	struct uslcom_softc *sc = ucom->sc_parent;


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-usb mailing list