pl2303: improve the chip type detection/distinction
authorFrank Schäfer <fschaefer.oss@googlemail.com>
Wed, 14 Aug 2013 18:09:11 +0000 (20:09 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 14 Aug 2013 19:12:43 +0000 (12:12 -0700)
The driver currently knows about 3 different PL2303 chip types:
The two legacy chip types type_0 and type_1 (PL2303H ?) and the HX
type.
The device distinction is currently completely based on the examination
of the USB descriptors.
During the last years, Prolific has introduced further PL2303 chips,
such as the HXD (HX rev. D), TA (which replaced the X/HX chips), SA,
RA, EA and TB variants.
Unfortunately, all these new chips are currently detected as HX chips,
because they are all using the same bMaxPacketSize0 = 0x40 value in the
USB device descriptor.

At this point it is not clear if these chips are really working with
the driver, there are just some positive indicators (like device
manufacturers claiming Linux support for these devices or commit
8d48fdf689 "correctly handle baudrates above 115200" which should only
be necessary for newer devices, ...)

For a complete support of all devices, we need to distinguish between
them, because they differ in several functional aspects, such  as the
maximum supported baud rate (HXD, TB, EA: 12Mbps, HX, TA: 6Mbps,
RA: 1Mbps, SA: 115.2kbps), handshaking line support, RS422/485 and
GPIO ports support (currently not supported by the driver).
And there might be further differences that we don't know yet.

This patch improves the chip type detection by evaluating the bcdDevice
value of the device descriptor. The values are taken from the
datasheets and are safe to use because manufacturers can't change them:

3.00: X/HX, TA
4.00: HXD, EA, RA, SA
5.00: TB

The rest of the device descriptors is completely identical, so no
further distinction is possible this way.
Anyway, Prolifics "checkChipVersion.exe"-tool is definitely able to
distinguish for example between the X/HX and the TA chips, so there
must be a possibility to improve the distinction further...

Signed-off-by: Frank Schäfer <fschaefer.oss@googlemail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/serial/pl2303.c

index 409000a..e7a84f0 100644 (file)
@@ -134,10 +134,17 @@ MODULE_DEVICE_TABLE(usb, id_table);
 
 
 enum pl2303_type {
-       type_0,         /* don't know the difference between type 0 and */
-       type_1,         /* type 1, until someone from prolific tells us... */
-       HX,             /* HX version of the pl2303 chip */
+       type_0,         /* H version ? */
+       type_1,         /* H version ? */
+       HX_TA,          /* HX(A) / X(A) / TA version  */ /* TODO: improve */
+       HXD_EA_RA_SA,   /* HXD / EA / RA / SA version */ /* TODO: improve */
+       TB,             /* TB version */
 };
+/*
+ * NOTE: don't know the difference between type 0 and type 1,
+ * until someone from Prolific tells us...
+ * TODO: distinguish between X/HX, TA and HXD, EA, RA, SA variants
+ */
 
 struct pl2303_serial_private {
        enum pl2303_type type;
@@ -194,8 +201,28 @@ static int pl2303_startup(struct usb_serial *serial)
                type = type_0;
                type_str = "type_0";
        } else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40) {
-               type = HX;
-               type_str = "X/HX";
+               /*
+                * NOTE: The bcdDevice version is the only difference between
+                * the device descriptors of the X/HX, HXD, EA, RA, SA, TA, TB
+                */
+               if (le16_to_cpu(serial->dev->descriptor.bcdDevice) == 0x300) {
+                       type = HX_TA;
+                       type_str = "X/HX/TA";
+               } else if (le16_to_cpu(serial->dev->descriptor.bcdDevice)
+                                                                    == 0x400) {
+                       type = HXD_EA_RA_SA;
+                       type_str = "HXD/EA/RA/SA";
+               } else if (le16_to_cpu(serial->dev->descriptor.bcdDevice)
+                                                                    == 0x500) {
+                       type = TB;
+                       type_str = "TB";
+               } else {
+                       dev_info(&serial->interface->dev,
+                                          "unknown/unsupported device type\n");
+                       kfree(spriv);
+                       kfree(buf);
+                       return -ENODEV;
+               }
        } else if (serial->dev->descriptor.bDeviceClass == 0x00
                   || serial->dev->descriptor.bDeviceClass == 0xFF) {
                type = type_1;
@@ -216,10 +243,10 @@ static int pl2303_startup(struct usb_serial *serial)
        pl2303_vendor_read(0x8383, 0, serial, buf);
        pl2303_vendor_write(0, 1, serial);
        pl2303_vendor_write(1, 0, serial);
-       if (type == HX)
-               pl2303_vendor_write(2, 0x44, serial);
-       else
+       if (type == type_0 || type == type_1)
                pl2303_vendor_write(2, 0x24, serial);
+       else
+               pl2303_vendor_write(2, 0x44, serial);
 
        kfree(buf);
        return 0;
@@ -284,12 +311,19 @@ static int pl2303_baudrate_encode_direct(int baud, enum pl2303_type type,
        const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600,
                                 4800, 7200, 9600, 14400, 19200, 28800, 38400,
                                 57600, 115200, 230400, 460800, 614400, 921600,
-                                1228800, 2457600, 3000000, 6000000 };
+                                1228800, 2457600, 3000000, 6000000, 12000000 };
        /*
-        * NOTE: The PL2303HX (tested with rev. 3A) also supports the following
-        * baud rates: 128000, 134400, 161280, 201600, 268800, 403200, 806400.
-        * As long as we are not using this encoding method for them, there is
-        * no point in complicating the code to support them.
+        * NOTE: With the exception of type_0/1 devices, the following
+        * additional baud rates are supported (tested with HX rev. 3A only):
+        * 110*, 56000*, 128000, 134400, 161280, 201600, 256000*, 268800,
+        * 403200, 806400.      (*: not HX)
+        *
+        * Maximum values: HXD, TB: 12000000; HX, TA: 6000000;
+        *                 type_0+1: 1228800; RA: 921600; SA: 115200
+        *
+        * As long as we are not using this encoding method for anything else
+        * than the type_0+1 and HX chips, there is no point in complicating
+        * the code to support them.
         */
        int i;
 
