Date: Sat, 5 Nov 2011 10:51:14 GMT From: JD Louw <jl@nanoteq.com> To: freebsd-gnats-submit@FreeBSD.org Subject: usb/162307: [uslcom][patch] cp2103 usb-to-serial driver does not support modem control lines Message-ID: <201111051051.pA5ApEAm025432@red.freebsd.org> Resent-Message-ID: <201111051100.pA5B0LLw021681@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>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@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:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201111051051.pA5ApEAm025432>