lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <1444261132-5001-1-git-send-email-konstantin.shkolnyy@gmail.com>
Date:	Wed,  7 Oct 2015 18:38:52 -0500
From:	Konstantin Shkolnyy <konstantin.shkolnyy@...il.com>
To:	johan@...nel.org
Cc:	linux-usb@...r.kernel.org, linux-kernel@...r.kernel.org,
	Konstantin Shkolnyy <konstantin.shkolnyy@...il.com>
Subject: [PATCH] USB: serial: cp210x: Workaround for cp2108 failure due to GET_LINE_CTL bug

cp2108 GET_LINE_CTL returns the 16-bit value with the 2 bytes swapped.
However, SET_LINE_CTL functions properly. When the driver tries to modify
the register, it reads it, modifies some bits and writes back. Because the
read bytes were swapped, this often results in an invalid value to be written.
In turn, this causes cp2108 respond with a stall. The stall sometimes doesn't
clear properly and cp2108 starts responding to following valid commands also
with stalls, effectively failing.

Signed-off-by: Konstantin Shkolnyy <konstantin.shkolnyy@...il.com>
---
 drivers/usb/serial/cp210x.c | 58 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index eac7cca..060a1e8 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -199,6 +199,7 @@ MODULE_DEVICE_TABLE(usb, id_table);
 
 struct cp210x_serial_private {
 	__u8			bInterfaceNumber;
+	bool			swap_get_line_ctl; /* set if CP2108 swaps bytes due to a bug */
 };
 
 static struct usb_serial_driver cp210x_device = {
@@ -343,6 +344,28 @@ static int cp210x_get_config(struct usb_serial_port *port, u8 request,
 		return result;
 	}
 
+	/* Workaround for swapped bytes in 16-bit value from CP210X_GET_LINE_CTL */
+	if (spriv->swap_get_line_ctl && request == CP210X_GET_LINE_CTL && size == 2) {
+		union {
+			struct {
+				u8 byte0;
+				u8 byte1;
+				u8 byte2;
+				u8 byte3;
+			};
+			u32 as_u32;
+		} tmp_data;
+		u8 old_byte0;
+		u8 old_byte1;
+
+		tmp_data.as_u32  = data[0]; /* from caller's buffer */
+		old_byte0        = tmp_data.byte0;
+		old_byte1        = tmp_data.byte1;
+		tmp_data.byte0   = old_byte1;
+		tmp_data.byte1   = old_byte0;
+		data[0]          = tmp_data.as_u32; /* to caller's buffer */
+	}
+
 	return 0;
 }
 
@@ -865,18 +888,53 @@ static void cp210x_break_ctl(struct tty_struct *tty, int break_state)
 
 static int cp210x_startup(struct usb_serial *serial)
 {
+	struct usb_serial_port *port;
 	struct usb_host_interface *cur_altsetting;
 	struct cp210x_serial_private *spriv;
+	unsigned int  line_ctl;
+	int           err;
+
+	/* We always expect a single port only */
+	if (serial->num_ports != 1) {
+		dev_err(&serial->dev->dev, "%s - expected 1 port, found %d\n",
+			__func__, serial->num_ports);
+		return -EINVAL;
+	}
+	port = serial->port[0];
 
 	spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
 	if (!spriv)
 		return -ENOMEM;
 
+	/* get_config and set_config rely on this spriv field */
 	cur_altsetting = serial->interface->cur_altsetting;
 	spriv->bInterfaceNumber = cur_altsetting->desc.bInterfaceNumber;
 
+	/* Detect CP2108 bug and activate workaround.
+	 * Write a known good value 0x800, read it back.
+	 * If it comes back swapped the bug is detected.
+	 */
+
+	/* The following get_config won't swap the bytes */
+	spriv->swap_get_line_ctl = false;
+
+	/* must be set before calling get_config and set_config */
 	usb_set_serial_data(serial, spriv);
 
+	line_ctl = 0x800;
+
+	err = cp210x_set_config(port, CP210X_SET_LINE_CTL, &line_ctl, 2);
+	if (err)
+		return err;
+
+	err = cp210x_get_config(port, CP210X_GET_LINE_CTL, &line_ctl, 2);
+	if (err)
+		return err;
+
+	if ((line_ctl & 0xffff) == 8)
+		/* Future get_config calls will swap the bytes */
+		spriv->swap_get_line_ctl = true;
+
 	return 0;
 }
 
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