USB: add SeaLevel 2106 SeaLINK support to ftdi_sio
[pandora-kernel.git] / drivers / usb / serial / ftdi_sio.c
index 8a74b19..e774a27 100644 (file)
 #include <asm/uaccess.h>
 #include <linux/usb.h>
 #include <linux/serial.h>
-#include "usb-serial.h"
+#include <linux/usb/serial.h>
 #include "ftdi_sio.h"
 
 /*
@@ -306,6 +306,8 @@ static struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = {
 
 
 static struct usb_device_id id_table_combined [] = {
+       { USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_ACTZWAVE_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_IRTRANS_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_IPLUS_PID) },
@@ -313,6 +315,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
        { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) },
        { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) },
@@ -336,10 +339,12 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_MTXORB_6_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) },
        { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) },
        { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) },
        { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) },
        { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2104_PID) },
+       { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2106_PID) },
        { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_1_PID) },
        { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_2_PID) },
        { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_1_PID) },
@@ -500,6 +505,12 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(PAPOUCH_VID, PAPOUCH_TMU_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_ACG_HFDUAL_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_YEI_SERVOCENTER31_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_THORLABS_PID) },
+       { USB_DEVICE(TESTO_VID, TESTO_USB_INTERFACE_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_GAMMA_SCOUT_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13M_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) },
        { },                                    /* Optional parameter entry */
        { }                                     /* Terminating entry */
 };
@@ -548,11 +559,17 @@ struct ftdi_private {
        spinlock_t rx_lock;     /* spinlock for receive state */
        struct work_struct rx_work;
        int rx_processed;
+       unsigned long rx_bytes;
 
        __u16 interface;        /* FT2232C port interface (0 for FT232/245) */
 
        int force_baud;         /* if non-zero, force the baud rate to this value */
        int force_rtscts;       /* if non-zero, force RTS-CTS to always be enabled */
+
+       spinlock_t tx_lock;     /* spinlock for transmit state */
+       unsigned long tx_bytes;
+       unsigned long tx_outstanding_bytes;
+       unsigned long tx_outstanding_urbs;
 };
 
 /* Used for TIOCMIWAIT */
@@ -626,6 +643,9 @@ static struct usb_serial_driver ftdi_sio_device = {
 #define HIGH 1
 #define LOW 0
 
+/* number of outstanding urbs to prevent userspace DoS from happening */
+#define URB_UPPER_LIMIT        42
+
 /*
  * ***************************************************************************
  * Utlity functions
@@ -1085,25 +1105,29 @@ static ssize_t store_event_char(struct device *dev, struct device_attribute *att
 static DEVICE_ATTR(latency_timer, S_IWUSR | S_IRUGO, show_latency_timer, store_latency_timer);
 static DEVICE_ATTR(event_char, S_IWUSR, NULL, store_event_char);
 
-static void create_sysfs_attrs(struct usb_serial *serial)
-{      
+static int create_sysfs_attrs(struct usb_serial *serial)
+{
        struct ftdi_private *priv;
        struct usb_device *udev;
+       int retval = 0;
 
        dbg("%s",__FUNCTION__);
-       
+
        priv = usb_get_serial_port_data(serial->port[0]);
        udev = serial->dev;
-       
+
        /* XXX I've no idea if the original SIO supports the event_char
         * sysfs parameter, so I'm playing it safe.  */
        if (priv->chip_type != SIO) {
                dbg("sysfs attributes for %s", ftdi_chip_name[priv->chip_type]);
-               device_create_file(&udev->dev, &dev_attr_event_char);
-               if (priv->chip_type == FT232BM || priv->chip_type == FT2232C) {
-                       device_create_file(&udev->dev, &dev_attr_latency_timer);
+               retval = device_create_file(&udev->dev, &dev_attr_event_char);
+               if ((!retval) &&
+                   (priv->chip_type == FT232BM || priv->chip_type == FT2232C)) {
+                       retval = device_create_file(&udev->dev,
+                                                   &dev_attr_latency_timer);
                }
        }
+       return retval;
 }
 
 static void remove_sysfs_attrs(struct usb_serial *serial)
@@ -1146,7 +1170,8 @@ static int ftdi_sio_attach (struct usb_serial *serial)
        struct usb_serial_port *port = serial->port[0];
        struct ftdi_private *priv;
        struct ftdi_sio_quirk *quirk;
-       
+       int retval;
+
        dbg("%s",__FUNCTION__);
 
        priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
@@ -1156,6 +1181,7 @@ static int ftdi_sio_attach (struct usb_serial *serial)
        }
 
        spin_lock_init(&priv->rx_lock);
