diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-02-09 15:57:05 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-02-09 15:57:05 +0300 |
commit | 54a21903df2f8020bd4bc9fe38668c031255e148 (patch) | |
tree | a7b4fd0d08dd860565df089e8fbdbda1bf8b3c7a | |
parent | 255348289f714b96e0e95f9b702f435a638e6fd4 (diff) | |
parent | 5182c2cf2a9bfb7f066ef0bdd2bb6330b94dd74e (diff) | |
download | linux-54a21903df2f8020bd4bc9fe38668c031255e148.tar.xz |
Merge tag 'usb-serial-4.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial into usb-next
Johan writes:
USB-serial updates for v4.11-rc1
These updates include
- a new driver for Renesas uPD78F0730-based devices
- several fixes of failures to check for short transfers, some of which could
lead to minor information leaks, and in one case a loop-condition underflow
- a fix of a long-standing regression in the ftdi_sio driver which resulted
in excessive bulk-in interrupts
- a fix for ftdi_sio line-status over-reporting which could lead to an
endless stream of NULL-characters being forwarded to user space
- a fix for a regression in the console driver
- a fix for another mos7840 NULL-pointer dereference due to a missing endpoint
sanity check
Included are also some clean ups and fixes for various minor issues, as well as
a couple of new device IDs that came in late.
All but the final patch have been in linux-next with no reported issues.
Signed-off-by: Johan Hovold <johan@kernel.org>
27 files changed, 772 insertions, 388 deletions
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/ark3116.c b/drivers/usb/serial/ark3116.c index 1532cde8a437..2779e59c30f1 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) @@ -118,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; @@ -186,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; } @@ -325,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 */ @@ -346,8 +344,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) @@ -366,23 +364,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 */ @@ -395,8 +399,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; } @@ -602,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); } diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 95aa5233726c..351745aec0e1 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -93,8 +93,8 @@ 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 line_status; /* active status of modem control inputs */ + u8 mcr; + u8 msr; u8 lcr; }; @@ -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, @@ -210,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); @@ -239,30 +238,11 @@ 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; - - /* 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); + r = ch341_set_handshake(dev, priv->mcr); out: kfree(buffer); return r; @@ -279,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) @@ -304,7 +289,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; } @@ -317,11 +302,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) @@ -334,14 +319,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 +333,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; @@ -374,7 +360,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 */ @@ -383,54 +369,54 @@ 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; } } 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) @@ -486,20 +472,20 @@ 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); } -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); @@ -514,8 +500,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) @@ -568,7 +554,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) { @@ -587,8 +573,8 @@ static int ch341_tiocmget(struct tty_struct *tty) unsigned int result; spin_lock_irqsave(&priv->lock, flags); - mcr = priv->line_control; - status = priv->line_status; + mcr = priv->mcr; + status = priv->msr; spin_unlock_irqrestore(&priv->lock, flags); result = ((mcr & CH341_BIT_DTR) ? TIOCM_DTR : 0) @@ -619,6 +605,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); diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index 8967715fe6fc..fdf89800ebc3 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); @@ -264,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); } 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 */ 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); diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 6a1df9e824ca..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; @@ -1482,16 +1490,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); diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 23d14b98ae2a..c540de15aad2 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); @@ -1531,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; @@ -1802,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); @@ -2067,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 @@ -2089,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; @@ -2428,8 +2434,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; } @@ -2480,20 +2490,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; } diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index d50e5773483f..92abf92e5669 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; } @@ -1560,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; @@ -2090,8 +2101,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) @@ -2116,12 +2126,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); @@ -2575,9 +2590,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", @@ -2634,9 +2650,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)); @@ -2779,7 +2796,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)); 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/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) 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 */ diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 6cb45757818f..595415e59d5d 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, @@ -104,16 +103,15 @@ 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; - struct ktermios termios; unsigned long line_state; /* modem line settings */ spinlock_t lock; }; @@ -143,10 +141,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 +175,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 +197,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); } @@ -233,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; @@ -255,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; @@ -278,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; @@ -438,19 +426,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 +459,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 +500,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 +510,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 +520,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; 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]; diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index 39e683096e94..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); @@ -161,19 +146,8 @@ 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__); - - usb_unlink_urb(port->interrupt_in_urb); usb_kill_urb(port->interrupt_in_urb); metrousb_send_unidirectional_cmd(UNI_CMD_CLOSE, port); @@ -186,8 +160,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", @@ -227,8 +199,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; } @@ -290,8 +260,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; @@ -305,8 +273,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); @@ -350,15 +316,12 @@ 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; 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, @@ -377,7 +340,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, diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 91bc170b408a..f075121c6e32 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; @@ -1846,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 ea27fb23967a..3821c53fcee9 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; } @@ -1023,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, @@ -1479,10 +1491,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) @@ -1952,7 +1964,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; @@ -2106,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; } diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 5ded6f524d59..3937b9c3cc69 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 @@ -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/pl2303.c b/drivers/usb/serial/pl2303.c index 1db4b61bdf7b..ca69eb42071b 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -450,7 +450,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; @@ -470,12 +470,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; } diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 5709cc93b083..fdbb904d153f 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; } @@ -463,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/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 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; diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 2a156144c76c..5aa7bbbeba3d 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; } @@ -322,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/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) - status = -ECOMM; + if (status < 0) + return status; - 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; 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 <maksim.salau@gmail.com> + * + * 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 + * <https://www.renesas.com/en-eu/doc/DocumentServer/026/U19660EJ1V0AN00.pdf> + * + * 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 <linux/module.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> + +#define DRIVER_DESC "Renesas uPD78F0730 USB to serial converter driver" + +#define DRIVER_AUTHOR "Maksim Salau <maksim.salau@gmail.com>" + +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"); 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; |