]> err.no Git - linux-2.6/blobdiff - drivers/usb/serial/mct_u232.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 into next
[linux-2.6] / drivers / usb / serial / mct_u232.c
index 4cd839b1407f3febab668bd6d27850e60fbae1f5..5fc2cef30e39f76603421616b3e4a06c9cebd586 100644 (file)
@@ -81,7 +81,7 @@
 /*
  * 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"
 
@@ -110,6 +110,10 @@ static int  mct_u232_tiocmget               (struct usb_serial_port *port,
 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.
  */
@@ -139,12 +143,11 @@ static struct usb_serial_driver mct_u232_device = {
        .description =       "MCT U232",
        .usb_driver =        &mct_u232_driver,
        .id_table =          id_table_combined,
-       .num_interrupt_in =  2,
-       .num_bulk_in =       0,
-       .num_bulk_out =      1,
        .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,
@@ -162,8 +165,11 @@ struct mct_u232_private {
        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
  */
@@ -173,64 +179,71 @@ struct mct_u232_private {
 /*
  * Later day 2.6.0-test kernels have new baud rates like B230400 which
  * we do not know how to support. We ignore them for the moment.
- * XXX Rate-limit the error message, it's user triggerable.
  */
-static int mct_u232_calculate_baud_rate(struct usb_serial *serial, int value)
+static int mct_u232_calculate_baud_rate(struct usb_serial *serial, speed_t value, speed_t *result)
 {
+       *result = value;
+
        if (le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_SITECOM_PID
          || le16_to_cpu(serial->dev->descriptor.idProduct) == MCT_U232_BELKIN_F5U109_PID) {
                switch (value) {
-               case    B300: return 0x01;
-               case    B600: return 0x02; /* this one not tested */
-               case   B1200: return 0x03;
-               case   B2400: return 0x04;
-               case   B4800: return 0x06;
-               case   B9600: return 0x08;
-               case  B19200: return 0x09;
-               case  B38400: return 0x0a;
-               case  B57600: return 0x0b;
-               case B115200: return 0x0c;
+               case    300: return 0x01;
+               case    600: return 0x02; /* this one not tested */
+               case   1200: return 0x03;
+               case   2400: return 0x04;
+               case   4800: return 0x06;
+               case   9600: return 0x08;
+               case  19200: return 0x09;
+               case  38400: return 0x0a;
+               case  57600: return 0x0b;
+               case 115200: return 0x0c;
                default:
-                       err("MCT USB-RS232: unsupported baudrate request 0x%x,"
-                           " using default of B9600", value);
+                       *result = 9600;
                        return 0x08;
                }
        } else {
+               /* FIXME: Can we use any divider - should we do
+                  divider = 115200/value;
+                  real baud = 115200/divider */
                switch (value) {
-               case    B300: value =     300; break;
-               case    B600: value =     600; break;
-               case   B1200: value =    1200; break;
-               case   B2400: value =    2400; break;
-               case   B4800: value =    4800; break;
-               case   B9600: value =    9600; break;
-               case  B19200: value =   19200; break;
-               case  B38400: value =   38400; break;
-               case  B57600: value =   57600; break;
-               case B115200: value =  115200; break;
+               case 300: break;
+               case 600: break;
+               case 1200: break;
+               case 2400: break;
+               case 4800: break;
+               case 9600: break;
+               case 19200: break;
+               case 38400: break;
+               case 57600: break;
+               case 115200: break;
                default:
-                       err("MCT USB-RS232: unsupported baudrate request 0x%x,"
-                           " using default of B9600", value);
                        value = 9600;
+                       *result = 9600;
                }
                return 115200/value;
        }
 }
 
-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,
+                                 speed_t value)
 {
        __le32 divisor;
         int rc;
         unsigned char zero_byte = 0;
+        unsigned char cts_enable_byte = 0;
+        speed_t speed;
 
-       divisor = cpu_to_le32(mct_u232_calculate_baud_rate(serial, value));
+       divisor = cpu_to_le32(mct_u232_calculate_baud_rate(serial, value, &speed));
 
         rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
                              MCT_U232_SET_BAUD_RATE_REQUEST,
                             MCT_U232_SET_REQUEST_TYPE,
                              0, 0, &divisor, MCT_U232_SET_BAUD_RATE_SIZE,
                             WDR_TIMEOUT);
-       if (rc < 0)
+       if (rc < 0)     /*FIXME: What value speed results */
                err("Set BAUD RATE %d failed (error = %d)", value, rc);
+       else
+               tty_encode_baud_rate(port->tty, speed, speed);
        dbg("set_baud_rate: value: 0x%x, divisor: 0x%x", value, divisor);
 
        /* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which
@@ -238,10 +251,17 @@ static int mct_u232_set_baud_rate(struct usb_serial *serial, int 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, 
@@ -252,14 +272,19 @@ static int mct_u232_set_baud_rate(struct usb_serial *serial, int value)
                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 */
@@ -374,7 +399,7 @@ static void mct_u232_shutdown (struct usb_serial *serial)
        struct mct_u232_private *priv;
        int i;
        
-       dbg("%s", __FUNCTION__);
+       dbg("%s", __func__);
 
        for (i=0; i < serial->num_ports; ++i) {
                /* My special items, the standard routines free my urbs */
@@ -396,7 +421,7 @@ static int  mct_u232_open (struct usb_serial_port *port, struct file *filp)
        unsigned char last_lcr;
        unsigned char last_msr;
 
-       dbg("%s port %d", __FUNCTION__, port->number);
+       dbg("%s port %d", __func__, port->number);
 
        /* Compensate for a hardware bug: although the Sitecom U232-P25
         * device reports a maximum output packet size of 32 bytes,
@@ -438,23 +463,45 @@ static int  mct_u232_open (struct usb_serial_port *port, struct file *filp)
        if (retval) {
                err("usb_submit_urb(read bulk) failed pipe 0x%x err %d",
                    port->read_urb->pipe, retval);
-               goto exit;
+               goto error;
        }
 
        port->interrupt_in_urb->dev = port->serial->dev;
        retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
-       if (retval)
+       if (retval) {
+               usb_kill_urb(port->read_urb);
                err(" usb_submit_urb(read int) failed pipe 0x%x err %d",
                    port->interrupt_in_urb->pipe, retval);
-
-exit:
+               goto error;
+       }
        return 0;
+
+error:
+       return retval;
 } /* mct_u232_open */
 
 
 static void mct_u232_close (struct usb_serial_port *port, struct file *filp)
 {
-       dbg("%s port %d", __FUNCTION__, port->number);
+       unsigned int c_cflag;
+       unsigned int control_state;
+       struct mct_u232_private *priv = usb_get_serial_port_data(port);
+       dbg("%s port %d", __func__, port->number);
+
+       if (port->tty) {
+               c_cflag = port->tty->termios->c_cflag;
+               mutex_lock(&port->serial->disc_mutex);
+               if (c_cflag & HUPCL && !port->serial->disconnected) {
+                       /* drop DTR and RTS */
+                       spin_lock_irq(&priv->lock);
+                       priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+                       control_state = priv->control_state;
+                       spin_unlock_irq(&priv->lock);
+                       mct_u232_set_modem_ctrl(port->serial, control_state);
+               }
+               mutex_unlock(&port->serial->disc_mutex);
+       }
+
 
        if (port->serial->dev) {
                /* shutdown our urbs */
@@ -467,15 +514,16 @@ static void mct_u232_close (struct usb_serial_port *port, struct file *filp)
 
 static void mct_u232_read_int_callback (struct urb *urb)
 {
-       struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+       struct usb_serial_port *port = urb->context;
        struct mct_u232_private *priv = usb_get_serial_port_data(port);
        struct usb_serial *serial = port->serial;
        struct tty_struct *tty;
        unsigned char *data = urb->transfer_buffer;
-       int status;
+       int retval;
+       int status = urb->status;
        unsigned long flags;
 
-       switch (urb->status) {
+       switch (status) {
        case 0:
                /* success */
                break;
@@ -483,20 +531,22 @@ static void mct_u232_read_int_callback (struct urb *urb)
        case -ENOENT:
        case -ESHUTDOWN:
                /* this urb is terminated, clean up */
-               dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+               dbg("%s - urb shutting down with status: %d",
+                   __func__, status);
                return;
        default:
-               dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+               dbg("%s - nonzero urb status received: %d",
+                   __func__, status);
                goto exit;
        }
 
        if (!serial) {
-               dbg("%s - bad serial pointer, exiting", __FUNCTION__);
+               dbg("%s - bad serial pointer, exiting", __func__);
                return;
        }
 
-        dbg("%s - port %d", __FUNCTION__, port->number);
-       usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
+        dbg("%s - port %d", __func__, port->number);
+       usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data);
 
        /*
         * Work-a-round: handle the 'usual' bulk-in pipe here
@@ -550,10 +600,10 @@ static void mct_u232_read_int_callback (struct urb *urb)
 #endif
        spin_unlock_irqrestore(&priv->lock, flags);
 exit:
-       status = usb_submit_urb (urb, GFP_ATOMIC);
-       if (status)
+       retval = usb_submit_urb (urb, GFP_ATOMIC);
+       if (retval)
                err ("%s - usb_submit_urb failed with result %d",
-                    __FUNCTION__, status);
+                    __func__, retval);
 } /* mct_u232_read_int_callback */
 
 static void mct_u232_set_termios (struct usb_serial_port *port,
@@ -561,11 +611,11 @@ static void mct_u232_set_termios (struct usb_serial_port *port,
 {
        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;
+       struct ktermios *termios = port->tty->termios;
+       unsigned int cflag = 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 */
@@ -581,21 +631,17 @@ static void mct_u232_set_termios (struct usb_serial_port *port,
         * 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;
-               }
+               dbg("%s: baud was B0", __func__);
+               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, tty_get_baud_rate(port->tty));
 
        if ((cflag & CBAUD) == B0 ) {
-               dbg("%s: baud is B0", __FUNCTION__);
+               dbg("%s: baud is B0", __func__);
                /* Drop RTS and DTR */
                control_state &= ~(TIOCM_DTR | TIOCM_RTS);
                        mct_u232_set_modem_ctrl(serial, control_state);
@@ -628,27 +674,14 @@ static void mct_u232_set_termios (struct usb_serial_port *port,
                break;
        }
 
+       termios->c_cflag &= ~CMSPAR;
+
        /* set the number of stop bits */
        last_lcr |= (cflag & CSTOPB) ?
                MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1;
 
        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;
@@ -663,7 +696,7 @@ static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state )
        unsigned char lcr;
        unsigned long flags;
 
-       dbg("%sstate=%d", __FUNCTION__, break_state);
+       dbg("%sstate=%d", __func__, break_state);
 
        spin_lock_irqsave(&priv->lock, flags);
        lcr = priv->last_lcr;
@@ -682,7 +715,7 @@ static int mct_u232_tiocmget (struct usb_serial_port *port, struct file *file)
        unsigned int control_state;
        unsigned long flags;
        
-       dbg("%s", __FUNCTION__);
+       dbg("%s", __func__);
 
        spin_lock_irqsave(&priv->lock, flags);
        control_state = priv->control_state;
@@ -699,7 +732,7 @@ static int mct_u232_tiocmset (struct usb_serial_port *port, struct file *file,
        unsigned int control_state;
        unsigned long flags;
        
-       dbg("%s", __FUNCTION__);
+       dbg("%s", __func__);
 
        spin_lock_irqsave(&priv->lock, flags);
        control_state = priv->control_state;
@@ -721,7 +754,7 @@ static int mct_u232_tiocmset (struct usb_serial_port *port, struct file *file,
 static int mct_u232_ioctl (struct usb_serial_port *port, struct file * file,
                           unsigned int cmd, unsigned long arg)
 {
-       dbg("%scmd=0x%x", __FUNCTION__, cmd);
+       dbg("%scmd=0x%x", __func__, cmd);
 
        /* Based on code from acm.c and others */
        switch (cmd) {
@@ -736,13 +769,57 @@ static int mct_u232_ioctl (struct usb_serial_port *port, struct file * file,
                return 0;
 
        default:
-               dbg("%s: arg not supported - 0x%04x", __FUNCTION__,cmd);
+               dbg("%s: arg not supported - 0x%04x", __func__,cmd);
                return(-ENOIOCTLCMD);
                break;
        }
        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", __func__, 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", __func__, 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)
 {