Greg --

Here is a patch for io_ti in 2.6.9-rc3.  This adds support
for all the new Edgeport devices and improves/fixes the
driver in several ways.  The changelog is below.

This patch applies to 2.6.9-rc3, but it was mostly tested
in 2.6.9-rc2.  It does not change set_termios to avoid
sleeping, since that will be changing again before the
final 2.6.9, I assume.  I have a patch for a non-sleeping
set_termios if needed.

Please apply.

Thanks,
-- Al

Change Log

- Updated to version 0.7.

- Added support for Edgeport 4s, 221c, 22c, 21c, 1, 1i, Watchports,
  Plus Power Port HP4CD, and Plus Power Port PCI.

- Added ION_SETMODE ioctl to set 232 or 485 mode for Edgeport 4s.

- Added a circular write buffer and locking to protect it.  This fixes
  OPOST processing problems.

- Added a "bounce buffer" and a semaphore to protect it when copying
  data from user space to the circular buffer.

- Rewrote TIChase to wait for the circular buffer to drain, to wait
  up to the closing wait time, and to wait for the last character out.

- Fixed hardware flow control by having it stop and restart reads
  and set and clear tty->hw_stopped.
 
- Removed the RELEVANT_IFLAG macro which mistakenly filtered out the
  software flow control stty flags.

- Added a semaphore to to protect num_ports_open to fix a race
  during simultaneous opens--two opens could both try to submit
  the interrupt urb, an error.

- Added a separate write_urb_in_use flag rather than relying
  on (urb->status == -EINPROGRESS).

- Added edge_tty_recv to pass data to the tty flip buffers.

- Freed memory on failed startup.

- Used usb_kill_urb instead of synchronous usb_unlink_urb.

- Added low latency as a module parameter, it is on by default.

- Added closing wait as a module parameter, it is 40 seconds by default.

- Simplified rounding the baud rate divisor.

- Added some error messages.

Signed-off-by: Al Borchers <[EMAIL PROTECTED]>

diff -urp -X dontdiff linux-2.6.9-rc3.orig/drivers/usb/serial/io_ti.c 
linux-2.6.9-rc3.new/drivers/usb/serial/io_ti.c
--- linux-2.6.9-rc3.orig/drivers/usb/serial/io_ti.c     2004-10-04 22:52:09.162742672 
-0500
+++ linux-2.6.9-rc3.new/drivers/usb/serial/io_ti.c      2004-10-04 22:23:42.000000000 
-0500
@@ -10,7 +10,11 @@
  *     (at your option) any later version.
  *
  * Supports the following devices:
- *     EP/1 EP/2 EP/4
+ *     EP/1 EP/2 EP/4 EP/21 EP/22 EP/221 EP/42 EP/421 WATCHPORT
+ *
+ * For questions or problems with this driver, contact Inside Out
+ * Networks technical support, or Peter Berger <[EMAIL PROTECTED]>,
+ * or Al Borchers <[EMAIL PROTECTED]>.
  *
  * Version history:
  *
@@ -34,18 +38,18 @@
 #include <linux/serial.h>
 #include <linux/ioctl.h>
 #include <asm/uaccess.h>
+#include <asm/semaphore.h>
 #include <linux/usb.h>
+
 #include "usb-serial.h"
 #include "io_16654.h"
 #include "io_usbvend.h"
 #include "io_ti.h"
 
-static int debug;
-
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.2"
+#define DRIVER_VERSION "v0.7"
 #define DRIVER_AUTHOR "Greg Kroah-Hartman <[EMAIL PROTECTED]> and David Iacovelli"
 #define DRIVER_DESC "Edgeport USB Serial Driver"
 
@@ -73,6 +77,17 @@ struct edgeport_uart_buf_desc {
 #define TI_MODE_DOWNLOAD       2   // Made it to download mode
 #define TI_MODE_TRANSITIONING  3   // Currently in boot mode but transitioning to 
download mode
 
+/* read urb state */
+#define EDGE_READ_URB_RUNNING  0
+#define EDGE_READ_URB_STOPPING 1
+#define EDGE_READ_URB_STOPPED  2
+
+#define EDGE_LOW_LATENCY       1
+#define EDGE_CLOSING_WAIT      4000    /* in .01 sec */
+
+#define EDGE_OUT_BUF_SIZE      1024
+#define EDGE_TMP_BUF_SIZE      1024
+
 
 /* Product information read from the Edgeport */
 struct product_info
@@ -81,6 +96,13 @@ struct product_info
        __u8    hardware_type;          // Type of hardware
 } __attribute__((packed));
 
+/* circular buffer */
+struct edge_buf {
+       unsigned int    buf_size;
+       char            *buf_buf;
+       char            *buf_get;
+       char            *buf_put;
+};
 
 struct edgeport_port {
        __u16 uart_base;
@@ -102,12 +124,18 @@ struct edgeport_port {
                                                   happen */
        struct edgeport_serial  *edge_serial;
        struct usb_serial_port  *port;
+       __u8 bUartMode;         /* Port type, 0: RS232, etc. */ 
+       spinlock_t ep_lock;
+       int ep_read_urb_state;
+       int ep_write_urb_in_use;
+       struct edge_buf *ep_out_buf;
 };
 
 struct edgeport_serial {
        struct product_info product_info;
        u8 TI_I2C_Type;                 // Type of I2C in UMP
        u8 TiReadI2C;                   // Set to TRUE if we have read the I2c in Boot 
Mode
+       struct semaphore es_sem;
        int num_ports_open;
        struct usb_serial *serial;
 };
@@ -116,6 +144,21 @@ struct edgeport_serial {
 /* Devices that this driver supports */
 static struct usb_device_id edgeport_1port_id_table [] = {
        { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_1) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_TI3410_EDGEPORT_1) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_TI3410_EDGEPORT_1I) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_PROXIMITY) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_MOTION) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_MOISTURE) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_TEMPERATURE) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_HUMIDITY) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_POWER) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_LIGHT) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_RADIATION) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_DISTANCE) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_ACCELERATION) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_PROX_DIST) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_PLUS_PWR_HP4CD) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_PLUS_PWR_PCI) },
        { }
 };
 
@@ -129,12 +172,32 @@ static struct usb_device_id edgeport_2po
        { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4) },
        { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4I) },
        { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22I) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_221C) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22C) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21C) },
+// The 4-port shows up as two 2-port devices
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4S) },
        { }
 };
 
 /* Devices that this driver supports */
 static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_1) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_TI3410_EDGEPORT_1) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_TI3410_EDGEPORT_1I) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_PROXIMITY) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_MOTION) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_MOISTURE) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_TEMPERATURE) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_HUMIDITY) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_POWER) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_LIGHT) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_RADIATION) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_DISTANCE) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_ACCELERATION) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_WP_PROX_DIST) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_PLUS_PWR_HP4CD) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_PLUS_PWR_PCI) },
        { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2) },
        { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2C) },
        { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_2I) },
