digi_acceleport: Drag the driver kicking and screaming into coding style
[safe/jmp/linux-2.6] / drivers / usb / serial / ftdi_sio.c
index 35cad42..7b1673a 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,13 +349,16 @@ 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) },
        { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_232RL_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(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) },
@@ -341,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) },
@@ -420,6 +462,14 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_ELV_ALC8500_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_PYRAMID_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1000PC_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_IBS_US485_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_IBS_PICPRO_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_IBS_PCMCIA_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_IBS_PK1_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_IBS_RS232MON_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_IBS_APP70_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) },
        /*
         * These will probably use user-space drivers.  Uncomment them if
         * you need them or use the user-specified vendor/product module
@@ -459,12 +509,12 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FALCOM_VID, FALCOM_TWIST_PID) },
        { USB_DEVICE(FALCOM_VID, FALCOM_SAMBA_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_SUUNTO_SPORTS_PID) },
+       { USB_DEVICE(TTI_VID, TTI_QL355P_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_RM_CANVIEW_PID) },
        { USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) },
        { USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) },
        { USB_DEVICE(BANDB_VID, BANDB_USO9ML2_PID) },
        { USB_DEVICE(FTDI_VID, EVER_ECO_PRO_CDS) },
-       { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_0_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_1_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_2_PID) },
        { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_0_PID) },
@@ -514,6 +564,10 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) },
        { 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 */
 };
@@ -533,6 +587,7 @@ static const char *ftdi_chip_name[] = {
        [FT8U232AM] = "FT8U232AM",
        [FT232BM] = "FT232BM",
        [FT2232C] = "FT2232C",
+       [FT232RL] = "FT232RL",
 };
 
 
@@ -544,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)
@@ -586,8 +609,9 @@ 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);
 static int  ftdi_open                  (struct usb_serial_port *port, struct file *filp);
 static void ftdi_close                 (struct usb_serial_port *port, struct file *filp);
 static int  ftdi_write                 (struct usb_serial_port *port, const unsigned char *buf, int count);
@@ -622,6 +646,8 @@ static struct usb_serial_driver ftdi_sio_device = {
        .num_bulk_out =         1,
        .num_ports =            1,
        .probe =                ftdi_sio_probe,
+       .port_probe =           ftdi_sio_port_probe,
+       .port_remove =          ftdi_sio_port_remove,
        .open =                 ftdi_open,
        .close =                ftdi_close,
        .throttle =             ftdi_throttle,
@@ -636,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,
 };
 
@@ -653,7 +678,7 @@ static struct usb_serial_driver ftdi_sio_device = {
 
 /*
  * ***************************************************************************
- * Utlity functions
+ * Utility functions
  * ***************************************************************************
  */
 
@@ -864,6 +889,7 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port)
                break;
        case FT232BM: /* FT232BM chip */
        case FT2232C: /* FT2232C chip */
+       case FT232RL:
                if (baud <= 3000000) {
                        div_value = ftdi_232bm_baud_to_divisor(baud);
                } else {
@@ -1006,9 +1032,12 @@ static void ftdi_determine_type(struct usb_serial_port *port)
                /* (It might be a BM because of the iSerialNumber bug,
                 * but it will still work as an AM device.) */
                priv->chip_type = FT8U232AM;
-       } else {
+       } else if (version < 0x600) {
                /* Assume its an FT232BM (or FT245BM) */
                priv->chip_type = FT232BM;
+       } else {
+               /* Assume its an FT232R  */
+               priv->chip_type = FT232RL;
        }
        info("Detected %s", ftdi_chip_name[priv->chip_type]);
 }
