]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/serial/ftdi_sio.c
Pull misc into release branch
[linux-2.6-omap-h63xx.git] / drivers / usb / serial / ftdi_sio.c
index 8ff9d54b21e669ebca6a521964427873d118ba81..7b1673a440775aaef3673915432cbed74c508051 100644 (file)
@@ -271,20 +271,58 @@ static int debug;
 static __u16 vendor = FTDI_VID;
 static __u16 product;
 
+struct ftdi_private {
+       ftdi_chip_type_t chip_type;
+                               /* type of the device, either SIO or FT8U232AM */
+       int baud_base;          /* baud base clock for divisor setting */
+       int custom_divisor;     /* custom_divisor kludge, this is for baud_base (different from what goes to the chip!) */
+       __u16 last_set_data_urb_value ;
+                               /* the last data state set - needed for doing a break */
+        int write_offset;       /* This is the offset in the usb data block to write the serial data -
+                                * it is different between devices
+                                */
+       int flags;              /* some ASYNC_xxxx flags are supported */
+       unsigned long last_dtr_rts;     /* saved modem control outputs */
+        wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
+       char prev_status, diff_status;        /* Used for TIOCMIWAIT */
+       __u8 rx_flags;          /* receive state flags (throttling) */
+       spinlock_t rx_lock;     /* spinlock for receive state */
+       struct delayed_work rx_work;
+       struct usb_serial_port *port;
+       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;
+};
+
 /* struct ftdi_sio_quirk is used by devices requiring special attention. */
 struct ftdi_sio_quirk {
-       void (*setup)(struct usb_serial *); /* Special settings during startup. */
+       int (*probe)(struct usb_serial *);
+       void (*port_probe)(struct ftdi_private *); /* Special settings for probed ports. */
 };
 
-static void  ftdi_USB_UIRT_setup       (struct usb_serial *serial);
-static void  ftdi_HE_TIRA1_setup       (struct usb_serial *serial);
+static int   ftdi_olimex_probe         (struct usb_serial *serial);
+static void  ftdi_USB_UIRT_setup       (struct ftdi_private *priv);
+static void  ftdi_HE_TIRA1_setup       (struct ftdi_private *priv);
+
+static struct ftdi_sio_quirk ftdi_olimex_quirk = {
+       .probe  = ftdi_olimex_probe,
+};
 
 static struct ftdi_sio_quirk ftdi_USB_UIRT_quirk = {
-       .setup = ftdi_USB_UIRT_setup,
+       .port_probe = ftdi_USB_UIRT_setup,
 };
 
 static struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = {
-       .setup = ftdi_HE_TIRA1_setup,
+       .port_probe = ftdi_HE_TIRA1_setup,
 };
 
 /*
@@ -311,6 +349,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_ACTZWAVE_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_IRTRANS_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_IPLUS_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_IPLUS2_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_DMX4ALL) },
        { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) },
@@ -319,6 +358,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_PID) },
        { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) },
        { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_XF_632_PID) },
@@ -342,6 +382,7 @@ static struct usb_device_id id_table_combined [] = {
        { 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(FTDI_VID, FTDI_USBX_707_PID) },
        { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) },
        { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) },
        { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) },
@@ -524,6 +565,9 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) },
        { USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) },
        { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_MAXSTREAM_PID) },
+       { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID),
+               .driver_info = (kernel_ulong_t)&ftdi_olimex_quirk },
        { },                                    /* Optional parameter entry */
        { }                                     /* Terminating entry */
 };
