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 <yakoven...@niistt.ru>
---
 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to