From 56e6d90797b4d8300e617a4f7b6ac216cf248041 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Tue, 3 Jan 2017 21:01:10 +0000 Subject: [PATCH 01/47] USB: serial: cypress_m8: remove unused variable The variable havedata was only being set but never used afterwards. Signed-off-by: Sudip Mukherjee Signed-off-by: Johan Hovold --- drivers/usb/serial/cypress_m8.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index bbeeb2bd55a8..90110de715e0 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -1069,7 +1069,6 @@ static void cypress_read_int_callback(struct urb *urb) unsigned char *data = urb->transfer_buffer; unsigned long flags; char tty_flag = TTY_NORMAL; - int havedata = 0; int bytes = 0; int result; int i = 0; @@ -1118,16 +1117,12 @@ static void cypress_read_int_callback(struct urb *urb) priv->current_status = data[0] & 0xF8; bytes = data[1] + 2; i = 2; - if (bytes > 2) - havedata = 1; break; case packet_format_2: /* This is for the CY7C63743... */ priv->current_status = data[0] & 0xF8; bytes = (data[0] & 0x07) + 1; i = 1; - if (bytes > 1) - havedata = 1; break; } spin_unlock_irqrestore(&priv->lock, flags); From a0467a967f347842b30739aae636c44980265265 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 6 Jan 2017 19:15:17 +0100 Subject: [PATCH 02/47] USB: serial: ch341: fix modem-status handling The modem-status register was read as part of device configuration at port_probe and then again at open (and reset-resume). During open (and reset-resume) the MSR was read before submitting the interrupt URB, something which could lead to an MSR-change going unnoticed when it races with open (reset-resume). Fix this by dropping the redundant reconfiguration of the port at every open, and only read the MSR after the interrupt URB has been submitted. Fixes: 664d5df92e88 ("USB: usb-serial ch341: support for DTR/RTS/CTS") Signed-off-by: Johan Hovold --- drivers/usb/serial/ch341.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 95aa5233726c..86692d2f8523 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -248,21 +248,11 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) if (r < 0) goto out; - /* expect 0xff 0xee */ - r = ch341_get_status(dev, priv); - if (r < 0) - goto out; - r = ch341_set_baudrate_lcr(dev, priv, priv->lcr); if (r < 0) goto out; r = ch341_set_handshake(dev, priv->line_control); - if (r < 0) - goto out; - - /* expect 0x9f 0xee */ - r = ch341_get_status(dev, priv); out: kfree(buffer); return r; @@ -334,14 +324,9 @@ static void ch341_close(struct usb_serial_port *port) /* open this device, set default parameters */ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; struct ch341_private *priv = usb_get_serial_port_data(port); int r; - r = ch341_configure(serial->dev, priv); - if (r) - return r; - if (tty) ch341_set_termios(tty, port, NULL); @@ -353,6 +338,12 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port) return r; } + r = ch341_get_status(port->serial->dev, priv); + if (r < 0) { + dev_err(&port->dev, "failed to read modem status: %d\n", r); + goto err_kill_interrupt_urb; + } + r = usb_serial_generic_open(tty, port); if (r) goto err_kill_interrupt_urb; @@ -619,6 +610,12 @@ static int ch341_reset_resume(struct usb_serial *serial) ret); return ret; } + + ret = ch341_get_status(port->serial->dev, priv); + if (ret < 0) { + dev_err(&port->dev, "failed to read modem status: %d\n", + ret); + } } return usb_serial_generic_resume(serial); From 91e0efcd4c23d98479a2f44753a53bc3456bba97 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 6 Jan 2017 19:15:19 +0100 Subject: [PATCH 03/47] USB: serial: ch341: clean up control debug messages Clean up the control-transfer debug messages by dropping redundant information and unnecessary casts. Signed-off-by: Johan Hovold --- drivers/usb/serial/ch341.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 86692d2f8523..84c10b63732b 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -107,8 +107,8 @@ static int ch341_control_out(struct usb_device *dev, u8 request, { int r; - dev_dbg(&dev->dev, "ch341_control_out(%02x,%02x,%04x,%04x)\n", - USB_DIR_OUT|0x40, (int)request, (int)value, (int)index); + dev_dbg(&dev->dev, "%s - (%02x,%04x,%04x)\n", __func__, + request, value, index); r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, @@ -125,9 +125,8 @@ static int ch341_control_in(struct usb_device *dev, { int r; - dev_dbg(&dev->dev, "ch341_control_in(%02x,%02x,%04x,%04x,%p,%u)\n", - USB_DIR_IN|0x40, (int)request, (int)value, (int)index, buf, - (int)bufsize); + dev_dbg(&dev->dev, "%s - (%02x,%04x,%04x,%u)\n", __func__, + request, value, index, bufsize); r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, From beea33d4f94bdd816172f5f554cbabd12c16d909 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 6 Jan 2017 19:15:20 +0100 Subject: [PATCH 04/47] USB: serial: ch341: rename shadow modem-control register Rename the shadow modem-control register currently named "line_control" to the less confusing "mcr". Signed-off-by: Johan Hovold --- drivers/usb/serial/ch341.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 84c10b63732b..cab7e2ca4e47 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -93,7 +93,7 @@ MODULE_DEVICE_TABLE(usb, id_table); struct ch341_private { spinlock_t lock; /* access lock */ unsigned baud_rate; /* set baud rate */ - u8 line_control; /* set line control value RTS/DTR */ + u8 mcr; u8 line_status; /* active status of modem control inputs */ u8 lcr; }; @@ -251,7 +251,7 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) if (r < 0) goto out; - r = ch341_set_handshake(dev, priv->line_control); + r = ch341_set_handshake(dev, priv->mcr); out: kfree(buffer); return r; @@ -306,11 +306,11 @@ static void ch341_dtr_rts(struct usb_serial_port *port, int on) /* drop DTR and RTS */ spin_lock_irqsave(&priv->lock, flags); if (on) - priv->line_control |= CH341_BIT_RTS | CH341_BIT_DTR; + priv->mcr |= CH341_BIT_RTS | CH341_BIT_DTR; else - priv->line_control &= ~(CH341_BIT_RTS | CH341_BIT_DTR); + priv->mcr &= ~(CH341_BIT_RTS | CH341_BIT_DTR); spin_unlock_irqrestore(&priv->lock, flags); - ch341_set_handshake(port->serial->dev, priv->line_control); + ch341_set_handshake(port->serial->dev, priv->mcr); } static void ch341_close(struct usb_serial_port *port) @@ -415,12 +415,12 @@ static void ch341_set_termios(struct tty_struct *tty, spin_lock_irqsave(&priv->lock, flags); if (C_BAUD(tty) == B0) - priv->line_control &= ~(CH341_BIT_DTR | CH341_BIT_RTS); + priv->mcr &= ~(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); + priv->mcr |= (CH341_BIT_DTR | CH341_BIT_RTS); spin_unlock_irqrestore(&priv->lock, flags); - ch341_set_handshake(port->serial->dev, priv->line_control); + ch341_set_handshake(port->serial->dev, priv->mcr); } static void ch341_break_ctl(struct tty_struct *tty, int break_state) @@ -476,14 +476,14 @@ static int ch341_tiocmset(struct tty_struct *tty, spin_lock_irqsave(&priv->lock, flags); if (set & TIOCM_RTS) - priv->line_control |= CH341_BIT_RTS; + priv->mcr |= CH341_BIT_RTS; if (set & TIOCM_DTR) - priv->line_control |= CH341_BIT_DTR; + priv->mcr |= CH341_BIT_DTR; if (clear & TIOCM_RTS) - priv->line_control &= ~CH341_BIT_RTS; + priv->mcr &= ~CH341_BIT_RTS; if (clear & TIOCM_DTR) - priv->line_control &= ~CH341_BIT_DTR; - control = priv->line_control; + priv->mcr &= ~CH341_BIT_DTR; + control = priv->mcr; spin_unlock_irqrestore(&priv->lock, flags); return ch341_set_handshake(port->serial->dev, control); @@ -577,7 +577,7 @@ static int ch341_tiocmget(struct tty_struct *tty) unsigned int result; spin_lock_irqsave(&priv->lock, flags); - mcr = priv->line_control; + mcr = priv->mcr; status = priv->line_status; spin_unlock_irqrestore(&priv->lock, flags); From e8024460354cc1b21e29c879338fd5c5021c8e7d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 6 Jan 2017 19:15:21 +0100 Subject: [PATCH 05/47] USB: serial: ch341: rename modem-status register Rename the shadow modem-status register currently named "line_status" to the less confusing "msr". Also rename the helper function used to parse the interrupt data. Signed-off-by: Johan Hovold --- drivers/usb/serial/ch341.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index cab7e2ca4e47..2981d1934874 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -94,7 +94,7 @@ struct ch341_private { spinlock_t lock; /* access lock */ unsigned baud_rate; /* set baud rate */ u8 mcr; - u8 line_status; /* active status of modem control inputs */ + u8 msr; u8 lcr; }; @@ -209,7 +209,7 @@ static int ch341_get_status(struct usb_device *dev, struct ch341_private *priv) goto out; spin_lock_irqsave(&priv->lock, flags); - priv->line_status = (~(*buffer)) & CH341_BITS_MODEM_STAT; + priv->msr = (~(*buffer)) & CH341_BITS_MODEM_STAT; spin_unlock_irqrestore(&priv->lock, flags); out: kfree(buffer); @@ -293,7 +293,7 @@ static int ch341_port_remove(struct usb_serial_port *port) static int ch341_carrier_raised(struct usb_serial_port *port) { struct ch341_private *priv = usb_get_serial_port_data(port); - if (priv->line_status & CH341_BIT_DCD) + if (priv->msr & CH341_BIT_DCD) return 1; return 0; } @@ -489,7 +489,7 @@ static int ch341_tiocmset(struct tty_struct *tty, return ch341_set_handshake(port->serial->dev, control); } -static void ch341_update_line_status(struct usb_serial_port *port, +static void ch341_update_status(struct usb_serial_port *port, unsigned char *data, size_t len) { struct ch341_private *priv = usb_get_serial_port_data(port); @@ -504,8 +504,8 @@ static void ch341_update_line_status(struct usb_serial_port *port, status = ~data[2] & CH341_BITS_MODEM_STAT; spin_lock_irqsave(&priv->lock, flags); - delta = status ^ priv->line_status; - priv->line_status = status; + delta = status ^ priv->msr; + priv->msr = status; spin_unlock_irqrestore(&priv->lock, flags); if (data[1] & CH341_MULT_STAT) @@ -558,7 +558,7 @@ static void ch341_read_int_callback(struct urb *urb) } usb_serial_debug_data(&port->dev, __func__, len, data); - ch341_update_line_status(port, data, len); + ch341_update_status(port, data, len); exit: status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { @@ -578,7 +578,7 @@ static int ch341_tiocmget(struct tty_struct *tty) spin_lock_irqsave(&priv->lock, flags); mcr = priv->mcr; - status = priv->line_status; + status = priv->msr; spin_unlock_irqrestore(&priv->lock, flags); result = ((mcr & CH341_BIT_DTR) ? TIOCM_DTR : 0) From 448b6dc5a964c3eca96260138684de10a0e3d9b9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 6 Jan 2017 19:15:22 +0100 Subject: [PATCH 06/47] USB: serial: ch341: rename LCR variable in set_termios Rename the line-control-register variable in set_termios to "lcr" and use u8 type to match the shadow register. Signed-off-by: Johan Hovold --- drivers/usb/serial/ch341.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 2981d1934874..c51ec9802856 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -364,7 +364,7 @@ static void ch341_set_termios(struct tty_struct *tty, struct ch341_private *priv = usb_get_serial_port_data(port); unsigned baud_rate; unsigned long flags; - unsigned char ctrl; + u8 lcr; int r; /* redundant changes may cause the chip to lose bytes */ @@ -373,43 +373,43 @@ static void ch341_set_termios(struct tty_struct *tty, baud_rate = tty_get_baud_rate(tty); - ctrl = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX; + lcr = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX; switch (C_CSIZE(tty)) { case CS5: - ctrl |= CH341_LCR_CS5; + lcr |= CH341_LCR_CS5; break; case CS6: - ctrl |= CH341_LCR_CS6; + lcr |= CH341_LCR_CS6; break; case CS7: - ctrl |= CH341_LCR_CS7; + lcr |= CH341_LCR_CS7; break; case CS8: - ctrl |= CH341_LCR_CS8; + lcr |= CH341_LCR_CS8; break; } if (C_PARENB(tty)) { - ctrl |= CH341_LCR_ENABLE_PAR; + lcr |= CH341_LCR_ENABLE_PAR; if (C_PARODD(tty) == 0) - ctrl |= CH341_LCR_PAR_EVEN; + lcr |= CH341_LCR_PAR_EVEN; if (C_CMSPAR(tty)) - ctrl |= CH341_LCR_MARK_SPACE; + lcr |= CH341_LCR_MARK_SPACE; } if (C_CSTOPB(tty)) - ctrl |= CH341_LCR_STOP_BITS_2; + lcr |= CH341_LCR_STOP_BITS_2; if (baud_rate) { priv->baud_rate = baud_rate; - r = ch341_set_baudrate_lcr(port->serial->dev, priv, ctrl); + r = ch341_set_baudrate_lcr(port->serial->dev, priv, lcr); 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; + priv->lcr = lcr; } } From 7c61b0d5e82bfe29b8dda55745afbf65b6ccc901 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 6 Jan 2017 19:15:23 +0100 Subject: [PATCH 07/47] USB: serial: ch341: change initial line-control settings Some CH340 devices appear unable to change the initial LCR settings, so set a sane 8N1 default during probe to enable basic support for such devices. Also drop a redundant LCR read during device initialisation. Signed-off-by: Johan Hovold --- drivers/usb/serial/ch341.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index c51ec9802856..351745aec0e1 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -238,15 +238,6 @@ static int ch341_configure(struct usb_device *dev, struct ch341_private *priv) if (r < 0) goto out; - /* expect two bytes 0x56 0x00 */ - r = ch341_control_in(dev, CH341_REQ_READ_REG, 0x2518, 0, buffer, size); - if (r < 0) - goto out; - - r = ch341_control_out(dev, CH341_REQ_WRITE_REG, 0x2518, 0x0050); - if (r < 0) - goto out; - r = ch341_set_baudrate_lcr(dev, priv, priv->lcr); if (r < 0) goto out; @@ -268,6 +259,11 @@ static int ch341_port_probe(struct usb_serial_port *port) spin_lock_init(&priv->lock); priv->baud_rate = DEFAULT_BAUD_RATE; + /* + * Some CH340 devices appear unable to change the initial LCR + * settings, so set a sane 8N1 default. + */ + priv->lcr = CH341_LCR_ENABLE_RX | CH341_LCR_ENABLE_TX | CH341_LCR_CS8; r = ch341_configure(port->serial->dev, priv); if (r < 0) From 0546579330f7280f9ab3aa19fae96142decd6926 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Jan 2017 12:05:38 +0100 Subject: [PATCH 08/47] USB: serial: kl5kusb105: make logging less verbose Replace a couple of dev_info with dev_dbg and remove another. Also use the port device for logging, and include a radix prefix when logging the baudrate. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/kl5kusb105.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 6cb45757818f..e6e083dace4b 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -143,10 +143,12 @@ static int klsi_105_chg_port_settings(struct usb_serial_port *port, if (rc < 0) dev_err(&port->dev, "Change port settings failed (error = %d)\n", rc); - dev_info(&port->serial->dev->dev, - "%d byte block, baudrate %x, databits %d, u1 %d, u2 %d\n", - settings->pktlen, settings->baudrate, settings->databits, - settings->unknown1, settings->unknown2); + + dev_dbg(&port->dev, + "pktlen %u, baudrate 0x%02x, databits %u, u1 %u, u2 %u\n", + settings->pktlen, settings->baudrate, settings->databits, + settings->unknown1, settings->unknown2); + return rc; } @@ -175,8 +177,6 @@ static int klsi_105_get_line_state(struct usb_serial_port *port, u8 *status_buf; __u16 status; - dev_info(&port->serial->dev->dev, "sending SIO Poll request\n"); - status_buf = kmalloc(KLSI_STATUSBUF_LEN, GFP_KERNEL); if (!status_buf) return -ENOMEM; @@ -199,8 +199,8 @@ static int klsi_105_get_line_state(struct usb_serial_port *port, } else { status = get_unaligned_le16(status_buf); - dev_info(&port->serial->dev->dev, "read status %x %x\n", - status_buf[0], status_buf[1]); + dev_dbg(&port->dev, "read status %02x %02x\n", + status_buf[0], status_buf[1]); *line_state_p = klsi_105_status2linestate(status); } From 2d11f28207c2f9c9aac56ac2f8440f7a99a40017 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Jan 2017 12:05:39 +0100 Subject: [PATCH 09/47] USB: serial: kl5kusb105: remove dead code Remove dead and broken code that only served as a reminder to one day implement modem control. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/kl5kusb105.c | 76 ++++----------------------------- 1 file changed, 8 insertions(+), 68 deletions(-) diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index e6e083dace4b..7550cf6c5f7c 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -89,7 +89,6 @@ static struct usb_serial_driver kl5kusb105d_device = { .open = klsi_105_open, .close = klsi_105_close, .set_termios = klsi_105_set_termios, - /*.break_ctl = klsi_105_break_ctl,*/ .tiocmget = klsi_105_tiocmget, .port_probe = klsi_105_port_probe, .port_remove = klsi_105_port_remove, @@ -438,19 +437,6 @@ static void klsi_105_set_termios(struct tty_struct *tty, */ baud = tty_get_baud_rate(tty); - if ((cflag & CBAUD) != (old_cflag & CBAUD)) { - /* reassert DTR and (maybe) RTS on transition from B0 */ - if ((old_cflag & CBAUD) == B0) { - dev_dbg(dev, "%s: baud was B0\n", __func__); -#if 0 - priv->control_state |= TIOCM_DTR; - /* don't set RTS if using hardware flow control */ - if (!(old_cflag & CRTSCTS)) - priv->control_state |= TIOCM_RTS; - mct_u232_set_modem_ctrl(serial, priv->control_state); -#endif - } - } switch (baud) { case 0: /* handled below */ break; @@ -484,17 +470,14 @@ static void klsi_105_set_termios(struct tty_struct *tty, baud = 9600; break; } - if ((cflag & CBAUD) == B0) { - dev_dbg(dev, "%s: baud is B0\n", __func__); - /* Drop RTS and DTR */ - /* maybe this should be simulated by sending read - * disable and read enable messages? - */ -#if 0 - priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); - mct_u232_set_modem_ctrl(serial, priv->control_state); -#endif - } + + /* + * FIXME: implement B0 handling + * + * Maybe this should be simulated by sending read disable and read + * enable messages? + */ + tty_encode_baud_rate(tty, baud, baud); if ((cflag & CSIZE) != (old_cflag & CSIZE)) { @@ -528,22 +511,6 @@ static void klsi_105_set_termios(struct tty_struct *tty, || (cflag & CSTOPB) != (old_cflag & CSTOPB)) { /* Not currently supported */ tty->termios.c_cflag &= ~(PARENB|PARODD|CSTOPB); -#if 0 - priv->last_lcr = 0; - - /* set the parity */ - if (cflag & PARENB) - priv->last_lcr |= (cflag & PARODD) ? - MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN; - else - priv->last_lcr |= MCT_U232_PARITY_NONE; - - /* set the number of stop bits */ - priv->last_lcr |= (cflag & CSTOPB) ? - MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1; - - mct_u232_set_line_ctrl(serial, priv->last_lcr); -#endif } /* * Set flow control: well, I do not really now how to handle DTR/RTS. @@ -554,14 +521,6 @@ static void klsi_105_set_termios(struct tty_struct *tty, || (cflag & CRTSCTS) != (old_cflag & CRTSCTS)) { /* Not currently supported */ tty->termios.c_cflag &= ~CRTSCTS; - /* Drop DTR/RTS if no flow control otherwise assert */ -#if 0 - if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS)) - priv->control_state |= TIOCM_DTR | TIOCM_RTS; - else - priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); - mct_u232_set_modem_ctrl(serial, priv->control_state); -#endif } memcpy(cfg, &priv->cfg, sizeof(*cfg)); spin_unlock_irqrestore(&priv->lock, flags); @@ -572,25 +531,6 @@ err: kfree(cfg); } -#if 0 -static void mct_u232_break_ctl(struct tty_struct *tty, int break_state) -{ - struct usb_serial_port *port = tty->driver_data; - struct usb_serial *serial = port->serial; - struct mct_u232_private *priv = - (struct mct_u232_private *)port->private; - unsigned char lcr = priv->last_lcr; - - dev_dbg(&port->dev, "%s - state=%d\n", __func__, break_state); - - /* LOCKING */ - if (break_state) - lcr |= MCT_U232_SET_BREAK; - - mct_u232_set_line_ctrl(serial, lcr); -} -#endif - static int klsi_105_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; From c2a24bb1e4b9e94cce1270a332c659240e1426bc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Jan 2017 12:05:40 +0100 Subject: [PATCH 10/47] USB: serial: kl5kusb105: clean up struct definition Drop redundant packed attribute from the port-settings struct which is already 1-byte aligned. Also replace __u8 with u8 for the field types as this is not a structure we share with user space. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/kl5kusb105.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 7550cf6c5f7c..28cc8ec1c92c 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -103,12 +103,12 @@ static struct usb_serial_driver * const serial_drivers[] = { }; struct klsi_105_port_settings { - __u8 pktlen; /* always 5, it seems */ - __u8 baudrate; - __u8 databits; - __u8 unknown1; - __u8 unknown2; -} __attribute__ ((packed)); + u8 pktlen; /* always 5, it seems */ + u8 baudrate; + u8 databits; + u8 unknown1; + u8 unknown2; +}; struct klsi_105_private { struct klsi_105_port_settings cfg; From 2c85e0a96126d718faf198ad57624150d4ada5e6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 10 Jan 2017 12:05:41 +0100 Subject: [PATCH 11/47] USB: serial: kl5kusb105: remove unused termios structure Remove unused termios structure from private data that was left by an earlier purge by commit b1cff285ae8d ("usb serial: Eliminate bogus ioctl code"). Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/kl5kusb105.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 28cc8ec1c92c..595415e59d5d 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -112,7 +112,6 @@ struct klsi_105_port_settings { struct klsi_105_private { struct klsi_105_port_settings cfg; - struct ktermios termios; unsigned long line_state; /* modem line settings */ spinlock_t lock; }; @@ -232,8 +231,6 @@ static int klsi_105_port_probe(struct usb_serial_port *port) spin_lock_init(&priv->lock); - /* priv->termios is left uninitialized until port opening */ - usb_set_serial_port_data(port, priv); return 0; @@ -254,7 +251,6 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) struct klsi_105_private *priv = usb_get_serial_port_data(port); int retval = 0; int rc; - int i; unsigned long line_state; struct klsi_105_port_settings *cfg; unsigned long flags; @@ -277,14 +273,7 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) cfg->unknown2 = 1; klsi_105_chg_port_settings(port, cfg); - /* set up termios structure */ spin_lock_irqsave(&priv->lock, flags); - priv->termios.c_iflag = tty->termios.c_iflag; - priv->termios.c_oflag = tty->termios.c_oflag; - priv->termios.c_cflag = tty->termios.c_cflag; - priv->termios.c_lflag = tty->termios.c_lflag; - for (i = 0; i < NCCS; i++) - priv->termios.c_cc[i] = tty->termios.c_cc[i]; priv->cfg.pktlen = cfg->pktlen; priv->cfg.baudrate = cfg->baudrate; priv->cfg.databits = cfg->databits; From 9fef37d7cf170522fb354d6d0ea6de09b9b16678 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:09 +0100 Subject: [PATCH 12/47] USB: serial: ark3116: fix register-accessor error handling The current implementation failed to detect short transfers, something which could lead to bits of the uninitialised heap transfer buffer leaking to user space. Fixes: 149fc791a452 ("USB: ark3116: Setup some basic infrastructure for new ark3116 driver.") Fixes: f4c1e8d597d1 ("USB: ark3116: Make existing functions 16450-aware and add close and release functions.") Cc: stable Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ark3116.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 1532cde8a437..7812052dc700 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -99,10 +99,17 @@ static int ark3116_read_reg(struct usb_serial *serial, usb_rcvctrlpipe(serial->dev, 0), 0xfe, 0xc0, 0, reg, buf, 1, ARK_TIMEOUT); - if (result < 0) + if (result < 1) { + dev_err(&serial->interface->dev, + "failed to read register %u: %d\n", + reg, result); + if (result >= 0) + result = -EIO; + return result; - else - return buf[0]; + } + + return buf[0]; } static inline int calc_divisor(int bps) From b631433b175f1002a31020e09bbfc2e5caecf290 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:10 +0100 Subject: [PATCH 13/47] USB: serial: ark3116: fix open error handling Fix open error handling which failed to detect errors when reading the MSR and LSR registers, something which could lead to the shadow registers being initialised from errnos. Note that calling the generic close implementation is sufficient in the error paths as the interrupt urb has not yet been submitted and the register updates have not been made. Fixes: f4c1e8d597d1 ("USB: ark3116: Make existing functions 16450-aware and add close and release functions.") Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ark3116.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 7812052dc700..754fc3e41005 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -373,23 +373,29 @@ static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port) dev_dbg(&port->dev, "%s - usb_serial_generic_open failed: %d\n", __func__, result); - goto err_out; + goto err_free; } /* remove any data still left: also clears error state */ ark3116_read_reg(serial, UART_RX, buf); /* read modem status */ - priv->msr = ark3116_read_reg(serial, UART_MSR, buf); + result = ark3116_read_reg(serial, UART_MSR, buf); + if (result < 0) + goto err_close; + priv->msr = *buf; + /* read line status */ - priv->lsr = ark3116_read_reg(serial, UART_LSR, buf); + result = ark3116_read_reg(serial, UART_LSR, buf); + if (result < 0) + goto err_close; + priv->lsr = *buf; result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); if (result) { dev_err(&port->dev, "submit irq_in urb failed %d\n", result); - ark3116_close(port); - goto err_out; + goto err_close; } /* activate interrupts */ @@ -402,8 +408,15 @@ static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port) if (tty) ark3116_set_termios(tty, port, NULL); -err_out: kfree(buf); + + return 0; + +err_close: + usb_serial_generic_close(port); +err_free: + kfree(buf); + return result; } From 427c3a95e3e29e65f59d99aaf320d7506f3eed57 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:11 +0100 Subject: [PATCH 14/47] USB: serial: ftdi_sio: fix modem-status error handling Make sure to detect short responses when fetching the modem status in order to avoid parsing uninitialised buffer data and having bits of it leak to user space. Note that we still allow for short 1-byte responses. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 23d14b98ae2a..6dee20fc7e45 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -2428,8 +2428,12 @@ static int ftdi_get_modem_status(struct usb_serial_port *port, FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE, 0, priv->interface, buf, len, WDR_TIMEOUT); - if (ret < 0) { + + /* NOTE: We allow short responses and handle that below. */ + if (ret < 1) { dev_err(&port->dev, "failed to get modem status: %d\n", ret); + if (ret >= 0) + ret = -EIO; ret = usb_translate_errors(ret); goto out; } From e3e574ad85a208cb179f33720bb5f12b453de33c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:12 +0100 Subject: [PATCH 15/47] USB: serial: ftdi_sio: fix latency-timer error handling Make sure to detect short responses when reading the latency timer to avoid using stale buffer data. Note that no heap data would currently leak through sysfs as ASYNC_LOW_LATENCY is set by default. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 6dee20fc7e45..b064fad8e3ee 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1439,10 +1439,13 @@ static int read_latency_timer(struct usb_serial_port *port) FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE, 0, priv->interface, buf, 1, WDR_TIMEOUT); - if (rv < 0) + if (rv < 1) { dev_err(&port->dev, "Unable to read latency timer: %i\n", rv); - else + if (rv >= 0) + rv = -EIO; + } else { priv->latency = buf[0]; + } kfree(buf); From e4457d9798adb96272468e93da663de9bd0a4198 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:13 +0100 Subject: [PATCH 16/47] USB: serial: io_edgeport: fix epic-descriptor handling Use a dedicated buffer for the DMA transfer and make sure to detect short transfers to avoid parsing a corrupt descriptor. Fixes: 6e8cf7751f9f ("USB: add EPIC support to the io_edgeport driver") Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/io_edgeport.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index d50e5773483f..993a36a3e557 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -492,20 +492,24 @@ static int get_epic_descriptor(struct edgeport_serial *ep) int result; struct usb_serial *serial = ep->serial; struct edgeport_product_info *product_info = &ep->product_info; - struct edge_compatibility_descriptor *epic = &ep->epic_descriptor; + struct edge_compatibility_descriptor *epic; struct edge_compatibility_bits *bits; struct device *dev = &serial->dev->dev; ep->is_epic = 0; + + epic = kmalloc(sizeof(*epic), GFP_KERNEL); + if (!epic) + return -ENOMEM; + result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), USB_REQUEST_ION_GET_EPIC_DESC, 0xC0, 0x00, 0x00, - &ep->epic_descriptor, - sizeof(struct edge_compatibility_descriptor), + epic, sizeof(*epic), 300); - - if (result > 0) { + if (result == sizeof(*epic)) { ep->is_epic = 1; + memcpy(&ep->epic_descriptor, epic, sizeof(*epic)); memset(product_info, 0, sizeof(struct edgeport_product_info)); product_info->NumPorts = epic->NumPorts; @@ -534,8 +538,16 @@ static int get_epic_descriptor(struct edgeport_serial *ep) dev_dbg(dev, " IOSPWriteLCR : %s\n", bits->IOSPWriteLCR ? "TRUE": "FALSE"); dev_dbg(dev, " IOSPSetBaudRate : %s\n", bits->IOSPSetBaudRate ? "TRUE": "FALSE"); dev_dbg(dev, " TrueEdgeport : %s\n", bits->TrueEdgeport ? "TRUE": "FALSE"); + + result = 0; + } else if (result >= 0) { + dev_warn(&serial->interface->dev, "short epic descriptor received: %d\n", + result); + result = -EIO; } + kfree(epic); + return result; } @@ -2779,7 +2791,7 @@ static int edge_startup(struct usb_serial *serial) dev_info(&serial->dev->dev, "%s detected\n", edge_serial->name); /* Read the epic descriptor */ - if (get_epic_descriptor(edge_serial) <= 0) { + if (get_epic_descriptor(edge_serial) < 0) { /* memcpy descriptor to Supports structures */ memcpy(&edge_serial->epic_descriptor.Supports, descriptor, sizeof(struct edge_compatibility_bits)); From 3c0e25d883d06a1fbd1ad35257e8abaa57befb37 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:14 +0100 Subject: [PATCH 17/47] USB: serial: io_edgeport: fix descriptor error handling Make sure to detect short control-message transfers and log an error when reading incomplete manufacturer and boot descriptors. Note that the default all-zero descriptors will now be used after a short transfer is detected instead of partially initialised ones. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/io_edgeport.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 993a36a3e557..8ab5f5b49ef3 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -2102,8 +2102,7 @@ static int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr, * rom_read * reads a number of bytes from the Edgeport device starting at the given * address. - * If successful returns the number of bytes read, otherwise it returns - * a negative error number of the problem. + * Returns zero on success or a negative error number. ****************************************************************************/ static int rom_read(struct usb_serial *serial, __u16 extAddr, __u16 addr, __u16 length, __u8 *data) @@ -2128,12 +2127,17 @@ static int rom_read(struct usb_serial *serial, __u16 extAddr, USB_REQUEST_ION_READ_ROM, 0xC0, addr, extAddr, transfer_buffer, current_length, 300); - if (result < 0) + if (result < current_length) { + if (result >= 0) + result = -EIO; break; + } memcpy(data, transfer_buffer, current_length); length -= current_length; addr += current_length; data += current_length; + + result = 0; } kfree(transfer_buffer); @@ -2587,9 +2591,10 @@ static void get_manufacturing_desc(struct edgeport_serial *edge_serial) EDGE_MANUF_DESC_LEN, (__u8 *)(&edge_serial->manuf_descriptor)); - if (response < 1) - dev_err(dev, "error in getting manufacturer descriptor\n"); - else { + if (response < 0) { + dev_err(dev, "error in getting manufacturer descriptor: %d\n", + response); + } else { char string[30]; dev_dbg(dev, "**Manufacturer Descriptor\n"); dev_dbg(dev, " RomSize: %dK\n", @@ -2646,9 +2651,10 @@ static void get_boot_desc(struct edgeport_serial *edge_serial) EDGE_BOOT_DESC_LEN, (__u8 *)(&edge_serial->boot_descriptor)); - if (response < 1) - dev_err(dev, "error in getting boot descriptor\n"); - else { + if (response < 0) { + dev_err(dev, "error in getting boot descriptor: %d\n", + response); + } else { dev_dbg(dev, "**Boot Descriptor:\n"); dev_dbg(dev, " BootCodeLength: %d\n", le16_to_cpu(edge_serial->boot_descriptor.BootCodeLength)); From 750acdd781cf7e97c8c60d2ff5053dd4b12bbc84 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:15 +0100 Subject: [PATCH 18/47] USB: serial: iuu_phoenix: remove unused buffer from open Remove code that allocated but never used a buffer during open. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/iuu_phoenix.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index d57fb5199218..030390f37b0a 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -976,7 +976,6 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct device *dev = &port->dev; - u8 *buf; int result; int baud; u32 actual; @@ -991,20 +990,8 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); - buf = kmalloc(10, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - priv->poll = 0; - /* initialize writebuf */ -#define FISH(a, b, c, d) do { \ - result = usb_control_msg(port->serial->dev, \ - usb_rcvctrlpipe(port->serial->dev, 0), \ - b, a, c, d, buf, 1, 1000); \ - dev_dbg(dev, "0x%x:0x%x:0x%x:0x%x %d - %x\n", a, b, c, d, result, \ - buf[0]); } while (0); - #define SOUP(a, b, c, d) do { \ result = usb_control_msg(port->serial->dev, \ usb_sndctrlpipe(port->serial->dev, 0), \ @@ -1017,7 +1004,7 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) /* sprintf(buf ,"%c%c%c%c",0x03,0x02,0x02,0x0); */ SOUP(0x03, 0x02, 0x02, 0x0); - kfree(buf); + iuu_led(port, 0xF000, 0xF000, 0, 0xFF); iuu_uart_on(port); if (boost < 100) From 36356a669eddb32917fc4b5c2b9b8bf80ede69de Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:16 +0100 Subject: [PATCH 19/47] USB: serial: mct_u232: fix modem-status error handling Make sure to detect short control-message transfers so that errors are logged when reading the modem status at open. Note that while this also avoids initialising the modem status using uninitialised heap data, these bits could not leak to user space as they are currently not used. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mct_u232.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 885655315de1..edbc81f205c2 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -322,8 +322,12 @@ static int mct_u232_get_modem_stat(struct usb_serial_port *port, MCT_U232_GET_REQUEST_TYPE, 0, 0, buf, MCT_U232_GET_MODEM_STAT_SIZE, WDR_TIMEOUT); - if (rc < 0) { + if (rc < MCT_U232_GET_MODEM_STAT_SIZE) { dev_err(&port->dev, "Get MODEM STATus failed (error = %d)\n", rc); + + if (rc >= 0) + rc = -EIO; + *msr = 0; } else { *msr = buf[0]; From 0d130367abf582e7cbf60075c2a7ab53817b1d14 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:17 +0100 Subject: [PATCH 20/47] USB: serial: mos7720: fix control-message error handling Make sure to log an error on short transfers when reading a device register. Also clear the provided buffer (which if often an uninitialised automatic variable) on errors as the driver currently does not bother to check for errors. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7720.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 91bc170b408a..acf431e02699 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -234,11 +234,16 @@ static int read_mos_reg(struct usb_serial *serial, unsigned int serial_portnum, status = usb_control_msg(usbdev, pipe, request, requesttype, value, index, buf, 1, MOS_WDR_TIMEOUT); - if (status == 1) + if (status == 1) { *data = *buf; - else if (status < 0) + } else { dev_err(&usbdev->dev, "mos7720: usb_control_msg() failed: %d\n", status); + if (status >= 0) + status = -EIO; + *data = 0; + } + kfree(buf); return status; From cd8db057e93ddaacbec025b567490555d2bca280 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:18 +0100 Subject: [PATCH 21/47] USB: serial: mos7840: fix control-message error handling Make sure to detect short transfers when reading a device register. The modem-status handling had sufficient error checks in place, but move handling of short transfers into the register accessor function itself for consistency. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index ea27fb23967a..155006acf918 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -284,9 +284,15 @@ static int mos7840_get_reg_sync(struct usb_serial_port *port, __u16 reg, ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, MCS_RD_RTYPE, 0, reg, buf, VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT); + if (ret < VENDOR_READ_LENGTH) { + if (ret >= 0) + ret = -EIO; + goto out; + } + *val = buf[0]; dev_dbg(&port->dev, "%s offset is %x, return val %x\n", __func__, reg, *val); - +out: kfree(buf); return ret; } @@ -352,8 +358,13 @@ static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg, ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, MCS_RD_RTYPE, Wval, reg, buf, VENDOR_READ_LENGTH, MOS_WDR_TIMEOUT); + if (ret < VENDOR_READ_LENGTH) { + if (ret >= 0) + ret = -EIO; + goto out; + } *val = buf[0]; - +out: kfree(buf); return ret; } @@ -1479,10 +1490,10 @@ static int mos7840_tiocmget(struct tty_struct *tty) return -ENODEV; status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr); - if (status != 1) + if (status < 0) return -EIO; status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr); - if (status != 1) + if (status < 0) return -EIO; result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) | ((mcr & MCR_RTS) ? TIOCM_RTS : 0) From b5fda434b13de8328c57421e31ac91d401c93b97 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:19 +0100 Subject: [PATCH 22/47] USB: serial: pl2303: fix line-setting error handling Make sure to return an error on zero-length transfers when retrieving the line settings even if the driver currently ignores the return value. Also remove a redundant check for short transfer when setting the line settings. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/pl2303.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 46fca6b75846..3ba713570c6b 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -449,7 +449,7 @@ static int pl2303_get_line_request(struct usb_serial_port *port, if (ret != 7) { dev_err(&port->dev, "%s - failed: %d\n", __func__, ret); - if (ret > 0) + if (ret >= 0) ret = -EIO; return ret; @@ -469,12 +469,8 @@ static int pl2303_set_line_request(struct usb_serial_port *port, ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE, 0, 0, buf, 7, 100); - if (ret != 7) { + if (ret < 0) { dev_err(&port->dev, "%s - failed: %d\n", __func__, ret); - - if (ret > 0) - ret = -EIO; - return ret; } From 8c34cb8ddfe808d557b51da983ff10c02793beb2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:20 +0100 Subject: [PATCH 23/47] USB: serial: quatech2: fix control-message error handling Make sure to detect short control-message transfers when fetching modem and line state in open and when retrieving registers. This specifically makes sure that an errno is returned to user space on errors in TIOCMGET instead of a zero bitmask. Also drop the unused getdevice function which also lacked appropriate error handling. Fixes: f7a33e608d9a ("USB: serial: add quatech2 usb to serial driver") Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/quatech2.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 5709cc93b083..cf29128327d3 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -188,22 +188,22 @@ static inline int qt2_setdevice(struct usb_device *dev, u8 *data) } -static inline int qt2_getdevice(struct usb_device *dev, u8 *data) -{ - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - QT_SET_GET_DEVICE, 0xc0, 0, 0, - data, 3, QT2_USB_TIMEOUT); -} - static inline int qt2_getregister(struct usb_device *dev, u8 uart, u8 reg, u8 *data) { - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - QT_SET_GET_REGISTER, 0xc0, reg, - uart, data, sizeof(*data), QT2_USB_TIMEOUT); + int ret; + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + QT_SET_GET_REGISTER, 0xc0, reg, + uart, data, sizeof(*data), QT2_USB_TIMEOUT); + if (ret < sizeof(*data)) { + if (ret >= 0) + ret = -EIO; + } + + return ret; } static inline int qt2_setregister(struct usb_device *dev, @@ -372,9 +372,11 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port) 0xc0, 0, device_port, data, 2, QT2_USB_TIMEOUT); - if (status < 0) { + if (status < 2) { dev_err(&port->dev, "%s - open port failed %i\n", __func__, status); + if (status >= 0) + status = -EIO; kfree(data); return status; } From 5ed8d41023751bdd3546f2fe4118304357efe8d2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:21 +0100 Subject: [PATCH 24/47] USB: serial: spcp8x5: fix modem-status handling Make sure to detect short control transfers and return zero on success when retrieving the modem status. This fixes the TIOCMGET implementation which since e1ed212d8593 ("USB: spcp8x5: add proper modem-status support") has returned TIOCM_LE on successful retrieval, and avoids leaking bits from the stack on short transfers. This also fixes the carrier-detect implementation which since the above mentioned commit unconditionally has returned true. Fixes: e1ed212d8593 ("USB: spcp8x5: add proper modem-status support") Cc: stable Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/spcp8x5.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 475e6c31b266..ddfd787c461c 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -232,11 +232,17 @@ static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status) ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_UART_STATUS, GET_UART_STATUS_TYPE, 0, GET_UART_STATUS_MSR, buf, 1, 100); - if (ret < 0) + if (ret < 1) { dev_err(&port->dev, "failed to get modem status: %d\n", ret); + if (ret >= 0) + ret = -EIO; + goto out; + } dev_dbg(&port->dev, "0xc0:0x22:0:6 %d - 0x02%x\n", ret, *buf); *status = *buf; + ret = 0; +out: kfree(buf); return ret; From 1eac5c244f705182d1552a53e2f74e2775ed95d6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:22 +0100 Subject: [PATCH 25/47] USB: serial: ssu100: fix control-message error handling Make sure to detect short control-message transfers rather than continue with zero-initialised data when retrieving modem status and during device initialisation. Fixes: 52af95459939 ("USB: add USB serial ssu100 driver") Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ssu100.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 2a156144c76c..55814538ff1f 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -80,9 +80,17 @@ static inline int ssu100_setdevice(struct usb_device *dev, u8 *data) static inline int ssu100_getdevice(struct usb_device *dev, u8 *data) { - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - QT_SET_GET_DEVICE, 0xc0, 0, 0, - data, 3, 300); + int ret; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + QT_SET_GET_DEVICE, 0xc0, 0, 0, + data, 3, 300); + if (ret < 3) { + if (ret >= 0) + ret = -EIO; + } + + return ret; } static inline int ssu100_getregister(struct usb_device *dev, @@ -90,10 +98,17 @@ static inline int ssu100_getregister(struct usb_device *dev, unsigned short reg, u8 *data) { - return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - QT_SET_GET_REGISTER, 0xc0, reg, - uart, data, sizeof(*data), 300); + int ret; + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + QT_SET_GET_REGISTER, 0xc0, reg, + uart, data, sizeof(*data), 300); + if (ret < sizeof(*data)) { + if (ret >= 0) + ret = -EIO; + } + + return ret; } @@ -289,8 +304,10 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port) QT_OPEN_CLOSE_CHANNEL, QT_TRANSFER_IN, 0x01, 0, data, 2, 300); - if (result < 0) { + if (result < 2) { dev_dbg(&port->dev, "%s - open failed %i\n", __func__, result); + if (result >= 0) + result = -EIO; kfree(data); return result; } From 39712e8bfa8d3aa6ce1e60fc9d62c9b076c17a30 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 12 Jan 2017 14:56:23 +0100 Subject: [PATCH 26/47] USB: serial: ti_usb_3410_5052: fix control-message error handling Make sure to detect and return an error on zero-length control-message transfers when reading from the device. This addresses a potential failure to detect an empty transmit buffer during close. Also remove a redundant check for short transfer when sending a command. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ti_usb_3410_5052.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 64b85b8dedf3..3107bf5d1c96 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -1553,13 +1553,10 @@ static int ti_command_out_sync(struct ti_device *tdev, __u8 command, (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT), value, moduleid, data, size, 1000); - if (status == size) - status = 0; + if (status < 0) + return status; - if (status > 0) - status = -ECOMM; - - return status; + return 0; } @@ -1575,8 +1572,7 @@ static int ti_command_in_sync(struct ti_device *tdev, __u8 command, if (status == size) status = 0; - - if (status > 0) + else if (status >= 0) status = -ECOMM; return status; From 2eee05020a0e7ee7c04422cbacdb07859e45dce6 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 13 Jan 2017 13:21:08 +0100 Subject: [PATCH 27/47] USB: serial: opticon: fix CTS retrieval at open The opticon driver used a control request at open to trigger a CTS status notification to be sent over the bulk-in pipe. When the driver was converted to using the generic read implementation, an inverted test prevented this request from being sent, something which could lead to TIOCMGET reporting an incorrect CTS state. Reported-by: Dan Carpenter Fixes: 7a6ee2b02751 ("USB: opticon: switch to generic read implementation") Cc: stable Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/opticon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 5ded6f524d59..b3c64f557d60 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -142,7 +142,7 @@ static int opticon_open(struct tty_struct *tty, struct usb_serial_port *port) usb_clear_halt(port->serial->dev, port->read_urb->pipe); res = usb_serial_generic_open(tty, port); - if (!res) + if (res) return res; /* Request CTS line state, sometimes during opening the current From c6dce2626606ef16434802989466636bc28c1419 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jan 2017 15:35:20 +0100 Subject: [PATCH 28/47] USB: serial: ftdi_sio: fix extreme low-latency setting Since commit 557aaa7ffab6 ("ft232: support the ASYNC_LOW_LATENCY flag") the FTDI driver has been using a receive latency-timer value of 1 ms instead of the device default of 16 ms. The latency timer is used to periodically empty a non-full receive buffer, but a status header is always sent when the timer expires including when the buffer is empty. This means that a two-byte bulk message is received every millisecond also for an otherwise idle port as long as it is open. Let's restore the pre-2009 behaviour which reduces the rate of the status messages to 1/16th (e.g. interrupt frequency drops from 1 kHz to 62.5 Hz) by not setting ASYNC_LOW_LATENCY by default. Anyone willing to pay the price for the minimum-latency behaviour should set the flag explicitly instead using the TIOCSSERIAL ioctl or a tool such as setserial (e.g. setserial /dev/ttyUSB0 low_latency). Note that since commit 0cbd81a9f6ba ("USB: ftdi_sio: remove tty->low_latency") the ASYNC_LOW_LATENCY flag has no other effects but to set a minimal latency timer. Reported-by: Antoine Aubert Fixes: 557aaa7ffab6 ("ft232: support the ASYNC_LOW_LATENCY flag") Cc: stable@vger.kernel.org # v2.6.31: e3e574ad85a2 Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index b064fad8e3ee..4bd556d9307d 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1805,8 +1805,6 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) mutex_init(&priv->cfg_lock); - priv->flags = ASYNC_LOW_LATENCY; - if (quirk && quirk->port_probe) quirk->port_probe(priv); From 59556608139edda5aeeed5f73e435e8ddbb16516 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jan 2017 18:22:53 +0100 Subject: [PATCH 29/47] USB: serial: ftdi_sio: clean up ioctl handler Clean up the ioctl handler and make sure to pass an unsigned-int rather than serial_struct pointer to the TIOCSERGETLSR helper as this it what the user argument really is. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 4bd556d9307d..e82dbb3d0883 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1534,7 +1534,7 @@ check_and_exit: } static int get_lsr_info(struct usb_serial_port *port, - struct serial_struct __user *retinfo) + unsigned int __user *retinfo) { struct ftdi_private *priv = usb_get_serial_port_data(port); unsigned int result = 0; @@ -2485,20 +2485,15 @@ static int ftdi_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; + void __user *argp = (void __user *)arg; - /* Based on code from acm.c and others */ switch (cmd) { - - case TIOCGSERIAL: /* gets serial port data */ - return get_serial_info(port, - (struct serial_struct __user *) arg); - - case TIOCSSERIAL: /* sets serial port data */ - return set_serial_info(tty, port, - (struct serial_struct __user *) arg); + case TIOCGSERIAL: + return get_serial_info(port, argp); + case TIOCSSERIAL: + return set_serial_info(tty, port, argp); case TIOCSERGETLSR: - return get_lsr_info(port, (struct serial_struct __user *)arg); - break; + return get_lsr_info(port, argp); default: break; } From 51211a3d9b59b5c00f81512c2a16d9f3f607c0d9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 25 Jan 2017 18:22:54 +0100 Subject: [PATCH 30/47] USB: serial: drop unused ASYNC flags Do not report ASYNC_SKIP_TEST or ASYNC_AUTO_IRQ as being set in TIOCGSERIAL handlers as these flags are not supported and do not really make any sense for USB serial devices in the first place. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/io_edgeport.c | 1 - drivers/usb/serial/io_ti.c | 1 - drivers/usb/serial/mos7720.c | 1 - drivers/usb/serial/mos7840.c | 1 - drivers/usb/serial/opticon.c | 1 - drivers/usb/serial/quatech2.c | 1 - drivers/usb/serial/ssu100.c | 1 - drivers/usb/serial/whiteheat.c | 1 - 8 files changed, 8 deletions(-) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 8ab5f5b49ef3..92abf92e5669 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -1572,7 +1572,6 @@ static int get_serial_info(struct edgeport_port *edge_port, tmp.line = edge_port->port->minor; tmp.port = edge_port->port->port_number; tmp.irq = 0; - tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; tmp.xmit_fifo_size = edge_port->maxTxCredits; tmp.baud_base = 9600; tmp.close_delay = 5*HZ; diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 9a0db2965fbb..ceaeebaa6f90 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2468,7 +2468,6 @@ static int get_serial_info(struct edgeport_port *edge_port, tmp.line = edge_port->port->minor; tmp.port = edge_port->port->port_number; tmp.irq = 0; - tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; tmp.xmit_fifo_size = edge_port->port->bulk_out_size; tmp.baud_base = 9600; tmp.close_delay = 5*HZ; diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index acf431e02699..f075121c6e32 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1851,7 +1851,6 @@ static int get_serial_info(struct moschip_port *mos7720_port, tmp.line = mos7720_port->port->minor; tmp.port = mos7720_port->port->port_number; tmp.irq = 0; - tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE; tmp.baud_base = 9600; tmp.close_delay = 5*HZ; diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 155006acf918..d1b92f582478 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1963,7 +1963,6 @@ static int mos7840_get_serial_info(struct moschip_port *mos7840_port, tmp.line = mos7840_port->port->minor; tmp.port = mos7840_port->port->port_number; tmp.irq = 0; - tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE; tmp.baud_base = 9600; tmp.close_delay = 5 * HZ; diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index b3c64f557d60..3937b9c3cc69 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -343,7 +343,6 @@ static int get_serial_info(struct usb_serial_port *port, tmp.line = port->minor; tmp.port = 0; tmp.irq = 0; - tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; tmp.xmit_fifo_size = 1024; tmp.baud_base = 9600; tmp.close_delay = 5*HZ; diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index cf29128327d3..fdbb904d153f 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -465,7 +465,6 @@ static int get_serial_info(struct usb_serial_port *port, tmp.line = port->minor; tmp.port = 0; tmp.irq = 0; - tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; tmp.xmit_fifo_size = port->bulk_out_size; tmp.baud_base = 9600; tmp.close_delay = 5*HZ; diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 55814538ff1f..5aa7bbbeba3d 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -339,7 +339,6 @@ static int get_serial_info(struct usb_serial_port *port, tmp.line = port->minor; tmp.port = 0; tmp.irq = 0; - tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; tmp.xmit_fifo_size = port->bulk_out_size; tmp.baud_base = 9600; tmp.close_delay = 5*HZ; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index d3ea90bef84d..5ab65eb1dacc 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -487,7 +487,6 @@ static int whiteheat_ioctl(struct tty_struct *tty, serstruct.type = PORT_16654; serstruct.line = port->minor; serstruct.port = port->port_number; - serstruct.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; serstruct.xmit_fifo_size = kfifo_size(&port->write_fifo); serstruct.custom_divisor = 0; serstruct.baud_base = 460800; From ea534e0b404762894ac55ee416c0ac9f5cd5045a Mon Sep 17 00:00:00 2001 From: Maksim Salau Date: Wed, 25 Jan 2017 23:40:40 +0300 Subject: [PATCH 31/47] USB: serial: add uPD78F0730 USB to Serial Adaptor Driver The adaptor can be found on development boards for 78k, RL78 and V850 microcontrollers produced by Renesas Electronics Corporation. This is not a full-featured USB to serial converter, however it allows basic communication and simple control which is enough for programming of on-board flash and debugging through a debug monitor. uPD78F0730 is a USB-enabled microcontroller with USB-to-UART conversion implemented in firmware. This chip is also present in some debugging adaptors which use it for USB-to-SPI conversion as well. The present driver doesn't cover SPI, only USB-to-UART conversion is supported. Signed-off-by: Maksim Salau Signed-off-by: Johan Hovold --- drivers/usb/serial/Kconfig | 9 + drivers/usb/serial/Makefile | 1 + drivers/usb/serial/upd78f0730.c | 440 ++++++++++++++++++++++++++++++++ 3 files changed, 450 insertions(+) create mode 100644 drivers/usb/serial/upd78f0730.c diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index d9bc8dafe000..a8d5f2e4878d 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -713,6 +713,15 @@ config USB_SERIAL_QT2 To compile this driver as a module, choose M here: the module will be called quatech-serial. +config USB_SERIAL_UPD78F0730 + tristate "USB Renesas uPD78F0730 Single Port Serial Driver" + help + Say Y here if you want to use the Renesas uPD78F0730 + serial driver. + + To compile this driver as a module, choose M here: the + module will be called upd78f0730. + config USB_SERIAL_DEBUG tristate "USB Debugging Device" help diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 9e43b7b002eb..5a21a82390e1 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_USB_SERIAL_SSU100) += ssu100.o obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o +obj-$(CONFIG_USB_SERIAL_UPD78F0730) += upd78f0730.o obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o obj-$(CONFIG_USB_SERIAL_WISHBONE) += wishbone-serial.o obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o diff --git a/drivers/usb/serial/upd78f0730.c b/drivers/usb/serial/upd78f0730.c new file mode 100644 index 000000000000..55b9a18b6c38 --- /dev/null +++ b/drivers/usb/serial/upd78f0730.c @@ -0,0 +1,440 @@ +/* + * Renesas Electronics uPD78F0730 USB to serial converter driver + * + * Copyright (C) 2014,2016 Maksim Salau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * Protocol of the adaptor is described in the application note U19660EJ1V0AN00 + * μPD78F0730 8-bit Single-Chip Microcontroller + * USB-to-Serial Conversion Software + * + * + * The adaptor functionality is limited to the following: + * - data bits: 7 or 8 + * - stop bits: 1 or 2 + * - parity: even, odd or none + * - flow control: none + * - baud rates: 0, 2400, 4800, 9600, 19200, 38400, 57600, 115200 + * - signals: DTR, RTS and BREAK + */ + +#include +#include +#include +#include +#include + +#define DRIVER_DESC "Renesas uPD78F0730 USB to serial converter driver" + +#define DRIVER_AUTHOR "Maksim Salau " + +static const struct usb_device_id id_table[] = { + { USB_DEVICE(0x045B, 0x0212) }, /* YRPBRL78G13, YRPBRL78G14 */ + { USB_DEVICE(0x0409, 0x0063) }, /* V850ESJX3-STICK */ + {} +}; + +MODULE_DEVICE_TABLE(usb, id_table); + +/* + * Each adaptor is associated with a private structure, that holds the current + * state of control signals (DTR, RTS and BREAK). + */ +struct upd78f0730_port_private { + struct mutex lock; /* mutex to protect line_signals */ + u8 line_signals; +}; + +/* Op-codes of control commands */ +#define UPD78F0730_CMD_LINE_CONTROL 0x00 +#define UPD78F0730_CMD_SET_DTR_RTS 0x01 +#define UPD78F0730_CMD_SET_XON_XOFF_CHR 0x02 +#define UPD78F0730_CMD_OPEN_CLOSE 0x03 +#define UPD78F0730_CMD_SET_ERR_CHR 0x04 + +/* Data sizes in UPD78F0730_CMD_LINE_CONTROL command */ +#define UPD78F0730_DATA_SIZE_7_BITS 0x00 +#define UPD78F0730_DATA_SIZE_8_BITS 0x01 +#define UPD78F0730_DATA_SIZE_MASK 0x01 + +/* Stop-bit modes in UPD78F0730_CMD_LINE_CONTROL command */ +#define UPD78F0730_STOP_BIT_1_BIT 0x00 +#define UPD78F0730_STOP_BIT_2_BIT 0x02 +#define UPD78F0730_STOP_BIT_MASK 0x02 + +/* Parity modes in UPD78F0730_CMD_LINE_CONTROL command */ +#define UPD78F0730_PARITY_NONE 0x00 +#define UPD78F0730_PARITY_EVEN 0x04 +#define UPD78F0730_PARITY_ODD 0x08 +#define UPD78F0730_PARITY_MASK 0x0C + +/* Flow control modes in UPD78F0730_CMD_LINE_CONTROL command */ +#define UPD78F0730_FLOW_CONTROL_NONE 0x00 +#define UPD78F0730_FLOW_CONTROL_HW 0x10 +#define UPD78F0730_FLOW_CONTROL_SW 0x20 +#define UPD78F0730_FLOW_CONTROL_MASK 0x30 + +/* Control signal bits in UPD78F0730_CMD_SET_DTR_RTS command */ +#define UPD78F0730_RTS 0x01 +#define UPD78F0730_DTR 0x02 +#define UPD78F0730_BREAK 0x04 + +/* Port modes in UPD78F0730_CMD_OPEN_CLOSE command */ +#define UPD78F0730_PORT_CLOSE 0x00 +#define UPD78F0730_PORT_OPEN 0x01 + +/* Error character substitution modes in UPD78F0730_CMD_SET_ERR_CHR command */ +#define UPD78F0730_ERR_CHR_DISABLED 0x00 +#define UPD78F0730_ERR_CHR_ENABLED 0x01 + +/* + * Declaration of command structures + */ + +/* UPD78F0730_CMD_LINE_CONTROL command */ +struct upd78f0730_line_control { + u8 opcode; + __le32 baud_rate; + u8 params; +} __packed; + +/* UPD78F0730_CMD_SET_DTR_RTS command */ +struct upd78f0730_set_dtr_rts { + u8 opcode; + u8 params; +}; + +/* UPD78F0730_CMD_SET_XON_OFF_CHR command */ +struct upd78f0730_set_xon_xoff_chr { + u8 opcode; + u8 xon; + u8 xoff; +}; + +/* UPD78F0730_CMD_OPEN_CLOSE command */ +struct upd78f0730_open_close { + u8 opcode; + u8 state; +}; + +/* UPD78F0730_CMD_SET_ERR_CHR command */ +struct upd78f0730_set_err_chr { + u8 opcode; + u8 state; + u8 err_char; +}; + +static int upd78f0730_send_ctl(struct usb_serial_port *port, + const void *data, int size) +{ + struct usb_device *usbdev = port->serial->dev; + void *buf; + int res; + + if (size <= 0 || !data) + return -EINVAL; + + buf = kmemdup(data, size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + res = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x00, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, + 0x0000, 0x0000, buf, size, USB_CTRL_SET_TIMEOUT); + + kfree(buf); + + if (res != size) { + struct device *dev = &port->dev; + + dev_err(dev, "failed to send control request %02x: %d\n", + *(u8 *)data, res); + /* The maximum expected length of a transfer is 6 bytes */ + if (res >= 0) + res = -EIO; + + return res; + } + + return 0; +} + +static int upd78f0730_port_probe(struct usb_serial_port *port) +{ + struct upd78f0730_port_private *private; + + private = kzalloc(sizeof(*private), GFP_KERNEL); + if (!private) + return -ENOMEM; + + mutex_init(&private->lock); + usb_set_serial_port_data(port, private); + + return 0; +} + +static int upd78f0730_port_remove(struct usb_serial_port *port) +{ + struct upd78f0730_port_private *private; + + private = usb_get_serial_port_data(port); + mutex_destroy(&private->lock); + kfree(private); + + return 0; +} + +static int upd78f0730_tiocmget(struct tty_struct *tty) +{ + struct device *dev = tty->dev; + struct upd78f0730_port_private *private; + struct usb_serial_port *port = tty->driver_data; + int signals; + int res; + + private = usb_get_serial_port_data(port); + + mutex_lock(&private->lock); + signals = private->line_signals; + mutex_unlock(&private->lock); + + res = ((signals & UPD78F0730_DTR) ? TIOCM_DTR : 0) | + ((signals & UPD78F0730_RTS) ? TIOCM_RTS : 0); + + dev_dbg(dev, "%s - res = %x\n", __func__, res); + + return res; +} + +static int upd78f0730_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct device *dev = tty->dev; + struct usb_serial_port *port = tty->driver_data; + struct upd78f0730_port_private *private; + struct upd78f0730_set_dtr_rts request; + int res; + + private = usb_get_serial_port_data(port); + + mutex_lock(&private->lock); + if (set & TIOCM_DTR) { + private->line_signals |= UPD78F0730_DTR; + dev_dbg(dev, "%s - set DTR\n", __func__); + } + if (set & TIOCM_RTS) { + private->line_signals |= UPD78F0730_RTS; + dev_dbg(dev, "%s - set RTS\n", __func__); + } + if (clear & TIOCM_DTR) { + private->line_signals &= ~UPD78F0730_DTR; + dev_dbg(dev, "%s - clear DTR\n", __func__); + } + if (clear & TIOCM_RTS) { + private->line_signals &= ~UPD78F0730_RTS; + dev_dbg(dev, "%s - clear RTS\n", __func__); + } + request.opcode = UPD78F0730_CMD_SET_DTR_RTS; + request.params = private->line_signals; + + res = upd78f0730_send_ctl(port, &request, sizeof(request)); + mutex_unlock(&private->lock); + + return res; +} + +static void upd78f0730_break_ctl(struct tty_struct *tty, int break_state) +{ + struct device *dev = tty->dev; + struct upd78f0730_port_private *private; + struct usb_serial_port *port = tty->driver_data; + struct upd78f0730_set_dtr_rts request; + + private = usb_get_serial_port_data(port); + + mutex_lock(&private->lock); + if (break_state) { + private->line_signals |= UPD78F0730_BREAK; + dev_dbg(dev, "%s - set BREAK\n", __func__); + } else { + private->line_signals &= ~UPD78F0730_BREAK; + dev_dbg(dev, "%s - clear BREAK\n", __func__); + } + request.opcode = UPD78F0730_CMD_SET_DTR_RTS; + request.params = private->line_signals; + + upd78f0730_send_ctl(port, &request, sizeof(request)); + mutex_unlock(&private->lock); +} + +static void upd78f0730_dtr_rts(struct usb_serial_port *port, int on) +{ + struct tty_struct *tty = port->port.tty; + unsigned int set = 0; + unsigned int clear = 0; + + if (on) + set = TIOCM_DTR | TIOCM_RTS; + else + clear = TIOCM_DTR | TIOCM_RTS; + + upd78f0730_tiocmset(tty, set, clear); +} + +static speed_t upd78f0730_get_baud_rate(struct tty_struct *tty) +{ + const speed_t baud_rate = tty_get_baud_rate(tty); + const speed_t supported[] = { + 0, 2400, 4800, 9600, 19200, 38400, 57600, 115200 + }; + int i; + + for (i = ARRAY_SIZE(supported) - 1; i >= 0; i--) { + if (baud_rate == supported[i]) + return baud_rate; + } + + /* If the baud rate is not supported, switch to the default one */ + tty_encode_baud_rate(tty, 9600, 9600); + + return tty_get_baud_rate(tty); +} + +static void upd78f0730_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, + struct ktermios *old_termios) +{ + struct device *dev = &port->dev; + struct upd78f0730_line_control request; + speed_t baud_rate; + + if (old_termios && !tty_termios_hw_change(&tty->termios, old_termios)) + return; + + if (C_BAUD(tty) == B0) + upd78f0730_dtr_rts(port, 0); + else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) + upd78f0730_dtr_rts(port, 1); + + baud_rate = upd78f0730_get_baud_rate(tty); + request.opcode = UPD78F0730_CMD_LINE_CONTROL; + request.baud_rate = cpu_to_le32(baud_rate); + request.params = 0; + dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud_rate); + + switch (C_CSIZE(tty)) { + case CS7: + request.params |= UPD78F0730_DATA_SIZE_7_BITS; + dev_dbg(dev, "%s - 7 data bits\n", __func__); + break; + default: + tty->termios.c_cflag &= ~CSIZE; + tty->termios.c_cflag |= CS8; + dev_warn(dev, "data size is not supported, using 8 bits\n"); + /* fall through */ + case CS8: + request.params |= UPD78F0730_DATA_SIZE_8_BITS; + dev_dbg(dev, "%s - 8 data bits\n", __func__); + break; + } + + if (C_PARENB(tty)) { + if (C_PARODD(tty)) { + request.params |= UPD78F0730_PARITY_ODD; + dev_dbg(dev, "%s - odd parity\n", __func__); + } else { + request.params |= UPD78F0730_PARITY_EVEN; + dev_dbg(dev, "%s - even parity\n", __func__); + } + + if (C_CMSPAR(tty)) { + tty->termios.c_cflag &= ~CMSPAR; + dev_warn(dev, "MARK/SPACE parity is not supported\n"); + } + } else { + request.params |= UPD78F0730_PARITY_NONE; + dev_dbg(dev, "%s - no parity\n", __func__); + } + + if (C_CSTOPB(tty)) { + request.params |= UPD78F0730_STOP_BIT_2_BIT; + dev_dbg(dev, "%s - 2 stop bits\n", __func__); + } else { + request.params |= UPD78F0730_STOP_BIT_1_BIT; + dev_dbg(dev, "%s - 1 stop bit\n", __func__); + } + + if (C_CRTSCTS(tty)) { + tty->termios.c_cflag &= ~CRTSCTS; + dev_warn(dev, "RTSCTS flow control is not supported\n"); + } + if (I_IXOFF(tty) || I_IXON(tty)) { + tty->termios.c_iflag &= ~(IXOFF | IXON); + dev_warn(dev, "XON/XOFF flow control is not supported\n"); + } + request.params |= UPD78F0730_FLOW_CONTROL_NONE; + dev_dbg(dev, "%s - no flow control\n", __func__); + + upd78f0730_send_ctl(port, &request, sizeof(request)); +} + +static int upd78f0730_open(struct tty_struct *tty, struct usb_serial_port *port) +{ + struct upd78f0730_open_close request = { + .opcode = UPD78F0730_CMD_OPEN_CLOSE, + .state = UPD78F0730_PORT_OPEN + }; + int res; + + res = upd78f0730_send_ctl(port, &request, sizeof(request)); + if (res) + return res; + + if (tty) + upd78f0730_set_termios(tty, port, NULL); + + return usb_serial_generic_open(tty, port); +} + +static void upd78f0730_close(struct usb_serial_port *port) +{ + struct upd78f0730_open_close request = { + .opcode = UPD78F0730_CMD_OPEN_CLOSE, + .state = UPD78F0730_PORT_CLOSE + }; + + usb_serial_generic_close(port); + upd78f0730_send_ctl(port, &request, sizeof(request)); +} + +static struct usb_serial_driver upd78f0730_device = { + .driver = { + .owner = THIS_MODULE, + .name = "upd78f0730", + }, + .id_table = id_table, + .num_ports = 1, + .port_probe = upd78f0730_port_probe, + .port_remove = upd78f0730_port_remove, + .open = upd78f0730_open, + .close = upd78f0730_close, + .set_termios = upd78f0730_set_termios, + .tiocmget = upd78f0730_tiocmget, + .tiocmset = upd78f0730_tiocmset, + .dtr_rts = upd78f0730_dtr_rts, + .break_ctl = upd78f0730_break_ctl, +}; + +static struct usb_serial_driver * const serial_drivers[] = { + &upd78f0730_device, + NULL +}; + +module_usb_serial_driver(serial_drivers, id_table); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL v2"); From 4481200364ab1d6820a77ed442775a6e4f7c979f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 31 Jan 2017 11:51:07 +0100 Subject: [PATCH 32/47] USB: serial: ark3116: fix endpoint-check return value Return -ENODEV rather than -EINVAL on probe errors due to a missing endpoint. Also clean up the endpoint sanity check somewhat and use the interface device for logging a more compact error in case an expected endpoint is missing. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ark3116.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 754fc3e41005..d0fcff20ca7e 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -125,17 +125,11 @@ static inline int calc_divisor(int bps) static int ark3116_attach(struct usb_serial *serial) { /* make sure we have our end-points */ - if ((serial->num_bulk_in == 0) || - (serial->num_bulk_out == 0) || - (serial->num_interrupt_in == 0)) { - dev_err(&serial->dev->dev, - "%s - missing endpoint - " - "bulk in: %d, bulk out: %d, int in %d\n", - KBUILD_MODNAME, - serial->num_bulk_in, - serial->num_bulk_out, - serial->num_interrupt_in); - return -EINVAL; + if (serial->num_bulk_in == 0 || + serial->num_bulk_out == 0 || + serial->num_interrupt_in == 0) { + dev_err(&serial->interface->dev, "missing endpoint\n"); + return -ENODEV; } return 0; From 41a2af93aeebd43daaac397b6662d5822f0742fc Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 31 Jan 2017 11:51:08 +0100 Subject: [PATCH 33/47] USB: serial: ark3116: remove redundant interrupt-urb check Remove redundant check of num_interrupt_in which has already been verified in probe (killing a NULL-urb would also have been fine). Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ark3116.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index d0fcff20ca7e..e3f6a4e2e2ad 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -347,8 +347,8 @@ static void ark3116_close(struct usb_serial_port *port) ark3116_write_reg(serial, UART_IER, 0); usb_serial_generic_close(port); - if (serial->num_interrupt_in) - usb_kill_urb(port->interrupt_in_urb); + + usb_kill_urb(port->interrupt_in_urb); } static int ark3116_open(struct tty_struct *tty, struct usb_serial_port *port) From 6fdb7b0c0e6412a351deb1eaab0e5eb9635d6086 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 31 Jan 2017 11:51:09 +0100 Subject: [PATCH 34/47] USB: serial: ark3116: use port device for info and error messages Use the port device rather than usb device in info and error messages. This makes sure that driver and tty port is included in the messages, while also making them more uniform. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/ark3116.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index e3f6a4e2e2ad..2779e59c30f1 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -187,10 +187,8 @@ static int ark3116_port_probe(struct usb_serial_port *port) if (priv->irda) ark3116_write_reg(serial, 0x9, 0); - dev_info(&serial->dev->dev, - "%s using %s mode\n", - KBUILD_MODNAME, - priv->irda ? "IrDA" : "RS232"); + dev_info(&port->dev, "using %s mode\n", priv->irda ? "IrDA" : "RS232"); + return 0; } @@ -326,9 +324,8 @@ static void ark3116_set_termios(struct tty_struct *tty, /* check for software flow control */ if (I_IXOFF(tty) || I_IXON(tty)) { - dev_warn(&serial->dev->dev, - "%s: don't know how to do software flow control\n", - KBUILD_MODNAME); + dev_warn(&port->dev, + "software flow control not implemented\n"); } /* Don't rewrite B0 */ @@ -616,9 +613,8 @@ static void ark3116_read_int_callback(struct urb *urb) result = usb_submit_urb(urb, GFP_ATOMIC); if (result) - dev_err(&urb->dev->dev, - "%s - Error %d submitting interrupt urb\n", - __func__, result); + dev_err(&port->dev, "failed to resubmit interrupt urb: %d\n", + result); } From 2d380889215fe20b8523345649dee0579821800c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 31 Jan 2017 17:17:27 +0100 Subject: [PATCH 35/47] USB: serial: digi_acceleport: fix OOB data sanity check Make sure to check for short transfers to avoid underflow in a loop condition when parsing the receive buffer. Also fix an off-by-one error in the incomplete sanity check which could lead to invalid data being parsed. Fixes: 8c209e6782ca ("USB: make actual_length in struct urb field u32") Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable # v2.6.30 Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/digi_acceleport.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 6a1df9e824ca..3b610f1e3f7c 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1482,16 +1482,20 @@ static int digi_read_oob_callback(struct urb *urb) struct usb_serial *serial = port->serial; struct tty_struct *tty; struct digi_port *priv = usb_get_serial_port_data(port); + unsigned char *buf = urb->transfer_buffer; int opcode, line, status, val; int i; unsigned int rts; + if (urb->actual_length < 4) + return -1; + /* handle each oob command */ - for (i = 0; i < urb->actual_length - 3;) { - opcode = ((unsigned char *)urb->transfer_buffer)[i++]; - line = ((unsigned char *)urb->transfer_buffer)[i++]; - status = ((unsigned char *)urb->transfer_buffer)[i++]; - val = ((unsigned char *)urb->transfer_buffer)[i++]; + for (i = 0; i < urb->actual_length - 4; i += 4) { + opcode = buf[i]; + line = buf[i + 1]; + status = buf[i + 2]; + val = buf[i + 3]; dev_dbg(&port->dev, "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n", opcode, line, status, val); From 1b0aed2b1600f6e5c7b9acfbd610a4e351ef5232 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 31 Jan 2017 17:17:28 +0100 Subject: [PATCH 36/47] USB: serial: digi_acceleport: fix incomplete rx sanity check Make sure the received data has the required headers before parsing it. Also drop the redundant urb-status check, which has already been handled by the caller. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/digi_acceleport.c | 38 +++++++++++++++++----------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 3b610f1e3f7c..eb433922598c 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1398,25 +1398,30 @@ static int digi_read_inb_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct digi_port *priv = usb_get_serial_port_data(port); - int opcode = ((unsigned char *)urb->transfer_buffer)[0]; - int len = ((unsigned char *)urb->transfer_buffer)[1]; - int port_status = ((unsigned char *)urb->transfer_buffer)[2]; - unsigned char *data = ((unsigned char *)urb->transfer_buffer) + 3; + unsigned char *buf = urb->transfer_buffer; + int opcode; + int len; + int port_status; + unsigned char *data; int flag, throttled; - int status = urb->status; - - /* do not process callbacks on closed ports */ - /* but do continue the read chain */ - if (urb->status == -ENOENT) - return 0; /* short/multiple packet check */ + if (urb->actual_length < 2) { + dev_warn(&port->dev, "short packet received\n"); + return -1; + } + + opcode = buf[0]; + len = buf[1]; + if (urb->actual_length != len + 2) { - dev_err(&port->dev, "%s: INCOMPLETE OR MULTIPLE PACKET, " - "status=%d, port=%d, opcode=%d, len=%d, " - "actual_length=%d, status=%d\n", __func__, status, - priv->dp_port_num, opcode, len, urb->actual_length, - port_status); + dev_err(&port->dev, "malformed packet received: port=%d, opcode=%d, len=%d, actual_length=%u\n", + priv->dp_port_num, opcode, len, urb->actual_length); + return -1; + } + + if (opcode == DIGI_CMD_RECEIVE_DATA && len < 1) { + dev_err(&port->dev, "malformed data packet received\n"); return -1; } @@ -1430,6 +1435,9 @@ static int digi_read_inb_callback(struct urb *urb) /* receive data */ if (opcode == DIGI_CMD_RECEIVE_DATA) { + port_status = buf[2]; + data = &buf[3]; + /* get flag from port_status */ flag = 0; From c528fcb116e61afc379a2e0a0f70906b937f1e2c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 31 Jan 2017 17:17:29 +0100 Subject: [PATCH 37/47] USB: serial: keyspan_pda: fix receive sanity checks Make sure to check for short transfers before parsing the receive buffer to avoid acting on stale data. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/keyspan_pda.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 83523fcf6fb9..d2dab2a341b8 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -139,6 +139,7 @@ static void keyspan_pda_rx_interrupt(struct urb *urb) { struct usb_serial_port *port = urb->context; unsigned char *data = urb->transfer_buffer; + unsigned int len = urb->actual_length; int retval; int status = urb->status; struct keyspan_pda_private *priv; @@ -159,18 +160,26 @@ static void keyspan_pda_rx_interrupt(struct urb *urb) goto exit; } + if (len < 1) { + dev_warn(&port->dev, "short message received\n"); + goto exit; + } + /* see if the message is data or a status interrupt */ switch (data[0]) { case 0: /* rest of message is rx data */ - if (urb->actual_length) { - tty_insert_flip_string(&port->port, data + 1, - urb->actual_length - 1); - tty_flip_buffer_push(&port->port); - } + if (len < 2) + break; + tty_insert_flip_string(&port->port, data + 1, len - 1); + tty_flip_buffer_push(&port->port); break; case 1: /* status interrupt */ + if (len < 3) { + dev_warn(&port->dev, "short interrupt message received\n"); + break; + } dev_dbg(&port->dev, "rx int, d1=%d, d2=%d\n", data[1], data[2]); switch (data[1]) { case 1: /* modemline change */ From 9a593656def0dc2f6c227851e8e602077267a5f1 Mon Sep 17 00:00:00 2001 From: Ken Lin Date: Sat, 4 Feb 2017 04:00:24 +0800 Subject: [PATCH 38/47] USB: serial: cp210x: add new IDs for GE Bx50v3 boards Add new USB IDs for cp2104/5 devices on Bx50v3 boards due to the design change. Signed-off-by: Ken Lin Cc: stable Signed-off-by: Johan Hovold --- drivers/usb/serial/cp210x.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index fff718352e0c..fbe69465eefa 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -178,6 +178,8 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */ { USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */ { USB_DEVICE(0x1901, 0x0194) }, /* GE Healthcare Remote Alarm Box */ + { USB_DEVICE(0x1901, 0x0195) }, /* GE B850/B650/B450 CP2104 DP UART interface */ + { USB_DEVICE(0x1901, 0x0196) }, /* GE B850 CP2105 DP UART interface */ { USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */ { USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */ { USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */ From a6bb1e17a39818b01b55d8e6238b4b5f06d55038 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 2 Feb 2017 17:38:35 +0100 Subject: [PATCH 39/47] USB: serial: ftdi_sio: fix line-status over-reporting FTDI devices use a receive latency timer to periodically empty the receive buffer and report modem and line status (also when the buffer is empty). When a break or error condition is detected the corresponding status flags will be set on a packet with nonzero data payload and the flags are not updated until the break is over or further characters are received. In order to avoid over-reporting break and error conditions, these flags must therefore only be processed for packets with payload. This specifically fixes the case where after an overrun, the error condition is continuously reported and NULL-characters inserted until further data is received. Reported-by: Michael Walle Fixes: 72fda3ca6fc1 ("USB: serial: ftd_sio: implement sysrq handling on break") Fixes: 166ceb690750 ("USB: ftdi_sio: clean up line-status handling") Cc: stable # v2.6.35 Signed-off-by: Johan Hovold --- drivers/usb/serial/ftdi_sio.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index e82dbb3d0883..c540de15aad2 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -2068,6 +2068,20 @@ static int ftdi_process_packet(struct usb_serial_port *port, priv->prev_status = status; } + /* save if the transmitter is empty or not */ + if (packet[1] & FTDI_RS_TEMT) + priv->transmit_empty = 1; + else + priv->transmit_empty = 0; + + len -= 2; + if (!len) + return 0; /* status only */ + + /* + * Break and error status must only be processed for packets with + * data payload to avoid over-reporting. + */ flag = TTY_NORMAL; if (packet[1] & FTDI_RS_ERR_MASK) { /* Break takes precedence over parity, which takes precedence @@ -2090,15 +2104,6 @@ static int ftdi_process_packet(struct usb_serial_port *port, } } - /* save if the transmitter is empty or not */ - if (packet[1] & FTDI_RS_TEMT) - priv->transmit_empty = 1; - else - priv->transmit_empty = 0; - - len -= 2; - if (!len) - return 0; /* status only */ port->icount.rx += len; ch = packet + 2; From 16620b483eaf7750413bae472f4363b6b959fcaa Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 6 Feb 2017 16:28:14 +0100 Subject: [PATCH 40/47] USB: serial: sierra: fix bogus alternate-setting assumption Interface numbers do not change when enabling alternate settings as comment and code in this driver suggested. Remove the confusing comment and redundant retrieval of the interface number in probe, while simplifying and renaming the interface-number helper. Fixes: 4db2299da213 ("sierra: driver interface blacklisting") Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/sierra.c | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index e1994e264cc0..465e851b2815 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -137,24 +137,9 @@ static int is_himemory(const u8 ifnum, return 0; } -static int sierra_calc_interface(struct usb_serial *serial) +static u8 sierra_interface_num(struct usb_serial *serial) { - int interface; - struct usb_interface *p_interface; - struct usb_host_interface *p_host_interface; - - /* Get the interface structure pointer from the serial struct */ - p_interface = serial->interface; - - /* Get a pointer to the host interface structure */ - p_host_interface = p_interface->cur_altsetting; - - /* read the interface descriptor for this active altsetting - * to find out the interface number we are on - */ - interface = p_host_interface->desc.bInterfaceNumber; - - return interface; + return serial->interface->cur_altsetting->desc.bInterfaceNumber; } static int sierra_probe(struct usb_serial *serial, @@ -165,7 +150,7 @@ static int sierra_probe(struct usb_serial *serial, u8 ifnum; udev = serial->dev; - ifnum = sierra_calc_interface(serial); + ifnum = sierra_interface_num(serial); /* * If this interface supports more than 1 alternate @@ -178,9 +163,6 @@ static int sierra_probe(struct usb_serial *serial, usb_set_interface(udev, ifnum, 1); } - /* ifnum could have changed - by calling usb_set_interface */ - ifnum = sierra_calc_interface(serial); - if (is_blacklisted(ifnum, (struct sierra_iface_info *)id->driver_info)) { dev_dbg(&serial->dev->dev, @@ -342,7 +324,7 @@ static int sierra_send_setup(struct usb_serial_port *port) /* If composite device then properly report interface */ if (serial->num_ports == 1) { - interface = sierra_calc_interface(serial); + interface = sierra_interface_num(serial); /* Control message is sent only to interfaces with * interrupt_in endpoints */ @@ -916,7 +898,7 @@ static int sierra_port_probe(struct usb_serial_port *port) /* Determine actual memory requirements */ if (serial->num_ports == 1) { /* Get interface number for composite device */ - ifnum = sierra_calc_interface(serial); + ifnum = sierra_interface_num(serial); himemoryp = &typeB_interface_list; } else { /* This is really the usb-serial port number of the interface From 965bbef552cdbf22ab7fb83e7f92b2f251d56f70 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 8 Feb 2017 13:49:16 +0100 Subject: [PATCH 41/47] USB: serial: metro-usb: drop unused interrupt-out callback Drop the unused interrupt-out callback. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/metro-usb.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index 39e683096e94..4f7cd30b5424 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -161,14 +161,6 @@ exit: __func__, result); } -static void metrousb_write_int_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - - dev_warn(&port->dev, "%s not implemented yet.\n", - __func__); -} - static void metrousb_cleanup(struct usb_serial_port *port) { dev_dbg(&port->dev, "%s\n", __func__); @@ -377,7 +369,6 @@ static struct usb_serial_driver metrousb_device = { .open = metrousb_open, .close = metrousb_cleanup, .read_int_callback = metrousb_read_int_callback, - .write_int_callback = metrousb_write_int_callback, .port_probe = metrousb_port_probe, .port_remove = metrousb_port_remove, .throttle = metrousb_throttle, From d395c9ab00901cdd804c36857e9079dd794c3b1c Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 8 Feb 2017 13:49:17 +0100 Subject: [PATCH 42/47] USB: serial: metro-usb: drop redundant URB unlink Drop redundant URB unlink as there's no need to unlink an URB which is about to be killed synchronously. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/metro-usb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index 4f7cd30b5424..b0c7746438df 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -165,7 +165,6 @@ static void metrousb_cleanup(struct usb_serial_port *port) { dev_dbg(&port->dev, "%s\n", __func__); - usb_unlink_urb(port->interrupt_in_urb); usb_kill_urb(port->interrupt_in_urb); metrousb_send_unidirectional_cmd(UNI_CMD_CLOSE, port); From 168fc6c3c33ee7e9b2355e1a17d07e8313eb60f5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 8 Feb 2017 13:49:18 +0100 Subject: [PATCH 43/47] USB: serial: metro-usb: drop function-tracing debugging Drop some unnecessary debug printks. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/metro-usb.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index b0c7746438df..f220a470197a 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -163,8 +163,6 @@ exit: static void metrousb_cleanup(struct usb_serial_port *port) { - dev_dbg(&port->dev, "%s\n", __func__); - usb_kill_urb(port->interrupt_in_urb); metrousb_send_unidirectional_cmd(UNI_CMD_CLOSE, port); @@ -177,8 +175,6 @@ static int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port) unsigned long flags = 0; int result = 0; - dev_dbg(&port->dev, "%s\n", __func__); - /* Make sure the urb is initialized. */ if (!port->interrupt_in_urb) { dev_err(&port->dev, "%s - interrupt urb not initialized\n", @@ -218,8 +214,6 @@ static int metrousb_open(struct tty_struct *tty, struct usb_serial_port *port) __func__, result); goto exit; } - - dev_dbg(&port->dev, "%s - port open\n", __func__); exit: return result; } @@ -281,8 +275,6 @@ static void metrousb_throttle(struct tty_struct *tty) struct metrousb_private *metro_priv = usb_get_serial_port_data(port); unsigned long flags = 0; - dev_dbg(tty->dev, "%s\n", __func__); - /* Set the private information for the port to stop reading data. */ spin_lock_irqsave(&metro_priv->lock, flags); metro_priv->throttled = 1; @@ -296,8 +288,6 @@ static int metrousb_tiocmget(struct tty_struct *tty) struct metrousb_private *metro_priv = usb_get_serial_port_data(port); unsigned long flags = 0; - dev_dbg(tty->dev, "%s\n", __func__); - spin_lock_irqsave(&metro_priv->lock, flags); control_state = metro_priv->control_state; spin_unlock_irqrestore(&metro_priv->lock, flags); @@ -341,8 +331,6 @@ static void metrousb_unthrottle(struct tty_struct *tty) unsigned long flags = 0; int result = 0; - dev_dbg(tty->dev, "%s\n", __func__); - /* Set the private information for the port to resume reading data. */ spin_lock_irqsave(&metro_priv->lock, flags); metro_priv->throttled = 0; From acfe27633b4c380a67956bd2f73fc9a2c932cc39 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 8 Feb 2017 13:49:19 +0100 Subject: [PATCH 44/47] USB: serial: metro-usb: drop redundant URB reinitialisation No need to reinitialise the interrupt-in URB with values that have not changed before (some) resubmissions. This also allows the interrupt-in callback to have a single path for URB resubmission. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/metro-usb.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index f220a470197a..cc84da8dbb84 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -135,23 +135,8 @@ static void metrousb_read_int_callback(struct urb *urb) throttled = metro_priv->throttled; spin_unlock_irqrestore(&metro_priv->lock, flags); - /* Continue trying to read if set. */ - if (!throttled) { - usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev, - usb_rcvintpipe(port->serial->dev, port->interrupt_in_endpointAddress), - port->interrupt_in_urb->transfer_buffer, - port->interrupt_in_urb->transfer_buffer_length, - metrousb_read_int_callback, port, 1); - - result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); - - if (result) - dev_err(&port->dev, - "%s - failed submitting interrupt in urb, error code=%d\n", - __func__, result); - } - return; - + if (throttled) + return; exit: /* Try to resubmit the urb. */ result = usb_submit_urb(urb, GFP_ATOMIC); @@ -337,7 +322,6 @@ static void metrousb_unthrottle(struct tty_struct *tty) spin_unlock_irqrestore(&metro_priv->lock, flags); /* Submit the urb to read from the port. */ - port->interrupt_in_urb->dev = port->serial->dev; result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC); if (result) dev_err(tty->dev, From 14816b16fa0adac24f82492f18fa62c55acabbbe Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 8 Feb 2017 18:53:08 +0100 Subject: [PATCH 45/47] USB: serial: console: fix uninitialised spinlock Since commit 4a510969374a ("tty: Make tty_files_lock per-tty") a new tty_struct spin lock is taken in the tty release path, but the USB-serial-console hack was never updated hence leaving the lock of its "fake" tty uninitialised. This was eventually detected by lockdep. Make sure to initialise the new lock also for the fake tty to address this regression. Yes, this code is a mess, but cleaning it up is left for another day. Fixes: 4a510969374a ("tty: Make tty_files_lock per-tty") Cc: stable # 4.6 Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/console.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index 8967715fe6fc..b6f1adefb758 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -143,6 +143,7 @@ static int usb_console_setup(struct console *co, char *options) tty->driver = usb_serial_tty_driver; tty->index = co->index; init_ldsem(&tty->ldisc_sem); + spin_lock_init(&tty->files_lock); INIT_LIST_HEAD(&tty->tty_files); kref_get(&tty->driver->kref); __module_get(tty->driver->owner); From 0e517c93dc027e49d4523fe32631606b12f0752d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 8 Feb 2017 18:53:09 +0100 Subject: [PATCH 46/47] USB: serial: console: clean up sanity checks Drop two redundant NULL checks from usb_serial_console_disconnect(). The usb_serial_console_disconnect function is called from the USB-serial-device disconnect callback when a device is going away. Hence there is no need to check for the serial-device pointer being NULL. The serial-device port pointers are stored in an array that is a member of the serial struct so the address of the first member of the array (which the array name decays to) is never NULL either. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/console.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index b6f1adefb758..fdf89800ebc3 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -265,8 +265,7 @@ static struct console usbcons = { void usb_serial_console_disconnect(struct usb_serial *serial) { - if (serial && serial->port && serial->port[0] - && serial->port[0] == usbcons_info.port) { + if (serial->port[0] == usbcons_info.port) { usb_serial_console_exit(); usb_serial_put(serial); } From 5182c2cf2a9bfb7f066ef0bdd2bb6330b94dd74e Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 9 Feb 2017 12:11:41 +0100 Subject: [PATCH 47/47] USB: serial: mos7840: fix another NULL-deref at open Fix another NULL-pointer dereference at open should a malicious device lack an interrupt-in endpoint. Note that the driver has a broken check for an interrupt-in endpoint which means that an interrupt URB has never even been submitted. Fixes: 3f5429746d91 ("USB: Moschip 7840 USB-Serial Driver") Cc: stable # v2.6.19: 5c75633ef751 Reviewed-by: Greg Kroah-Hartman Signed-off-by: Johan Hovold --- drivers/usb/serial/mos7840.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index d1b92f582478..3821c53fcee9 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1034,6 +1034,7 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port) * (can't set it up in mos7840_startup as the structures * * were not set up at that time.) */ if (port0->open_ports == 1) { + /* FIXME: Buffer never NULL, so URB is not submitted. */ if (serial->port[0]->interrupt_in_buffer == NULL) { /* set up interrupt urb */ usb_fill_int_urb(serial->port[0]->interrupt_in_urb, @@ -2116,7 +2117,8 @@ static int mos7840_calc_num_ports(struct usb_serial *serial) static int mos7840_attach(struct usb_serial *serial) { if (serial->num_bulk_in < serial->num_ports || - serial->num_bulk_out < serial->num_ports) { + serial->num_bulk_out < serial->num_ports || + serial->num_interrupt_in < 1) { dev_err(&serial->interface->dev, "missing endpoints\n"); return -ENODEV; }