]> git.hungrycats.org Git - linux/commitdiff
[PATCH] USB: add error reporting functionality to the pl2303 driver.
authorGreg Kroah-Hartman <greg@kroah.com>
Thu, 24 Apr 2003 07:54:09 +0000 (00:54 -0700)
committerGreg Kroah-Hartman <greg@kroah.com>
Thu, 24 Apr 2003 07:54:09 +0000 (00:54 -0700)
drivers/usb/serial/pl2303.c

index 1d3ec53f94a069a9fe01d814f6a529ab499c682d..ad8ff4de830c5d67578efd53c9250618c025753d 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * Prolific PL2303 USB to serial adaptor driver
  *
- * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2003 IBM Corp.
  *
  * Original driver for 2.2.x by anonymous
  *
@@ -111,6 +112,16 @@ static struct usb_driver pl2303_driver = {
 #define VENDOR_READ_REQUEST_TYPE       0xc0
 #define VENDOR_READ_REQUEST            0x01
 
+#define UART_STATE                     0x08
+#define UART_DCD                       0x01
+#define UART_DSR                       0x02
+#define UART_BREAK_ERROR               0x04
+#define UART_RING                      0x08
+#define UART_FRAME_ERROR               0x10
+#define UART_PARITY_ERROR              0x20
+#define UART_OVERRUN_ERROR             0x40
+#define UART_CTS                       0x80
+
 /* function prototypes for a PL2303 serial converter */
 static int pl2303_open (struct usb_serial_port *port, struct file *filp);
 static void pl2303_close (struct usb_serial_port *port, struct file *filp);
@@ -158,6 +169,7 @@ static struct usb_serial_device_type pl2303_device = {
 struct pl2303_private {
        spinlock_t lock;
        u8 line_control;
+       u8 line_status;
        u8 termios_initialized;
 };
 
@@ -227,6 +239,7 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
 {
        struct usb_serial *serial = port->serial;
        struct pl2303_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
        unsigned int cflag;
        unsigned char *buf;
        int baud;
@@ -239,13 +252,13 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
                return;
        }
 
-       spin_lock(&priv->lock);
+       spin_lock_irqsave(&priv->lock, flags);
        if (!priv->termios_initialized) {
                *(port->tty->termios) = tty_std_termios;
                port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
                priv->termios_initialized = 1;
        }
-       spin_unlock(&priv->lock);
+       spin_unlock_irqrestore(&priv->lock, flags);
 
        cflag = port->tty->termios->c_cflag;
        /* check that they really want us to change something */
@@ -355,13 +368,13 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
        if (cflag && CBAUD) {
                u8 control;
 
-               spin_lock (&priv->lock);
+               spin_lock_irqsave(&priv->lock, flags);
                if ((cflag && CBAUD) == B0)
                        priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
                else
                        priv->line_control |= (CONTROL_DTR | CONTROL_RTS);
                control = priv->line_control;
-               spin_unlock (&priv->lock);
+               spin_unlock_irqrestore(&priv->lock, flags);
                set_control_lines (serial->dev, control);
        }
        
@@ -450,6 +463,7 @@ static void pl2303_close (struct usb_serial_port *port, struct file *filp)
 {
        struct usb_serial *serial;
        struct pl2303_private *priv;
+       unsigned long flags;
        unsigned int c_cflag;
        int result;
 
@@ -486,9 +500,9 @@ static void pl2303_close (struct usb_serial_port *port, struct file *filp)
                if (c_cflag & HUPCL) {
                        /* drop DTR and RTS */
                        priv = usb_get_serial_port_data(port);
-                       spin_lock (&priv->lock);
+                       spin_lock_irqsave(&priv->lock, flags);
                        priv->line_control = 0;
-                       spin_unlock (&priv->lock);
+                       spin_unlock_irqrestore (&priv->lock, flags);
                        set_control_lines (port->serial->dev, 0);
                }
        }
@@ -499,9 +513,10 @@ static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
                            unsigned int set, unsigned int clear)
 {
        struct pl2303_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
        u8 control;
 
-       spin_lock (&priv->lock);
+       spin_lock_irqsave (&priv->lock, flags);
        if (set & TIOCM_RTS)
                priv->line_control |= CONTROL_RTS;
        if (set & TIOCM_DTR)
@@ -511,7 +526,7 @@ static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
        if (clear & TIOCM_DTR)
                priv->line_control &= ~CONTROL_DTR;
        control = priv->line_control;
-       spin_unlock (&priv->lock);
+       spin_unlock_irqrestore (&priv->lock, flags);
 
        return set_control_lines (port->serial->dev, control);
 }
@@ -519,14 +534,15 @@ static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
 static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file)
 {
        struct pl2303_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
        unsigned int mcr;
        unsigned int result;
 
        dbg("%s (%d)", __FUNCTION__, port->number);
 
-       spin_lock (&priv->lock);
+       spin_lock_irqsave (&priv->lock, flags);
        mcr = priv->line_control;
-       spin_unlock (&priv->lock);
+       spin_unlock_irqrestore (&priv->lock, flags);
 
        result = ((mcr & CONTROL_DTR)           ? TIOCM_DTR : 0)
                  | ((mcr & CONTROL_RTS)        ? TIOCM_RTS : 0);
@@ -588,9 +604,13 @@ static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs)
 {
        struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
        struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-       //unsigned char *data = urb->transfer_buffer;
+       struct pl2303_private *priv = usb_get_serial_port_data(port);
+       unsigned char *data = urb->transfer_buffer;
+       unsigned long flags;
        int status;
 
+       dbg("%s (%d)", __FUNCTION__, port->number);
+
        switch (urb->status) {
        case 0:
                /* success */
@@ -612,8 +632,14 @@ static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs)
 
        usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, urb->transfer_buffer);
 
