This patch adds DCD line support to CP210x USB serial driver. First it enables CP210x events embedding to incoming URB's by calling: cp210x_set_config_single(port, CP210X_EMBED_EVENTS, CP210X_ESCCHAR); Then it parses incoming URB's via custom routine: cp210x_process_read_urb(...) searches for event sequences and handles all of DCD changes calling usb_serial_handle_dcd_change(...) Signed-off-by: Valentin Yakovenkov <yakovenkov@xxxxxxxxx> --- drivers/usb/serial/cp210x.c | 115 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 7a76fe4..d9cba4e 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -46,6 +46,7 @@ static void cp210x_break_ctl(struct tty_struct *, int); static int cp210x_startup(struct usb_serial *); static void cp210x_release(struct usb_serial *); static void cp210x_dtr_rts(struct usb_serial_port *p, int on); +static void cp210x_process_read_urb(struct urb *urb); static const struct usb_device_id id_table[] = { { USB_DEVICE(0x045B, 0x0053) }, /* Renesas RX610 RX-Stick */ @@ -201,8 +202,17 @@ static const struct usb_device_id id_table[] = { MODULE_DEVICE_TABLE(usb, id_table); +#define CP210X_ESCCHAR 0x1e +#define CP210X_STATE_IDLE 0 +#define CP210X_STATE_ESC 1 +#define CP210X_STATE_LS0 2 +#define CP210X_STATE_LS1 3 +#define CP210X_STATE_LS 4 +#define CP210X_STATE_MS 5 + struct cp210x_serial_private { __u8 bInterfaceNumber; + int cp210x_tstate; }; static struct usb_serial_driver cp210x_device = { @@ -222,7 +232,8 @@ static struct usb_serial_driver cp210x_device = { .tiocmset = cp210x_tiocmset, .attach = cp210x_startup, .release = cp210x_release, - .dtr_rts = cp210x_dtr_rts + .dtr_rts = cp210x_dtr_rts, + .process_read_urb = cp210x_process_read_urb, }; static struct usb_serial_driver * const serial_drivers[] = { @@ -460,6 +471,11 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) { int result; + struct usb_serial *serial = port->serial; + struct cp210x_serial_private *spriv = usb_get_serial_data(serial); + + spriv->cp210x_tstate = CP210X_STATE_IDLE; + result = cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_ENABLE); if (result) { @@ -474,6 +490,15 @@ static int cp210x_open(struct tty_struct *tty, struct usb_serial_port *port) if (tty) cp210x_change_speed(tty, port, NULL); + /* Enable events embedding to data stream */ + result = cp210x_set_config_single(port, CP210X_EMBED_EVENTS, + CP210X_ESCCHAR); + if (result) { + dev_err(&port->dev, "%s - Unable to enable event embedding on UART\n", + __func__); + return result; + } + return usb_serial_generic_open(tty, port); } @@ -483,6 +508,94 @@ static void cp210x_close(struct usb_serial_port *port) cp210x_set_config_single(port, CP210X_IFC_ENABLE, UART_DISABLE); } +static void cp210x_process_read_urb(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + char *ch = (char *)urb->transfer_buffer; + char *tbuf = (char *)urb->transfer_buffer; + int i; + int tcnt = 0; + struct usb_serial *serial = port->serial; + struct cp210x_serial_private *spriv = usb_get_serial_data(serial); + + if (!urb->actual_length) + return; + + /* Process escape chars */ + for (i = 0; i < urb->actual_length; i++) { + char c = ch[i]; + + switch (spriv->cp210x_tstate) { + case CP210X_STATE_IDLE: + if (c == CP210X_ESCCHAR) + spriv->cp210x_tstate = CP210X_STATE_ESC; + else + tbuf[tcnt++] = c; + break; + + case CP210X_STATE_ESC: + if (c == 0x01) + spriv->cp210x_tstate = CP210X_STATE_LS0; + else if (c == 0x02) + spriv->cp210x_tstate = CP210X_STATE_LS; + else if (c == 0x03) + spriv->cp210x_tstate = CP210X_STATE_MS; + else { + tbuf[tcnt++] = (c == 0x00) ? CP210X_ESCCHAR : c; + spriv->cp210x_tstate = CP210X_STATE_IDLE; + } + break; + + case CP210X_STATE_LS0: + spriv->cp210x_tstate = CP210X_STATE_LS1; + break; + + case CP210X_STATE_LS1: + tbuf[tcnt++] = c; + spriv->cp210x_tstate = CP210X_STATE_IDLE; + break; + + case CP210X_STATE_LS: + spriv->cp210x_tstate = CP210X_STATE_IDLE; + break; + + case CP210X_STATE_MS: + if (c & 0x08) { + /* DCD change event */ + struct tty_struct *tty; + + port->icount.dcd++; + tty = tty_port_tty_get(&port->port); + if (tty) + usb_serial_handle_dcd_change(port, tty, + c & 0x80); + tty_kref_put(tty); + + } + wake_up_interruptible(&port->port.delta_msr_wait); + spriv->cp210x_tstate = CP210X_STATE_IDLE; + break; + } + } + + /* + * The per character mucking around with sysrq path it too slow for + * stuff like 3G modems, so shortcircuit it in the 99.9999999% of + * cases where the USB serial is not a console anyway. + */ + if (!port->port.console || !port->sysrq) { + tty_insert_flip_string(&port->port, tbuf, tcnt); + } else { + ch = tbuf; + for (i = 0; i < tcnt; i++, ch++) { + if (!usb_serial_handle_sysrq_char(port, *ch)) + tty_insert_flip_char(&port->port, *ch, + TTY_NORMAL); + } + } + tty_flip_buffer_push(&port->port); +} + /* * cp210x_get_termios * Reads the baud rate, data bits, parity, stop bits and flow control mode -- 2.5.0 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html