@@ -304,8 +338,14 @@ static int pl2303_baudrate_encode_direct(int baud, enum pl2303_type type,
                baud = baud_sup[i - 1];
        else
                baud = baud_sup[i];
-       /* type_0, type_1 only support up to 1228800 baud */
-       if (type != HX)
+       /* Respect the chip type specific baud rate limits */
+       /*
+        * FIXME: as long as we don't know how to distinguish between the
+        * HXD, EA, RA, and SA chip variants, allow the max. value of 12M.
+        */
+       if (type == HX_TA)
+               baud = min_t(int, baud, 6000000);
+       else if (type == type_0 || type == type_1)
                baud = min_t(int, baud, 1228800);
        /* Direct (standard) baud rate encoding method */
        put_unaligned_le32(baud, buf);
@@ -344,10 +384,19 @@ static int pl2303_baudrate_encode_divisor(int baud, enum pl2303_type type,
         * Baud rates smaller than the specified 75 baud are definitely working
         * fine.
         */
-       if (type == HX)
-               baud = min_t(int, baud, 6000000 * 1.1);
-       else
+       if (type == type_0 || type == type_1)
                baud = min_t(int, baud, 1228800 * 1.1);
+       else if (type == HX_TA)
+               baud = min_t(int, baud, 6000000 * 1.1);
+       else if (type == HXD_EA_RA_SA)
+               /* HXD, EA: 12Mbps; RA: 1Mbps; SA: 115200 bps */
+               /*
+                * FIXME: as long as we don't know how to distinguish between
+                * these chip variants, allow the max. of these values
+                */
+               baud = min_t(int, baud, 12000000 * 1.1);
+       else if (type == TB)
+               baud = min_t(int, baud, 12000000 * 1.1);
        /* Determine factors A and B */
        A = 0;
        B = 12000000 * 32 / baud;  /* 12MHz */
@@ -411,7 +460,7 @@ static void pl2303_encode_baudrate(struct tty_struct *tty,
         * the device likely uses the same baud rate generator for both methods
         * so that there is likley no difference.
         */
-       if (type != HX)
+       if (type == type_0 || type == type_1)
                baud = pl2303_baudrate_encode_direct(baud, type, buf);
        else
                baud = pl2303_baudrate_encode_divisor(baud, type, buf);
@@ -549,10 +598,10 @@ static void pl2303_set_termios(struct tty_struct *tty,
        dev_dbg(&port->dev, "0xa1:0x21:0:0  %d - %7ph\n", i, buf);
 
        if (C_CRTSCTS(tty)) {
-               if (spriv->type == HX)
-                       pl2303_vendor_write(0x0, 0x61, serial);
-               else
+               if (spriv->type == type_0 || spriv->type == type_1)
                        pl2303_vendor_write(0x0, 0x41, serial);
+               else
+                       pl2303_vendor_write(0x0, 0x61, serial);
        } else {
                pl2303_vendor_write(0x0, 0x0, serial);
        }
@@ -589,7 +638,7 @@ static int pl2303_open(struct tty_struct *tty, struct usb_serial_port *port)
        struct pl2303_serial_private *spriv = usb_get_serial_data(serial);
        int result;
 
-       if (spriv->type != HX) {
+       if (spriv->type == type_0 || spriv->type == type_1) {
                usb_clear_halt(serial->dev, port->write_urb->pipe);
                usb_clear_halt(serial->dev, port->read_urb->pipe);
        } else {