Greg KH requested more frequent and smaller patches so here is the current working driver.

(I've been holding back to give a big update and breaking the 'release frequently' rule - sorry about that)

Following patches will be smaller.

THis patch adds support for CrystalFontz and MatrixOrb LCD devices.

Greatly improves write speed using buffer pools in a similar way to other drivers (but includes more protection against race conditions).

Various speed changes for different chipsets.

Includes various patches from other people.

More detail on the changes is in the patch.

----
Bill Ryder






diff -rauX ../dontdiff orig/drivers/usb/serial/ftdi_sio.c 
new/drivers/usb/serial/ftdi_sio.c
--- orig/drivers/usb/serial/ftdi_sio.c  2003-03-05 21:19:16.000000000 +1300
+++ new/drivers/usb/serial/ftdi_sio.c   2003-03-05 21:27:22.000000000 +1300
@@ -17,6 +17,43 @@
  * See http://ftdi-usb-sio.sourceforge.net for upto date testing info
  *     and extra documentation
  *
+ * (23/Feb/2003) Bill Ryder
+ *      Added matrix orb device vid/pids from Wayne Wylupski
+ *      Added bitbang and latency patch from Thomas Jarosch
+ *
+ * (19/Feb/2003) Ian Abbott
+ *      For TIOCSSERIAL, set alt_speed to 0 when ASYNC_SPD_MASK value has
+ *      changed to something other than ASYNC_SPD_HI, ASYNC_SPD_VHI,
+ *      ASYNC_SPD_SHI or ASYNC_SPD_WARP.  Also, unless ASYNC_SPD_CUST is in
+ *      force, don't bother changing baud rate when custom_divisor has changed.
+ *
+ * (18/Feb/2003) Ian Abbott
+ *      Fixed TIOCMGET handling to include state of DTR and RTS, the state
+ *      of which are now saved by set_dtr() and set_rts().
+ *      Fixed improper storage class for buf in set_dtr() and set_rts().
+ *      Added FT232BM chip type and support for its extra baud rates (compared
+ *      to FT8U232AM).
+ *      Took account of special case divisor values for highest baud rates of
+ *      FT8U232AM and FT232BM.
+ *      For TIOCSSERIAL, forced alt_speed to 0 when ASYNC_SPD_CUST kludge used,
+ *      as previous alt_speed setting is now stale.
+ *      Moved startup code common between the startup routines for the
+ *      different chip types into a common subroutine.
+ *
+ * (17/Feb/2003) Bill Ryder
+ *      Added write urb buffer pool on a per device basis
+ *      Added more checking for open file on callbacks (fixed OOPS)
+ *      Added CrystalFontz 632 and 634 PIDs 
+ *         (thanx to CrystalFontz for the sample devices - they flushed out
+ *           some driver bugs)
+ *      Minor debugging message changes
+ *      Added throttle, unthrottle and chars_in_buffer functions
+ *      Fixed FTDI_SIO (the original device) bug
+ *      Fixed some shutdown handling
+ *      
+ * 
+ * 
+ * 
  * (07/Jun/2002) Kuba Ober
  *     Changed FTDI_SIO_BASE_BAUD_TO_DIVISOR macro into ftdi_baud_to_divisor
  *     function. It was getting too complex.
@@ -139,7 +176,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.2.1"
+#define DRIVER_VERSION "v1.3.1"
 #define DRIVER_AUTHOR "Greg Kroah-Hartman <[EMAIL PROTECTED]>, Bill Ryder <[EMAIL 
PROTECTED]>, Kuba Ober <[EMAIL PROTECTED]>"
 #define DRIVER_DESC "USB FTDI Serial Converters Driver"
 
@@ -156,13 +193,44 @@
  *   so .. 8U232AM's baudrate setting codes are different
  * - it has a two byte status code.
  * - it returns characters every 16ms (the FTDI does it every 40ms)
+ *
+ * the bcdDevice value is used to differentiate FT232BM and FT245BM from
+ * the earlier FT8U232AM and FT8U232BM.  For now, include all known VID/PID
+ * combinations in both tables.
+ * FIXME: perhaps bcdDevice can also identify 12MHz devices, but I don't know
+ * if those ever went into mass production. [Ian Abbott]
  */
 
 
 static struct usb_device_id id_table_8U232AM [] = {
-       { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) },
-       { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
-       { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) },
+       { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_PID, 0, 0x3ff) },
+       { USB_DEVICE_VER(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID, 0, 0x3ff) },
+       { USB_DEVICE_VER(FTDI_VID, FTDI_XF_634_PID, 0, 0x3ff) },
+       { USB_DEVICE_VER(FTDI_VID, FTDI_XF_632_PID, 0, 0x3ff) },
+       { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_0_PID, 0, 0x3ff) },
+       { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_1_PID, 0, 0x3ff) },
+       { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_2_PID, 0, 0x3ff) },
+       { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_3_PID, 0, 0x3ff) },
+       { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_4_PID, 0, 0x3ff) },
+       { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_5_PID, 0, 0x3ff) },
+       { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_6_PID, 0, 0x3ff) },
+       { }                                             /* Terminating entry */
+};
+
+
+static struct usb_device_id id_table_FT232BM [] = {
+       { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_PID, 0x400, 0xffff) },
+       { USB_DEVICE_VER(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID, 0x400, 0xffff) },
+       { USB_DEVICE_VER(FTDI_VID, FTDI_XF_634_PID, 0x400, 0xffff) },
+       { USB_DEVICE_VER(FTDI_VID, FTDI_XF_632_PID, 0x400, 0xffff) },
+       { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_0_PID, 0x400, 0xffff) },
+       { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_1_PID, 0x400, 0xffff) },
+       { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_2_PID, 0x400, 0xffff) },
+       { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_3_PID, 0x400, 0xffff) },
+       { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_4_PID, 0x400, 0xffff) },
+       { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_5_PID, 0x400, 0xffff) },
+       { USB_DEVICE_VER(FTDI_MTXORB_VID, FTDI_MTXORB_6_PID, 0x400, 0xffff) },
+
        { }                                             /* Terminating entry */
 };
 
@@ -170,13 +238,27 @@
 static __devinitdata struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) },
-       { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_XF_634_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_XF_632_PID) },
        { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) },
-       { }                                             /* Terminating entry */
+       { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_0_PID) },
+       { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_1_PID) },
+       { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_2_PID) },
+       { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_3_PID) },
+       { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_4_PID) },
+       { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_5_PID) },
+       { USB_DEVICE(FTDI_MTXORB_VID, FTDI_MTXORB_6_PID) },     { }                    
                         /* Terminating entry */
 };
 
 MODULE_DEVICE_TABLE (usb, id_table_combined);
 
+
+/* constants which set the number of write urb buffers */
+#define NUM_URBS                       32
+/* Don't be tempted to increase this buffer to > 64 ! I tried it and it doesn't work 
*/
+#define URB_TRANSFER_BUFFER_SIZE       64 /* the device's max packet size */
+
+
 struct ftdi_private {
        ftdi_chip_type_t chip_type;
                                /* type of the device, either SIO or FT8U232AM */
@@ -188,8 +270,13 @@
                                 * 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 */
+
+       struct urb      *write_urb_pool[NUM_URBS];
+       spinlock_t      write_urb_pool_lock;
+
 };
 
 /* Used for TIOCMIWAIT */
@@ -203,20 +290,25 @@
 /* function prototypes for a FTDI serial converter */
 static int  ftdi_SIO_startup           (struct usb_serial *serial);
 static int  ftdi_8U232AM_startup       (struct usb_serial *serial);
+static int  ftdi_FT232BM_startup       (struct usb_serial *serial);
 static void ftdi_shutdown              (struct usb_serial *serial);
 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, int from_user, 
const unsigned char *buf, int count);
 static int  ftdi_write_room            (struct usb_serial_port *port);