-       //FIXME need to update state of terminal lines variable
+       if (urb->actual_length > UART_STATE)
+               goto exit;
 
+       /* Save off the uart status for others to look at */
+       spin_lock_irqsave(&priv->lock, flags);
+       priv->line_status = data[UART_STATE];
+       spin_unlock_irqrestore(&priv->lock, flags);
+               
 exit:
        status = usb_submit_urb (urb, GFP_ATOMIC);
        if (status)
@@ -626,10 +652,14 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
 {
        struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
        struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+       struct pl2303_private *priv = usb_get_serial_port_data(port);
        struct tty_struct *tty;
        unsigned char *data = urb->transfer_buffer;
+       unsigned long flags;
        int i;
        int result;
+       u8 status;
+       char tty_flag;
 
        if (port_paranoia_check (port, __FUNCTION__))
                return;
@@ -663,13 +693,34 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
 
        usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
 
+       /* get tty_flag from status */
+       tty_flag = TTY_NORMAL;
+
+       spin_lock_irqsave(&priv->lock, flags);
+       status = priv->line_status;
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       /* break takes precedence over parity, */
+       /* which takes precedence over framing errors */
+       if (status & UART_BREAK_ERROR )
+               tty_flag = TTY_BREAK;
+       else if (status & UART_PARITY_ERROR)
+               tty_flag = TTY_PARITY;
+       else if (status & UART_FRAME_ERROR)
+               tty_flag = TTY_FRAME;
+       dbg("%s - tty_flag = %d", __FUNCTION__, tty_flag);
+
        tty = port->tty;
        if (tty && urb->actual_length) {
+               /* overrun is special, not associated with a char */
+               if (status & UART_OVERRUN_ERROR)
+                       tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+
                for (i = 0; i < urb->actual_length; ++i) {
                        if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
                                tty_flip_buffer_push(tty);
                        }
-                       tty_insert_flip_char (tty, data[i], 0);
+                       tty_insert_flip_char (tty, data[i], tty_flag);
                }
                tty_flip_buffer_push (tty);
        }