Skip to contentSkip to conversation

USB: serial: pl2303: amend device-type detection

PATCH New Johan Hovold 1 participant 6 messages Mar 11, 2021

Subsystems (2)
lore
Share & tools
lore

Johan Hovold

author maintainer
#0 lore raw

cover USB: serial: pl2303: amend device-type detection

This series cleans up, amends and tightens the pl2303 device-type detection.

There are number of different PL2303 devices out there with various features and we need to be able to detect more types reliably.

Specifically this will be used to add support for the alternate divisor encoding scheme that is used by TA devices.

Johan

Johan Hovold (5):
USB: serial: pl2303: clean up type detection
USB: serial: pl2303: amend and tighten type detection
USB: serial: pl2303: rename legacy PL2303H type
USB: serial: pl2303: tighten type HXN (G) detection
USB: serial: pl2303: add device-type names

 drivers/usb/serial/pl2303.c | 131 +++++++++++++++++++++++++++---------
 1 file changed, 98 insertions(+), 33 deletions(-)
Signature · 1 line
2.26.2
author maintainer
#1 lore raw

PATCH 1/5 USB: serial: pl2303: clean up type detection

Clean up the type detection somewhat in preparation for adding support for more types.

Note this also fixes the type debug printk for the new HXN type.

---
 drivers/usb/serial/pl2303.c | 68 +++++++++++++++++++++++--------------
 1 file changed, 42 insertions(+), 26 deletions(-)
drivers/usb/serial/pl2303.c +42 -26
index eed9acd1ae08..db840b471adb 100644
@@ -362,42 +362,52 @@ static int pl2303_calc_num_ports(struct usb_serial *serial,
 	return 1;
 }
 
+static enum pl2303_type pl2303_detect_type(struct usb_serial *serial)
+{
+	struct usb_device_descriptor *desc = &serial->dev->descriptor;
+	int ret;
+	u8 buf;
+
+	/*
+	 * Legacy types 0 and 1, difference unknown.
+	 */
+	if (desc->bDeviceClass == 0x02)
+		return TYPE_01;		/* type 0 */
+
+	if (desc->bMaxPacketSize0 != 0x40) {
+		if (desc->bDeviceClass == 0x00 || desc->bDeviceClass == 0xff)
+			return TYPE_01;	/* type 1 */
+
+		return TYPE_01;		/* type 0 */
+	}
+
+	/*
+	 * Assume it's an HXN-type if the device doesn't support the old read
+	 * request value.
+	 */
+	ret = usb_control_msg_recv(serial->dev, 0, VENDOR_READ_REQUEST,
+			VENDOR_READ_REQUEST_TYPE, PL2303_READ_TYPE_HX_STATUS,
+			0, &buf, 1, 100, GFP_KERNEL);
+	if (ret)
+		return TYPE_HXN;
+
+	return TYPE_HX;
+}
+
 static int pl2303_startup(struct usb_serial *serial)
 {
 	struct pl2303_serial_private *spriv;
-	enum pl2303_type type = TYPE_01;
+	enum pl2303_type type;
 	unsigned char *buf;
-	int res;
 
 	spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
 	if (!spriv)
 		return -ENOMEM;
 
-	buf = kmalloc(1, GFP_KERNEL);
-	if (!buf) {
-		kfree(spriv);
-		return -ENOMEM;
-	}
+	type = pl2303_detect_type(serial);
 
-	if (serial->dev->descriptor.bDeviceClass == 0x02)
-		type = TYPE_01;		/* type 0 */
-	else if (serial->dev->descriptor.bMaxPacketSize0 == 0x40)
-		type = TYPE_HX;
-	else if (serial->dev->descriptor.bDeviceClass == 0x00)
-		type = TYPE_01;		/* type 1 */
-	else if (serial->dev->descriptor.bDeviceClass == 0xFF)
-		type = TYPE_01;		/* type 1 */
 	dev_dbg(&serial->interface->dev, "device type: %d\n", type);
 
-	if (type == TYPE_HX) {
-		res = usb_control_msg(serial->dev,
-				usb_rcvctrlpipe(serial->dev, 0),
-				VENDOR_READ_REQUEST, VENDOR_READ_REQUEST_TYPE,
-				PL2303_READ_TYPE_HX_STATUS, 0, buf, 1, 100);
-		if (res != 1)
-			type = TYPE_HXN;
-	}
-
 	spriv->type = &pl2303_type_data[type];
 	spriv->quirks = (unsigned long)usb_get_serial_data(serial);
 	spriv->quirks |= spriv->type->quirks;
@@ -405,6 +415,12 @@ static int pl2303_startup(struct usb_serial *serial)
 	usb_set_serial_data(serial, spriv);
 
 	if (type != TYPE_HXN) {
+		buf = kmalloc(1, GFP_KERNEL);
+		if (!buf) {
+			kfree(spriv);
+			return -ENOMEM;
+		}
+
 		pl2303_vendor_read(serial, 0x8484, buf);
 		pl2303_vendor_write(serial, 0x0404, 0);
 		pl2303_vendor_read(serial, 0x8484, buf);
@@ -419,9 +435,9 @@ static int pl2303_startup(struct usb_serial *serial)
 			pl2303_vendor_write(serial, 2, 0x24);
 		else
 			pl2303_vendor_write(serial, 2, 0x44);
-	}
 
-	kfree(buf);
+		kfree(buf);
+	}
 
 	return 0;
 }