+static int  ftdi_chars_in_buffer       (struct usb_serial_port *port);
 static void ftdi_write_bulk_callback   (struct urb *urb);
 static void ftdi_read_bulk_callback    (struct urb *urb);
 static void ftdi_set_termios           (struct usb_serial_port *port, struct termios 
* old);
 static int  ftdi_ioctl                 (struct usb_serial_port *port, struct file * 
file, unsigned int cmd, unsigned long arg);
 static void ftdi_break_ctl             (struct usb_serial_port *port, int break_state 
);
-static unsigned short int ftdi_baud_base_to_divisor
-                                       (int baud, int base);
-static unsigned short int ftdi_baud_to_divisor
-                                       (int baud);
+static void ftdi_throttle              (struct usb_serial_port *port);
+static void ftdi_unthrottle            (struct usb_serial_port *port);
+
+static unsigned short int ftdi_232am_baud_base_to_divisor (int baud, int base);
+static unsigned short int ftdi_232am_baud_to_divisor (int baud);
+static __u32 ftdi_232bm_baud_base_to_divisor (int baud, int base);
+static __u32 ftdi_232bm_baud_to_divisor (int baud);
 
 static struct usb_serial_device_type ftdi_SIO_device = {
        .owner =                THIS_MODULE,
@@ -228,8 +320,11 @@
        .num_ports =            1,
        .open =                 ftdi_open,
        .close =                ftdi_close,
+       .throttle =             ftdi_throttle,
+       .unthrottle =           ftdi_unthrottle,
        .write =                ftdi_write,
        .write_room =           ftdi_write_room,
+       .chars_in_buffer =      ftdi_chars_in_buffer,
        .read_bulk_callback =   ftdi_read_bulk_callback,
        .write_bulk_callback =  ftdi_write_bulk_callback,
        .ioctl =                ftdi_ioctl,
@@ -241,7 +336,7 @@
 
 static struct usb_serial_device_type ftdi_8U232AM_device = {
        .owner =                THIS_MODULE,
-       .name =                 "FTDI 8U232AM",
+       .name =                 "FTDI 8U232AM Compatible",
        .id_table =             id_table_8U232AM,
        .num_interrupt_in =     0,
        .num_bulk_in =          1,
@@ -249,8 +344,11 @@
        .num_ports =            1,
        .open =                 ftdi_open,
        .close =                ftdi_close,
+       .throttle =             ftdi_throttle,
+       .unthrottle =           ftdi_unthrottle,
        .write =                ftdi_write,
        .write_room =           ftdi_write_room,
+       .chars_in_buffer =      ftdi_chars_in_buffer,
        .read_bulk_callback =   ftdi_read_bulk_callback,
        .write_bulk_callback =  ftdi_write_bulk_callback,
        .ioctl =                ftdi_ioctl,
@@ -260,8 +358,35 @@
        .shutdown =             ftdi_shutdown,
 };
 
+static struct usb_serial_device_type ftdi_FT232BM_device = {
+       .owner =                THIS_MODULE,
+       .name =                 "FTDI FT232BM Compatible",
+       .id_table =             id_table_FT232BM,
+       .num_interrupt_in =     0,
+       .num_bulk_in =          1,
+       .num_bulk_out =         1,
+       .num_ports =            1,
+       .open =                 ftdi_open,
+       .close =                ftdi_close,
+       .throttle =             ftdi_throttle,
+       .unthrottle =           ftdi_unthrottle,
+       .write =                ftdi_write,
+       .write_room =           ftdi_write_room,
+       .chars_in_buffer =      ftdi_chars_in_buffer,
+       .read_bulk_callback =   ftdi_read_bulk_callback,
+       .write_bulk_callback =  ftdi_write_bulk_callback,
+       .ioctl =                ftdi_ioctl,
+       .set_termios =          ftdi_set_termios,
+       .break_ctl =            ftdi_break_ctl,
+       .startup =              ftdi_FT232BM_startup,
+       .shutdown =             ftdi_shutdown,
+};
+
+
+
 #define WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */
 
+/* High and low are for DTR, RTS etc etc */
 #define HIGH 1
 #define LOW 0
 
@@ -271,7 +396,7 @@
  * ***************************************************************************
  */
 
-static unsigned short int ftdi_baud_base_to_divisor(int baud, int base)
+static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base)
 {
        unsigned short int divisor;
        int divisor3 = base / 2 / baud; // divisor shifted 3 bits to the left
@@ -281,22 +406,47 @@
        if (divisor3 == 1) divisor |= 0xc000; else // 0.125
        if (divisor3 >= 4) divisor |= 0x4000; else // 0.5
        if (divisor3 != 0) divisor |= 0x8000;      // 0.25
+       if (divisor == 1) divisor = 0;  /* special case for maximum baud rate */
        return divisor;
 }
 
-static unsigned short int ftdi_baud_to_divisor(int baud)
+static unsigned short int ftdi_232am_baud_to_divisor(int baud)
+{
+        return(ftdi_232am_baud_base_to_divisor(baud, 48000000));
+}
+
+static __u32 ftdi_232bm_baud_base_to_divisor(int baud, int base)
 {
-        return(ftdi_baud_base_to_divisor(baud, 48000000));
+       static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
+       __u32 divisor;
+       int divisor3 = base / 2 / baud; // divisor shifted 3 bits to the left
+       divisor = divisor3 >> 3;
+       divisor |= (__u32)divfrac[divisor3 & 0x7] << 14;
+       /* Deal with special cases for highest baud rates. */
+       if (divisor == 1) divisor = 0; else     // 1.0
+       if (divisor == 0x4001) divisor = 1;     // 1.5
+       return divisor;
 }
 