+       spin_lock_init(&priv->tx_lock);
         init_waitqueue_head(&priv->delta_msr_wait);
        /* This will push the characters through immediately rather
           than queue a task to deliver them */
@@ -1186,15 +1212,18 @@ static int ftdi_sio_attach (struct usb_serial *serial)
        usb_set_serial_port_data(serial->port[0], priv);
 
        ftdi_determine_type (serial->port[0]);
-       create_sysfs_attrs(serial);
+       retval = create_sysfs_attrs(serial);
+       if (retval)
+               dev_err(&serial->dev->dev, "Error creating sysfs files, "
+                       "continuing\n");
 
        /* Check for device requiring special set up. */
        quirk = (struct ftdi_sio_quirk *)usb_get_serial_data(serial);
        if (quirk && quirk->setup) {
                quirk->setup(serial);
        }
-       
-       return (0);
+
+       return 0;
 } /* ftdi_sio_attach */
 
 
@@ -1270,6 +1299,13 @@ static int  ftdi_open (struct usb_serial_port *port, struct file *filp)
 
        dbg("%s", __FUNCTION__);
 
+       spin_lock_irqsave(&priv->tx_lock, flags);
+       priv->tx_bytes = 0;
+       spin_unlock_irqrestore(&priv->tx_lock, flags);
+       spin_lock_irqsave(&priv->rx_lock, flags);
+       priv->rx_bytes = 0;
+       spin_unlock_irqrestore(&priv->rx_lock, flags);
+
        if (port->tty)
                port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 
@@ -1372,6 +1408,7 @@ static int ftdi_write (struct usb_serial_port *port,
        int data_offset ;       /* will be 1 for the SIO and 0 otherwise */
        int status;
        int transfer_size;
+       unsigned long flags;
 
        dbg("%s port %d, %d bytes", __FUNCTION__, port->number, count);
 
@@ -1379,6 +1416,13 @@ static int ftdi_write (struct usb_serial_port *port,
                dbg("write request of 0 bytes");
                return 0;
        }
+       spin_lock_irqsave(&priv->tx_lock, flags);
+       if (priv->tx_outstanding_urbs > URB_UPPER_LIMIT) {
+               spin_unlock_irqrestore(&priv->tx_lock, flags);
+               dbg("%s - write limit hit\n", __FUNCTION__);
+               return 0;
+       }
+       spin_unlock_irqrestore(&priv->tx_lock, flags);
        
        data_offset = priv->write_offset;
         dbg("data_offset set to %d",data_offset);
@@ -1445,6 +1489,12 @@ static int ftdi_write (struct usb_serial_port *port,
                err("%s - failed submitting write urb, error %d", __FUNCTION__, status);
                count = status;
                kfree (buffer);
+       } else {
+               spin_lock_irqsave(&priv->tx_lock, flags);
+               ++priv->tx_outstanding_urbs;
+               priv->tx_outstanding_bytes += count;
+               priv->tx_bytes += count;
+               spin_unlock_irqrestore(&priv->tx_lock, flags);
        }
 
        /* we are done with this urb, so let the host driver
@@ -1460,7 +1510,11 @@ static int ftdi_write (struct usb_serial_port *port,
 
 static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
 {
+       unsigned long flags;
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+       struct ftdi_private *priv;
+       int data_offset;       /* will be 1 for the SIO and 0 otherwise */
+       unsigned long countback;
 
        /* free up the transfer buffer, as usb_free_urb() does not do this */
        kfree (urb->transfer_buffer);
@@ -1472,34 +1526,67 @@ static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
                return;
        }
 