@@ -555,38 +599,6 @@ static const char *ftdi_chip_name[] = {
 #define THROTTLED              0x01
 #define ACTUALLY_THROTTLED     0x02
 
-struct ftdi_private {
-       ftdi_chip_type_t chip_type;
-                               /* type of the device, either SIO or FT8U232AM */
-       int baud_base;          /* baud base clock for divisor setting */
-       int custom_divisor;     /* custom_divisor kludge, this is for baud_base (different from what goes to the chip!) */
-       __u16 last_set_data_urb_value ;
-                               /* the last data state set - needed for doing a break */
-        int write_offset;       /* This is the offset in the usb data block to write the serial data -
-                                * it is different between devices
-                                */
-       int flags;              /* some ASYNC_xxxx flags are supported */
-       unsigned long last_dtr_rts;     /* saved modem control outputs */
-        wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
-       char prev_status, diff_status;        /* Used for TIOCMIWAIT */
-       __u8 rx_flags;          /* receive state flags (throttling) */
-       spinlock_t rx_lock;     /* spinlock for receive state */
-       struct delayed_work rx_work;
-       struct usb_serial_port *port;
-       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 */
 #define FTDI_STATUS_B0_MASK    (FTDI_RS0_CTS | FTDI_RS0_DSR | FTDI_RS0_RI | FTDI_RS0_RLSD)
 #define FTDI_STATUS_B1_MASK    (FTDI_RS_BI)
@@ -597,7 +609,6 @@ struct ftdi_private {
 
 /* function prototypes for a FTDI serial converter */
 static int  ftdi_sio_probe     (struct usb_serial *serial, const struct usb_device_id *id);
-static int  ftdi_sio_attach            (struct usb_serial *serial);
 static void ftdi_shutdown              (struct usb_serial *serial);
 static int  ftdi_sio_port_probe        (struct usb_serial_port *port);
 static int  ftdi_sio_port_remove       (struct usb_serial_port *port);
@@ -651,7 +662,6 @@ static struct usb_serial_driver ftdi_sio_device = {
        .ioctl =                ftdi_ioctl,
        .set_termios =          ftdi_set_termios,
        .break_ctl =            ftdi_break_ctl,
-       .attach =               ftdi_sio_attach,
        .shutdown =             ftdi_shutdown,
 };
 
@@ -668,7 +678,7 @@ static struct usb_serial_driver ftdi_sio_device = {
 
 /*
  * ***************************************************************************
- * Utlity functions
+ * Utility functions
  * ***************************************************************************
  */
 
@@ -1137,7 +1147,9 @@ static int create_sysfs_attrs(struct usb_serial_port *port)
                dbg("sysfs attributes for %s", ftdi_chip_name[priv->chip_type]);
                retval = device_create_file(&port->dev, &dev_attr_event_char);
                if ((!retval) &&
-                   (priv->chip_type == FT232BM || priv->chip_type == FT2232C)) {
+                   (priv->chip_type == FT232BM ||
+                    priv->chip_type == FT2232C ||
+                    priv->chip_type == FT232RL)) {
                        retval = device_create_file(&port->dev,
                                                    &dev_attr_latency_timer);
                }
@@ -1170,14 +1182,24 @@ static void remove_sysfs_attrs(struct usb_serial_port *port)
 /* Probe function to check for special devices */
 static int ftdi_sio_probe (struct usb_serial *serial, const struct usb_device_id *id)
 {
+       struct ftdi_sio_quirk *quirk = (struct ftdi_sio_quirk *)id->driver_info;
+
+       if (quirk && quirk->probe) {
+               int ret = quirk->probe(serial);
+               if (ret != 0)
+                       return ret;
+       }
+
        usb_set_serial_data(serial, (void *)id->driver_info);
 
-       return (0);
+       return 0;
 }
 
 static int ftdi_sio_port_probe(struct usb_serial_port *port)
 {
        struct ftdi_private *priv;
+       struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial);
+
 
        dbg("%s",__FUNCTION__);
 
@@ -1194,6 +1216,9 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
           than queue a task to deliver them */
        priv->flags = ASYNC_LOW_LATENCY;
 
+       if (quirk && quirk->port_probe)
+               quirk->port_probe(priv);
+
        /* Increase the size of read buffers */
        kfree(port->bulk_in_buffer);
        port->bulk_in_buffer = kmalloc (BUFSZ, GFP_KERNEL);
@@ -1224,29 +1249,13 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
        return 0;
 }
 
-/* attach subroutine */
-static int ftdi_sio_attach (struct usb_serial *serial)
-{
-       /* Check for device requiring special set up. */
-       struct ftdi_sio_quirk *quirk = usb_get_serial_data(serial);
-
-       if (quirk && quirk->setup)
-               quirk->setup(serial);
-
-       return 0;
-} /* ftdi_sio_attach */
-
-
 /* Setup for the USB-UIRT device, which requires hardwired
  * baudrate (38400 gets mapped to 312500) */
 /* Called from usbserial:serial_probe */
-static void ftdi_USB_UIRT_setup (struct usb_serial *serial)
+static void ftdi_USB_UIRT_setup (struct ftdi_private *priv)
 {
-       struct ftdi_private *priv;
-
        dbg("%s",__FUNCTION__);
 
-       priv = usb_get_serial_port_data(serial->port[0]);
        priv->flags |= ASYNC_SPD_CUST;
        priv->custom_divisor = 77;
        priv->force_baud = B38400;
@@ -1254,19 +1263,34 @@ static void ftdi_USB_UIRT_setup (struct usb_serial *serial)
 
 /* Setup for the HE-TIRA1 device, which requires hardwired
  * baudrate (38400 gets mapped to 100000) and RTS-CTS enabled.  */
-static void ftdi_HE_TIRA1_setup (struct usb_serial *serial)
+static void ftdi_HE_TIRA1_setup (struct ftdi_private *priv)
 {
-       struct ftdi_private *priv;
-
        dbg("%s",__FUNCTION__);
 
-       priv = usb_get_serial_port_data(serial->port[0]);
        priv->flags |= ASYNC_SPD_CUST;
        priv->custom_divisor = 240;
        priv->force_baud = B38400;
        priv->force_rtscts = 1;
 } /* ftdi_HE_TIRA1_setup */
 
+/*
+ * First port on Olimex arm-usb-ocd is reserved for JTAG interface
+ * and can be accessed from userspace using openocd.
+ */
+static int ftdi_olimex_probe(struct usb_serial *serial)
+{
+       struct usb_device *udev = serial->dev;
+       struct usb_interface *interface = serial->interface;
+
+       dbg("%s",__FUNCTION__);
+
+       if (interface == udev->actconfig->interface[0]) {
+               info("Ignoring reserved serial port on Olimex arm-usb-ocd\n");
+               return -ENODEV;
+       }
+
+       return 0;
+}
 
 /* ftdi_shutdown is called from usbserial:usb_serial_disconnect
  *   it is called when the usb device is disconnected
@@ -1433,6 +1457,7 @@ static int ftdi_write (struct usb_serial_port *port,
                dbg("%s - write limit hit\n", __FUNCTION__);
                return 0;
        }
+       priv->tx_outstanding_urbs++;
        spin_unlock_irqrestore(&priv->tx_lock, flags);
 
        data_offset = priv->write_offset;
@@ -1450,14 +1475,15 @@ static int ftdi_write (struct usb_serial_port *port,
        buffer = kmalloc (transfer_size, GFP_ATOMIC);
        if (!buffer) {
                err("%s ran out of kernel memory for urb ...", __FUNCTION__);
-               return -ENOMEM;
+               count = -ENOMEM;
+               goto error_no_buffer;
        }
 
        urb = usb_alloc_urb(0, GFP_ATOMIC);
        if (!urb) {
                err("%s - no more free urbs", __FUNCTION__);
-               kfree (buffer);
-               return -ENOMEM;
+               count = -ENOMEM;
+               goto error_no_urb;
        }
 
        /* Copy data */
@@ -1499,10 +1525,9 @@ static int ftdi_write (struct usb_serial_port *port,
        if (status) {
                err("%s - failed submitting write urb, error %d", __FUNCTION__, status);
                count = status;
-               kfree (buffer);
+               goto error;
        } 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);
@@ -1510,10 +1535,19 @@ static int ftdi_write (struct usb_serial_port *port,
 
        /* we are done with this urb, so let the host driver
         * really free it when it is finished with it */
-       usb_free_urb (urb);
+       usb_free_urb(urb);
 
        dbg("%s write returning: %d", __FUNCTION__, count);
        return count;
+error:
+       usb_free_urb(urb);
+error_no_urb:
+       kfree (buffer);
+error_no_buffer:
+       spin_lock_irqsave(&priv->tx_lock, flags);
+       priv->tx_outstanding_urbs--;
+       spin_unlock_irqrestore(&priv->tx_lock, flags);
+       return count;
 } /* ftdi_write */
 
 
@@ -1526,14 +1560,15 @@ static void ftdi_write_bulk_callback (struct urb *urb)
        struct ftdi_private *priv;
        int data_offset;       /* will be 1 for the SIO and 0 otherwise */
        unsigned long countback;
+       int status = urb->status;
 
        /* free up the transfer buffer, as usb_free_urb() does not do this */
        kfree (urb->transfer_buffer);
 
        dbg("%s - port %d", __FUNCTION__, port->number);
 
-       if (urb->status) {
-               dbg("nonzero write bulk status received: %d", urb->status);
+       if (status) {
+               dbg("nonzero write bulk status received: %d", status);
                return;
        }
 
@@ -1609,6 +1644,7 @@ static void ftdi_read_bulk_callback (struct urb *urb)
        struct ftdi_private *priv;
        unsigned long countread;
        unsigned long flags;
+       int status = urb->status;
 
        if (urb->number_of_packets > 0) {
                err("%s transfer_buffer_length %d actual_length %d number of packets %d",__FUNCTION__,
@@ -1637,9 +1673,10 @@ static void ftdi_read_bulk_callback (struct urb *urb)
                err("%s - Not my urb!", __FUNCTION__);
        }
 
-       if (urb->status) {
+       if (status) {
                /* This will happen at close every time so it is a dbg not an err */
-               dbg("(this is ok on close) nonzero read bulk status received: %d", urb->status);
+               dbg("(this is ok on close) nonzero read bulk status received: "
+                   "%d", status);
                return;
        }