-static int set_rts(struct usb_device *dev,
-                  unsigned int pipe,
-                  int high_or_low)
+static __u32 ftdi_232bm_baud_to_divisor(int baud)
 {
-       static char buf[1];
-       unsigned ftdi_high_or_low = (high_or_low? FTDI_SIO_SET_RTS_HIGH : 
-                               FTDI_SIO_SET_RTS_LOW);
-       return(usb_control_msg(dev, pipe,
+        return(ftdi_232bm_baud_base_to_divisor(baud, 48000000));
+}
+
+static int set_rts(struct usb_serial_port *port, int high_or_low)
+{
+       struct ftdi_private * priv = (struct ftdi_private *)port->private;
+       char buf[1];
+       unsigned ftdi_high_or_low;
+       if (high_or_low) {
+               ftdi_high_or_low = FTDI_SIO_SET_RTS_HIGH;
+               priv->last_dtr_rts |= TIOCM_RTS;
+       } else {
+               ftdi_high_or_low = FTDI_SIO_SET_RTS_LOW;
+               priv->last_dtr_rts &= ~TIOCM_RTS;
+       }
+       return(usb_control_msg(port->serial->dev,
+                              usb_sndctrlpipe(port->serial->dev, 0),
                               FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
                               FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
                               ftdi_high_or_low, 0, 
@@ -304,14 +454,20 @@
 }
 
 
-static int set_dtr(struct usb_device *dev,
-                   unsigned int pipe,
-                   int high_or_low)
+static int set_dtr(struct usb_serial_port *port, int high_or_low)
 {
-       static char buf[1];
-       unsigned ftdi_high_or_low = (high_or_low? FTDI_SIO_SET_DTR_HIGH : 
-                               FTDI_SIO_SET_DTR_LOW);
-       return(usb_control_msg(dev, pipe,
+       struct ftdi_private * priv = (struct ftdi_private *)port->private;
+       char buf[1];
+       unsigned ftdi_high_or_low;
+       if (high_or_low) {
+               ftdi_high_or_low = FTDI_SIO_SET_DTR_HIGH;
+               priv->last_dtr_rts |= TIOCM_DTR;
+       } else {
+               ftdi_high_or_low = FTDI_SIO_SET_DTR_LOW;
+               priv->last_dtr_rts &= ~TIOCM_DTR;
+       }
+       return(usb_control_msg(port->serial->dev,
+                              usb_sndctrlpipe(port->serial->dev, 0),
                               FTDI_SIO_SET_MODEM_CTRL_REQUEST, 
                               FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
                               ftdi_high_or_low, 0, 
@@ -319,30 +475,36 @@
 }
 
 
-static __u16 get_ftdi_divisor(struct usb_serial_port * port);
+static __u32 get_ftdi_divisor(struct usb_serial_port * port);
 
 
 static int change_speed(struct usb_serial_port *port)
 {
        char buf[1];
         __u16 urb_value;
+       __u16 urb_index;
+       __u32 urb_index_value;
 
-       urb_value = get_ftdi_divisor(port);
+       urb_index_value = get_ftdi_divisor(port);
+       urb_value = (__u16)urb_index_value;
+       urb_index = (__u16)(urb_index_value >> 16);
        
        return (usb_control_msg(port->serial->dev,
                            usb_sndctrlpipe(port->serial->dev, 0),
                            FTDI_SIO_SET_BAUDRATE_REQUEST,
                            FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE,
-                           urb_value, 0,
+                           urb_value, urb_index,
                            buf, 0, 100) < 0);
 }
 
 
-static __u16 get_ftdi_divisor(struct usb_serial_port * port)
+static __u32 get_ftdi_divisor(struct usb_serial_port * port)
 { /* get_ftdi_divisor */
        
        struct ftdi_private * priv = (struct ftdi_private *)port->private;
-       __u16 urb_value = 0;
+       __u32 div_value = 0;
+       int div_okay = 1;
+       char *chip_name = "";
        int baud;
 
        /*
@@ -387,37 +549,53 @@
        if (!baud) baud = 9600; 
        switch(priv->chip_type) {
        case SIO: /* SIO chip */
+               chip_name = "SIO";
                switch(baud) {
-               case 300: urb_value = ftdi_sio_b300; break;
-               case 600: urb_value = ftdi_sio_b600; break;
-               case 1200: urb_value = ftdi_sio_b1200; break;
-               case 2400: urb_value = ftdi_sio_b2400; break;
-               case 4800: urb_value = ftdi_sio_b4800; break;
-               case 9600: urb_value = ftdi_sio_b9600; break;
-               case 19200: urb_value = ftdi_sio_b19200; break;
-               case 38400: urb_value = ftdi_sio_b38400; break;
-               case 57600: urb_value = ftdi_sio_b57600;  break;
-               case 115200: urb_value = ftdi_sio_b115200; break;
+               case 300: div_value = ftdi_sio_b300; break;
+               case 600: div_value = ftdi_sio_b600; break;
+               case 1200: div_value = ftdi_sio_b1200; break;
+               case 2400: div_value = ftdi_sio_b2400; break;
+               case 4800: div_value = ftdi_sio_b4800; break;
+               case 9600: div_value = ftdi_sio_b9600; break;
+               case 19200: div_value = ftdi_sio_b19200; break;
+               case 38400: div_value = ftdi_sio_b38400; break;
+               case 57600: div_value = ftdi_sio_b57600;  break;
+               case 115200: div_value = ftdi_sio_b115200; break;
                } /* baud */
-               if (urb_value == 0)
-                       dbg("%s - Baudrate (%d) requested is not supported", 
__FUNCTION__,  baud);
+               if (div_value == 0) {
+                       dbg("%s - Baudrate (%d) requested is not supported", 
__FUNCTION__,  baud);
+                       div_value = ftdi_sio_b9600;
+                       div_okay = 0;
+               }
                break;
        case FT8U232AM: /* 8U232AM chip */
+               chip_name = "FT8U232AM";
+               if (baud <= 3000000) {
+                       div_value = ftdi_232am_baud_to_divisor(baud);
+               } else {
+                       dbg("%s - Baud rate too high!", __FUNCTION__);
+                       div_value = ftdi_232am_baud_to_divisor(9600);
+                       div_okay = 0;
+               }
+               break;
+       case FT232BM: /* FT232BM chip */
+               chip_name = "FT232BM";
                if (baud <= 3000000) {
-                       urb_value = ftdi_baud_to_divisor(baud);
+                       div_value = ftdi_232bm_baud_to_divisor(baud);
                } else {
                        dbg("%s - Baud rate too high!", __FUNCTION__);
+                       div_value = ftdi_232bm_baud_to_divisor(9600);
+                       div_okay = 0;
                }
                break;
        } /* priv->chip_type */
 
-       if (urb_value == 0) {
-               urb_value = ftdi_sio_b9600;
-       } else {
-               dbg("%s - Baud rate set to %d (divisor %d) on chip %s", __FUNCTION__, 
baud, urb_value, (priv->chip_type == SIO) ? "SIO" : "FT8U232AM" );
+       if (div_okay) {
+               dbg("%s - Baud rate set to %d (divisor 0x%lX) on chip %s",
+                       __FUNCTION__, baud, (unsigned long)div_value, chip_name);
        }
 
-       return(urb_value);
+       return(div_value);
 }
 
 
@@ -473,17 +651,23 @@
        port->tty->low_latency = (priv->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
 
 check_and_exit:
-       if (((old_priv.flags & ASYNC_SPD_MASK) !=
-            (priv->flags & ASYNC_SPD_MASK)) ||
-           (old_priv.custom_divisor != priv->custom_divisor)) {
+       if ((old_priv.flags & ASYNC_SPD_MASK) !=
+            (priv->flags & ASYNC_SPD_MASK)) {
                if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
                        port->tty->alt_speed = 57600;
-               if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+               else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
                        port->tty->alt_speed = 115200;
-               if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+               else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
                        port->tty->alt_speed = 230400;
-               if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+               else if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
                        port->tty->alt_speed = 460800;
+               else
+                       port->tty->alt_speed = 0;
+       }
+       if (((old_priv.flags & ASYNC_SPD_MASK) !=
+            (priv->flags & ASYNC_SPD_MASK)) ||
+           (((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) &&
+            (old_priv.custom_divisor != priv->custom_divisor))) {
                change_speed(port);
        }
        
@@ -497,66 +681,166 @@
  * ***************************************************************************
  */
 
-/* Startup for the SIO chip */
-static int ftdi_SIO_startup (struct usb_serial *serial)
+/* Common startup subroutine */
+/* Called from ftdi_SIO_startup, etc. */
+static int ftdi_common_startup (struct usb_serial *serial)
 {
        struct ftdi_private *priv;
+       int i ;
+       struct urb *urb; 
+       
+       dbg("%s",__FUNCTION__);
 
        priv = serial->port->private = kmalloc(sizeof(struct ftdi_private), 
GFP_KERNEL);
        if (!priv){
                err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct 
ftdi_private));
                return -ENOMEM;
        }
+       memset(priv, 0, sizeof(*priv));
 
-       priv->chip_type = SIO;
-       priv->baud_base = 12000000 / 16;
-       priv->custom_divisor = 0;
-       priv->write_offset = 1;
-       priv->prev_status = priv->diff_status = 0;
+        init_waitqueue_head(&priv->delta_msr_wait);
        /* This will push the characters through immediately rather
           than queue a task to deliver them */
        priv->flags = ASYNC_LOW_LATENCY;
+
+       /* create our write urb pool and transfer buffers - shared across all ftdi 
devices */ 
+       spin_lock_init (&priv->write_urb_pool_lock);
+       for (i = 0; i < NUM_URBS; ++i) {
+               urb = usb_alloc_urb(0);
+               priv->write_urb_pool[i] = urb;
+               if (urb == NULL) {
+                       err("Unable to create new urb in urb pool");
+                       continue;
+               }
+
+               urb->transfer_buffer = NULL;
+               urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+               if (!urb->transfer_buffer) {
+                       err("%s - out of memory for urb buffers.", 
+                           __FUNCTION__);
+                       continue;
+               }
+       }
+
+       
+       return (0);
+}
+
+
+/* Startup for the SIO chip */
+/* Called from usbserial:serial_probe */
+static int ftdi_SIO_startup (struct usb_serial *serial)
+{
+       struct ftdi_private *priv;
+       int err;
+
+       dbg("%s",__FUNCTION__);
+
+       err = ftdi_common_startup(serial);
+       if (err){
+               return (err);
+       }
+
+       priv = serial->port->private;
+       priv->chip_type = SIO;
+       priv->baud_base = 12000000 / 16;
+       priv->write_offset = 1;
        
        return (0);
 }
 
 /* Startup for the 8U232AM chip */
+/* Called from usbserial:serial_probe */
 static int ftdi_8U232AM_startup (struct usb_serial *serial)
-{
+{ /* ftdi_8U232AM_startup */
        struct ftdi_private *priv;
+       int err;
 
-       priv = serial->port->private = kmalloc(sizeof(struct ftdi_private), 
GFP_KERNEL);
-       if (!priv){
-               err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct 
ftdi_private));
-               return -ENOMEM;
+       dbg("%s",__FUNCTION__);
+       err = ftdi_common_startup(serial);
+       if (err){
+               return (err);
        }
 
+       priv = serial->port->private;
        priv->chip_type = FT8U232AM;
        priv->baud_base = 48000000 / 2; /* Would be / 16, but FTDI supports 0.125, 
0.25 and 0.5 divisor fractions! */
-       priv->custom_divisor = 0;
-       priv->write_offset = 0;
-        init_waitqueue_head(&priv->delta_msr_wait);
-       /* This will push the characters through immediately rather
-          than queue a task to deliver them */
-       priv->flags = ASYNC_LOW_LATENCY;
        
        return (0);
-}
+} /* ftdi_8U232AM_startup */
+
+/* Startup for the FT232BM chip */
+/* Called from usbserial:serial_probe */
+static int ftdi_FT232BM_startup (struct usb_serial *serial)
+{ /* ftdi_FT232BM_startup */
+       struct ftdi_private *priv;
+       int err;
+
+       dbg("%s",__FUNCTION__);
+       err = ftdi_common_startup(serial);
+       if (err){
+               return (err);
+       }
+
+       priv = serial->port->private;
+       priv->chip_type = FT232BM;
+       priv->baud_base = 48000000 / 2; /* Would be / 16, but FT232BM supports 
multiple of 0.125 divisor fractions! */
+       
+       return (0);
+} /* ftdi_FT232BM_startup */
+
+/* ftdi_shutdown is called from usbserial:usb_serial_disconnect 
+ *   it is called when the usb device is disconnected
+ *
+ *   usbserial:usb_serial_disconnect
+ *      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 */
+       
+       struct usb_serial_port *port = &serial->port[0];        
+       struct ftdi_private *priv = serial->port->private; 
+       int i;
+       unsigned long flags; 
+
        dbg("%s", __FUNCTION__);
 
-       /* stop reads and writes on all ports */
-       while (serial->port[0].open_count > 0) {
-               ftdi_close (&serial->port[0], NULL);
-       }
-       if (serial->port[0].private){
-               kfree(serial->port[0].private);
-               serial->port[0].private = NULL;
+       /* all open ports are closed at this point 
+         *    (by usbserial.c:__serial_close, which calls ftdi_close)  
+        */
+
+       
+       /* Only execute this if this is the final open port for this device */
+       if (port->open_count == 0){
+               spin_lock_irqsave (&priv->write_urb_pool_lock, flags);
+
+               for (i = 0; i < NUM_URBS; ++i) {
+                       if (priv->write_urb_pool[i]) {
+                               /* FIXME - uncomment the following usb_unlink_urb call 
when
+                                * the host controllers get fixed to set urb->dev = 
NULL after
+                                * the urb is finished.  Otherwise this call oopses. */
+                               /* usb_unlink_urb(priv->write_urb_pool[i]); */
+                               if (priv->write_urb_pool[i]->transfer_buffer) {
+                                       
kfree(priv->write_urb_pool[i]->transfer_buffer);
+                                       priv->write_urb_pool[i]->transfer_buffer = 
NULL;
+                               }
+                               usb_free_urb (priv->write_urb_pool[i]);
+                               priv->write_urb_pool[i] = NULL;
+                       }
+               }
+
+               spin_unlock_irqrestore (&priv->write_urb_pool_lock, flags);
+               /* usb_disconnect shuts down the port->read_urb so don't do it here */
+               /* as was done previously */
+       }
+       if (serial->port->private){
+               kfree(serial->port->private);
+               serial->port->private = NULL;
        }
-}
+} /* ftdi_shutdown */
 
 
 static int  ftdi_open (struct usb_serial_port *port, struct file *filp)
@@ -590,13 +874,17 @@
        /* FIXME: Flow control might be enabled, so it should be checked -
           we have no control of defaults! */
        /* Turn on RTS and DTR since we are not flow controlling by default */
-       if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0) {
+       if (set_dtr(port, HIGH) < 0) {
                err("%s Error from DTR HIGH urb", __FUNCTION__);
        }
-       if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),HIGH) < 0){
+       if (set_rts(port, HIGH) < 0){
                err("%s Error from RTS HIGH urb", __FUNCTION__);
        }
 
+       /* Make sure write_urb is initialised since a write_pool is used now */
+       port->write_urb = NULL; /* prevents usbserial.c from trying something silly */
+
+
        /* Start reading from the device */
        FILL_BULK_URB(port->read_urb, serial->dev, 
                      usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
@@ -606,18 +894,32 @@
        if (result)
                err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
 
+
        return result;
 } /* ftdi_open */
 
 
+
+/* 
+ * usbserial:__serial_close  only calls ftdi_close if the point is open
+ *
+ *   This only gets called when it is the last close
+ *   
+ *   
+ */
+
 static void ftdi_close (struct usb_serial_port *port, struct file *filp)
 { /* ftdi_close */
-       struct usb_serial *serial = port->serial; /* Checked in usbserial.c */
+       struct usb_serial *serial;
        unsigned int c_cflag = port->tty->termios->c_cflag;
        char buf[1];
 
        dbg("%s", __FUNCTION__);
 
+       serial = get_usb_serial ( port, __FUNCTION__);
+       if (!serial)
+               return;
+
        if (serial->dev) {
                if (c_cflag & HUPCL){
                        /* Disable flow control */
@@ -630,115 +932,188 @@
                        }           
 
                        /* drop DTR */
-                       if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0), LOW) 
< 0){
+                       if (set_dtr(port, LOW) < 0){
                                err("Error from DTR LOW urb");
                        }
                        /* drop RTS */
-                       if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) 
< 0) {
+                       if (set_rts(port, LOW) < 0) {
                                err("Error from RTS LOW urb");
                        }       
+                       /* shutdown our bulk read */
+                       if (port->read_urb) {
+                               usb_unlink_urb (port->read_urb);        
+                       }
+                       /* unlink the running write urbs */
+                       
+
                } /* Note change no line is hupcl is off */
+       } /* if (serial->dev) */
+
 
-               /* shutdown our bulk reads and writes */
-               /* ***CHECK*** behaviour when there is nothing queued */
-               usb_unlink_urb (port->write_urb);
-               usb_unlink_urb (port->read_urb);
-       }
 } /* ftdi_close */
 
 
   
