USB: cypress_m8: Limit baud rate to <=4800 for USB low speed devices
authorMike Isely <isely@pobox.com>
Mon, 11 Feb 2008 02:23:32 +0000 (20:23 -0600)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 25 Apr 2008 04:16:38 +0000 (21:16 -0700)
The cypress app note for the M8 states that for the USB low speed
version of the part, throughput is effectively limited to 800
bytes/sec.  So if we were to try a faster baud rate in such cases then
we risk overrun errors on receive.  Best to just identify this case
and limit the rate to 4800 baud or less (by ignoring any request to
set a faster rate).  The old baud rate setting code was somewhat
fragile; this change also hopefully makes it easier in the future to
better checking / limiting.

Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/serial/cypress_m8.c

index bdeda09369511247c19fca11daddeb7bc82ab7af..4bf45c711b9d4b09510c5945610bc97eeab81d8f 100644 (file)
@@ -291,6 +291,59 @@ static struct usb_serial_driver cypress_ca42v2_device = {
  *****************************************************************************/
 
 
+static int analyze_baud_rate(struct usb_serial_port *port, unsigned baud_mask)
+{
+       int new_rate;
+       struct cypress_private *priv;
+       priv = usb_get_serial_port_data(port);
+
+       /*
+        * The general purpose firmware for the Cypress M8 allows for
+        * a maximum speed of 57600bps (I have no idea whether DeLorme
+        * chose to use the general purpose firmware or not), if you
+        * need to modify this speed setting for your own project
+        * please add your own chiptype and modify the code likewise.
+        * The Cypress HID->COM device will work successfully up to
+        * 115200bps (but the actual throughput is around 3kBps).
+        */
+       new_rate = mask_to_rate(baud_mask);
+       if (new_rate < 0) {
+               dbg("%s - failed setting baud rate, untranslatable speed",
+                   __func__);
+               return -1;
+       }
+       if (port->serial->dev->speed == USB_SPEED_LOW) {
+               /*
+                * Mike Isely <isely@pobox.com> 2-Feb-2008: The
+                * Cypress app note that describes this mechanism
+                * states the the low-speed part can't handle more
+                * than 800 bytes/sec, in which case 4800 baud is the
+                * safest speed for a part like that.
+                */
+               if (new_rate > 4800) {
+                       dbg("%s - failed setting baud rate, device incapable "
+                           "speed %d", __func__, new_rate);
+                       return -1;
+               }
+       }
+       switch (priv->chiptype) {
+       case CT_EARTHMATE:
+               if (new_rate <= 600) {
+                       /* 300 and 600 baud rates are supported under
+                        * the generic firmware, but are not used with
+                        * NMEA and SiRF protocols */
+                       dbg("%s - failed setting baud rate, unsupported speed "
+                           "of %d on Earthmate GPS", __func__, new_rate);
+                       return -1;
+               }
+               break;
+       default:
+               break;
+       }
+       return new_rate;
+}
+
+
 /* This function can either set or retrieve the current serial line settings */
 static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_mask, int data_bits, int stop_bits,
                                   int parity_enable, int parity_type, int reset, int cypress_request_type)
@@ -309,54 +362,15 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m
 
        switch(cypress_request_type) {
                case CYPRESS_SET_CONFIG:
-
-                       /*
-                        * The general purpose firmware for the Cypress M8 allows for a maximum speed
-                        * of 57600bps (I have no idea whether DeLorme chose to use the general purpose
-                        * firmware or not), if you need to modify this speed setting for your own
-                        * project please add your own chiptype and modify the code likewise.  The
-                        * Cypress HID->COM device will work successfully up to 115200bps (but the
-                        * actual throughput is around 3kBps).
-                        */
+                       new_baudrate = priv->baud_rate;
                        if (baud_mask != priv->cbr_mask) {
                                dbg("%s - baud rate is changing", __FUNCTION__);
-                               if ( priv->chiptype == CT_EARTHMATE ) {
-                                       /* 300 and 600 baud rates are supported under the generic firmware,
-                                        * but are not used with NMEA and SiRF protocols */
-                                       
-                                       if ( (baud_mask == B300) || (baud_mask == B600) ) {
-                                               err("%s - failed setting baud rate, unsupported speed",
-                                                   __FUNCTION__);
-                                               new_baudrate = priv->baud_rate;
-                                       } else if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) {
-                                               err("%s - failed setting baud rate, unsupported speed",
-                                                   __FUNCTION__);
-                                               new_baudrate = priv->baud_rate;
-                                       }
-                               } else if (priv->chiptype == CT_CYPHIDCOM) {
-                                       if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) {
-                                               err("%s - failed setting baud rate, unsupported speed",
-                                                   __FUNCTION__);
-                                               new_baudrate = priv->baud_rate;
-                                       }
-                               } else if (priv->chiptype == CT_CA42V2) {
-                                       if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) {
-                                               err("%s - failed setting baud rate, unsupported speed",
-                                                   __FUNCTION__);
-                                               new_baudrate = priv->baud_rate;
-                                       }
-                               } else if (priv->chiptype == CT_GENERIC) {
-                                       if ( (new_baudrate = mask_to_rate(baud_mask)) == -1) {
-                                               err("%s - failed setting baud rate, unsupported speed",
-                                                   __FUNCTION__);
-                                               new_baudrate = priv->baud_rate;
-                                       }
-                               } else {
-                                       info("%s - please define your chiptype", __FUNCTION__);
-                                       new_baudrate = priv->baud_rate;
+                               retval = analyze_baud_rate(port, baud_mask);
+                               if (retval >=  0) {
+                                       new_baudrate = retval;
+                                       dbg("%s - New baud rate set to %d",
+                                           __func__, new_baudrate);
                                }
-                       } else {  /* baud rate not changing, keep the old */
-                               new_baudrate = priv->baud_rate;
                        }
                        dbg("%s - baud rate is being sent as %d", __FUNCTION__, new_baudrate);