Skip to content

Commit

Permalink
Merge tag 'usb-serial-4.10-rc4' of git://git.kernel.org/pub/scm/linux…
Browse files Browse the repository at this point in the history
…/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>
  • Loading branch information
Greg Kroah-Hartman committed Jan 12, 2017
2 parents d6169d0 + 2d5a9c7 commit 97f9c5f
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 39 deletions.
108 changes: 73 additions & 35 deletions drivers/usb/serial/ch341.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
}
Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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;

Expand All @@ -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)
Expand Down Expand Up @@ -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);
Expand All @@ -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;

return 0;

err_kill_interrupt_urb:
usb_kill_urb(port->interrupt_in_urb);

out: return r;
return r;
}

/* Old_termios contains the original termios settings and
Expand All @@ -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)) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 = {
Expand Down
9 changes: 5 additions & 4 deletions drivers/usb/serial/kl5kusb105.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit 97f9c5f

Please sign in to comment.