USB: belkin_sa: implement line status handling
[safe/jmp/linux-2.6] / drivers / usb / serial / belkin_sa.c
index c396736..36df352 100644 (file)
@@ -3,6 +3,7 @@
  *
  *  Copyright (C) 2000         William Greathouse (wgreathouse@smva.com)
  *  Copyright (C) 2000-2001    Greg Kroah-Hartman (greg@kroah.com)
+ *  Copyright (C) 2010         Johan Hovold (jhovold@gmail.com)
  *
  *  This program is largely derived from work by the linux-usb group
  *  and associated source files.  Please see the usb/serial files for
@@ -84,7 +85,7 @@ static int debug;
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.2"
+#define DRIVER_VERSION "v1.3"
 #define DRIVER_AUTHOR "William Greathouse <wgreathouse@smva.com>"
 #define DRIVER_DESC "USB Belkin Serial converter driver"
 
@@ -95,6 +96,7 @@ static int  belkin_sa_open(struct tty_struct *tty,
                        struct usb_serial_port *port);
 static void belkin_sa_close(struct usb_serial_port *port);
 static void belkin_sa_read_int_callback(struct urb *urb);
+static void belkin_sa_process_read_urb(struct urb *urb);
 static void belkin_sa_set_termios(struct tty_struct *tty,
                        struct usb_serial_port *port, struct ktermios * old);
 static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state);
@@ -135,7 +137,7 @@ static struct usb_serial_driver belkin_device = {
        .open =                 belkin_sa_open,
        .close =                belkin_sa_close,
        .read_int_callback =    belkin_sa_read_int_callback,
-                                       /* How we get the status info */
+       .process_read_urb =     belkin_sa_process_read_urb,
        .set_termios =          belkin_sa_set_termios,
        .break_ctl =            belkin_sa_break_ctl,
        .tiocmget =             belkin_sa_tiocmget,
@@ -289,31 +291,7 @@ static void belkin_sa_read_int_callback(struct urb *urb)
        else
                priv->control_state &= ~TIOCM_CD;
 
-       /* Now to report any errors */
        priv->last_lsr = data[BELKIN_SA_LSR_INDEX];
-#if 0
-       /*
-        * fill in the flip buffer here, but I do not know the relation
-        * to the current/next receive buffer or characters.  I need
-        * to look in to this before committing any code.
-        */
-       if (priv->last_lsr & BELKIN_SA_LSR_ERR) {
-               tty = tty_port_tty_get(&port->port);
-               /* Overrun Error */
-               if (priv->last_lsr & BELKIN_SA_LSR_OE) {
-               }
-               /* Parity Error */
-               if (priv->last_lsr & BELKIN_SA_LSR_PE) {
-               }
-               /* Framing Error */
-               if (priv->last_lsr & BELKIN_SA_LSR_FE) {
-               }
-               /* Break Indicator */
-               if (priv->last_lsr & BELKIN_SA_LSR_BI) {
-               }
-               tty_kref_put(tty);
-       }
-#endif
        spin_unlock_irqrestore(&priv->lock, flags);
 exit:
        retval = usb_submit_urb(urb, GFP_ATOMIC);
@@ -322,6 +300,53 @@ exit:
                        "result %d\n", __func__, retval);
 }
 
+static void belkin_sa_process_read_urb(struct urb *urb)
+{
+       struct usb_serial_port *port = urb->context;
+       struct belkin_sa_private *priv = usb_get_serial_port_data(port);
+       struct tty_struct *tty;
+       unsigned char *data = urb->transfer_buffer;
+       unsigned long flags;
+       unsigned char status;
+       char tty_flag;
+
+       /* Update line status */
+       tty_flag = TTY_NORMAL;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       status = priv->last_lsr;
+       priv->last_lsr &= ~BELKIN_SA_LSR_ERR;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       if (!urb->actual_length)
+               return;
+
+       tty = tty_port_tty_get(&port->port);
+       if (!tty)
+               return;
+
+       if (status & BELKIN_SA_LSR_ERR) {
+               /* Break takes precedence over parity, which takes precedence
+                * over framing errors. */
+               if (status & BELKIN_SA_LSR_BI)
+                       tty_flag = TTY_BREAK;
+               else if (status & BELKIN_SA_LSR_PE)
+                       tty_flag = TTY_PARITY;
+               else if (status & BELKIN_SA_LSR_FE)
+                       tty_flag = TTY_FRAME;
+               dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag);
+
+               /* Overrun is special, not associated with a char. */
+               if (status & BELKIN_SA_LSR_OE)
+                       tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+       }
+
+       tty_insert_flip_string_fixed_flag(tty, data, tty_flag,
+                                                       urb->actual_length);
+       tty_flip_buffer_push(tty);
+       tty_kref_put(tty);
+}
+
 static void belkin_sa_set_termios(struct tty_struct *tty,
                struct usb_serial_port *port, struct ktermios *old_termios)
 {