@@ -144,6 +207,10 @@ static struct usb_device_id id_table_com
        { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4) },
        { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4I) },
        { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22I) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_221C) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_22C) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_21C) },
+       { USB_DEVICE(USB_VENDOR_ID_ION, ION_DEVICE_ID_TI_EDGEPORT_4S) },
        { }
 };
 
@@ -160,12 +227,36 @@ static struct usb_driver io_driver = {
 
 static struct EDGE_FIRMWARE_VERSION_INFO OperationalCodeImageVersion;
 
+static int debug;
+
 static int TIStayInBootMode = 0;
+static int low_latency = EDGE_LOW_LATENCY;
+static int closing_wait = EDGE_CLOSING_WAIT;
 static int ignore_cpu_rev = 0;
 
+static char edge_tmp_buf[EDGE_TMP_BUF_SIZE];
+static struct semaphore edge_tmp_buf_sem;
 
 
+static void edge_tty_recv(struct device *dev, struct tty_struct *tty, unsigned char 
*data, int length);
+
+static void stop_read(struct edgeport_port *edge_port);
+static int restart_read(struct edgeport_port *edge_port);
+
 static void edge_set_termios (struct usb_serial_port *port, struct termios 
*old_termios);
+static void edge_send(struct usb_serial_port *port);
+
+/* circular buffer */
+static struct edge_buf *edge_buf_alloc(unsigned int size);
+static void edge_buf_free(struct edge_buf *eb);
+static void edge_buf_clear(struct edge_buf *eb);
+static unsigned int edge_buf_data_avail(struct edge_buf *eb);
+static unsigned int edge_buf_space_avail(struct edge_buf *eb);
+static unsigned int edge_buf_put(struct edge_buf *eb, const char *buf,
+       unsigned int count);
+static unsigned int edge_buf_get(struct edge_buf *eb, char *buf,
+       unsigned int count);
+
 
 static int TIReadVendorRequestSync (struct usb_device *dev,
                                __u8            request,
@@ -241,7 +332,6 @@ static int TIWriteCommandSync (struct us
 
 }
 
-
 /* clear tx/rx buffers and fifo in TI UMP */
 static int TIPurgeDataSync (struct usb_serial_port *port, __u16 mask)
 {
@@ -436,7 +526,7 @@ static int TIWriteDownloadI2C (struct ed
                                                buffer,                 // 
TransferBuffer
                                                write_length);          // 
TransferBufferLength
                if (status) {
-                       dbg ("%s - ERROR %d", __FUNCTION__, status);
+                       dev_err (&serial->serial->dev->dev, "%s - ERROR %d\n", 
__FUNCTION__, status);
                        return status;
                }
                
@@ -510,74 +600,54 @@ exit_is_tx_active:
        return bytes_left;
 }
 
-static void TIChasePort(struct edgeport_port *port)
+static void TIChasePort(struct edgeport_port *port, unsigned long timeout, int flush)
 {
-       int loops;
-       int last_count;
-       int write_size;
-
-restart_tx_loop:
-       // Base the LoopTime on the baud rate
-       if (port->baud_rate == 0)
-               port->baud_rate = 1200;
-
-       write_size = port->tx.count;
-       loops = max(100, (100*write_size)/(port->baud_rate/10));
-       dbg ("%s - write_size %d, baud %d loop = %d", __FUNCTION__,
-            write_size, port->baud_rate, loops);
-
-       while (1) {
-               // Save Last count
-               last_count = port->tx.count;
-
-               dbg ("%s - Tx Buffer Size = %d loops = %d", __FUNCTION__,
-                    last_count, loops);
-
-               /* Is the Edgeport Buffer empty? */
-               if (port->tx.count == 0)
+       int baud_rate;
+       struct tty_struct *tty = port->port->tty;
+       wait_queue_t wait;
+       unsigned long flags;
+
+       if (!timeout)
+               timeout = (HZ*EDGE_CLOSING_WAIT)/100;
+
+       /* wait for data to drain from the buffer */
+       spin_lock_irqsave(&port->ep_lock, flags);
+       init_waitqueue_entry(&wait, current);
+       add_wait_queue(&tty->write_wait, &wait);
+       for (;;) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (edge_buf_data_avail(port->ep_out_buf) == 0
+               || timeout == 0 || signal_pending(current)
+               || !usb_get_intfdata(port->port->serial->interface))  /* disconnect */
+                       break;
+               spin_unlock_irqrestore(&port->ep_lock, flags);
+               timeout = schedule_timeout(timeout);
+               spin_lock_irqsave(&port->ep_lock, flags);
+       }
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&tty->write_wait, &wait);
+       if (flush)
+               edge_buf_clear(port->ep_out_buf);
+       spin_unlock_irqrestore(&port->ep_lock, flags);
+
+       /* wait for data to drain from the device */
+       timeout += jiffies;
+       while ((long)(jiffies - timeout) < 0 && !signal_pending(current)
+       && usb_get_intfdata(port->port->serial->interface)) {  /* not disconnected */
+               if (!TIIsTxActive(port))
                        break;
-
-               /* Block the thread for 10ms */
                msleep(10);
-
-               if (last_count == port->tx.count) {
-                       /* No activity.. count down. */
-                       --loops;
-                       if (loops == 0) {
-                               dbg ("%s - Wait for TxEmpty - TIMEOUT",
-                                    __FUNCTION__);
-                               return;
-                       }
-               } else {
-                       /* Reset timeout value back to a minimum of 1 second */
-                       dbg ("%s - Wait for TxEmpty  Reset Count", __FUNCTION__);
-                       goto restart_tx_loop;
-               }
        }
 
-       dbg ("%s - Local Tx Buffer Empty -- Waiting for TI UMP to EMPTY X/Y and FIFO",
-            __FUNCTION__);
-
-       write_size = TIIsTxActive (port);
-       loops = max(50, (100*write_size)/(port->baud_rate/10));
-       dbg ("%s - write_size %d, baud %d loop = %d", __FUNCTION__, 
-            write_size, port->baud_rate, loops);
-
-       while (1) {
-               /* This function takes 4 ms; */
-               if (!TIIsTxActive (port)) {
-                       /* Delay a few char times */
-                       msleep(50);
-                       dbg ("%s - Empty", __FUNCTION__);
-                       return;
-               }
+       /* disconnected */
+       if (!usb_get_intfdata(port->port->serial->interface))
+               return;
 
-               --loops;
-               if (loops == 0) {
-                       dbg ("%s - TIMEOUT", __FUNCTION__);
-                       return;
-               }
-       }
+       /* wait one more character time, based on baud rate */
+       /* (TIIsTxActive doesn't seem to wait for the last byte) */
+       if ((baud_rate=port->baud_rate) == 0)
+               baud_rate = 50;
+       msleep(max(1,(10000+baud_rate-1)/baud_rate));
 }
 
 static int TIChooseConfiguration (struct usb_device *dev)
@@ -688,6 +758,7 @@ static int TiValidateI2cImage (struct ed
        struct ti_i2c_desc *rom_desc;
        int start_address = 2;
        __u8 *buffer;
+       __u16 ttype;
 
        rom_desc = kmalloc (sizeof (*rom_desc), GFP_KERNEL);
        if (!rom_desc) {
@@ -701,12 +772,12 @@ static int TiValidateI2cImage (struct ed
                return -ENOMEM;
        }
 
-       // Read the first byte (Signature0) must be 0x52
+       // Read the first byte (Signature0) must be 0x52 or 0x10
        status = TIReadRom (serial, 0, 1, buffer);
        if (status)
                goto ExitTiValidateI2cImage; 
 
-       if (*buffer != 0x52) {
+       if (*buffer != UMP5152 && *buffer != UMP3410) {
                dev_err (dev, "%s - invalid buffer signature\n", __FUNCTION__);
                status = -ENODEV;
                goto ExitTiValidateI2cImage;
@@ -730,7 +801,9 @@ static int TiValidateI2cImage (struct ed
                dbg ("%s Type = 0x%x", __FUNCTION__, rom_desc->Type);
 
                // Skip type 2 record
-               if ((rom_desc->Type & 0x0f) != I2C_DESC_TYPE_FIRMWARE_BASIC) {
+               ttype = rom_desc->Type & 0x0f;
+               if ( ttype != I2C_DESC_TYPE_FIRMWARE_BASIC
+                       && ttype != I2C_DESC_TYPE_FIRMWARE_AUTO ) {
                        // Read the descriptor data
                        status = TIReadRom(serial,
                                                start_address+sizeof(struct 
ti_i2c_desc),
@@ -840,7 +913,7 @@ static int BuildI2CFirmwareHeader (__u8 
 
        memcpy (buffer + sizeof(struct ti_i2c_firmware_rec),
                &PagableOperationalCodeImage[sizeof(struct ti_i2c_image_header)],
-               img_header->Length);
+               le16_to_cpu(img_header->Length));
 
        for (i=0; i < buffer_size; i++) {
                cs = (__u8)(cs + buffer[i]);
@@ -878,7 +951,7 @@ static int TIGetI2cTypeInBootMode (struc
                dbg ("%s - read 2 status error = %d", __FUNCTION__, status);
        else
                dbg ("%s - read 2 data = 0x%x", __FUNCTION__, data);
-       if ((!status) && data == 0x52) {
+       if ((!status) && (data == UMP5152 || data == UMP3410)) {
                dbg ("%s - ROM_TYPE_II", __FUNCTION__);
                serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II;
                return 0;
@@ -895,7 +968,7 @@ static int TIGetI2cTypeInBootMode (struc
                dbg ("%s - read 3 status error = %d", __FUNCTION__, status);
        else
                dbg ("%s - read 2 data = 0x%x", __FUNCTION__, data);
-       if ((!status) && data == 0x52) {
+       if ((!status) && (data == UMP5152 || data == UMP3410)) {
                dbg ("%s - ROM_TYPE_III", __FUNCTION__);
                serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_III;
                return 0;
@@ -983,7 +1056,7 @@ static int TIDownloadFirmware (struct ed
 
        interface = &serial->serial->interface->cur_altsetting->desc;
        if (!interface) {
-               dev_err (&serial->serial->dev->dev, "%s - no interface set, error!", 
__FUNCTION__);
+               dev_err (dev, "%s - no interface set, error!\n", __FUNCTION__);
                return -ENODEV;
        }
 
@@ -1249,7 +1322,7 @@ static int TIDownloadFirmware (struct ed
 
                        dbg ("%s - Update complete 0x%x", __FUNCTION__, status);
                        if (status) {
-                               dbg ("%s - UMPC_COPY_DNLD_TO_I2C failed", 
__FUNCTION__);
+                               dev_err (dev, "%s - UMPC_COPY_DNLD_TO_I2C failed\n", 
__FUNCTION__);
                                kfree (rom_desc);
                                kfree (ti_manuf_desc);
                                return status;
@@ -1356,7 +1429,7 @@ static int TIDownloadFirmware (struct ed
                header = (struct ti_i2c_image_header *)buffer;
                
                // update length and checksum after padding
-               header->Length   = (__u16)(buffer_size - sizeof(struct 
ti_i2c_image_header));
+               header->Length   = cpu_to_le16((__u16)(buffer_size - sizeof(struct 
ti_i2c_image_header)));
                header->CheckSum = cs;
 
                // Download the operational code 
@@ -1560,6 +1633,7 @@ static __u8 MapLineStatus (__u8 ti_lsr)
 static void handle_new_msr (struct edgeport_port *edge_port, __u8 msr)
 {
        struct async_icount *icount;
+       struct tty_struct *tty;
 
        dbg ("%s - %02x", __FUNCTION__, msr);
 
@@ -1581,6 +1655,17 @@ static void handle_new_msr (struct edgep
        /* Save the new modem status */
        edge_port->shadow_msr = msr & 0xf0;
 
+       tty = edge_port->port->tty;
+       /* handle CTS flow control */
+       if (tty && C_CRTSCTS(tty)) {
+               if (msr & EDGEPORT_MSR_CTS) {
+                       tty->hw_stopped = 0;
+                       tty_wakeup(tty);
+               } else {
+                       tty->hw_stopped = 1;
+               }
+       }
+
        return;
 }
 
@@ -1602,10 +1687,8 @@ static void handle_new_lsr (struct edgep
        }
 
        /* Place LSR data byte into Rx buffer */
-       if (lsr_data && edge_port->port->tty) {
-               tty_insert_flip_char(edge_port->port->tty, data, 0);
-               tty_flip_buffer_push(edge_port->port->tty);
-       }
+       if (lsr_data && edge_port->port->tty)
+               edge_tty_recv(&edge_port->port->dev, edge_port->port->tty, &data, 1);
 
        /* update input line counters */
        icount = &edge_port->icount;
@@ -1646,7 +1729,7 @@ static void edge_interrupt_callback (str
                dbg("%s - urb shutting down with status: %d", __FUNCTION__, 
urb->status);
                return;
        default:
-               dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+               dev_err(&urb->dev->dev, "%s - nonzero urb status received: %d\n", 
__FUNCTION__, urb->status);
                goto exit;
        }
 
@@ -1715,21 +1798,31 @@ static void edge_bulk_in_callback (struc
        struct edgeport_port *edge_port = (struct edgeport_port *)urb->context;
        unsigned char *data = urb->transfer_buffer;
        struct tty_struct *tty;
-       int status;
-       int i;
+       int status = 0;
        int port_number;
 
        dbg("%s", __FUNCTION__);
 
-       if (urb->status) {
-               dbg ("%s - nonzero read bulk status received: %d",
-                    __FUNCTION__, urb->status);
+       switch (urb->status) {
+       case 0:
+               /* success */
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               /* this urb is terminated, clean up */
+               dbg("%s - urb shutting down with status: %d", __FUNCTION__, 
urb->status);
+               return;
+       default:
+               dev_err (&urb->dev->dev,"%s - nonzero read bulk status received: %d\n",
+                    __FUNCTION__, urb->status );
+       }
 
-               if (urb->status == -EPIPE) {
-                       /* clear any problem that might have happened on this pipe */
-                       usb_clear_halt (edge_port->port->serial->dev, urb->pipe);
-                       goto exit;
-               }
+       if (urb->status == -EPIPE)
+               goto exit;
+
+       if (urb->status) {
+               dev_err(&urb->dev->dev,"%s - stopping read!\n", __FUNCTION__);
                return;
        }
 
@@ -1752,52 +1845,78 @@ static void edge_bulk_in_callback (struc
                if (edge_port->close_pending) {
                        dbg ("%s - close is pending, dropping data on the floor.", 
__FUNCTION__);
                } else {
-                       for (i = 0; i < urb->actual_length ; ++i) {
-                               /* if we insert more than TTY_FLIPBUF_SIZE characters,
-                                * we drop them. */
-                               if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
-                                       tty_flip_buffer_push(tty);
-                               }
-                               /* this doesn't actually push the data through unless
-                                * tty->low_latency is set */
-                               tty_insert_flip_char(tty, data[i], 0);
-                       }
-                       tty_flip_buffer_push(tty);
+                       edge_tty_recv(&edge_port->port->dev, tty, data, 
urb->actual_length);
                }
                edge_port->icount.rx += urb->actual_length;
        }
 
 exit:
-       /* continue always trying to read */
-       status = usb_submit_urb (urb, GFP_ATOMIC);
+       /* continue read unless stopped */
+       spin_lock(&edge_port->ep_lock);
+       if (edge_port->ep_read_urb_state == EDGE_READ_URB_RUNNING) {
+               urb->dev = edge_port->port->serial->dev;
+               status = usb_submit_urb(urb, GFP_ATOMIC);
+       } else if (edge_port->ep_read_urb_state == EDGE_READ_URB_STOPPING) {
+               edge_port->ep_read_urb_state = EDGE_READ_URB_STOPPED;
+       }
+       spin_unlock(&edge_port->ep_lock);
        if (status)
                dev_err (&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n",
                         __FUNCTION__, status);
 }
 
+static void edge_tty_recv(struct device *dev, struct tty_struct *tty, unsigned char 
*data, int length)
+{
+       int cnt;
+
+       do {
+               if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+                       tty_flip_buffer_push(tty);
+                       if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+                               dev_err(dev, "%s - dropping data, %d bytes lost\n",
+                                       __FUNCTION__, length);
+                               return;
+                       }
+               }
+               cnt = min(length, TTY_FLIPBUF_SIZE - tty->flip.count);
+               memcpy(tty->flip.char_buf_ptr, data, cnt);
+               memset(tty->flip.flag_buf_ptr, 0, cnt);
+               tty->flip.char_buf_ptr += cnt;
+               tty->flip.flag_buf_ptr += cnt;
+               tty->flip.count += cnt;
+               data += cnt;
+               length -= cnt;
+       } while (length > 0);
+
+       tty_flip_buffer_push(tty);
+}
+
 static void edge_bulk_out_callback (struct urb *urb, struct pt_regs *regs)
 {
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
-       struct tty_struct *tty;
+       struct edgeport_port *edge_port = usb_get_serial_port_data(port);
 
        dbg ("%s - port %d", __FUNCTION__, port->number);
 
-       if (urb->status) {
-               dbg ("%s - nonzero write bulk status received: %d",
-                    __FUNCTION__, urb->status);
+       edge_port->ep_write_urb_in_use = 0;
 
-               if (urb->status == -EPIPE) {
-                       /* clear any problem that might have happened on this pipe */
-                       usb_clear_halt (port->serial->dev, urb->pipe);
-               }
+       switch (urb->status) {
+       case 0:
+               /* success */
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               /* this urb is terminated, clean up */
+               dbg("%s - urb shutting down with status: %d", __FUNCTION__, 
urb->status);
                return;
+       default:
+               dev_err (&urb->dev->dev,"%s - nonzero write bulk status received: 
%d\n",
+                    __FUNCTION__, urb->status);
        }
 
-       tty = port->tty;
-       if (tty) {
-               /* let the tty driver wakeup if it has a special write_wakeup function 
*/
-               tty_wakeup(tty);
-       }
+       /* send any buffered data */
+       edge_send(port);
 }
 
 static int edge_open (struct usb_serial_port *port, struct file * filp)
@@ -1816,11 +1935,8 @@ static int edge_open (struct usb_serial_
        if (edge_port == NULL)
                return -ENODEV;
 
-       /* force low_latency on so that our tty_push actually forces the data through, 
-          otherwise it is scheduled, and with high data rates (like with OHCI) data
-          can get lost. */
        if (port->tty)
-               port->tty->low_latency = 1;
+               port->tty->low_latency = low_latency;
 
        port_number = port->number - port->serial->minor;
        switch (port_number) {
@@ -1847,8 +1963,11 @@ static int edge_open (struct usb_serial_
 
        /* turn off loopback */
        status = TIClearLoopBack (edge_port);
-       if (status)
+       if (status) {
+               dev_err(&port->dev,"%s - cannot send clear loopback command, %d\n",
+                       __FUNCTION__, status);
                return status;
+       }
        
        /* set up the port settings */
        edge_set_termios (port, NULL);
@@ -1874,8 +1993,10 @@ static int edge_open (struct usb_serial_
                                        open_settings,
                                        NULL,
                                        0);
-       if (status)
+       if (status) {
+               dev_err(&port->dev,"%s - cannot send open command, %d\n", 
__FUNCTION__, status);
                return status;
+       }
 
        /* Start the DMA? */
        status = TIWriteCommandSync (dev,
@@ -1884,13 +2005,17 @@ static int edge_open (struct usb_serial_
                                        0,
                                        NULL,
                                        0);
-       if (status)
+       if (status) {
+               dev_err(&port->dev,"%s - cannot send start DMA command, %d\n", 
__FUNCTION__, status);
                return status;
+       }
 
        /* Clear TX and RX buffers in UMP */
        status = TIPurgeDataSync (port, UMP_PORT_DIR_OUT | UMP_PORT_DIR_IN);
-       if (status)
+       if (status) {
+               dev_err(&port->dev,"%s - cannot send clear buffers command, %d\n", 
__FUNCTION__, status);
                return status;
+       }
 
        /* Read Initial MSR */
        status = TIReadVendorRequestSync (dev,
@@ -1899,18 +2024,27 @@ static int edge_open (struct usb_serial_
                                        (__u16)(UMPM_UART1_PORT + port_number), // 
wIndex (Address)
                                        &edge_port->shadow_msr,                 // 
TransferBuffer
                                        1);                                     // 
TransferBufferLength
-       if (status)
+       if (status) {
+               dev_err(&port->dev,"%s - cannot send read MSR command, %d\n", 
__FUNCTION__, status);
                return status;
+       }
 
        dbg ("ShadowMSR 0x%X", edge_port->shadow_msr);
  
+       /* Set Initial MCR */
+       edge_port->shadow_mcr = MCR_RTS | MCR_DTR;
+       dbg ("ShadowMCR 0x%X", edge_port->shadow_mcr);
+
        edge_serial = edge_port->edge_serial;
+       if (down_interruptible(&edge_serial->es_sem))
+               return -ERESTARTSYS;
        if (edge_serial->num_ports_open == 0) {
                /* we are the first port to be opened, let's post the interrupt urb */
                urb = edge_serial->serial->port[0]->interrupt_in_urb;
                if (!urb) {
                        dev_err (&port->dev, "%s - no interrupt urb present, 
exiting\n", __FUNCTION__);
-                       return -EINVAL;
+                       status = -EINVAL;
+                       goto up_es_sem;
                }
                urb->complete = edge_interrupt_callback;
                urb->context = edge_serial;
@@ -1918,7 +2052,7 @@ static int edge_open (struct usb_serial_
                status = usb_submit_urb (urb, GFP_KERNEL);
                if (status) {
                        dev_err (&port->dev, "%s - usb_submit_urb failed with value 
%d\n", __FUNCTION__, status);
-                       return status;
+                       goto up_es_sem;
                }
        }
 
@@ -1933,25 +2067,34 @@ static int edge_open (struct usb_serial_
        urb = port->read_urb;
        if (!urb) {
                dev_err (&port->dev, "%s - no read urb present, exiting\n", 
__FUNCTION__);
-               return -EINVAL;
+               status = -EINVAL;
+               goto unlink_int_urb;
        }
+       edge_port->ep_read_urb_state = EDGE_READ_URB_RUNNING;
        urb->complete = edge_bulk_in_callback;
        urb->context = edge_port;
        urb->dev = dev;
        status = usb_submit_urb (urb, GFP_KERNEL);
        if (status) {
                dev_err (&port->dev, "%s - read bulk usb_submit_urb failed with value 
%d\n", __FUNCTION__, status);
-               return status;
+               goto unlink_int_urb;
        }
 
        ++edge_serial->num_ports_open;
 
        dbg("%s - exited", __FUNCTION__);
 
-       return 0;
+       goto up_es_sem;
+
+unlink_int_urb:
+       if (edge_port->edge_serial->num_ports_open == 0)
+               usb_kill_urb (port->serial->port[0]->interrupt_in_urb);
+up_es_sem:
+       up(&edge_serial->es_sem);
+       return status;
 }
 
-static void edge_close (struct usb_serial_port *port, struct file * filp)
+static void edge_close (struct usb_serial_port *port, struct file *filp)
 {
        struct edgeport_serial *edge_serial;
        struct edgeport_port *edge_port;
@@ -1969,10 +2112,12 @@ static void edge_close (struct usb_seria
         * this flag and dump add read data */
        edge_port->close_pending = 1;
 
-       /* chase the port close */
-       TIChasePort (edge_port);
+       /* chase the port close and flush */
+       TIChasePort (edge_port, (HZ*closing_wait)/100, 1);
 
-       usb_unlink_urb (port->read_urb);
+       usb_kill_urb(port->read_urb);
+       usb_kill_urb(port->write_urb);
+       edge_port->ep_write_urb_in_use = 0;
 
        /* assuming we can still talk to the device,
         * send a close port command to it */
@@ -1984,12 +2129,14 @@ static void edge_close (struct usb_seria
                                     0,
                                     NULL,
                                     0);
+       down(&edge_serial->es_sem);
        --edge_port->edge_serial->num_ports_open;
        if (edge_port->edge_serial->num_ports_open <= 0) {
                /* last port is now closed, let's shut down our interrupt urb */
-               usb_unlink_urb (port->serial->port[0]->interrupt_in_urb);
+               usb_kill_urb (port->serial->port[0]->interrupt_in_urb);
                edge_port->edge_serial->num_ports_open = 0;
        }
+       up(&edge_serial->es_sem);
        edge_port->close_pending = 0;
 
        dbg("%s - exited", __FUNCTION__);
@@ -1998,7 +2145,7 @@ static void edge_close (struct usb_seria
 static int edge_write (struct usb_serial_port *port, int from_user, const unsigned 
char *data, int count)
 {
        struct edgeport_port *edge_port = usb_get_serial_port_data(port);
-       int result;
+       unsigned long flags;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
 
@@ -2011,21 +2158,62 @@ static int edge_write (struct usb_serial
                return -ENODEV;
        if (edge_port->close_pending == 1)
                return -ENODEV;
-       
-       if (port->write_urb->status == -EINPROGRESS) {
-               dbg ("%s - already writing", __FUNCTION__);
-               return 0;
-       }
-
-       count = min (count, port->bulk_out_size);
 
+       /* copy user data (which can sleep) */
        if (from_user) {
-               if (copy_from_user(port->write_urb->transfer_buffer, data, count))
+               if (count > EDGE_TMP_BUF_SIZE)
+                       count = EDGE_TMP_BUF_SIZE;
+               down(&edge_tmp_buf_sem);
+               if (copy_from_user(edge_tmp_buf, data, count) != 0) {
+                       up(&edge_tmp_buf_sem);
+                       dev_err(&port->dev,"%s - cannot copy from user space\n", 
__FUNCTION__);
                        return -EFAULT;
-       } else {
-               memcpy (port->write_urb->transfer_buffer, data, count);
+               }
+               data = edge_tmp_buf;
        }
 
+       spin_lock_irqsave(&edge_port->ep_lock, flags);
+       count = edge_buf_put(edge_port->ep_out_buf, data, count);
+       spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
+       if (from_user)
+               up(&edge_tmp_buf_sem);
+
+       edge_send(port);
+
+       return count;
+}
+
+static void edge_send(struct usb_serial_port *port)
+{
+       int count, result;
+       struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+       struct tty_struct *tty = port->tty;
+       unsigned long flags;
+
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       spin_lock_irqsave(&edge_port->ep_lock, flags);
+
+       if (edge_port->ep_write_urb_in_use) {
+               spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+               return;
+       }
+
+       count = edge_buf_get(edge_port->ep_out_buf,
+                               port->write_urb->transfer_buffer,
+                               port->bulk_out_size);
+
+       if (count == 0) {
+               spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+               return;
+       }
+
+       edge_port->ep_write_urb_in_use = 1;
+
+       spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
        usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, 
port->write_urb->transfer_buffer);
 
        /* set up our urb */
@@ -2038,33 +2226,38 @@ static int edge_write (struct usb_serial
 
        /* send the data out the bulk port */
        result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
-       if (result)
+       if (result) {
                dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", 
__FUNCTION__, result);
-       else
-               result = count;
-
-       if (result > 0)
+               edge_port->ep_write_urb_in_use = 0;
+               // TODO: reschedule edge_send
+       } else {
                edge_port->icount.tx += count;
+       }
 
-       return result;
+       /* wakeup any process waiting for writes to complete */
+       /* there is now more room in the buffer for new writes */
+       if (tty) {
+               /* let the tty driver wakeup if it has a special write_wakeup function 
*/
+               tty_wakeup(tty);
+       }
 }
 
 static int edge_write_room (struct usb_serial_port *port)
 {
        struct edgeport_port *edge_port = usb_get_serial_port_data(port);
        int room = 0;
+       unsigned long flags;
 
-       dbg("%s", __FUNCTION__);
+       dbg("%s - port %d", __FUNCTION__, port->number);
 
        if (edge_port == NULL)
                return -ENODEV;
        if (edge_port->close_pending == 1)
                return -ENODEV;
-       
-       dbg("%s - port %d", __FUNCTION__, port->number);
 
-       if (port->write_urb->status != -EINPROGRESS)
-               room = port->bulk_out_size;
+       spin_lock_irqsave(&edge_port->ep_lock, flags);
+       room = edge_buf_space_avail(edge_port->ep_out_buf);
+       spin_unlock_irqrestore(&edge_port->ep_lock, flags);
 
        dbg("%s - returns %d", __FUNCTION__, room);
        return room;
@@ -2074,18 +2267,18 @@ static int edge_chars_in_buffer (struct 
 {
        struct edgeport_port *edge_port = usb_get_serial_port_data(port);
        int chars = 0;
+       unsigned long flags;
 
-       dbg("%s", __FUNCTION__);
+       dbg("%s - port %d", __FUNCTION__, port->number);
 
        if (edge_port == NULL)
                return -ENODEV;
        if (edge_port->close_pending == 1)
                return -ENODEV;
 
-       dbg("%s - port %d", __FUNCTION__, port->number);
-
-       if (port->write_urb->status == -EINPROGRESS)
-               chars = port->write_urb->transfer_buffer_length;
+       spin_lock_irqsave(&edge_port->ep_lock, flags);
+       chars = edge_buf_data_avail(edge_port->ep_out_buf);
+       spin_unlock_irqrestore(&edge_port->ep_lock, flags);
 
        dbg ("%s - returns %d", __FUNCTION__, chars);
        return chars;
@@ -2107,21 +2300,21 @@ static void edge_throttle (struct usb_se
                dbg ("%s - no tty available", __FUNCTION__);
                return;
        }
+
        /* if we are implementing XON/XOFF, send the stop character */
        if (I_IXOFF(tty)) {
                unsigned char stop_char = STOP_CHAR(tty);
                status = edge_write (port, 0, &stop_char, 1);
                if (status <= 0) {
-                       return;
+                       dev_err(&port->dev, "%s - failed to write stop character, 
%d\n", __FUNCTION__, status);
                }
        }
 
-       /* if we are implementing RTS/CTS, toggle that line */
-       if (tty->termios->c_cflag & CRTSCTS) {
-               status = TIClearRts (edge_port);
-       }
+       /* if we are implementing RTS/CTS, stop reads */
+       /* and the Edgeport will clear the RTS line */
+       if (C_CRTSCTS(tty))
+               stop_read(edge_port);
 
-       usb_unlink_urb (port->read_urb);
 }
 
 static void edge_unthrottle (struct usb_serial_port *port)
@@ -2146,29 +2339,61 @@ static void edge_unthrottle (struct usb_
                unsigned char start_char = START_CHAR(tty);
                status = edge_write (port, 0, &start_char, 1);
                if (status <= 0) {
-                       return;
+                       dev_err(&port->dev, "%s - failed to write start character, 
%d\n", __FUNCTION__, status);
                }
        }
 
-       /* if we are implementing RTS/CTS, toggle that line */
-       if (tty->termios->c_cflag & CRTSCTS) {
-               status = TISetRts (edge_port);
+       /* if we are implementing RTS/CTS, restart reads */
+       /* are the Edgeport will assert the RTS line */
+       if (C_CRTSCTS(tty)) {
+               status = restart_read(edge_port);
+               if (status)
+                       dev_err(&port->dev, "%s - read bulk usb_submit_urb failed with 
value %d\n", __FUNCTION__, status);
        }
 
-       port->read_urb->dev = port->serial->dev;
-       status = usb_submit_urb (port->read_urb, GFP_ATOMIC);
-       if (status) {
-               dev_err (&port->dev, "%s - usb_submit_urb failed with value %d\n", 
__FUNCTION__, status);
-       }
 }
 
+static void stop_read(struct edgeport_port *edge_port)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&edge_port->ep_lock, flags);
+
+       if (edge_port->ep_read_urb_state == EDGE_READ_URB_RUNNING)
+               edge_port->ep_read_urb_state = EDGE_READ_URB_STOPPING;
+       edge_port->shadow_mcr &= ~MCR_RTS;
+
+       spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+}
+
+static int restart_read(struct edgeport_port *edge_port)
+{
+       struct urb *urb;
+       int status = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&edge_port->ep_lock, flags);
+
+       if (edge_port->ep_read_urb_state == EDGE_READ_URB_STOPPED) {
+               urb = edge_port->port->read_urb;
+               urb->complete = edge_bulk_in_callback;
+               urb->context = edge_port;
+               urb->dev = edge_port->port->serial->dev;
+               status = usb_submit_urb(urb, GFP_KERNEL);
+       }
+       edge_port->ep_read_urb_state = EDGE_READ_URB_RUNNING;
+       edge_port->shadow_mcr |= MCR_RTS;
+
+       spin_unlock_irqrestore(&edge_port->ep_lock, flags);
+
+       return status;
+}
 
 static void change_port_settings (struct edgeport_port *edge_port, struct termios 
*old_termios)
 {
        struct ump_uart_config *config;
        struct tty_struct *tty;
        int baud;
-       int round;
        unsigned cflag;
        int status;
        int port_number = edge_port->port->number - edge_port->port->serial->minor;
@@ -2195,7 +2420,7 @@ static void change_port_settings (struct
        /* These flags must be set */
        config->wFlags |= UMP_MASK_UART_FLAGS_RECEIVE_MS_INT;
        config->wFlags |= UMP_MASK_UART_FLAGS_AUTO_START_ON_ERR;
-       config->bUartMode = 0;
+       config->bUartMode = (__u8)(edge_port->bUartMode);
 
        switch (cflag & CSIZE) {
                case CS5:
@@ -2247,6 +2472,8 @@ static void change_port_settings (struct
                dbg("%s - RTS/CTS is enabled", __FUNCTION__);
        } else {
                dbg("%s - RTS/CTS is disabled", __FUNCTION__);
+               tty->hw_stopped = 0;
+               restart_read(edge_port);
        }
 
        /* if we are implementing XON/XOFF, set the start and stop character in the 
device */
@@ -2279,10 +2506,8 @@ static void change_port_settings (struct
                /* pick a default, any default... */
                baud = 9600;
        }
-       config->wBaudRate = (__u16)(461550L / baud);
-       round = 4615500L / baud;
-       if ((round - (config->wBaudRate * 10)) >= 5)
-               config->wBaudRate++;
+       edge_port->baud_rate = baud;
+       config->wBaudRate = (__u16)((461550L + baud/2) / baud);
 
        dbg ("%s - baud rate = %d, wBaudRate = %d", __FUNCTION__, baud, 
config->wBaudRate);
 
@@ -2329,20 +2554,18 @@ static void edge_set_termios (struct usb
        cflag = tty->termios->c_cflag;
        /* check that they really want us to change something */
        if (old_termios) {
-               if ((cflag == old_termios->c_cflag) &&
-                   (RELEVANT_IFLAG(tty->termios->c_iflag) == 
RELEVANT_IFLAG(old_termios->c_iflag))) {
+               if (cflag == old_termios->c_cflag &&
+                   tty->termios->c_iflag == old_termios->c_iflag) {
                        dbg ("%s - nothing to change", __FUNCTION__);
                        return;
                }
        }
 
        dbg("%s - clfag %08x iflag %08x", __FUNCTION__, 
-           tty->termios->c_cflag,
-           RELEVANT_IFLAG(tty->termios->c_iflag));
+           tty->termios->c_cflag, tty->termios->c_iflag);
        if (old_termios) {
                dbg("%s - old clfag %08x old iflag %08x", __FUNCTION__,
-                   old_termios->c_cflag,
-                   RELEVANT_IFLAG(old_termios->c_iflag));
+                   old_termios->c_cflag, old_termios->c_iflag);
        }
 
        dbg("%s - port %d", __FUNCTION__, port->number);
@@ -2358,7 +2581,7 @@ static void edge_set_termios (struct usb
 
 static int edge_tiocmset (struct usb_serial_port *port, struct file *file, unsigned 
int set, unsigned int clear)
 {
-        struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+       struct edgeport_port *edge_port = usb_get_serial_port_data(port);
        unsigned int mcr;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
@@ -2387,7 +2610,7 @@ static int edge_tiocmset (struct usb_ser
 
 static int edge_tiocmget(struct usb_serial_port *port, struct file *file)
 {
-        struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+       struct edgeport_port *edge_port = usb_get_serial_port_data(port);
        unsigned int result = 0;
        unsigned int msr;
        unsigned int mcr;
@@ -2426,7 +2649,7 @@ static int get_serial_info (struct edgep
        tmp.xmit_fifo_size      = edge_port->port->bulk_out_size;
        tmp.baud_base           = 9600;
        tmp.close_delay         = 5*HZ;
-       tmp.closing_wait        = 30*HZ;
+       tmp.closing_wait        = closing_wait;
 //     tmp.custom_divisor      = state->custom_divisor;
 //     tmp.hub6                = state->hub6;
 //     tmp.io_type             = state->io_type;
@@ -2442,10 +2665,24 @@ static int edge_ioctl (struct usb_serial
        struct edgeport_port *edge_port = usb_get_serial_port_data(port);
        struct async_icount cnow;
        struct async_icount cprev;
+       unsigned int tmp;
 
        dbg("%s - port %d, cmd = 0x%x", __FUNCTION__, port->number, cmd);
 
        switch (cmd) {
+               case ION_SETMODE :
+                       if (port->serial->vendor == USB_VENDOR_ID_ION &&
+                       port->serial->product == ION_DEVICE_ID_TI_EDGEPORT_4S) {
+                               edge_port->bUartMode = (__u8)arg;
+                               return 0;
+                       } else
+                               return -ENOTTY;
+               case ION_GETMODE :
+                       tmp = (unsigned int)edge_port->bUartMode;
+                       if (copy_to_user((void *)arg, &tmp, sizeof(tmp) ))
+                               return -EFAULT;
+                       return 0;
+                       
                case TIOCINQ:
                        dbg("%s - (%d) TIOCINQ", __FUNCTION__, port->number);
 //                     return get_number_bytes_avail(edge_port, (unsigned int *) arg);
@@ -2507,7 +2744,7 @@ static void edge_break (struct usb_seria
        dbg ("%s - state = %d", __FUNCTION__, break_state);
 
        /* chase the port close */
-       TIChasePort (edge_port);
+       TIChasePort (edge_port, 0, 0);
 
        if (break_state == -1) {
                status = TISetBreak (edge_port);
@@ -2537,6 +2774,7 @@ static int edge_startup (struct usb_seri
                return -ENOMEM;
        }
        memset (edge_serial, 0, sizeof(struct edgeport_serial));
+       sema_init(&edge_serial->es_sem, 1);
        edge_serial->serial = serial;
        usb_set_serial_data(serial, edge_serial);
 
@@ -2551,25 +2789,47 @@ static int edge_startup (struct usb_seri
                edge_port = kmalloc (sizeof(struct edgeport_port), GFP_KERNEL);
                if (edge_port == NULL) {
                        dev_err(&serial->dev->dev, "%s - Out of memory\n", 
__FUNCTION__);
-                       return -ENOMEM;
+                       goto cleanup;
                }
                memset (edge_port, 0, sizeof(struct edgeport_port));
+               spin_lock_init(&edge_port->ep_lock);
+               edge_port->ep_out_buf = edge_buf_alloc(EDGE_OUT_BUF_SIZE);
+               if (edge_port->ep_out_buf == NULL) {
+                       dev_err(&serial->dev->dev, "%s - Out of memory\n", 
__FUNCTION__);
+                       kfree(edge_port);
+                       goto cleanup;
+               }
                edge_port->port = serial->port[i];
                edge_port->edge_serial = edge_serial;
                usb_set_serial_port_data(serial->port[i], edge_port);
+               edge_port->bUartMode = 0;       /* Default is RS232 */
        }
        
        return 0;
+
+cleanup:
+       for (--i; i>=0; --i) {
+               edge_port = usb_get_serial_port_data(serial->port[i]);
+               edge_buf_free(edge_port->ep_out_buf);
+               kfree(edge_port);
+               usb_set_serial_port_data(serial->port[i], NULL);
+       }
+       return -ENOMEM;
 }
 
 static void edge_shutdown (struct usb_serial *serial)
 {
        int i;
+       struct edgeport_port *edge_port;
 
        dbg ("%s", __FUNCTION__);
 
        for (i=0; i < serial->num_ports; ++i) {
-               kfree (usb_get_serial_port_data(serial->port[i]));
+               edge_port = usb_get_serial_port_data(serial->port[i]);
+               if (edge_port) {
+                       edge_buf_free(edge_port->ep_out_buf);
+                       kfree(edge_port);
+               }
                usb_set_serial_port_data(serial->port[i], NULL);
        }
        kfree (usb_get_serial_data(serial));
@@ -2577,6 +2837,185 @@ static void edge_shutdown (struct usb_se
 }
 
 
+/* Circular Buffer */
+
+/*
+ * edge_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+
+static struct edge_buf *edge_buf_alloc(unsigned int size)
+{
+       struct edge_buf *eb;
+
+
+       if (size == 0)
+               return NULL;
+
+       eb = (struct edge_buf *)kmalloc(sizeof(struct edge_buf), GFP_KERNEL);
+       if (eb == NULL)
+               return NULL;
+
+       eb->buf_buf = kmalloc(size, GFP_KERNEL);
+       if (eb->buf_buf == NULL) {
+               kfree(eb);
+               return NULL;
+       }
+
+       eb->buf_size = size;
+       eb->buf_get = eb->buf_put = eb->buf_buf;
+
+       return eb;
+}
+
+
+/*
+ * edge_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+
+void edge_buf_free(struct edge_buf *eb)
+{
+       if (eb != NULL) {
+               if (eb->buf_buf != NULL)
+                       kfree(eb->buf_buf);
+               kfree(eb);
+       }
+}
+
+
+/*
+ * edge_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+
+static void edge_buf_clear(struct edge_buf *eb)
+{
+        if (eb != NULL)
+                eb->buf_get = eb->buf_put;
+                /* equivalent to a get of all data available */
+}
+
+
+/*
+ * edge_buf_data_avail
+ *
+ * Return the number of bytes of data available in the circular
+ * buffer.
+ */
+
+static unsigned int edge_buf_data_avail(struct edge_buf *eb)
+{
+       if (eb != NULL)
+               return ((eb->buf_size + eb->buf_put - eb->buf_get) % eb->buf_size);
+       else
+               return 0;
+}
+
+
+/*
+ * edge_buf_space_avail
+ *
+ * Return the number of bytes of space available in the circular
+ * buffer.
+ */
+
+static unsigned int edge_buf_space_avail(struct edge_buf *eb)
+{
+       if (eb != NULL)
+               return ((eb->buf_size + eb->buf_get - eb->buf_put - 1) % eb->buf_size);
+       else
+               return 0;
+}
+
+
+/*
+ * edge_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+
+static unsigned int edge_buf_put(struct edge_buf *eb, const char *buf,
+       unsigned int count)
+{
+       unsigned int len;
+
+
+       if (eb == NULL)
+               return 0;
+
+       len  = edge_buf_space_avail(eb);
+       if (count > len)
+               count = len;
+
+       if (count == 0)
+               return 0;
+
+       len = eb->buf_buf + eb->buf_size - eb->buf_put;
+       if (count > len) {
+               memcpy(eb->buf_put, buf, len);
+               memcpy(eb->buf_buf, buf+len, count - len);
+               eb->buf_put = eb->buf_buf + count - len;
+       } else {
+               memcpy(eb->buf_put, buf, count);
+               if (count < len)
+                       eb->buf_put += count;
+               else /* count == len */
+                       eb->buf_put = eb->buf_buf;
+       }
+
+       return count;
+}
+
+
+/*
+ * edge_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+
+static unsigned int edge_buf_get(struct edge_buf *eb, char *buf,
+       unsigned int count)
+{
+       unsigned int len;
+
+
+       if (eb == NULL)
+               return 0;
+
+       len = edge_buf_data_avail(eb);
+       if (count > len)
+               count = len;
+
+       if (count == 0)
+               return 0;
+
+       len = eb->buf_buf + eb->buf_size - eb->buf_get;
+       if (count > len) {
+               memcpy(buf, eb->buf_get, len);
+               memcpy(buf+len, eb->buf_buf, count - len);
+               eb->buf_get = eb->buf_buf + count - len;
+       } else {
+               memcpy(buf, eb->buf_get, count);
+               if (count < len)
+                       eb->buf_get += count;
+               else /* count == len */
+                       eb->buf_get = eb->buf_buf;
+       }
+
+       return count;
+}
+
+
 static struct usb_serial_device_type edgeport_1port_device = {
        .owner                  = THIS_MODULE,
        .name                   = "Edgeport TI 1 port adapter",
@@ -2641,6 +3080,7 @@ static int __init edgeport_init(void)
        if (retval) 
                goto failed_usb_register;
        info(DRIVER_DESC " " DRIVER_VERSION);
+       sema_init(&edge_tmp_buf_sem, 1);
        return 0;
 failed_usb_register:
        usb_serial_deregister(&edgeport_2port_device);
@@ -2668,6 +3108,12 @@ MODULE_LICENSE("GPL");
 module_param(debug, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(debug, "Debug enabled or not");
 
+module_param(low_latency, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(low_latency, "Low latency enabled or not");
+
+module_param(closing_wait, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(closing_wait, "Maximum wait for data to drain, in .01 secs");
+
 module_param(ignore_cpu_rev, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(ignore_cpu_rev, "Ignore the cpu revision when connecting to a 
device");
 
diff -urp -X dontdiff linux-2.6.9-rc3.orig/drivers/usb/serial/io_ti.h 
linux-2.6.9-rc3.new/drivers/usb/serial/io_ti.h
--- linux-2.6.9-rc3.orig/drivers/usb/serial/io_ti.h     2004-08-14 05:55:10.000000000 
-0500
+++ linux-2.6.9-rc3.new/drivers/usb/serial/io_ti.h      2004-10-04 18:51:05.000000000 
-0500
@@ -177,4 +177,11 @@ struct ump_interrupt                       /* Interrupt pack
 #define TIUMP_INTERRUPT_CODE_LSR       0x03
 #define TIUMP_INTERRUPT_CODE_MSR       0x04
 
+/*
+ * IOCTL DEFINITIONS
+ */
+#define ION_IOC_MAGIC  'E'     /* "Edgeport" */
+#define ION_SETMODE    _IOW(ION_IOC_MAGIC,0x01, unsigned int)  /* Set Edgeport 4S 
mode, e.g: RS232, RS422... */
+#define ION_GETMODE    _IOR(ION_IOC_MAGIC,0x02, unsigned int)  /* Get Edgeport 4S 
mode, e.g: RS232, RS422... */
+
 #endif






-------------------------------------------------------
This SF.net email is sponsored by: IT Product Guide on ITManagersJournal
Use IT products in your business? Tell us what you think of them. Give us
Your Opinions, Get Free ThinkGeek Gift Certificates! Click to find out more
http://productguide.itmanagersjournal.com/guidepromo.tmpl
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to