+       priv = usb_get_serial_port_data(port);
+       if (!priv) {
+               dbg("%s - bad port private data pointer - exiting", __FUNCTION__);
+               return;
+       }
+       /* account for transferred data */
+       countback = urb->actual_length;
+       data_offset = priv->write_offset;
+       if (data_offset > 0) {
+               /* Subtract the control bytes */
+               countback -= (data_offset * ((countback + (PKTSZ - 1)) / PKTSZ));
+       }
+       spin_lock_irqsave(&priv->tx_lock, flags);
+       --priv->tx_outstanding_urbs;
+       priv->tx_outstanding_bytes -= countback;
+       spin_unlock_irqrestore(&priv->tx_lock, flags);
+
        usb_serial_port_softint(port);
 } /* ftdi_write_bulk_callback */
 
 
 static int ftdi_write_room( struct usb_serial_port *port )
 {
+       struct ftdi_private *priv = usb_get_serial_port_data(port);
+       int room;
+       unsigned long flags;
+
        dbg("%s - port %d", __FUNCTION__, port->number);
 
-       /*
-        * We really can take anything the user throws at us
-        * but let's pick a nice big number to tell the tty
-        * layer that we have lots of free space
-        */
-       return 2048;
+       spin_lock_irqsave(&priv->tx_lock, flags);
+       if (priv->tx_outstanding_urbs < URB_UPPER_LIMIT) {
+               /*
+                * We really can take anything the user throws at us
+                * but let's pick a nice big number to tell the tty
+                * layer that we have lots of free space
+                */
+               room = 2048;
+       } else {
+               room = 0;
+       }
+       spin_unlock_irqrestore(&priv->tx_lock, flags);
+       return room;
 } /* ftdi_write_room */
 
 
 static int ftdi_chars_in_buffer (struct usb_serial_port *port)
 { /* ftdi_chars_in_buffer */
+       struct ftdi_private *priv = usb_get_serial_port_data(port);
+       int buffered;
+       unsigned long flags;
+
        dbg("%s - port %d", __FUNCTION__, port->number);
 
-       /* 
-        * We can't really account for how much data we
-        * have sent out, but hasn't made it through to the
-        * device, so just tell the tty layer that everything
-        * is flushed.
-        */
-       return 0;
+       spin_lock_irqsave(&priv->tx_lock, flags);
+       buffered = (int)priv->tx_outstanding_bytes;
+       spin_unlock_irqrestore(&priv->tx_lock, flags);
+       if (buffered < 0) {
+               err("%s outstanding tx bytes is negative!", __FUNCTION__);
+               buffered = 0;
+       }
+       return buffered;
 } /* ftdi_chars_in_buffer */
 
 
@@ -1509,6 +1596,8 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
        struct tty_struct *tty;
        struct ftdi_private *priv;
+       unsigned long countread;
+       unsigned long flags;
 
        if (urb->number_of_packets > 0) {
                err("%s transfer_buffer_length %d actual_length %d number of packets %d",__FUNCTION__,
@@ -1543,6 +1632,13 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
                return;
        }
 
+       /* count data bytes, but not status bytes */
+       countread = urb->actual_length;
+       countread -= 2 * ((countread + (PKTSZ - 1)) / PKTSZ);
+       spin_lock_irqsave(&priv->rx_lock, flags);
+       priv->rx_bytes += countread;
+       spin_unlock_irqrestore(&priv->rx_lock, flags);
+
        ftdi_process_read(port);
 
 } /* ftdi_read_bulk_callback */