-/* The ftdi_sio requires the first byte to have:
+/* The SIO requires the first byte to have:
  *  B0 1
  *  B1 0
  *  B2..7 length of message excluding byte 0
+ *
+ * The new devices do not require this byte
  */
 static int ftdi_write (struct usb_serial_port *port, int from_user,
                           const unsigned char *buf, int count)
 { /* ftdi_write */
-       struct usb_serial *serial = port->serial;
+       struct usb_serial *serial = get_usb_serial ( port, __FUNCTION__);
        struct ftdi_private *priv = (struct ftdi_private *)port->private;
-       unsigned char *first_byte = port->write_urb->transfer_buffer;
-       int data_offset ;
+       unsigned char *first_byte;
+       int data_offset ;       /* will be 1 for the SIO and 0 otherwise */
        int result;
+       int user_bytes_sent = 0 ;   /* amount of user data sent */
+
+       /* Variables for urb pool management */
+       unsigned char *current_position = (unsigned char *)buf;
+       int i; 
+       struct urb *urb; /* pointer to urb from urb pool */
+       unsigned long flags;
+       /* end of urb pool management */
+       
        
        dbg("%s port %d, %d bytes", __FUNCTION__, port->number, count);
 
        if (count == 0) {
                err("write request of 0 bytes");
-               return 0;
+               goto exit;
        }
        
        data_offset = priv->write_offset;
         dbg("data_offset set to %d",data_offset);
 
-       if (port->write_urb->status == -EINPROGRESS) {
-               dbg("%s - already writing", __FUNCTION__);
-               return (0);
-       }               
-
-       count += data_offset;
-       count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
-
-       /* Copy in the data to send */
-       if (from_user) {
-               if (copy_from_user(port->write_urb->transfer_buffer + data_offset,
-                                  buf, count - data_offset )){
-                       return -EFAULT;
+       while (count > 0) {
+               /* urb_byte_count = user_byte_count + data_offset */
+               int urb_byte_count;  /* Number of bytes of URB data   */
+               int user_byte_count; /* Number of bytes of user data */
+
+               /* Find a free urb in the list */
+               urb = NULL;
+
+               spin_lock_irqsave (&(priv->write_urb_pool_lock), flags) ; 
+
+               for (i = 0 ; i < NUM_URBS; i++) {
+                       if (priv->write_urb_pool[i] -> status != -EINPROGRESS) {
+                         urb = priv->write_urb_pool[i];
+                         /* Must make sure another device doesn't grab this */
+                         /* BUT unfortunately the uhci stack errors if it sees this */
+                         /* so have to increase the size of the spin_lock */
+                         /* urb->status = -EINPROGRESS; */
+                               break;
+                       }
                }
-       } else {
-               memcpy(port->write_urb->transfer_buffer + data_offset,
-                      buf, count - data_offset );
-       }  
-
-       first_byte = port->write_urb->transfer_buffer;
-       if (data_offset > 0){
-               /* Write the control byte at the front of the packet*/
-               *first_byte = 1 | ((count-data_offset) << 2) ; 
-       }
 
-       dbg("%s Bytes: %d, First Byte: 0x%02x", __FUNCTION__,count, first_byte[0]);
-       usb_serial_debug_data (__FILE__, __FUNCTION__, count, first_byte);
+
+               if (urb == NULL) {
+                       spin_unlock_irqrestore (&priv->write_urb_pool_lock, flags);
+                       dbg("%s - no more free urbs", __FUNCTION__);
+                       goto exit;
+               }
+
+               /* Allocate memory for the urb if necessary */
+               if (urb->transfer_buffer == NULL) {
+                       urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, 
GFP_KERNEL);
+                       if (urb->transfer_buffer == NULL) {
+                               spin_unlock_irqrestore (&priv->write_urb_pool_lock, 
flags);
+                               err("%s ran out of kernel memory for urb ...", 
__FUNCTION__);
+                               goto exit;
+                       }
+               }
                
-       /* send the data out the bulk port */
-       FILL_BULK_URB(port->write_urb, serial->dev, 
-                     usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
-                     port->write_urb->transfer_buffer, count,
-                     ftdi_write_bulk_callback, port);
+               /* The original sio needs the first byte to contain the bytecount 
+                * so the urb may be one byte bigger than the user data 
+                */
+               urb_byte_count = min (count + data_offset, URB_TRANSFER_BUFFER_SIZE);
+               user_byte_count = urb_byte_count - data_offset; 
+
+               /* Copy in the data to send */
+               if (from_user) {
+                       if (copy_from_user(urb->transfer_buffer + data_offset,
+                                          current_position, user_byte_count )){
+                               spin_unlock_irqrestore (&priv->write_urb_pool_lock, 
flags);
+                               return -EFAULT;
+                       }
+               } else {
+                       memcpy(urb->transfer_buffer + data_offset,
+                              current_position, user_byte_count );
+               }  
+
+               first_byte = urb->transfer_buffer;
+               if (data_offset > 0){
+                       /* Write the control byte at the front of the packet*/
+                       *first_byte = 1 | ((user_byte_count) << 2) ; 
+                       dbg("%s Bytes: %d, First Byte: 0x%02x", __FUNCTION__,count, 
first_byte[0]);
+               }
                
-       result = usb_submit_urb(port->write_urb);
-       if (result) {
-               err("%s - failed submitting write urb, error %d", __FUNCTION__, 
result);
-               return 0;
-       }
+               usb_serial_debug_data (__FILE__, __FUNCTION__, urb_byte_count, 
first_byte);
+               
+               /* fill the buffer and send it */
+               FILL_BULK_URB(urb, serial->dev, 
+                             usb_sndbulkpipe(serial->dev, 
port->bulk_out_endpointAddress),
+                             urb->transfer_buffer, urb_byte_count,
+                             ftdi_write_bulk_callback, port);
+               urb->transfer_flags |= USB_QUEUE_BULK;
+               
+               result = usb_submit_urb(urb);
+               if (result) {
+                       spin_unlock_irqrestore (&priv->write_urb_pool_lock, flags);
+                       err("%s - failed submitting write urb, error %d", 
__FUNCTION__, result);
+                       user_bytes_sent = result;
+                       goto exit;
+               }
+               spin_unlock_irqrestore (&priv->write_urb_pool_lock, flags);
+
+               /* house keeping */
+               current_position += user_byte_count;
+               user_bytes_sent += user_byte_count;
+               count -= user_byte_count;
+               
+       } /* while  count > 0 */
+
+ exit:
+
+       dbg("%s write returning: %d", __FUNCTION__, user_bytes_sent);
+       return user_bytes_sent;
 
-       dbg("%s write returning: %d", __FUNCTION__, count - data_offset);
-       return (count - data_offset);
 } /* ftdi_write */
 
 
