/*
  * Version Information
  */
-#define DRIVER_VERSION "z2.0"          /* Linux in-kernel version */
+#define DRIVER_VERSION "z2.1"          /* Linux in-kernel version */
 #define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>"
 #define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver"
 
 static int  mct_u232_tiocmset           (struct usb_serial_port *port,
                                          struct file *file, unsigned int set,
                                          unsigned int clear);
+static void mct_u232_throttle           (struct usb_serial_port *port);
+static void mct_u232_unthrottle                 (struct usb_serial_port *port);
+
+
 /*
  * All of the device info needed for the MCT USB-RS232 converter.
  */
        .num_ports =         1,
        .open =              mct_u232_open,
        .close =             mct_u232_close,
+       .throttle =          mct_u232_throttle,
+       .unthrottle =        mct_u232_unthrottle,
        .read_int_callback = mct_u232_read_int_callback,
        .ioctl =             mct_u232_ioctl,
        .set_termios =       mct_u232_set_termios,
        unsigned char        last_lcr;      /* Line Control Register */
        unsigned char        last_lsr;      /* Line Status Register */
        unsigned char        last_msr;      /* Modem Status Register */
+       unsigned int         rx_flags;      /* Throttling flags */
 };
 
+#define THROTTLED              0x01
+
 /*
  * Handle vendor specific USB requests
  */
        }
 }
 
-static int mct_u232_set_baud_rate(struct usb_serial *serial, int value)
+static int mct_u232_set_baud_rate(struct usb_serial *serial, struct usb_serial_port *port,
+                                 int value)
 {
        __le32 divisor;
         int rc;
         unsigned char zero_byte = 0;
+        unsigned char cts_enable_byte = 0;
 
        divisor = cpu_to_le32(mct_u232_calculate_baud_rate(serial, value));
 
           'baud rate change' message.  The actual functionality of the
           request codes in these messages is not fully understood but these
           particular codes are never seen in any operation besides a baud
-          rate change.  Both of these messages send a single byte of data
-          whose value is always zero.  The second of these two extra messages
-          is required in order for data to be properly written to an RS-232
-          device which does not assert the 'CTS' signal. */
+          rate change.  Both of these messages send a single byte of data.
+          In the first message, the value of this byte is always zero.
+
+          The second message has been determined experimentally to control
+          whether data will be transmitted to a device which is not asserting
+          the 'CTS' signal.  If the second message's data byte is zero, data
+          will be transmitted even if 'CTS' is not asserted (i.e. no hardware
+          flow control).  if the second message's data byte is nonzero (a value
+          of 1 is used by this driver), data will not be transmitted to a device
+          which is not asserting 'CTS'.
+       */
 
        rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
                             MCT_U232_SET_UNKNOWN1_REQUEST, 
                err("Sending USB device request code %d failed (error = %d)", 
                    MCT_U232_SET_UNKNOWN1_REQUEST, rc);
 
+       if (port && C_CRTSCTS(port->tty)) {
+          cts_enable_byte = 1;
+       }
+
+        dbg("set_baud_rate: send second control message, data = %02X", cts_enable_byte);
        rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
-                            MCT_U232_SET_UNKNOWN2_REQUEST, 
+                            MCT_U232_SET_CTS_REQUEST,
                             MCT_U232_SET_REQUEST_TYPE,
-                            0, 0, &zero_byte, MCT_U232_SET_UNKNOWN2_SIZE, 
+                            0, 0, &cts_enable_byte, MCT_U232_SET_CTS_SIZE,
                             WDR_TIMEOUT);
        if (rc < 0)