@@ -1024,11 +1053,10 @@ static ssize_t show_latency_timer(struct device *dev, struct device_attribute *a
 {
        struct usb_serial_port *port = to_usb_serial_port(dev);
        struct ftdi_private *priv = usb_get_serial_port_data(port);
-       struct usb_device *udev;
+       struct usb_device *udev = port->serial->dev;
        unsigned short latency = 0;
        int rv = 0;
 
-       udev = to_usb_device(dev);
 
        dbg("%s",__FUNCTION__);
 
@@ -1052,13 +1080,11 @@ static ssize_t store_latency_timer(struct device *dev, struct device_attribute *
 {
        struct usb_serial_port *port = to_usb_serial_port(dev);
        struct ftdi_private *priv = usb_get_serial_port_data(port);
-       struct usb_device *udev;
+       struct usb_device *udev = port->serial->dev;
        char buf[1];
        int v = simple_strtoul(valbuf, NULL, 10);
        int rv = 0;
 
-       udev = to_usb_device(dev);
-
        dbg("%s: setting latency timer = %i", __FUNCTION__, v);
 
        rv = usb_control_msg(udev,
@@ -1083,13 +1109,11 @@ static ssize_t store_event_char(struct device *dev, struct device_attribute *att
 {
        struct usb_serial_port *port = to_usb_serial_port(dev);
        struct ftdi_private *priv = usb_get_serial_port_data(port);
-       struct usb_device *udev;
+       struct usb_device *udev = port->serial->dev;
        char buf[1];
        int v = simple_strtoul(valbuf, NULL, 10);
        int rv = 0;
 
-       udev = to_usb_device(dev);
-
        dbg("%s: setting event char = %i", __FUNCTION__, v);
 
        rv = usb_control_msg(udev,
@@ -1110,46 +1134,40 @@ 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 int create_sysfs_attrs(struct usb_serial *serial)
+static int create_sysfs_attrs(struct usb_serial_port *port)
 {
-       struct ftdi_private *priv;
-       struct usb_device *udev;
+       struct ftdi_private *priv = usb_get_serial_port_data(port);
        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]);
-               retval = device_create_file(&udev->dev, &dev_attr_event_char);
+               retval = device_create_file(&port->dev, &dev_attr_event_char);
                if ((!retval) &&
-                   (priv->chip_type == FT232BM || priv->chip_type == FT2232C)) {
-                       retval = device_create_file(&udev->dev,
+                   (priv->chip_type == FT232BM ||
+                    priv->chip_type == FT2232C ||
+                    priv->chip_type == FT232RL)) {
+                       retval = device_create_file(&port->dev,
                                                    &dev_attr_latency_timer);
                }
        }
        return retval;
 }
 
-static void remove_sysfs_attrs(struct usb_serial *serial)
+static void remove_sysfs_attrs(struct usb_serial_port *port)
 {
-       struct ftdi_private *priv;
-       struct usb_device *udev;
+       struct ftdi_private *priv = usb_get_serial_port_data(port);
 
        dbg("%s",__FUNCTION__);
 
-       priv = usb_get_serial_port_data(serial->port[0]);
-       udev = serial->dev;
-
        /* XXX see create_sysfs_attrs */
        if (priv->chip_type != SIO) {
-               device_remove_file(&udev->dev, &dev_attr_event_char);
+               device_remove_file(&port->dev, &dev_attr_event_char);
                if (priv->chip_type == FT232BM || priv->chip_type == FT2232C) {
-                       device_remove_file(&udev->dev, &dev_attr_latency_timer);
+                       device_remove_file(&port->dev, &dev_attr_latency_timer);
                }
        }
 
@@ -1164,18 +1182,24 @@ static void remove_sysfs_attrs(struct usb_serial *serial)
 /* 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;
 }
 
-/* attach subroutine */
-static int ftdi_sio_attach (struct usb_serial *serial)
+static int ftdi_sio_port_probe(struct usb_serial_port *port)
 {
-       struct usb_serial_port *port = serial->port[0];
        struct ftdi_private *priv;
-       struct ftdi_sio_quirk *quirk;
-       int retval;
+       struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial);
+
 
        dbg("%s",__FUNCTION__);
 
@@ -1192,6 +1216,9 @@ static int ftdi_sio_attach (struct usb_serial *serial)
           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);
@@ -1215,34 +1242,20 @@ static int ftdi_sio_attach (struct usb_serial *serial)
        kfree(port->bulk_out_buffer);
        port->bulk_out_buffer = NULL;
 
-       usb_set_serial_port_data(serial->port[0], priv);
-
-       ftdi_determine_type (serial->port[0]);
-       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);
-       }
+       usb_set_serial_port_data(port, priv);
 
+       ftdi_determine_type (port);
+       create_sysfs_attrs(port);
        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;
@@ -1250,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
@@ -1271,17 +1299,18 @@ static void ftdi_HE_TIRA1_setup (struct usb_serial *serial)
  *      calls __serial_close for each open of the port
  *      shutdown is called then (ie ftdi_shutdown)
  */
-
-
 static void ftdi_shutdown (struct usb_serial *serial)
-{ /* ftdi_shutdown */
+{
+       dbg("%s", __FUNCTION__);
+}
 
-       struct usb_serial_port *port = serial->port[0];
+static int ftdi_sio_port_remove(struct usb_serial_port *port)
+{
        struct ftdi_private *priv = usb_get_serial_port_data(port);
 
        dbg("%s", __FUNCTION__);
 
-       remove_sysfs_attrs(serial);
+       remove_sysfs_attrs(port);
 
        /* all open ports are closed at this point
          *    (by usbserial.c:__serial_close, which calls ftdi_close)
@@ -1291,8 +1320,9 @@ static void ftdi_shutdown (struct usb_serial *serial)
                usb_set_serial_port_data(port, NULL);
                kfree(priv);
        }
-} /* ftdi_shutdown */
 
+       return 0;
+}
 
 static int  ftdi_open (struct usb_serial_port *port, struct file *filp)
 { /* ftdi_open */
@@ -1427,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;
@@ -1444,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 */
@@ -1493,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);
@@ -1504,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 */
 
 
@@ -1520,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;
        }
 
@@ -1603,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__,
@@ -1631,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;
        }