aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/serial/cp210x.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/serial/cp210x.c')
-rw-r--r--drivers/usb/serial/cp210x.c115
1 files changed, 70 insertions, 45 deletions
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 3c80bfbf3bec..189279869a8b 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -233,6 +233,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x1FB9, 0x0602) }, /* Lake Shore Model 648 Magnet Power Supply */
{ USB_DEVICE(0x1FB9, 0x0700) }, /* Lake Shore Model 737 VSM Controller */
{ USB_DEVICE(0x1FB9, 0x0701) }, /* Lake Shore Model 776 Hall Matrix */
+ { USB_DEVICE(0x2184, 0x0030) }, /* GW Instek GDM-834x Digital Multimeter */
{ USB_DEVICE(0x2626, 0xEA60) }, /* Aruba Networks 7xxx USB Serial Console */
{ USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */
{ USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */
@@ -258,6 +259,7 @@ struct cp210x_serial_private {
speed_t max_speed;
bool use_actual_rate;
bool no_flow_control;
+ bool no_event_mode;
};
enum cp210x_event_state {
@@ -400,6 +402,7 @@ struct cp210x_special_chars {
};
/* CP210X_VENDOR_SPECIFIC values */
+#define CP210X_GET_FW_VER 0x000E
#define CP210X_READ_2NCONFIG 0x000E
#define CP210X_GET_FW_VER_2N 0x0010
#define CP210X_READ_LATCH 0x00C2
@@ -638,7 +641,7 @@ static int cp210x_read_reg_block(struct usb_serial_port *port, u8 req,
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
req, REQTYPE_INTERFACE_TO_HOST, 0,
port_priv->bInterfaceNumber, dmabuf, bufsize,
- USB_CTRL_SET_TIMEOUT);
+ USB_CTRL_GET_TIMEOUT);
if (result == bufsize) {
memcpy(buf, dmabuf, bufsize);
result = 0;
@@ -1112,12 +1115,16 @@ static void cp210x_change_speed(struct tty_struct *tty,
static void cp210x_enable_event_mode(struct usb_serial_port *port)
{
+ struct cp210x_serial_private *priv = usb_get_serial_data(port->serial);
struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
int ret;
if (port_priv->event_mode)
return;
+ if (priv->no_event_mode)
+ return;
+
port_priv->event_state = ES_DATA;
port_priv->event_mode = true;
@@ -1145,33 +1152,6 @@ static void cp210x_disable_event_mode(struct usb_serial_port *port)
port_priv->event_mode = false;
}
-static int cp210x_set_chars(struct usb_serial_port *port,
- struct cp210x_special_chars *chars)
-{
- struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
- struct usb_serial *serial = port->serial;
- void *dmabuf;
- int result;
-
- dmabuf = kmemdup(chars, sizeof(*chars), GFP_KERNEL);
- if (!dmabuf)
- return -ENOMEM;
-
- result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- CP210X_SET_CHARS, REQTYPE_HOST_TO_INTERFACE, 0,
- port_priv->bInterfaceNumber,
- dmabuf, sizeof(*chars), USB_CTRL_SET_TIMEOUT);
-
- kfree(dmabuf);
-
- if (result < 0) {
- dev_err(&port->dev, "failed to set special chars: %d\n", result);
- return result;
- }
-
- return 0;
-}
-
static bool cp210x_termios_change(const struct ktermios *a, const struct ktermios *b)
{
bool iflag_change, cc_change;
@@ -1192,6 +1172,7 @@ static void cp210x_set_flow_control(struct tty_struct *tty,
struct cp210x_flow_ctl flow_ctl;
u32 flow_repl;
u32 ctl_hs;
+ bool crtscts;
int ret;
/*
@@ -1218,9 +1199,12 @@ static void cp210x_set_flow_control(struct tty_struct *tty,
chars.bXonChar = START_CHAR(tty);
chars.bXoffChar = STOP_CHAR(tty);
- ret = cp210x_set_chars(port, &chars);
- if (ret)
- return;
+ ret = cp210x_write_reg_block(port, CP210X_SET_CHARS, &chars,
+ sizeof(chars));
+ if (ret) {
+ dev_err(&port->dev, "failed to set special chars: %d\n",
+ ret);
+ }
}
mutex_lock(&port_priv->mutex);
@@ -1249,14 +1233,14 @@ static void cp210x_set_flow_control(struct tty_struct *tty,
flow_repl |= CP210X_SERIAL_RTS_FLOW_CTL;
else
flow_repl |= CP210X_SERIAL_RTS_INACTIVE;
- port_priv->crtscts = true;
+ crtscts = true;
} else {
ctl_hs &= ~CP210X_SERIAL_CTS_HANDSHAKE;
if (port_priv->rts)
flow_repl |= CP210X_SERIAL_RTS_ACTIVE;
else
flow_repl |= CP210X_SERIAL_RTS_INACTIVE;
- port_priv->crtscts = false;
+ crtscts = false;
}
if (I_IXOFF(tty)) {
@@ -1279,8 +1263,12 @@ static void cp210x_set_flow_control(struct tty_struct *tty,
flow_ctl.ulControlHandshake = cpu_to_le32(ctl_hs);
flow_ctl.ulFlowReplace = cpu_to_le32(flow_repl);
- cp210x_write_reg_block(port, CP210X_SET_FLOW, &flow_ctl,
+ ret = cp210x_write_reg_block(port, CP210X_SET_FLOW, &flow_ctl,
sizeof(flow_ctl));
+ if (ret)
+ goto out_unlock;
+
+ port_priv->crtscts = crtscts;
out_unlock:
mutex_unlock(&port_priv->mutex);
}
@@ -2092,6 +2080,33 @@ static void cp210x_init_max_speed(struct usb_serial *serial)
priv->use_actual_rate = use_actual_rate;
}
+static void cp2102_determine_quirks(struct usb_serial *serial)
+{
+ struct cp210x_serial_private *priv = usb_get_serial_data(serial);
+ u8 *buf;
+ int ret;
+
+ buf = kmalloc(2, GFP_KERNEL);
+ if (!buf)
+ return;
+ /*
+ * Some (possibly counterfeit) CP2102 do not support event-insertion
+ * mode and respond differently to malformed vendor requests.
+ * Specifically, they return one instead of two bytes when sent a
+ * two-byte part-number request.
+ */
+ ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ CP210X_VENDOR_SPECIFIC, REQTYPE_DEVICE_TO_HOST,
+ CP210X_GET_PARTNUM, 0, buf, 2, USB_CTRL_GET_TIMEOUT);
+ if (ret == 1) {
+ dev_dbg(&serial->interface->dev,
+ "device does not support event-insertion mode\n");
+ priv->no_event_mode = true;
+ }
+
+ kfree(buf);
+}
+
static int cp210x_get_fw_version(struct usb_serial *serial, u16 value)
{
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
@@ -2111,12 +2126,31 @@ static int cp210x_get_fw_version(struct usb_serial *serial, u16 value)
return 0;
}
-static void cp210x_determine_quirks(struct usb_serial *serial)
+static void cp210x_determine_type(struct usb_serial *serial)
{
struct cp210x_serial_private *priv = usb_get_serial_data(serial);
int ret;
+ ret = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
+ CP210X_GET_PARTNUM, &priv->partnum,
+ sizeof(priv->partnum));
+ if (ret < 0) {
+ dev_warn(&serial->interface->dev,
+ "querying part number failed\n");
+ priv->partnum = CP210X_PARTNUM_UNKNOWN;
+ return;
+ }
+
+ dev_dbg(&serial->interface->dev, "partnum = 0x%02x\n", priv->partnum);
+
switch (priv->partnum) {
+ case CP210X_PARTNUM_CP2102:
+ cp2102_determine_quirks(serial);
+ break;
+ case CP210X_PARTNUM_CP2105:
+ case CP210X_PARTNUM_CP2108:
+ cp210x_get_fw_version(serial, CP210X_GET_FW_VER);
+ break;
case CP210X_PARTNUM_CP2102N_QFN28:
case CP210X_PARTNUM_CP2102N_QFN24:
case CP210X_PARTNUM_CP2102N_QFN20:
@@ -2140,18 +2174,9 @@ static int cp210x_attach(struct usb_serial *serial)
if (!priv)
return -ENOMEM;
- result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
- CP210X_GET_PARTNUM, &priv->partnum,
- sizeof(priv->partnum));
- if (result < 0) {
- dev_warn(&serial->interface->dev,
- "querying part number failed\n");
- priv->partnum = CP210X_PARTNUM_UNKNOWN;
- }
-
usb_set_serial_data(serial, priv);
- cp210x_determine_quirks(serial);
+ cp210x_determine_type(serial);
cp210x_init_max_speed(serial);
result = cp210x_gpio_init(serial);