diff mbox series

[06/12] USB: serial: ftdi_sio: tighten device-type detection

Message ID 20220911140216.30481-7-johan@kernel.org
State New
Headers show
Series USB: serial: ftdi_sio: type cleanup and HP/HA support | expand

Commit Message

Johan Hovold Sept. 11, 2022, 2:02 p.m. UTC
Clean up and tighten the device-type detection, which is based on
bcdDevice.

Don't make assumptions about unknown (future) types (currently assumed
to be either FT2232C or FT-X depending on bNumInterfaces) and instead
log an error and refuse to bind so that we can add proper support when
needed.

Note that the bcdDevice values have been provided by FTDI.

Signed-off-by: Johan Hovold <johan@kernel.org>
---
 drivers/usb/serial/ftdi_sio.c | 118 ++++++++++++++++------------------
 1 file changed, 55 insertions(+), 63 deletions(-)
diff mbox series

Patch

diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 707dc3d67a19..f372f55a1bdf 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1547,89 +1547,73 @@  static int get_lsr_info(struct usb_serial_port *port,
 	return 0;
 }
 
-
-/* Determine type of FTDI chip based on USB config and descriptor. */
-static void ftdi_determine_type(struct usb_serial_port *port)
+static int ftdi_determine_type(struct usb_serial_port *port)
 {
 	struct ftdi_private *priv = usb_get_serial_port_data(port);
 	struct usb_serial *serial = port->serial;
 	struct usb_device *udev = serial->dev;
-	unsigned version;
-	unsigned interfaces;
+	unsigned int version, ifnum;
+
+	version = le16_to_cpu(udev->descriptor.bcdDevice);
+	ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
 
-	/* Assume it is not the original SIO device for now. */
 	priv->baud_base = 48000000 / 2;
+	priv->channel = 0;
 
-	version = le16_to_cpu(udev->descriptor.bcdDevice);
-	interfaces = udev->actconfig->desc.bNumInterfaces;
-	dev_dbg(&port->dev, "%s: bcdDevice = 0x%x, bNumInterfaces = %u\n", __func__,
-		version, interfaces);
-	if (interfaces > 1) {
-		struct usb_interface *intf = serial->interface;
-		int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
-
-		/* Multiple interfaces.*/
-		if (version == 0x0800) {
-			priv->chip_type = FT4232H;
-			/* Hi-speed - baud clock runs at 120MHz */
-			priv->baud_base = 120000000 / 2;
-		} else if (version == 0x0700) {
-			priv->chip_type = FT2232H;
-			/* Hi-speed - baud clock runs at 120MHz */
-			priv->baud_base = 120000000 / 2;
-		} else
-			priv->chip_type = FT2232C;
-
-		if (ifnum == 0)
-			priv->channel = CHANNEL_A;
-		else if (ifnum == 1)
-			priv->channel = CHANNEL_B;
-		else if (ifnum == 2)
-			priv->channel = CHANNEL_C;
-		else if (ifnum == 3)
-			priv->channel = CHANNEL_D;
-
-		/* BM-type devices have a bug where bcdDevice gets set
-		 * to 0x200 when iSerialNumber is 0.  */
-		if (version < 0x500) {
-			dev_dbg(&port->dev,
-				"%s: something fishy - bcdDevice too low for multi-interface device\n",
-				__func__);
-		}
-	} else if (version < 0x200) {
-		/* Old device.  Assume it's the original SIO. */
-		priv->chip_type = SIO;
-		priv->baud_base = 12000000 / 16;
-	} else if (version < 0x400) {
-		/* Assume it's an FT8U232AM (or FT8U245AM) */
+	switch (version) {
+	case 0x200:
 		priv->chip_type = FT232A;
+
 		/*
-		 * It might be a BM type because of the iSerialNumber bug.
-		 * If iSerialNumber==0 and the latency timer is readable,
-		 * assume it is BM type.
+		 * FT232B devices have a bug where bcdDevice gets set to 0x200
+		 * when iSerialNumber is 0. Assume it is an FT232B in case the
+		 * latency timer is readable.
 		 */
 		if (udev->descriptor.iSerialNumber == 0 &&
 				_read_latency_timer(port) >= 0) {
-			dev_dbg(&port->dev,
-				"%s: has latency timer so not an AM type\n",
-				__func__);
 			priv->chip_type = FT232B;
 		}
-	} else if (version < 0x600) {
-		/* Assume it's an FT232BM (or FT245BM) */
+		break;
+	case 0x400:
 		priv->chip_type = FT232B;
-	} else if (version < 0x900) {
-		/* Assume it's an FT232RL */
+		break;
+	case 0x500:
+		priv->chip_type = FT2232C;
+		priv->channel = CHANNEL_A + ifnum;
+		break;
+	case 0x600:
 		priv->chip_type = FT232R;
-	} else if (version < 0x1000) {
-		/* Assume it's an FT232H */
+		break;
+	case 0x700:
+		priv->chip_type = FT2232H;
+		priv->channel = CHANNEL_A + ifnum;
+		priv->baud_base = 120000000 / 2;
+		break;
+	case 0x800:
+		priv->chip_type = FT4232H;
+		priv->channel = CHANNEL_A + ifnum;
+		priv->baud_base = 120000000 / 2;
+		break;
+	case 0x900:
 		priv->chip_type = FT232H;
-	} else {
-		/* Assume it's an FT-X series device */
+		priv->baud_base = 120000000 / 2;
+		break;
+	case 0x1000:
 		priv->chip_type = FTX;
+		break;
+	default:
+		if (version < 0x200) {
+			priv->chip_type = SIO;
+			priv->baud_base = 12000000 / 16;
+		} else {
+			dev_err(&port->dev, "unknown device type: 0x%02x\n", version);
+			return -ENODEV;
+		}
 	}
 
 	dev_info(&udev->dev, "Detected %s\n", ftdi_chip_name[priv->chip_type]);
+
+	return 0;
 }
 
 
@@ -2256,7 +2240,10 @@  static int ftdi_sio_port_probe(struct usb_serial_port *port)
 
 	usb_set_serial_port_data(port, priv);
 
-	ftdi_determine_type(port);
+	result = ftdi_determine_type(port);
+	if (result)
+		goto err_free;
+
 	ftdi_set_max_packet_size(port);
 	if (read_latency_timer(port) < 0)
 		priv->latency = 16;
@@ -2271,6 +2258,11 @@  static int ftdi_sio_port_probe(struct usb_serial_port *port)
 	}
 
 	return 0;
+
+err_free:
+	kfree(priv);
+
+	return result;
 }
 
 /* Setup for the USB-UIRT device, which requires hardwired