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