Commit 7d26a78f authored by Frank Schäfer's avatar Frank Schäfer Committed by Greg Kroah-Hartman

USB: pl2303: distinguish between original and cloned HX chips

According to Prolific, several (unauthorized) cheap and less functional
clones of the PL2303HX chip are in circulation. [1]
I've had the chance to test such a cloned device and it turned out that
it doesn't support any baud rates above 115200 baud (original: 6 Mbaud)
It also doesn't support the divisior based baud rate encoding method,
so no continuous baud rate adjustment is possible.
Nevertheless, these devices have been working (unintentionally) with
the driver up to commit 61fa8d69 ("pl2303: also use the divisor based
baud rate encoding method for baud rates < 115200 with HX chips"), and
this commit broke support for them.
Fortunately, it is pretty simple to distinguish between the original
and the cloned HX chips, so I've added a check and an extra chip type
to keep the clones working.
The same check is used by the latest Prolific Windows driver, so it
should be solid.

[1] http://www.prolific.com.tw/US/ShowProduct.aspx?p_id=225&pcid=41Signed-off-by: default avatarFrank Schäfer <fschaefer.oss@googlemail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent dfe29020
...@@ -139,6 +139,7 @@ enum pl2303_type { ...@@ -139,6 +139,7 @@ enum pl2303_type {
HX_TA, /* HX(A) / X(A) / TA version */ /* TODO: improve */ HX_TA, /* HX(A) / X(A) / TA version */ /* TODO: improve */
HXD_EA_RA_SA, /* HXD / EA / RA / SA version */ /* TODO: improve */ HXD_EA_RA_SA, /* HXD / EA / RA / SA version */ /* TODO: improve */
TB, /* TB version */ TB, /* TB version */
HX_CLONE, /* Cheap and less functional clone of the HX chip */
}; };
/* /*
* NOTE: don't know the difference between type 0 and type 1, * NOTE: don't know the difference between type 0 and type 1,
...@@ -206,8 +207,23 @@ static int pl2303_startup(struct usb_serial *serial) ...@@ -206,8 +207,23 @@ static int pl2303_startup(struct usb_serial *serial)
* the device descriptors of the X/HX, HXD, EA, RA, SA, TA, TB * the device descriptors of the X/HX, HXD, EA, RA, SA, TA, TB
*/ */
if (le16_to_cpu(serial->dev->descriptor.bcdDevice) == 0x300) { if (le16_to_cpu(serial->dev->descriptor.bcdDevice) == 0x300) {
type = HX_TA; /* Check if the device is a clone */
type_str = "X/HX/TA"; pl2303_vendor_read(0x9494, 0, serial, buf);
/*
* NOTE: Not sure if this read is really needed.
* The HX returns 0x00, the clone 0x02, but the Windows
* driver seems to ignore the value and continues.
*/
pl2303_vendor_write(0x0606, 0xaa, serial);
pl2303_vendor_read(0x8686, 0, serial, buf);
if (buf[0] != 0xaa) {
type = HX_CLONE;
type_str = "X/HX clone (limited functionality)";
} else {
type = HX_TA;
type_str = "X/HX/TA";
}
pl2303_vendor_write(0x0606, 0x00, serial);
} else if (le16_to_cpu(serial->dev->descriptor.bcdDevice) } else if (le16_to_cpu(serial->dev->descriptor.bcdDevice)
== 0x400) { == 0x400) {
type = HXD_EA_RA_SA; type = HXD_EA_RA_SA;
...@@ -305,8 +321,9 @@ static int pl2303_baudrate_encode_direct(int baud, enum pl2303_type type, ...@@ -305,8 +321,9 @@ static int pl2303_baudrate_encode_direct(int baud, enum pl2303_type type,
{ {
/* /*
* NOTE: Only the values defined in baud_sup are supported ! * NOTE: Only the values defined in baud_sup are supported !
* => if unsupported values are set, the PL2303 seems to * => if unsupported values are set, the PL2303 uses 9600 baud instead
* use 9600 baud (at least my PL2303X always does) * => HX clones just don't work at unsupported baud rates < 115200 baud,
* for baud rates > 115200 they run at 115200 baud
*/ */
const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600, const int baud_sup[] = { 75, 150, 300, 600, 1200, 1800, 2400, 3600,
4800, 7200, 9600, 14400, 19200, 28800, 38400, 4800, 7200, 9600, 14400, 19200, 28800, 38400,
...@@ -316,14 +333,14 @@ static int pl2303_baudrate_encode_direct(int baud, enum pl2303_type type, ...@@ -316,14 +333,14 @@ static int pl2303_baudrate_encode_direct(int baud, enum pl2303_type type,
* NOTE: With the exception of type_0/1 devices, the following * NOTE: With the exception of type_0/1 devices, the following
* additional baud rates are supported (tested with HX rev. 3A only): * additional baud rates are supported (tested with HX rev. 3A only):
* 110*, 56000*, 128000, 134400, 161280, 201600, 256000*, 268800, * 110*, 56000*, 128000, 134400, 161280, 201600, 256000*, 268800,
* 403200, 806400. (*: not HX) * 403200, 806400. (*: not HX and HX clones)
* *
* Maximum values: HXD, TB: 12000000; HX, TA: 6000000; * Maximum values: HXD, TB: 12000000; HX, TA: 6000000;
* type_0+1: 1228800; RA: 921600; SA: 115200 * type_0+1: 1228800; RA: 921600; HX clones, SA: 115200
* *
* As long as we are not using this encoding method for anything else * 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 * than the type_0+1, HX and HX clone chips, there is no point in
* the code to support them. * complicating the code to support them.
*/ */
int i; int i;
...@@ -347,6 +364,8 @@ static int pl2303_baudrate_encode_direct(int baud, enum pl2303_type type, ...@@ -347,6 +364,8 @@ static int pl2303_baudrate_encode_direct(int baud, enum pl2303_type type,
baud = min_t(int, baud, 6000000); baud = min_t(int, baud, 6000000);
else if (type == type_0 || type == type_1) else if (type == type_0 || type == type_1)
baud = min_t(int, baud, 1228800); baud = min_t(int, baud, 1228800);
else if (type == HX_CLONE)
baud = min_t(int, baud, 115200);
/* Direct (standard) baud rate encoding method */ /* Direct (standard) baud rate encoding method */
put_unaligned_le32(baud, buf); put_unaligned_le32(baud, buf);
...@@ -359,7 +378,8 @@ static int pl2303_baudrate_encode_divisor(int baud, enum pl2303_type type, ...@@ -359,7 +378,8 @@ static int pl2303_baudrate_encode_divisor(int baud, enum pl2303_type type,
/* /*
* Divisor based baud rate encoding method * Divisor based baud rate encoding method
* *
* NOTE: it's not clear if the type_0/1 chips support this method * NOTE: HX clones do NOT support this method.
* It's not clear if the type_0/1 chips support it.
* *
* divisor = 12MHz * 32 / baudrate = 2^A * B * divisor = 12MHz * 32 / baudrate = 2^A * B
* *
...@@ -452,7 +472,7 @@ static void pl2303_encode_baudrate(struct tty_struct *tty, ...@@ -452,7 +472,7 @@ static void pl2303_encode_baudrate(struct tty_struct *tty,
* 1) Direct method: encodes the baud rate value directly * 1) Direct method: encodes the baud rate value directly
* => supported by all chip types * => supported by all chip types
* 2) Divisor based method: encodes a divisor to a base value (12MHz*32) * 2) Divisor based method: encodes a divisor to a base value (12MHz*32)
* => supported by HX chips (and likely not by type_0/1 chips) * => not supported by HX clones (and likely type_0/1 chips)
* *
* NOTE: Although the divisor based baud rate encoding method is much * NOTE: Although the divisor based baud rate encoding method is much
* more flexible, some of the standard baud rate values can not be * more flexible, some of the standard baud rate values can not be
...@@ -460,7 +480,7 @@ static void pl2303_encode_baudrate(struct tty_struct *tty, ...@@ -460,7 +480,7 @@ static void pl2303_encode_baudrate(struct tty_struct *tty,
* the device likely uses the same baud rate generator for both methods * the device likely uses the same baud rate generator for both methods
* so that there is likley no difference. * so that there is likley no difference.
*/ */
if (type == type_0 || type == type_1) if (type == type_0 || type == type_1 || type == HX_CLONE)
baud = pl2303_baudrate_encode_direct(baud, type, buf); baud = pl2303_baudrate_encode_direct(baud, type, buf);
else else
baud = pl2303_baudrate_encode_divisor(baud, type, buf); baud = pl2303_baudrate_encode_divisor(baud, type, buf);
...@@ -813,6 +833,7 @@ static void pl2303_break_ctl(struct tty_struct *tty, int break_state) ...@@ -813,6 +833,7 @@ static void pl2303_break_ctl(struct tty_struct *tty, int break_state)
result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
BREAK_REQUEST, BREAK_REQUEST_TYPE, state, BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
0, NULL, 0, 100); 0, NULL, 0, 100);
/* NOTE: HX clones don't support sending breaks, -EPIPE is returned */
if (result) if (result)
dev_err(&port->dev, "error sending break = %d\n", result); dev_err(&port->dev, "error sending break = %d\n", result);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment