USB-serial fixes for v4.10-rc4
These fixes address a number of issues in the ch341 driver and includes a partial revert of a change in how we set the line settings that went into 4.10-rc1 but which turned out to have undesired side effects. This included deasserting the modem-control lines when configuring the device, but also prevented a certain class of CH340 devices from working with the driver. Included are also two fixes for two minor information leaks in kl5kusb105 and ch341 due to failures to detect short control transfers. Signed-off-by: Johan Hovold <johan@kernel.org> -----BEGIN PGP SIGNATURE----- iQIuBAABCAAYBQJYd5F7ERxqb2hhbkBrZXJuZWwub3JnAAoJEEEN5E/e4bSVJPoP /2nebFgZd4efzi1NQNwowwjF4f0tIRx1ysMEfwf0l5QjtY8ZFoXmkIb0q6puIqSB 6sl1s+1t+FMXlnIXYLO6nSxMl18o/hOCZ/1fw7rj6QnbtSiLxTrUa13tdRujsWLW 1JFltCqThnToIxzms5ns+Pxhkq1T+cyLD0dCa++8VFNmr20E+ZGip3KGiu1exT/V lJPV+sEMaCDlqnKhnW313KDcJ5zL/lxPDwKdFD0+6+uTOcoeZ7wNUaTHDUhNyFWV d0nqxp9bQ2mENIVGIkZy+gewBQHJuiw+oo6qjDIbUn3mSMcS08D22+ZTEc56sLl5 cbVxr5KAVqz5v/XovY6b9lvaBUTytvm2g9xaLk7QoY3Mn8r8TPWK5PxbZ7ZO62v6 bi7+FUUp2UW7NoGXbz315PsomsalCwQSj2PS96Zoh82mY7qYxORsRuPrdFe6RjHL EVQMoq2HmlhwBa7bJmrm9h52Za8LBc0qH+szpwCLubCbnDQlsHLF+bQgrNs29c4f 07JjhjtK5ihNU00llren1pJHRtgszoqOArjiU6BNpCBs16VBxWp9ZtZv1shd1Pyp v1VYPidmdGNUC+PdWe35rqYwaNIQa9yF/+0J0c0iVxbyQ/1IxSKLe8R7SQJGLYGE pjtrD9sZixFGwF0Xz9mR96DRMTWbCRIzMVGE5cBFIkIB =inRb -----END PGP SIGNATURE----- Merge tag 'usb-serial-4.10-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial into usb-linus Johan writes: USB-serial fixes for v4.10-rc4 These fixes address a number of issues in the ch341 driver and includes a partial revert of a change in how we set the line settings that went into 4.10-rc1 but which turned out to have undesired side effects. This included deasserting the modem-control lines when configuring the device, but also prevented a certain class of CH340 devices from working with the driver. Included are also two fixes for two minor information leaks in kl5kusb105 and ch341 due to failures to detect short control transfers. Signed-off-by: Johan Hovold <johan@kernel.org>
This commit is contained in:
commit
97f9c5f211
|
@ -95,6 +95,7 @@ struct ch341_private {
|
|||
unsigned baud_rate; /* set baud rate */
|
||||
u8 line_control; /* set line control value RTS/DTR */
|
||||
u8 line_status; /* active status of modem control inputs */
|
||||
u8 lcr;
|
||||
};
|
||||
|
||||
static void ch341_set_termios(struct tty_struct *tty,
|
||||
|
@ -112,6 +113,8 @@ static int ch341_control_out(struct usb_device *dev, u8 request,
|
|||
r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
|
||||
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
|
||||
value, index, NULL, 0, DEFAULT_TIMEOUT);
|
||||
if (r < 0)
|
||||
dev_err(&dev->dev, "failed to send control message: %d\n", r);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -129,11 +132,24 @@ static int ch341_control_in(struct usb_device *dev,
|
|||
r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
|
||||
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
|
||||
value, index, buf, bufsize, DEFAULT_TIMEOUT);
|
||||
return r;
|
||||
if (r < bufsize) {
|
||||
if (r >= 0) {
|
||||
dev_err(&dev->dev,
|
||||
"short control message received (%d < %u)\n",
|
||||
r, bufsize);
|
||||
r = -EIO;
|
||||
}
|
||||
|
||||
dev_err(&dev->dev, "failed to receive control message: %d\n",
|
||||
r);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ch341_init_set_baudrate(struct usb_device *dev,
|
||||
struct ch341_private *priv, unsigned ctrl)
|
||||
static int ch341_set_baudrate_lcr(struct usb_device *dev,
|
||||
struct ch341_private *priv, u8 lcr)
|
||||
{
|
||||
short a;
|
||||
int r;
|
||||
|
@ -156,9 +172,19 @@ static int ch341_init_set_baudrate(struct usb_device *dev,
|
|||
factor = 0x10000 - factor;
|
||||
a = (factor & 0xff00) | divisor;
|
||||
|
||||
/* 0x9c is "enable SFR_UART Control register and timer" */
|
||||
r = ch341_control_out(dev, CH341_REQ_SERIAL_INIT,
|
||||
0x9c | (ctrl << 8), a | 0x80);
|
||||
/*
|
||||
* CH341A buffers data until a full endpoint-size packet (32 bytes)
|
||||
* has been received unless bit 7 is set.
|
||||
*/
|
||||
a |= BIT(7);
|
||||
|
||||
r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x1312, a);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, lcr);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -170,9 +196,9 @@ static int ch341_set_handshake(struct usb_device *dev, u8 control)
|
|||
|
||||
static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
|
||||
{
|
||||
const unsigned int size = 2;
|
||||
char *buffer;
|
||||
int r;
|
||||
const unsigned size = 8;
|
||||
unsigned long flags;
|
||||
|
||||
buffer = kmalloc(size, GFP_KERNEL);
|
||||
|
@ -183,14 +209,9 @@ static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv)
|
|||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
/* setup the private status if available */
|
||||
if (r == 2) {
|
||||
r = 0;
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
} else
|
||||
r = -EPROTO;
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT;
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
out: kfree(buffer);
|
||||
return r;
|
||||
|
@ -200,9 +221,9 @@ out: kfree(buffer);
|
|||
|
||||
static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
|
||||
{
|
||||
const unsigned int size = 2;
|
||||
char *buffer;
|
||||
int r;
|
||||
const unsigned size = 8;
|
||||
|
||||
buffer = kmalloc(size, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
|
@ -232,7 +253,7 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv)
|
|||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = ch341_init_set_baudrate(dev, priv, 0);
|
||||
r = ch341_set_baudrate_lcr(dev, priv, priv->lcr);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
|
@ -258,7 +279,6 @@ static int ch341_port_probe(struct usb_serial_port *port)
|
|||
|
||||
spin_lock_init(&priv->lock);
|
||||
priv->baud_rate = DEFAULT_BAUD_RATE;
|
||||
priv->line_control = CH341_BIT_RTS | CH341_BIT_DTR;
|
||||
|
||||
r = ch341_configure(port->serial->dev, priv);
|
||||
if (r < 0)
|
||||
|
@ -320,7 +340,7 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
|
|||
|
||||
r = ch341_configure(serial->dev, priv);
|
||||
if (r)
|
||||
goto out;
|
||||
return r;
|
||||
|
||||
if (tty)
|
||||
ch341_set_termios(tty, port, NULL);
|
||||
|
@ -330,12 +350,19 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
|
|||
if (r) {
|
||||
dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n",
|
||||
__func__, r);
|
||||
goto out;
|
||||
return r;
|
||||
}
|
||||
|
||||
r = usb_serial_generic_open(tty, port);
|
||||
if (r)
|
||||
goto err_kill_interrupt_urb;
|
||||
|
||||
out: return r;
|
||||
return 0;
|
||||
|
||||
err_kill_interrupt_urb:
|
||||
usb_kill_urb(port->interrupt_in_urb);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Old_termios contains the original termios settings and
|
||||
|
@ -356,7 +383,6 @@ static void ch341_set_termios(struct tty_struct *tty,
|
|||
|
||||
baud_rate = tty_get_baud_rate(tty);
|
||||
|
||||
priv->baud_rate = baud_rate;
|
||||
ctrl = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX;
|
||||
|
||||
switch (C_CSIZE(tty)) {
|
||||
|
@ -386,22 +412,25 @@ static void ch341_set_termios(struct tty_struct *tty,
|
|||
ctrl |= CH341_LCR_STOP_BITS_2;
|
||||
|
||||
if (baud_rate) {
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
r = ch341_init_set_baudrate(port->serial->dev, priv, ctrl);
|
||||
priv->baud_rate = baud_rate;
|
||||
|
||||
r = ch341_set_baudrate_lcr(port->serial->dev, priv, ctrl);
|
||||
if (r < 0 && old_termios) {
|
||||
priv->baud_rate = tty_termios_baud_rate(old_termios);
|
||||
tty_termios_copy_hw(&tty->termios, old_termios);
|
||||
} else if (r == 0) {
|
||||
priv->lcr = ctrl;
|
||||
}
|
||||
} else {
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
ch341_set_handshake(port->serial->dev, priv->line_control);
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (C_BAUD(tty) == B0)
|
||||
priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS);
|
||||
else if (old_termios && (old_termios->c_cflag & CBAUD) == B0)
|
||||
priv->line_control |= (CH341_BIT_DTR | CH341_BIT_RTS);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
ch341_set_handshake(port->serial->dev, priv->line_control);
|
||||
}
|
||||
|
||||
static void ch341_break_ctl(struct tty_struct *tty, int break_state)
|
||||
|
@ -576,14 +605,23 @@ static int ch341_tiocmget(struct tty_struct *tty)
|
|||
|
||||
static int ch341_reset_resume(struct usb_serial *serial)
|
||||
{
|
||||
struct ch341_private *priv;
|
||||
|
||||
priv = usb_get_serial_port_data(serial->port[0]);
|
||||
struct usb_serial_port *port = serial->port[0];
|
||||
struct ch341_private *priv = usb_get_serial_port_data(port);
|
||||
int ret;
|
||||
|
||||
/* reconfigure ch341 serial port after bus-reset */
|
||||
ch341_configure(serial->dev, priv);
|
||||
|
||||
return 0;
|
||||
if (tty_port_initialized(&port->port)) {
|
||||
ret = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
|
||||
if (ret) {
|
||||
dev_err(&port->dev, "failed to submit interrupt urb: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return usb_serial_generic_resume(serial);
|
||||
}
|
||||
|
||||
static struct usb_serial_driver ch341_device = {
|
||||
|
|
|
@ -192,10 +192,11 @@ static int klsi_105_get_line_state(struct usb_serial_port *port,
|
|||
status_buf, KLSI_STATUSBUF_LEN,
|
||||
10000
|
||||
);
|
||||
if (rc < 0)
|
||||
dev_err(&port->dev, "Reading line status failed (error = %d)\n",
|
||||
rc);
|
||||
else {
|
||||
if (rc != KLSI_STATUSBUF_LEN) {
|
||||
dev_err(&port->dev, "reading line status failed: %d\n", rc);
|
||||
if (rc >= 0)
|
||||
rc = -EIO;
|
||||
} else {
|
||||
status = get_unaligned_le16(status_buf);
|
||||
|
||||
dev_info(&port->serial->dev->dev, "read status %x %x\n",
|
||||
|
|
Loading…
Reference in New Issue