+/* This function may get called when the device is closed */
+
 static void ftdi_write_bulk_callback (struct urb *urb)
 {
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
-       struct usb_serial *serial;
+       struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
 
        dbg("%s", __FUNCTION__);
 
-       if (port_paranoia_check (port, "ftdi_write_bulk_callback")) {
+       if (port_paranoia_check (port, __FUNCTION__))
                return;
-       }
-       
-       serial = port->serial;
-       if (serial_paranoia_check (serial, "ftdi_write_bulk_callback")) {
-               return;
-       }
        
        if (urb->status) {
                dbg("nonzero write bulk status received: %d", urb->status);
                return;
        }
-       queue_task(&port->tqueue, &tq_immediate);
-       mark_bh(IMMEDIATE_BH);
+
+       if (!serial) {
+               dbg("%s - bad serial pointer, exiting", __FUNCTION__);
+               return;
+       }
+
+       /* Have to check for validity of queueing up the tasks */
+       dbg("%s - port->open_count = %d", __FUNCTION__, port->open_count);
+
+       if (port->open_count > 0){
+               queue_task(&port->tqueue, &tq_immediate);
+               mark_bh(IMMEDIATE_BH);
+       } 
 
        return;
 } /* ftdi_write_bulk_callback */
@@ -747,22 +1122,59 @@
 static int ftdi_write_room( struct usb_serial_port *port )
 {
        struct ftdi_private *priv = (struct ftdi_private *)port->private;
-       int room;
+       int room = 0;
+       int i;
+       unsigned long flags;
 
-       if ( port->write_urb->status == -EINPROGRESS) {
-               /* There is a race here with the _write routines but it won't hurt */
-               room = 0;
-       } else { 
-               room = port->bulk_out_size - priv->write_offset;
+
+       spin_lock_irqsave (&priv->write_urb_pool_lock, flags);
+
+       for (i = 0; i < NUM_URBS; i++) {
+               if (priv->write_urb_pool[i]->status != -EINPROGRESS) {
+                       room += URB_TRANSFER_BUFFER_SIZE - priv->write_offset;
+               }
        }
+       
+       spin_unlock_irqrestore (&priv->write_urb_pool_lock, flags);
+
+       dbg("%s - returns %d", __FUNCTION__, room);     
        return(room);
 } /* ftdi_write_room */
 
 
+static int ftdi_chars_in_buffer (struct usb_serial_port *port)
+{ /* ftdi_chars_in_buffer */
+       unsigned long flags;
+       int i;
+       int chars = 0;
+       struct ftdi_private *priv = (struct ftdi_private *)port->private;
+       int data_offset = priv->write_offset;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       spin_lock_irqsave (&priv->write_urb_pool_lock, flags);
+
+       /* tally up the number of bytes waiting */
+       for (i = 0; i < NUM_URBS; ++i) {
+               if (priv->write_urb_pool[i]->status == -EINPROGRESS) {
+                       chars += URB_TRANSFER_BUFFER_SIZE - data_offset;
+               }
+       }
+
+       spin_unlock_irqrestore (&priv->write_urb_pool_lock, flags);
+
+       dbg("%s - returns %d", __FUNCTION__, chars);
+
+       return (chars);
+
+} /* ftdi_chars_in_buffer */
+
+
+
 static void ftdi_read_bulk_callback (struct urb *urb)
 { /* ftdi_read_bulk_callback */
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
-       struct usb_serial *serial;
+       struct usb_serial *serial = get_usb_serial(port,__FUNCTION__);
                struct tty_struct *tty = port->tty ;
        struct ftdi_private *priv = (struct ftdi_private *) port->private;
        char error_flag;
@@ -772,27 +1184,36 @@
        int i;
        int result;
 
-       dbg("%s - port %d", __FUNCTION__, port->number);
+       dbg("%s", __FUNCTION__);
 
-       if (port_paranoia_check (port, "ftdi_sio_read_bulk_callback")) {
+       if (port_paranoia_check (port, __FUNCTION__)) {
                return;
        }
+       if (port->open_count <= 0)
+               return;
 
-       serial = port->serial;
-       if (serial_paranoia_check (serial, "ftdi_sio_read_bulk_callback")) {
+       if (!serial){
+               dbg("%s - bad serial pointer - exiting",__FUNCTION__);
+               return;
+       }
+       
+       if (!tty) {
+               dbg("%s - bad tty pointer - exiting",__FUNCTION__);
                return;
        }
 
+
        if (urb->status) {
                /* This will happen at close every time so it is a dbg not an err */
-               dbg("nonzero read bulk status received: %d", urb->status);
+               dbg("(this is ok on close) nonzero read bulk status received: %d", 
urb->status);
                return;
        }
 
+        /* The first two bytes of every read packet are status */
        if (urb->actual_length > 2) {
                usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, 
data);
        } else {
-                dbg("Just status 0o%03o0o%03o",data[0],data[1]);
+                dbg("Status only: %03oo %03oo",data[0],data[1]);
         }
 
 
@@ -847,8 +1268,6 @@
                        tty_insert_flip_char(tty, data[i], error_flag);
                }
                tty_flip_buffer_push(tty);
-
-
        } 
 
 #ifdef NOT_CORRECT_BUT_KEEPING_IT_FOR_NOW
@@ -873,15 +1292,18 @@
        }
 #endif
 
-       /* Continue trying to always read  */
-       FILL_BULK_URB(port->read_urb, serial->dev, 
-                     usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
-                     port->read_urb->transfer_buffer, 
port->read_urb->transfer_buffer_length,
-                     ftdi_read_bulk_callback, port);
-
-       result = usb_submit_urb(port->read_urb);
-       if (result)
-               err("%s - failed resubmitting read urb, error %d", __FUNCTION__, 
result);
+       /* if the port is closed stop trying to read */
+       if (port->open_count > 0){
+               /* Continue trying to always read  */
+               FILL_BULK_URB(port->read_urb, serial->dev, 
+                             usb_rcvbulkpipe(serial->dev, 
port->bulk_in_endpointAddress),
+                             port->read_urb->transfer_buffer, 
port->read_urb->transfer_buffer_length,
+                             ftdi_read_bulk_callback, port);
+
+               result = usb_submit_urb(port->read_urb);
+               if (result)
+                       err("%s - failed resubmitting read urb, error %d", 
__FUNCTION__, result);
+       }
 
        return;
 } /* ftdi_read_bulk_callback */
