ChangeSet 1.1101.3.1, 2003/04/09 16:40:19-07:00, bryder@paradise.net.nz [PATCH] USB: ftdi_sio update Changes are (in brief - see the patch for proper credits etc): Added CrystalFontz LCD device ID's Added MatrixOrb ID's Added throttle/unthrotle Fixed FTDI_SIO bug Added write_urb buffer pool for much better write performance Minor bugfixes New baudrate divisor handling DTR/RTS updates (hardware flow control works well now). Minor code refactorisation drivers/usb/serial/ftdi_sio.c | 855 ++++++++++++++++++++++++++++++++---------- drivers/usb/serial/ftdi_sio.h | 56 ++ 2 files changed, 705 insertions(+), 206 deletions(-) diff -Nru a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c --- a/drivers/usb/serial/ftdi_sio.c Fri Apr 18 15:01:37 2003 +++ b/drivers/usb/serial/ftdi_sio.c Fri Apr 18 15:01:37 2003 @@ -17,6 +17,42 @@ * 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 + * + * (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 +175,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.2.1" +#define DRIVER_VERSION "v1.3.2" #define DRIVER_AUTHOR "Greg Kroah-Hartman , Bill Ryder , Kuba Ober " #define DRIVER_DESC "USB FTDI Serial Converters Driver" @@ -156,13 +192,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 +237,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 +269,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 +289,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 +319,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 +335,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 +343,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 +357,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 +395,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 +405,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_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) +{ + 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 unsigned short int ftdi_baud_to_divisor(int baud) +static __u32 ftdi_232bm_baud_to_divisor(int baud) { - return(ftdi_baud_base_to_divisor(baud, 48000000)); + return(ftdi_232bm_baud_base_to_divisor(baud, 48000000)); } -static int set_rts(struct usb_device *dev, - unsigned int pipe, - int high_or_low) +static int set_rts(struct usb_serial_port *port, int high_or_low) { - 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, + 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 +453,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 +474,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 +548,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 +650,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 +680,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 +873,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 +893,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 +931,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; + } + } + + /* 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]); + } - /* 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); + usb_serial_debug_data (__FILE__, __FUNCTION__, urb_byte_count, first_byte); - result = usb_submit_urb(port->write_urb); - if (result) { - err("%s - failed submitting write urb, error %d", __FUNCTION__, result); - return 0; - } + /* 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 +1121,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 +1183,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 +1267,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 +1291,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 +1406,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__); } @@ -1060,6 +1481,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 +1503,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 +1513,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 +1530,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 +1615,58 @@ */ } } - /* 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); break; + } - return 0; + + + /* 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 +1674,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 -Nru a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h --- a/drivers/usb/serial/ftdi_sio.h Fri Apr 18 15:01:37 2003 +++ b/drivers/usb/serial/ftdi_sio.h Fri Apr 18 15:01:37 2003 @@ -14,20 +14,39 @@ * of the protocol required to talk to the device and ongoing assistence * during development. * - * Bill Ryder - bryder@sgi.com of Silicon Graphics, Inc.- wrote the + * Bill Ryder - bryder@sgi.com formerly of Silicon Graphics, Inc.- wrote the * FTDI_SIO implementation. * - * Philipp Gühring - pg@futureware.at - 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 */ @@ -105,14 +124,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 {