-               err("Sending USB device request code %d failed (error = %d)", 
-                   MCT_U232_SET_UNKNOWN2_REQUEST, rc);
+         err("Sending USB device request code %d failed (error = %d)",
+             MCT_U232_SET_CTS_REQUEST, rc);
 
         return rc;
 } /* mct_u232_set_baud_rate */
 
 static void mct_u232_close (struct usb_serial_port *port, struct file *filp)
 {
+       unsigned int c_cflag;
+       unsigned long flags;
+       unsigned int control_state;
+       struct mct_u232_private *priv = usb_get_serial_port_data(port);
        dbg("%s port %d", __FUNCTION__, port->number);
 
+       if (port->tty) {
+               c_cflag = port->tty->termios->c_cflag;
+               if (c_cflag & HUPCL) {
+                  /* drop DTR and RTS */
+                  spin_lock_irqsave(&priv->lock, flags);
+                  priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+                  control_state = priv->control_state;
+                  spin_unlock_irqrestore(&priv->lock, flags);
+                  mct_u232_set_modem_ctrl(port->serial, control_state);
+               }
+       }
+
+
        if (port->serial->dev) {
                /* shutdown our urbs */
                usb_kill_urb(port->write_urb);
 {
        struct usb_serial *serial = port->serial;
        struct mct_u232_private *priv = usb_get_serial_port_data(port);
-       unsigned int iflag = port->tty->termios->c_iflag;
        unsigned int cflag = port->tty->termios->c_cflag;
        unsigned int old_cflag = old_termios->c_cflag;
        unsigned long flags;
-       unsigned int control_state, new_state;
+       unsigned int control_state;
        unsigned char last_lcr;
 
        /* get a local copy of the current port settings */
         * Premature optimization is the root of all evil.
         */
 
-        /* reassert DTR and (maybe) RTS on transition from B0 */
+        /* reassert DTR and RTS on transition from B0 */
        if ((old_cflag & CBAUD) == B0) {
                dbg("%s: baud was B0", __FUNCTION__);
-               control_state |= TIOCM_DTR;
-               /* don't set RTS if using hardware flow control */
-               if (!(old_cflag & CRTSCTS)) {
-                       control_state |= TIOCM_RTS;
-               }
+               control_state |= TIOCM_DTR | TIOCM_RTS;
                mct_u232_set_modem_ctrl(serial, control_state);
        }
 
-       mct_u232_set_baud_rate(serial, cflag & CBAUD);
+       mct_u232_set_baud_rate(serial, port, cflag & CBAUD);
 
        if ((cflag & CBAUD) == B0 ) {
                dbg("%s: baud is B0", __FUNCTION__);
 
        mct_u232_set_line_ctrl(serial, last_lcr);
 
-       /*
-        * Set flow control: well, I do not really now how to handle DTR/RTS.
-        * Just do what we have seen with SniffUSB on Win98.
-        */
-       /* Drop DTR/RTS if no flow control otherwise assert */
-       new_state = control_state;
-       if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS))
-               new_state |= TIOCM_DTR | TIOCM_RTS;
-       else
-               new_state &= ~(TIOCM_DTR | TIOCM_RTS);
-       if (new_state != control_state) {
-               mct_u232_set_modem_ctrl(serial, new_state);
-               control_state = new_state;
-       }
-
        /* save off the modified port settings */
        spin_lock_irqsave(&priv->lock, flags);
        priv->control_state = control_state;
        return 0;
 } /* mct_u232_ioctl */
 
+static void mct_u232_throttle (struct usb_serial_port *port)
+{
+       struct mct_u232_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
+       unsigned int control_state;
+       struct tty_struct *tty;
+
+       tty = port->tty;
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       spin_lock_irqsave(&priv->lock, flags);
+       priv->rx_flags |= THROTTLED;
+       if (C_CRTSCTS(tty)) {
+         priv->control_state &= ~TIOCM_RTS;
+         control_state = priv->control_state;
+         spin_unlock_irqrestore(&priv->lock, flags);
+         (void) mct_u232_set_modem_ctrl(port->serial, control_state);
+       } else {
+         spin_unlock_irqrestore(&priv->lock, flags);
+       }
+}
+
+
+static void mct_u232_unthrottle (struct usb_serial_port *port)
+{
+       struct mct_u232_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
+       unsigned int control_state;
+       struct tty_struct *tty;
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
+
+       tty = port->tty;
+       spin_lock_irqsave(&priv->lock, flags);
+       if ((priv->rx_flags & THROTTLED) && C_CRTSCTS(tty)) {
+         priv->rx_flags &= ~THROTTLED;
+         priv->control_state |= TIOCM_RTS;
+         control_state = priv->control_state;
+         spin_unlock_irqrestore(&priv->lock, flags);
+         (void) mct_u232_set_modem_ctrl(port->serial, control_state);
+       } else {
+         spin_unlock_irqrestore(&priv->lock, flags);
+       }
+}
 
 static int __init mct_u232_init (void)
 {