@@ -985,10 +1407,10 @@
                        err("%s error from disable flowcontrol urb", __FUNCTION__);
                }           
                /* Drop RTS and DTR */
-               if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0){
+               if (set_dtr(port, LOW) < 0){
                        err("%s Error from DTR LOW urb", __FUNCTION__);
                }
-               if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),LOW) < 0){
+               if (set_rts(port, LOW) < 0){
                        err("%s Error from RTS LOW urb", __FUNCTION__);
                }       
                
@@ -1034,9 +1456,12 @@
        struct usb_serial *serial = port->serial;
        struct ftdi_private *priv = (struct ftdi_private *)port->private;
 
+       ftdi_bitbang_t bitbang;
+       ftdi_eeprom_t eeprom;
+
        __u16 urb_value=0; /* Will hold the new flags */
        char buf[2];
-       int  ret, mask;
+       int  ret, mask, i;
        
        dbg("%s cmd 0x%04x", __FUNCTION__, cmd);
 
@@ -1060,6 +1485,7 @@
                        }
                        break;
                case FT8U232AM:
+               case FT232BM:
                        /* the 8U232AM returns a two byte value (the sio is a 1 byte 
value) - in the same
                           format as the data returned from the in point */
                        if ((ret = usb_control_msg(serial->dev, 
@@ -1081,7 +1507,8 @@
                return put_user((buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) |
                                (buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) |
                                (buf[0]  & FTDI_SIO_RI_MASK  ? TIOCM_RI  : 0) |
-                               (buf[0]  & FTDI_SIO_RLSD_MASK ? TIOCM_CD  : 0),
+                               (buf[0]  & FTDI_SIO_RLSD_MASK ? TIOCM_CD  : 0) |
+                               priv->last_dtr_rts,
                                (unsigned long *) arg);
                break;
 
@@ -1090,13 +1517,16 @@
                if (get_user(mask, (unsigned long *) arg))
                        return -EFAULT;
                urb_value = ((mask & TIOCM_DTR) ? HIGH : LOW);
-               if (set_dtr(serial->dev, usb_sndctrlpipe(serial->dev, 0),urb_value) < 
0){
+               if ((ret = set_dtr(port, urb_value)) < 0){
                        err("Error from DTR set urb (TIOCMSET)");
+                       return(ret);
                }
                urb_value = ((mask & TIOCM_RTS) ? HIGH : LOW);
-               if (set_rts(serial->dev, usb_sndctrlpipe(serial->dev, 0),urb_value) < 
0){
+               if ((ret = set_rts(port, urb_value)) < 0){
                        err("Error from RTS set urb (TIOCMSET)");
-               }       
+                       return(ret);
+               }
+               return(0);
                break;
                                        
        case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */
@@ -1104,43 +1534,37 @@
                if (get_user(mask, (unsigned long *) arg))
                        return -EFAULT;
                if (mask & TIOCM_DTR){
-                       if ((ret = set_dtr(serial->dev, 
-                                          usb_sndctrlpipe(serial->dev, 0),
-                                          HIGH)) < 0) {
+                       if ((ret = set_dtr(port, HIGH)) < 0) {
                                err("Urb to set DTR failed");
                                return(ret);
                        }
                }
                if (mask & TIOCM_RTS) {
-                       if ((ret = set_rts(serial->dev, 
-                                          usb_sndctrlpipe(serial->dev, 0),
-                                          HIGH)) < 0){
+                       if ((ret = set_rts(port, HIGH)) < 0){
                                err("Urb to set RTS failed");
                                return(ret);
                        }
                }
-                                       break;
+               return(0);
+               break;
 
        case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */
                dbg("%s TIOCMBIC", __FUNCTION__);
                if (get_user(mask, (unsigned long *) arg))
                        return -EFAULT;
                if (mask & TIOCM_DTR){
-                       if ((ret = set_dtr(serial->dev, 
-                                          usb_sndctrlpipe(serial->dev, 0),
-                                          LOW)) < 0){
+                       if ((ret = set_dtr(port, LOW)) < 0){
                                err("Urb to unset DTR failed");
                                return(ret);
                        }
                }       
                if (mask & TIOCM_RTS) {
-                       if ((ret = set_rts(serial->dev, 
-                                          usb_sndctrlpipe(serial->dev, 0),
-                                          LOW)) < 0){
+                       if ((ret = set_rts(port, LOW)) < 0){
                                err("Urb to unset RTS failed");
                                return(ret);
                        }
                }
+               return(0);
                break;
 
                /*
@@ -1195,25 +1619,203 @@
                                 */
                        }
                }
-               /* NOTREACHED */
-
+               return(0);
+               break;
        default:
-         /* This is not an error - turns out the higher layers will do 
-          *  some ioctls itself (see comment above)
-          */
-               dbg("%s arg not supported - it was 0x%04x", __FUNCTION__,cmd);
-               return(-ENOIOCTLCMD);
+               /* Still may be a bitbang command */
                break;
+               
        }
-       return 0;
+
+       if (_IOC_TYPE(cmd) == FTDI_IOCTL_CHAR) {
+               dbg("%s in bitbang ioctl\n",__FUNCTION__);
+               switch (_IOC_NR (cmd)) {
+
+               case _IOC_NR(FTDI_IOCTL_SET_BITBANG_MODE):
+                       copy_from_user (&bitbang, (void *)arg, sizeof 
(ftdi_bitbang_t));
+
+                       urb_value = bitbang.bitmask;
+                       urb_value += bitbang.enable << 8;
+
+                       if ((ret = usb_control_msg(serial->dev,
+                                                  usb_sndctrlpipe(serial->dev, 0),
+                                                  FTDI_SET_BITBANG_MODE_REQUEST,
+                                                  FTDI_SET_BITBANG_MODE_REQUEST_TYPE,
+                                                  urb_value, 0,
+                                                  buf, 0, WDR_TIMEOUT)) < 0 ) {
+                               err("%s: Could not set Bit Bang mode - err: %d",
+                                    __FUNCTION__, ret);
+                               return(ret);
+                       }
+
+                       if (bitbang.enable)
+                               dbg ("%s: Enabled Bit Bang mode\n", __FUNCTION__);
+                       else
+                               dbg ("%s: Disabled Bit Bang mode\n", __FUNCTION__);
+
+                       return 0;
+                       break;
+               case _IOC_NR(FTDI_IOCTL_GET_DATAPINS):
+                       if ((ret = usb_control_msg(serial->dev,
+                                                  usb_rcvctrlpipe(serial->dev, 0),
+                                                  FTDI_GET_DATAPINS_REQUEST,
+                                                  FTDI_GET_DATAPINS_REQUEST_TYPE,
+                                                  0, 0,
+                                                  buf, 1, WDR_TIMEOUT)) < 0 ) {
+                               err("%s: Could not read pins directly - err: %d",
+                                    __FUNCTION__, ret);
+                               return(ret);
+                       }
+
+                       dbg ("%s: Read pins: %d\n", __FUNCTION__, (unsigned 
char)buf[0]);
+
+                       if (copy_to_user((void *)arg, &buf, 1))
+                               return -EFAULT;
+                       return 0;
+                       break;
+               case _IOC_NR(FTDI_IOCTL_GET_EEPROM):
+                       for (i = 0; i < 64; i++) {
+                               if ((ret = usb_control_msg(serial->dev,
+                                                       usb_rcvctrlpipe(serial->dev, 
0),
+                                                       FTDI_E2_READ_REQUEST,
+                                                       FTDI_E2_READ_REQUEST_TYPE,
+                                                       0, i,
+                                                       buf, 2, WDR_TIMEOUT)) < 0 ) {
+                                       err("%s: Could not read pins directly - err: 
%d",
+                                       __FUNCTION__, ret);
+                                       return(ret);
+                               }
+
+                               eeprom.data[i*2] = buf[0];
+                               eeprom.data[(i*2)+1] = buf[1];
+                       }
+
+                       if (copy_to_user((void *)arg, &eeprom, sizeof(ftdi_eeprom_t)))
+                               return -EFAULT;
+
+                       return 0;
+                       break;
+
+               case _IOC_NR(FTDI_IOCTL_SET_EEPROM):
+                       copy_from_user (&eeprom, (void *)arg, sizeof (ftdi_eeprom_t));
+
+                       for (i = 0; i < 64; i++) {
+                               urb_value = eeprom.data[i*2];
+                               urb_value += eeprom.data[(i*2)+1] << 8;
+
+                               if ((ret = usb_control_msg(serial->dev,
+                                                       usb_sndctrlpipe(serial->dev, 
0),
+                                                       FTDI_E2_WRITE_REQUEST,
+                                                       FTDI_E2_WRITE_REQUEST_TYPE,
+                                                       urb_value, i,
+                                                       buf, 0, WDR_TIMEOUT)) < 0 ) {
+                                       err("%s: Could write eeprom - err: %d",
+                                       __FUNCTION__, ret);
+                                       return(ret);
+                               }
+
+                       }
+
+                       return 0;
+                       break;
+               case _IOC_NR(FTDI_IOCTL_ERASE_EEPROM):
+                       info ("Erasing eeprom!");
+
+                       if ((ret = usb_control_msg(serial->dev,
+                                                  usb_sndctrlpipe(serial->dev, 0),
+                                                  FTDI_E2_ERASE_REQUEST,
+                                                  FTDI_E2_ERASE_REQUEST_TYPE,
+                                                  0, 0,
+                                                  buf, 0, WDR_TIMEOUT)) < 0 ) {
+                               err("%s: Could not erase eeprom - err: %d",
+                                    __FUNCTION__, ret);
+                               return(ret);
+                       }
+
+                       return 0;
+                       break;
+               case _IOC_NR(FTDI_IOCTL_SET_LATENCY):
+                       urb_value = 0;
+                       copy_from_user (&urb_value, (void *)arg, 1);
+           
+                       if ((ret = usb_control_msg(serial->dev,
+                                                  usb_sndctrlpipe(serial->dev, 0),
+                                                  FTDI_SET_LATENCY_REQUEST,
+                                                  FTDI_SET_LATENCY_REQUEST_TYPE,
+                                                  urb_value, 0,
+                                                  buf, 0, WDR_TIMEOUT)) < 0 ) {
+                               err("%s: Could not set latency - err: %d",
+                                    __FUNCTION__, ret);
+                               return(ret);
+                       }
+                       return 0;
+                       break;
+               case _IOC_NR(FTDI_IOCTL_GET_LATENCY):
+                       if ((ret = usb_control_msg(serial->dev,
+                                                  usb_rcvctrlpipe(serial->dev, 0),
+                                                  FTDI_GET_LATENCY_REQUEST,
+                                                  FTDI_GET_LATENCY_REQUEST_TYPE,
+                                                  0, 0,
+                                                  buf, 1, WDR_TIMEOUT)) < 0 ) {
+                               err("%s: Could not get latency - err: %d",
+                                    __FUNCTION__, ret);
+                               return(ret);
+                       }
+
+                       dbg ("%s: Read latency: %d", __FUNCTION__, (unsigned 
char)buf[0]);
+
+                       if (copy_to_user((void *)arg, &buf, 1))
+                               return -EFAULT;
+                       return 0;
+                       break;
+               }
+
+       }
+
+       /* This is not necessarily an error - turns out the higher layers will do 
+        *  some ioctls itself (see comment above)
+        */
+       dbg("%s arg not supported - it was 0x%04x - check /usr/include/asm/ioctls.h", 
__FUNCTION__, cmd);
+
+       return(-ENOIOCTLCMD);
 } /* ftdi_ioctl */
 
 
+static void ftdi_throttle (struct usb_serial_port *port)
+{
+       dbg("%s - port %d", __FUNCTION__, port->number);
+       usb_unlink_urb (port->read_urb);
+}
+
+
+static void ftdi_unthrottle (struct usb_serial_port *port)
+{
+       int result;
+       struct usb_serial *serial = port->serial;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       port->read_urb->dev = serial->dev;
+
+       FILL_BULK_URB(port->read_urb, serial->dev, 
+                     usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+                     port->read_urb->transfer_buffer, 
port->read_urb->transfer_buffer_length,
+                     ftdi_read_bulk_callback, port);
+
+       result = usb_submit_urb(port->read_urb);
+       if (result)
+               err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
+}
+
 static int __init ftdi_init (void)
 {
+
        dbg("%s", __FUNCTION__);
        usb_serial_register (&ftdi_SIO_device);
        usb_serial_register (&ftdi_8U232AM_device);
+       usb_serial_register (&ftdi_FT232BM_device);
+
+
        info(DRIVER_VERSION ":" DRIVER_DESC);
        return 0;
 }
@@ -1221,9 +1823,13 @@
 
 static void __exit ftdi_exit (void)
 {
+
        dbg("%s", __FUNCTION__);
-       usb_serial_deregister (&ftdi_SIO_device);
+
+       usb_serial_deregister (&ftdi_FT232BM_device);
        usb_serial_deregister (&ftdi_8U232AM_device);
+       usb_serial_deregister (&ftdi_SIO_device);
+
 }
 
 
diff -rauX ../dontdiff orig/drivers/usb/serial/ftdi_sio.h 
new/drivers/usb/serial/ftdi_sio.h
--- orig/drivers/usb/serial/ftdi_sio.h  2003-03-05 21:19:16.000000000 +1300
+++ new/drivers/usb/serial/ftdi_sio.h   2003-03-05 21:27:22.000000000 +1300
@@ -14,20 +14,39 @@
  * of the protocol required to talk to the device and ongoing assistence
  * during development.
  *
- * Bill Ryder - [EMAIL PROTECTED] of Silicon Graphics, Inc.- wrote the 
+ * Bill Ryder - [EMAIL PROTECTED] formerly of Silicon Graphics, Inc.- wrote the 
  * FTDI_SIO implementation.
  *
- * Philipp Gühring - [EMAIL PROTECTED] - added the Device ID of the USB relais
- * from Rudolf Gugler
  */
 
 #define FTDI_VID       0x0403  /* Vendor Id */
 #define FTDI_SIO_PID   0x8372  /* Product Id SIO application of 8U100AX  */
 #define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */
-#define FTDI_RELAIS_PID        0xFA10  /* Relais device from Rudolf Gugler */
 #define FTDI_NF_RIC_VID        0x0DCD  /* Vendor Id */
 #define FTDI_NF_RIC_PID        0x0001  /* Product Id */
 
+
+/* www.crystalfontz.com devices - thanx for providing free devices for evaluation ! */
+/* they use the ftdi chipset for the USB interface and the vendor id is the same */
+#define FTDI_XF_634_PID  0xFC09        /* Four line device */
+#define FTDI_XF_632_PID  0xFC08        /* Two line device */
+
+
+/*
+ * The following are the values for the Matrix Orbital LCD displays,
+ * which are the FT232BM ( similar to the 8U232AM )
+ */
+#define FTDI_MTXORB_VID                FTDI_VID        /* Matrix Orbital Product Id */
+#define FTDI_MTXORB_0_PID      0xFA00  /* Matrix Orbital Product Id */
+#define FTDI_MTXORB_1_PID      0xFA01  /* Matrix Orbital Product Id */
+#define FTDI_MTXORB_2_PID      0xFA02  /* Matrix Orbital Product Id */
+#define FTDI_MTXORB_3_PID      0xFA03  /* Matrix Orbital Product Id */
+#define FTDI_MTXORB_4_PID      0xFA04  /* Matrix Orbital Product Id */
+#define FTDI_MTXORB_5_PID      0xFA05  /* Matrix Orbital Product Id */
+#define FTDI_MTXORB_6_PID      0xFA06  /* Matrix Orbital Product Id */
+
+
+/* Commands */
 #define FTDI_SIO_RESET                 0 /* Reset the port */
 #define FTDI_SIO_MODEM_CTRL    1 /* Set the modem control register */
 #define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */
@@ -37,6 +56,16 @@
 #define FTDI_SIO_SET_EVENT_CHAR        6 /* Set the event character */
 #define FTDI_SIO_SET_ERROR_CHAR        7 /* Set the error character */
 
+#define FTDI_SET_BITBANG_MODE 0xB /* Set the Bit Bang mode of BM type chips */
+#define FTDI_GET_DATAPINS 0xC /* Read the current status of the pins directly */
+
+#define FTDI_E2_READ  0x90 /* Read a word of data from eeprom */
+#define FTDI_E2_WRITE 0x91 /* Write a word of data to eeprom */
+#define FTDI_E2_ERASE 0x92 /* Erase the whole eeprom */
+
+#define FTDI_SET_LATENCY 0x9 /* Set latency of the FTDI input buffer */
+#define FTDI_GET_LATENCY 0x9 /* Get latency of the FTDI input buffer */ 
+
 /* Port Identifier Table */
 #define PIT_DEFAULT            0 /* SIOA */
 #define PIT_SIOA               1 /* SIOA */
@@ -79,7 +108,7 @@
 
 /* FTDI_SIO_SET_BAUDRATE */
 #define FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE 0x40
-#define FTDI_SIO_SET_BAUDRATE_REQUEST 3
+#define FTDI_SIO_SET_BAUDRATE_REQUEST FTDI_SIO_SET_BAUD_RATE
 
 /*
  * BmRequestType:  0100 0000B
@@ -105,14 +134,37 @@
  *   automagically re-encode the resulting value to take fractions into consideration.
  * As all values are integers, some bit twiddling is in order:
  *   BaudDivisor = (BaseClock / 16 / BaudRate) |
- *   (((BaseClock / 2 / BaudRate) & 2) ? 0x8000 : 0) | // 0.25
- *   (((BaseClock / 2 / BaudRate) & 4) ? 0x4000 : 0) | // 0.5
- *   (((BaseClock / 2 / BaudRate) & 0x7) == 1 ? 0xc000) // 0.125 - this line due to 
funny encoding only
+ *   (((BaseClock / 2 / BaudRate) & 4) ? 0x4000    // 0.5
+ *    : ((BaseClock / 2 / BaudRate) & 2) ? 0x8000  // 0.25
+ *    : ((BaseClock / 2 / BaudRate) & 1) ? 0xc000  // 0.125
+ *    : 0)
+ *
+ * For the FT232BM, a 17th divisor bit was introduced to encode the multiples
+ * of 0.125 missing from the FT8U232AM.  Bits 16 to 14 are coded as follows
+ * (the first four codes are the same as for the FT8U232AM, where bit 16 is
+ * always 0):
+ *   000 - add .000 to divisor
+ *   001 - add .500 to divisor
+ *   010 - add .250 to divisor
+ *   011 - add .125 to divisor
+ *   100 - add .375 to divisor
+ *   101 - add .625 to divisor
+ *   110 - add .750 to divisor
+ *   111 - add .875 to divisor
+ * Bits 15 to 0 of the 17-bit divisor are placed in the urb value.  Bit 16 is 
+ * placed in bit 0 of the urb index.
+ *
+ * Note that there are a couple of special cases to support the highest baud
+ * rates.  If the calculated divisor value is 1, this needs to be replaced with
+ * 0.  Additionally for the FT232BM, if the calculated divisor value is 0x4001
+ * (1.5), this needs to be replaced with 0x0001 (1) (but this divisor value is
+ * not supported by the FT8U232AM).
  */
 
 typedef enum {
        SIO = 1,
        FT8U232AM = 2,
+       FT232BM = 3,
 } ftdi_chip_type_t;
 
 typedef enum {
@@ -335,6 +387,159 @@
  *         1 = active 
  */
 
+/* FTDI_SET_BITBANG_MODE
+ * Enables or disables the Bit Bang Mode of BM type chips
+ * This mode replaces the normal function of the chip and turns
+ * the 8 pins to an 8-bit bi-directional bus.
+ *
+ * The clock for the Bit Bang mode is actually 16 times the baudrate.
+   A value of 9600 baud would transfer the data at (9600 x 16) = 153600 bytes
+   per second or 1 every 6,5 uS
+*/
+
+#define FTDI_SET_BITBANG_MODE_REQUEST_TYPE 0x40
+#define FTDI_SET_BITBANG_MODE_REQUEST FTDI_SET_BITBANG_MODE
+
+/*
+ *   BmRequestType:  0100 0000b
+ *   bRequest:       FTDI_SET_BITBANG_MODE
+ *   wValue:         lValue: Bit Mask, hValue: Enable
+ *   wIndex:         0
+ *   wLength:        0
+ *   Data:           None
+ *
+ *   lValue: Bitmask: The bitmask byte sets the direction of the pins.
+ *                     A '1' means the corresponding bit is to be an output,
+ *                     a '0' means the corresponding bit is to be an input.
+ *
+ *   hValue: 1 turns Bit Bang mode on, 0 turns Bit Bang mode off
+ *
+ */
+
+/* FTDI_GET_DATAPINS */
+/* Immediate read of the 8 data pins
+ * This function is very useful in combination with the Bit Bang mode
+ *
+ * Note: Data in the read pipe can be older than this as it is
+         continuously sample the pins until its buffers become full
+ *
+*/
+
+#define FTDI_GET_DATAPINS_REQUEST_TYPE 0x40
+#define FTDI_GET_DATAPINS_REQUEST FTDI_GET_DATAPINS
+
+/*
+ *   BmRequestType:  1100 0000b
+ *   bRequest:       FTDI_GET_DATAPINS
+ *   wValue:         0
+ *   wIndex:         0
+ *   wLength:        1
+ *   Data:           Current status of the pins
+ *
+ */
+
+
+#define FTDI_E2_READ_REQUEST_TYPE 0xC0
+#define FTDI_E2_READ_REQUEST FTDI_E2_READ
+
+/*
+ *   BmRequestType:  1100 0000b
+ *   bRequest:       FTDI_E2_READ
+ *   wValue:         0
+ *   wIndex:         Address of word to read
+ *   wLength:        2
+ *   Data:           Will return a word of data from E2Address
+ *
+ */
+
+#define FTDI_E2_WRITE_REQUEST_TYPE 0x40
+#define FTDI_E2_WRITE_REQUEST FTDI_E2_WRITE
+
+/*
+ *   BmRequestType:  1100 0000b
+ *   bRequest:       FTDI_E2_WRITE
+ *   wValue:         Word to write
+ *   wIndex:         Address of word to read
+ *   wLength:        0
+ *   Data:           None
+ *
+ */
+
+#define FTDI_E2_ERASE_REQUEST_TYPE 0x40
+#define FTDI_E2_ERASE_REQUEST FTDI_E2_ERASE
+
+/*
+ *   BmRequestType:  1100 0000b
+ *   bRequest:       FTDI_E2_ERASE
+ *   wValue:         0
+ *   wIndex:         0
+ *   wLength:        0
+ *   Data:           None
+ *
+ */
+
+/* FTDI_SET_LATENCY
+ * Value is the time in milliseconds between sending data back
+ * to the PC when there is less than a full buffer. It should
+ * be in the range 1-255. The default value is 16ms for compatibility.
+*/
+
+#define FTDI_SET_LATENCY_REQUEST_TYPE 0x40
+#define FTDI_SET_LATENCY_REQUEST FTDI_SET_LATENCY
+
+/*
+ *   BmRequestType:  0100 0000b
+ *   bRequest:       FTDI_SET_LATENCY
+ *   wValue:         lValue: Value, hValue: 0
+ *   wIndex:         0
+ *   wLength:        0
+ *   Data:           None
+ *
+ */
+
+/* FTDI_GET_LATENCY
+ * Get the latency of the FTDI chip input buffer.
+   See above for a more detailed explanation.
+*/
+
+#define FTDI_GET_LATENCY_REQUEST_TYPE 0xC0
+#define FTDI_GET_LATENCY_REQUEST FTDI_GET_LATENCY
+
+/*
+ *   BmRequestType:  1100 0000b
+ *   bRequest:       FTDI_GET_LATENCY
+ *   wValue:         0
+ *   wIndex:         0
+ *   wLength:        1
+ *   Data:           Current latency of the FTDI chip input buffer
+ *
+ */
+
+
+/*
+   Special FTDI ioctls like Bit Bang Mode (BM type chips)
+   or eeprom reading/writing/erasing
+*/
+
+#define FTDI_IOCTL_CHAR 'N'
+#define FTDI_IOCTL_BASE 0x20
+
+typedef struct {
+       unsigned char enable;
+       unsigned char bitmask; /* description of bitmask: see above */
+} ftdi_bitbang_t;
+
+typedef struct {
+       unsigned char data[128]; /* eeprom size is fixed to 128 bytes by hardware */
+} ftdi_eeprom_t;
+
+#define FTDI_IOCTL_SET_BITBANG_MODE _IOW (FTDI_IOCTL_CHAR, FTDI_IOCTL_BASE, 
ftdi_bitbang_t)
+#define FTDI_IOCTL_GET_DATAPINS _IOR (FTDI_IOCTL_CHAR, FTDI_IOCTL_BASE+1, unsigned 
char)
+#define FTDI_IOCTL_GET_EEPROM _IOWR (FTDI_IOCTL_CHAR, FTDI_IOCTL_BASE+2, 
ftdi_eeprom_t)
+#define FTDI_IOCTL_SET_EEPROM _IOW (FTDI_IOCTL_CHAR, FTDI_IOCTL_BASE+3, ftdi_eeprom_t)
+#define FTDI_IOCTL_ERASE_EEPROM _IO (FTDI_IOCTL_CHAR, FTDI_IOCTL_BASE+4)
+#define FTDI_IOCTL_SET_LATENCY _IOW (FTDI_IOCTL_CHAR, FTDI_IOCTL_BASE+5, unsigned 
char)
+#define FTDI_IOCTL_GET_LATENCY _IOR (FTDI_IOCTL_CHAR, FTDI_IOCTL_BASE+6, unsigned 
char)
 
 
 /* Descriptors returned by the device 

Reply via email to