Signature · 1 line
2.26.2
author maintainer
#2 lore raw

PATCH 2/5 USB: serial: pl2303: amend and tighten type detection

In reply to Johan Hovold

Add support for detecting the HX, TA, TB and HXD device types and refuse to bind to any unknown types.

Note that the HX type includes the XA variant, while the HXD type includes the EA, RA and SA variants.

---
 drivers/usb/serial/pl2303.c | 50 +++++++++++++++++++++++++++++++------
 1 file changed, 42 insertions(+), 8 deletions(-)
drivers/usb/serial/pl2303.c +42 -8
index db840b471adb..42dcc3cfb449 100644
@@ -174,8 +174,11 @@ static void pl2303_set_break(struct usb_serial_port *port, bool enable);
 
 enum pl2303_type {
 	TYPE_01,	/* Type 0 and 1 (difference unknown) */
-	TYPE_HX,	/* HX version of the pl2303 chip */
-	TYPE_HXN,	/* HXN version of the pl2303 chip */
+	TYPE_HX,
+	TYPE_TA,
+	TYPE_TB,
+	TYPE_HXD,
+	TYPE_HXN,
 	TYPE_COUNT
 };
 
@@ -206,6 +209,15 @@ static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {
 		.no_autoxonxoff		= true,
 	},
 	[TYPE_HX] = {
+		.max_baud_rate		= 6000000,
+	},
+	[TYPE_TA] = {
+		.max_baud_rate		= 6000000,
+	},
+	[TYPE_TB] = {
+		.max_baud_rate		= 12000000,
+	},
+	[TYPE_HXD] = {
 		.max_baud_rate		= 12000000,
 	},
 	[TYPE_HXN] = {
@@ -362,9 +374,10 @@ static int pl2303_calc_num_ports(struct usb_serial *serial,
 	return 1;
 }
 
-static enum pl2303_type pl2303_detect_type(struct usb_serial *serial)
+static int pl2303_detect_type(struct usb_serial *serial)
 {
 	struct usb_device_descriptor *desc = &serial->dev->descriptor;
+	u16 bcdDevice, bcdUSB;
 	int ret;
 	u8 buf;
 
@@ -391,7 +404,24 @@ static enum pl2303_type pl2303_detect_type(struct usb_serial *serial)
 	if (ret)
 		return TYPE_HXN;
 
-	return TYPE_HX;
+	bcdDevice = le16_to_cpu(desc->bcdDevice);
+	bcdUSB = le16_to_cpu(desc->bcdUSB);
+
+	switch (bcdDevice) {
+	case 0x300:
+		if (bcdUSB == 0x200)
+			return TYPE_TA;
+
+		return TYPE_HX;
+	case 0x400:
+		return TYPE_HXD;
+	case 0x500:
+		return TYPE_TB;
+	}
+
+	dev_err(&serial->interface->dev,
+			"unknown device type, please report to [email protected]\n");
+	return -ENODEV;
 }
 
 static int pl2303_startup(struct usb_serial *serial)
@@ -399,15 +429,19 @@ static int pl2303_startup(struct usb_serial *serial)
 	struct pl2303_serial_private *spriv;
 	enum pl2303_type type;
 	unsigned char *buf;
+	int ret;
+
+	ret = pl2303_detect_type(serial);
+	if (ret < 0)
+		return ret;
+
+	type = ret;
+	dev_dbg(&serial->interface->dev, "device type: %d\n", type);
 
 	spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
 	if (!spriv)
 		return -ENOMEM;
 
-	type = pl2303_detect_type(serial);
-
-	dev_dbg(&serial->interface->dev, "device type: %d\n", type);
-
 	spriv->type = &pl2303_type_data[type];
 	spriv->quirks = (unsigned long)usb_get_serial_data(serial);
 	spriv->quirks |= spriv->type->quirks;
Signature · 1 line
2.26.2
author maintainer
#3 lore raw

PATCH 3/5 USB: serial: pl2303: rename legacy PL2303H type

In reply to Johan Hovold

Rename the legacy type which is supposedly a PL2303H which came in two variants (and which we handle the same way).

---
 drivers/usb/serial/pl2303.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/usb/serial/pl2303.c +6 -6
index 42dcc3cfb449..cd2acd8c5246 100644
@@ -173,7 +173,7 @@ MODULE_DEVICE_TABLE(usb, id_table);
 static void pl2303_set_break(struct usb_serial_port *port, bool enable);
 
 enum pl2303_type {
-	TYPE_01,	/* Type 0 and 1 (difference unknown) */
+	TYPE_H,
 	TYPE_HX,
 	TYPE_TA,
 	TYPE_TB,
@@ -203,7 +203,7 @@ struct pl2303_private {
 };
 
 static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {
-	[TYPE_01] = {
+	[TYPE_H] = {
 		.max_baud_rate		= 1228800,
 		.quirks			= PL2303_QUIRK_LEGACY,
 		.no_autoxonxoff		= true,
@@ -382,16 +382,16 @@ static int pl2303_detect_type(struct usb_serial *serial)
 	u8 buf;
 
 	/*
-	 * Legacy types 0 and 1, difference unknown.
+	 * Legacy PL2303H, variants 0 and 1 (difference unknown).
 	 */
 	if (desc->bDeviceClass == 0x02)
-		return TYPE_01;		/* type 0 */
+		return TYPE_H;		/* variant 0 */
 
 	if (desc->bMaxPacketSize0 != 0x40) {
 		if (desc->bDeviceClass == 0x00 || desc->bDeviceClass == 0xff)
-			return TYPE_01;	/* type 1 */
+			return TYPE_H;	/* variant 1 */
 
-		return TYPE_01;		/* type 0 */
+		return TYPE_H;		/* variant 0 */
 	}
 
 	/*
Signature · 1 line
2.26.2
author maintainer
#4 lore raw

PATCH 4/5 USB: serial: pl2303: tighten type HXN (G) detection

In reply to Johan Hovold

Tighten the detection of the new HXN (G) type instead of assuming that every device which doesn't support the old read request is an HXN.

---
 drivers/usb/serial/pl2303.c | 32 ++++++++++++++++++++------------
 1 file changed, 20 insertions(+), 12 deletions(-)
drivers/usb/serial/pl2303.c +20 -12
index cd2acd8c5246..e742187c8a7f 100644
@@ -374,12 +374,22 @@ static int pl2303_calc_num_ports(struct usb_serial *serial,
 	return 1;
 }
 
+static bool pl2303_supports_hx_status(struct usb_serial *serial)
+{
+	int ret;
+	u8 buf;
+
+	ret = usb_control_msg_recv(serial->dev, 0, VENDOR_READ_REQUEST,
+			VENDOR_READ_REQUEST_TYPE, PL2303_READ_TYPE_HX_STATUS,
+			0, &buf, 1, 100, GFP_KERNEL);
+
+	return ret == 0;
+}
+
 static int pl2303_detect_type(struct usb_serial *serial)
 {
 	struct usb_device_descriptor *desc = &serial->dev->descriptor;
 	u16 bcdDevice, bcdUSB;
-	int ret;
-	u8 buf;
 
 	/*
 	 * Legacy PL2303H, variants 0 and 1 (difference unknown).
@@ -394,20 +404,18 @@ static int pl2303_detect_type(struct usb_serial *serial)
 		return TYPE_H;		/* variant 0 */
 	}
 
-	/*
-	 * Assume it's an HXN-type if the device doesn't support the old read
-	 * request value.
-	 */
-	ret = usb_control_msg_recv(serial->dev, 0, VENDOR_READ_REQUEST,
-			VENDOR_READ_REQUEST_TYPE, PL2303_READ_TYPE_HX_STATUS,
-			0, &buf, 1, 100, GFP_KERNEL);
-	if (ret)
-		return TYPE_HXN;
-
 	bcdDevice = le16_to_cpu(desc->bcdDevice);
 	bcdUSB = le16_to_cpu(desc->bcdUSB);
 
 	switch (bcdDevice) {
+	case 0x100:
+		/*
+		 * Assume it's an HXN-type if the device doesn't support the old read
+		 * request value.
+		 */
+		if (bcdUSB == 0x200 && !pl2303_supports_hx_status(serial))
+			return TYPE_HXN;
+		break;
 	case 0x300:
 		if (bcdUSB == 0x200)
 			return TYPE_TA;
Signature · 1 line
2.26.2
author maintainer
#5 lore raw

PATCH 5/5 USB: serial: pl2303: add device-type names

In reply to Johan Hovold

Add names for the device types to be printed at probe when debugging is enabled.

Note that the HXN type is referred to as G for now as that is the name the vendor uses.

---
 drivers/usb/serial/pl2303.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
drivers/usb/serial/pl2303.c +8 -1
index e742187c8a7f..7208966891d0 100644
@@ -183,6 +183,7 @@ enum pl2303_type {
 };
 
 struct pl2303_type_data {
+	const char *name;
 	speed_t max_baud_rate;
 	unsigned long quirks;
 	unsigned int no_autoxonxoff:1;
@@ -204,23 +205,29 @@ struct pl2303_private {
 
 static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = {
 	[TYPE_H] = {
+		.name			= "H",
 		.max_baud_rate		= 1228800,
 		.quirks			= PL2303_QUIRK_LEGACY,
 		.no_autoxonxoff		= true,
 	},
 	[TYPE_HX] = {
+		.name			= "HX",
 		.max_baud_rate		= 6000000,
 	},
 	[TYPE_TA] = {
+		.name			= "TA",
 		.max_baud_rate		= 6000000,
 	},
 	[TYPE_TB] = {
+		.name			= "TB",
 		.max_baud_rate		= 12000000,
 	},
 	[TYPE_HXD] = {
+		.name			= "HXD",
 		.max_baud_rate		= 12000000,
 	},
 	[TYPE_HXN] = {
+		.name			= "G",
 		.max_baud_rate		= 12000000,
 		.no_divisors		= true,
 	},
@@ -444,7 +451,7 @@ static int pl2303_startup(struct usb_serial *serial)
 		return ret;
 
 	type = ret;
-	dev_dbg(&serial->interface->dev, "device type: %d\n", type);
+	dev_dbg(&serial->interface->dev, "device type: %s\n", pl2303_type_data[type].name);
 
 	spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
 	if (!spriv)
Signature · 1 line
2.26.2