patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / drivers / usb / serial / ftdi_sio.c
index d9949de..c15f3e5 100644 (file)
@@ -17,6 +17,9 @@
  * See http://ftdi-usb-sio.sourceforge.net for upto date testing info
  *     and extra documentation
  *
+ * (27/May/2004) Ian Abbott
+ *      Improved throttling code, mostly stolen from the WhiteHEAT driver.
+ *
  * (26/Mar/2004) Jan Capek
  *      Added PID's for ICD-U20/ICD-U40 - incircuit PIC debuggers from CCS Inc.
  *
@@ -584,6 +587,10 @@ static struct usb_driver ftdi_driver = {
 #define BUFSZ 512
 #define PKTSZ 64
 
+/* rx_flags */
+#define THROTTLED              0x01
+#define ACTUALLY_THROTTLED     0x02
+
 struct ftdi_private {
        ftdi_chip_type_t chip_type;
                                /* type of the device, either SIO or FT8U232AM */
@@ -598,6 +605,8 @@ struct ftdi_private {
        unsigned long last_dtr_rts;     /* saved modem control outputs */
         wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
        char prev_status, diff_status;        /* Used for TIOCMIWAIT */
+       __u8 rx_flags;          /* receive state flags (throttling) */
+       spinlock_t rx_lock;     /* spinlock for receive state */
 
        int force_baud;         /* if non-zero, force the baud rate to this value */
        int force_rtscts;       /* if non-zero, force RTS-CTS to always be enabled */
@@ -625,6 +634,7 @@ 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, struct pt_regs *regs);
 static void ftdi_read_bulk_callback    (struct urb *urb, struct pt_regs *regs);
+static void ftdi_process_read          (struct usb_serial_port *port);
 static void ftdi_set_termios           (struct usb_serial_port *port, struct termios * old);
 static int  ftdi_tiocmget               (struct usb_serial_port *port, struct file *file);
 static int  ftdi_tiocmset              (struct usb_serial_port *port, struct file * file, unsigned int set, unsigned int clear);
@@ -1110,6 +1120,7 @@ static int ftdi_common_startup (struct usb_serial *serial)
        }
        memset(priv, 0, sizeof(*priv));
 
+       spin_lock_init(&priv->rx_lock);
         init_waitqueue_head(&priv->delta_msr_wait);
        /* This will push the characters through immediately rather
           than queue a task to deliver them */
@@ -1273,8 +1284,8 @@ static void ftdi_shutdown (struct usb_serial *serial)
         */
 
        if (priv) {
-               kfree(priv);
                usb_set_serial_port_data(port, NULL);
+               kfree(priv);
        }
 } /* ftdi_shutdown */
 
@@ -1282,8 +1293,9 @@ static void ftdi_shutdown (struct usb_serial *serial)
 static int  ftdi_open (struct usb_serial_port *port, struct file *filp)
 { /* ftdi_open */
        struct termios tmp_termios;
-       struct usb_serial *serial = port->serial;
+       struct usb_device *dev = port->serial->dev;
        struct ftdi_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
        
        int result = 0;
        char buf[1]; /* Needed for the usb_control_msg I think */
@@ -1295,7 +1307,7 @@ static int  ftdi_open (struct usb_serial_port *port, struct file *filp)
 
        /* No error checking for this (will get errors later anyway) */
        /* See ftdi_sio.h for description of what is reset */
-       usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+       usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                        FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE, 
                        FTDI_SIO_RESET_SIO, 
                        0, buf, 0, WDR_TIMEOUT);
@@ -1317,9 +1329,14 @@ static int  ftdi_open (struct usb_serial_port *port, struct file *filp)
                err("%s Error from RTS HIGH urb", __FUNCTION__);
        }
 
+       /* Not throttled */
+       spin_lock_irqsave(&priv->rx_lock, flags);
+       priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
+       spin_unlock_irqrestore(&priv->rx_lock, flags);
+
        /* Start reading from the device */
-       usb_fill_bulk_urb(port->read_urb, serial->dev, 
-                     usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+       usb_fill_bulk_urb(port->read_urb, dev,
+                     usb_rcvbulkpipe(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, GFP_KERNEL);
@@ -1342,47 +1359,42 @@ static int  ftdi_open (struct usb_serial_port *port, struct file *filp)
 
 static void ftdi_close (struct usb_serial_port *port, struct file *filp)
 { /* ftdi_close */
-       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 */
-                       if (usb_control_msg(serial->dev, 
-                                           usb_sndctrlpipe(serial->dev, 0),
-                                           FTDI_SIO_SET_FLOW_CTRL_REQUEST,
-                                           FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
-                                           0, 0, buf, 0, WDR_TIMEOUT) < 0) {
-                               err("error from flowcontrol urb");
-                       }           
+       if (c_cflag & HUPCL){
+               /* Disable flow control */
+               if (usb_control_msg(port->serial->dev, 
+                                   usb_sndctrlpipe(port->serial->dev, 0),
+                                   FTDI_SIO_SET_FLOW_CTRL_REQUEST,
+                                   FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
+                                   0, 0, buf, 0, WDR_TIMEOUT) < 0) {
+                       err("error from flowcontrol urb");
+               }           
 
-                       /* drop DTR */
-                       if (set_dtr(port, LOW) < 0){
-                               err("Error from DTR LOW urb");
-                       }
-                       /* drop RTS */
-                       if (set_rts(port, LOW) < 0) {
-                               err("Error from RTS LOW urb");
-                       }
-               } /* Note change no line if hupcl is off */
-               
-               /* shutdown our bulk read */
-               if (port->read_urb) {
-                       if (usb_unlink_urb (port->read_urb) < 0) {
-                               err("Error unlinking read urb");
-                       }
+               /* drop DTR */
+               if (set_dtr(port, LOW) < 0){
+                       err("Error from DTR LOW urb");
                }
-
-       } /* if (serial->dev) */
-
-
+               /* drop RTS */
+               if (set_rts(port, LOW) < 0) {
+                       err("Error from RTS LOW urb");
+               }
+       } /* Note change no line if hupcl is off */
+       
+       /* shutdown our bulk read */
+       if (port->read_urb) {
+               if (usb_unlink_urb (port->read_urb) < 0) {
+                       /* Generally, this isn't an error.  If the previous
+                          read bulk callback occurred (or is about to occur)
+                          while the port was being closed or was throtted
+                          (and is still throttled), the read urb will not
+                          have been submitted. */
+                       dbg("%s - failed to unlink read urb (generally not an error)", __FUNCTION__);
+               }
+       }
 } /* ftdi_close */
 
 
@@ -1397,7 +1409,6 @@ 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)
 { /* ftdi_write */
-       struct usb_serial *serial = get_usb_serial ( port, __FUNCTION__);
        struct ftdi_private *priv = usb_get_serial_port_data(port);
        struct urb *urb;
        unsigned char *buffer;
@@ -1484,8 +1495,8 @@ static int ftdi_write (struct usb_serial_port *port, int from_user,
        usb_serial_debug_data (__FILE__, __FUNCTION__, transfer_size, buffer);
 
        /* fill the buffer and send it */
-       usb_fill_bulk_urb(urb, serial->dev, 
-                     usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
+       usb_fill_bulk_urb(urb, port->serial->dev, 
+                     usb_sndbulkpipe(port->serial->dev, port->bulk_out_endpointAddress),
                      buffer, transfer_size,
                      ftdi_write_bulk_callback, port);
 
@@ -1513,9 +1524,6 @@ static void ftdi_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
        /* free up the transfer buffer, as usb_free_urb() does not do this */
        kfree (urb->transfer_buffer);
 
-       if (port_paranoia_check (port, __FUNCTION__))
-               return;
-       
        dbg("%s - port %d", __FUNCTION__, port->number);
        
        if (urb->status) {
@@ -1558,16 +1566,8 @@ static int ftdi_chars_in_buffer (struct usb_serial_port *port)
 static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
 { /* ftdi_read_bulk_callback */
        struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
-       struct usb_serial *serial;
        struct tty_struct *tty;
        struct ftdi_private *priv;
-       char error_flag;
-               unsigned char *data = urb->transfer_buffer;
-
-       int i;
-       int result;
-       int need_flip;
-       int packet_offset;
 
        if (urb->number_of_packets > 0) {
                err("%s transfer_buffer_length %d actual_length %d number of packets %d",__FUNCTION__,
@@ -1575,20 +1575,11 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
                err("%s transfer_flags %x ", __FUNCTION__,urb->transfer_flags );
        }
 
-       dbg("%s", __FUNCTION__);
+       dbg("%s - port %d", __FUNCTION__, port->number);
 
-       if (port_paranoia_check (port, __FUNCTION__)) {
-               return;
-       }
        if (port->open_count <= 0)
                return;
 
-       serial = get_usb_serial(port,__FUNCTION__);
-       if (!serial){
-               dbg("%s - bad serial pointer - exiting",__FUNCTION__);
-               return;
-       }
-       
        tty = port->tty;
        if (!tty) {
                dbg("%s - bad tty pointer - exiting",__FUNCTION__);
@@ -1596,6 +1587,14 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
        }
 
        priv = usb_get_serial_port_data(port);
+       if (!priv) {
+               dbg("%s - bad port private data pointer - exiting", __FUNCTION__);
+               return;
+       }
+
+       if (urb != port->read_urb) {
+               err("%s - Not my urb!", __FUNCTION__);
+       }
 
        if (urb->status) {
                /* This will happen at close every time so it is a dbg not an err */
@@ -1603,6 +1602,59 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
                return;
        }
 
+       /* If throttled, delay receive processing until unthrottled. */
+       spin_lock(&priv->rx_lock);
+       if (priv->rx_flags & THROTTLED) {
+               dbg("Deferring read urb processing until unthrottled");
+               priv->rx_flags |= ACTUALLY_THROTTLED;
+               spin_unlock(&priv->rx_lock);
+               return;
+       }
+       spin_unlock(&priv->rx_lock);
+
+       ftdi_process_read(port);
+
+} /* ftdi_read_bulk_callback */
+
+
+static void ftdi_process_read (struct usb_serial_port *port)
+{ /* ftdi_process_read */
+       struct urb *urb;
+       struct tty_struct *tty;
+       struct ftdi_private *priv;
+       char error_flag;
+               unsigned char *data;
+
+       int i;
+       int result;
+       int need_flip;
+       int packet_offset;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       if (port->open_count <= 0)
+               return;
+
+       tty = port->tty;
+       if (!tty) {
+               dbg("%s - bad tty pointer - exiting",__FUNCTION__);
+               return;
+       }
+
+       priv = usb_get_serial_port_data(port);
+       if (!priv) {
+               dbg("%s - bad port private data pointer - exiting", __FUNCTION__);
+               return;
+       }
+
+       urb = port->read_urb;
+       if (!urb) {
+               dbg("%s - bad read_urb pointer - exiting", __FUNCTION__);
+               return;
+       }
+
+       data = urb->transfer_buffer;
+
         /* 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);
@@ -1696,8 +1748,8 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
        /* if the port is closed stop trying to read */
        if (port->open_count > 0){
                /* Continue trying to always read  */
-               usb_fill_bulk_urb(port->read_urb, serial->dev, 
-                             usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+               usb_fill_bulk_urb(port->read_urb, port->serial->dev, 
+                             usb_rcvbulkpipe(port->serial->dev, port->bulk_in_endpointAddress),
                              port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
                              ftdi_read_bulk_callback, port);
 
@@ -1707,12 +1759,11 @@ static void ftdi_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
        }
 
        return;
-} /* ftdi_read_bulk_callback */
+} /* ftdi_process_read */
 
 
 static void ftdi_break_ctl( struct usb_serial_port *port, int break_state )
 {
-       struct usb_serial *serial = port->serial;
        struct ftdi_private *priv = usb_get_serial_port_data(port);
        __u16 urb_value = 0; 
        char buf[1];
@@ -1728,7 +1779,7 @@ static void ftdi_break_ctl( struct usb_serial_port *port, int break_state )
        }
 
        
-       if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+       if (usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
                            FTDI_SIO_SET_DATA_REQUEST, 
                            FTDI_SIO_SET_DATA_REQUEST_TYPE,
                            urb_value , 0,
@@ -1748,7 +1799,7 @@ static void ftdi_break_ctl( struct usb_serial_port *port, int break_state )
 
 static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_termios)
 { /* ftdi_termios */
-       struct usb_serial *serial = port->serial;
+       struct usb_device *dev = port->serial->dev;
        unsigned int cflag = port->tty->termios->c_cflag;
        struct ftdi_private *priv = usb_get_serial_port_data(port);
        __u16 urb_value; /* will hold the new flags */
@@ -1807,7 +1858,7 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_
         *  or'ed with this value  */
        priv->last_set_data_urb_value = urb_value;
        
-       if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+       if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                            FTDI_SIO_SET_DATA_REQUEST, 
                            FTDI_SIO_SET_DATA_REQUEST_TYPE,
                            urb_value , 0,
@@ -1818,7 +1869,7 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_
        /* Now do the baudrate */
        if ((cflag & CBAUD) == B0 ) {
                /* Disable flow control */
-               if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+               if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                                    FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
                                    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
                                    0, 0, 
@@ -1844,8 +1895,8 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_
        /* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */
        if (cflag & CRTSCTS) {
                dbg("%s Setting to CRTSCTS flow control", __FUNCTION__);
-               if (usb_control_msg(serial->dev, 
-                                   usb_sndctrlpipe(serial->dev, 0),
+               if (usb_control_msg(dev, 
+                                   usb_sndctrlpipe(dev, 0),
                                    FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
                                    FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
                                    0 , FTDI_SIO_RTS_CTS_HS,
@@ -1870,8 +1921,8 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_
                        vstop=port->tty->termios->c_cc[VSTOP];
                        urb_value=(vstop << 8) | (vstart);
 
-                       if (usb_control_msg(serial->dev,
-                                           usb_sndctrlpipe(serial->dev, 0),
+                       if (usb_control_msg(dev,
+                                           usb_sndctrlpipe(dev, 0),
                                            FTDI_SIO_SET_FLOW_CTRL_REQUEST,
                                            FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
                                            urb_value , FTDI_SIO_XON_XOFF_HS,
@@ -1882,8 +1933,8 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_
                        /* else clause to only run if cfag ! CRTSCTS and iflag ! XOFF */
                        /* CHECKME Assuming XON/XOFF handled by tty stack - not by device */
                        dbg("%s Turning off hardware flow control", __FUNCTION__);
-                       if (usb_control_msg(serial->dev, 
-                                           usb_sndctrlpipe(serial->dev, 0),
+                       if (usb_control_msg(dev, 
+                                           usb_sndctrlpipe(dev, 0),
                                            FTDI_SIO_SET_FLOW_CTRL_REQUEST, 
                                            FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
                                            0, 0, 
@@ -1899,7 +1950,6 @@ static void ftdi_set_termios (struct usb_serial_port *port, struct termios *old_
 
 static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file)
 {
-       struct usb_serial *serial = port->serial;
        struct ftdi_private *priv = usb_get_serial_port_data(port);
        unsigned char buf[2];
        int ret;
@@ -1908,8 +1958,8 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file)
        switch (priv->chip_type) {
        case SIO:
                /* Request the status from the device */
-               if ((ret = usb_control_msg(serial->dev, 
-                                          usb_rcvctrlpipe(serial->dev, 0),
+               if ((ret = usb_control_msg(port->serial->dev, 
+                                          usb_rcvctrlpipe(port->serial->dev, 0),
                                           FTDI_SIO_GET_MODEM_STATUS_REQUEST, 
                                           FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
                                           0, 0, 
@@ -1923,8 +1973,8 @@ static int ftdi_tiocmget (struct usb_serial_port *port, struct file *file)
        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, 
-                                          usb_rcvctrlpipe(serial->dev, 0),
+               if ((ret = usb_control_msg(port->serial->dev, 
+                                          usb_rcvctrlpipe(port->serial->dev, 0),
                                           FTDI_SIO_GET_MODEM_STATUS_REQUEST, 
                                           FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
                                           0, 0, 
@@ -2099,28 +2149,32 @@ static int ftdi_ioctl (struct usb_serial_port *port, struct file * file, unsigne
 
 static void ftdi_throttle (struct usb_serial_port *port)
 {
+       struct ftdi_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
+
        dbg("%s - port %d", __FUNCTION__, port->number);
-       usb_unlink_urb (port->read_urb);
+
+       spin_lock_irqsave(&priv->rx_lock, flags);
+       priv->rx_flags |= THROTTLED;
+       spin_unlock_irqrestore(&priv->rx_lock, flags);
 }
 
 
 static void ftdi_unthrottle (struct usb_serial_port *port)
 {
-       int result;
-       struct usb_serial *serial = port->serial;
+       struct ftdi_private *priv = usb_get_serial_port_data(port);
+       int actually_throttled;
+       unsigned long flags;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
 
-       port->read_urb->dev = serial->dev;
+       spin_lock_irqsave(&priv->rx_lock, flags);
+       actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED;
+       priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
+       spin_unlock_irqrestore(&priv->rx_lock, flags);
 
-       usb_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, GFP_ATOMIC);
-       if (result)
-               err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
+       if (actually_throttled)
+               ftdi_process_read(port);
 }
 
 static int __init ftdi_init (void)