diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2020-03-26 11:15:30 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2020-03-26 11:15:30 +0100 |
commit | 0fa84af850a4f89f5d8d589d2efea69acbd771fd (patch) | |
tree | 99d372ff64c54a5110070a022a4678c35bbdc88b /drivers/usb | |
parent | a599a0fb629a61ef0c8c47312152866d24faeaff (diff) | |
parent | 57aa9f294b09463492f604feaa5cc719beaace32 (diff) | |
download | linux-0fa84af850a4f89f5d8d589d2efea69acbd771fd.tar.bz2 |
Merge tag 'usb-serial-5.7-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial into usb-next
Johan writes:
USB-serial updates for 5.7-rc1
Here are the USB-serial updates for 5.7-rc1, including:
- support for a new family of Fintek devices
- fix for an io-edgeport slab-out-of-bounds access
- fixes for a couple of kernel-doc issues
Included are also various clean ups and some new modem device ids.
All but the io-edgeport fix have been in linux-next with no reported
issues.
* tag 'usb-serial-5.7-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial:
USB: serial: io_edgeport: fix slab-out-of-bounds read in edge_interrupt_callback
USB: serial: option: add Wistron Neweb D19Q1
USB: serial: option: add BroadMobi BM806U
USB: serial: option: add support for ASKEY WWHC050
USB: serial: f81232: add control driver for F81534A
USB: serial: fix tty cleanup-op kernel-doc
USB: serial: clean up carrier-detect helper
USB: serial: f81232: set F81534A serial port with RS232 mode
USB: serial: f81232: add F81534A support
USB: serial: f81232: use devm_kzalloc for port data
USB: serial: f81232: add tx_empty function
USB: serial: f81232: extract LSR handler
USB: serial: digi_acceleport: remove redundant assignment to pointer priv
USB: serial: relax unthrottle memory barrier
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/serial/digi_acceleport.c | 2 | ||||
-rw-r--r-- | drivers/usb/serial/f81232.c | 354 | ||||
-rw-r--r-- | drivers/usb/serial/generic.c | 12 | ||||
-rw-r--r-- | drivers/usb/serial/io_edgeport.c | 2 | ||||
-rw-r--r-- | drivers/usb/serial/option.c | 6 | ||||
-rw-r--r-- | drivers/usb/serial/usb-serial.c | 2 |
6 files changed, 332 insertions, 46 deletions
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 578ebdd86520..91055a191995 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1472,7 +1472,7 @@ static int digi_read_oob_callback(struct urb *urb) struct usb_serial_port *port = urb->context; struct usb_serial *serial = port->serial; struct tty_struct *tty; - struct digi_port *priv = usb_get_serial_port_data(port); + struct digi_port *priv; unsigned char *buf = urb->transfer_buffer; int opcode, line, status, val; unsigned long flags; diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 43fa1f0716b7..dcda7fb164b4 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Fintek F81232 USB to serial adaptor driver + * Fintek F81532A/534A/535/536 USB to 2/4/8/12 serial adaptor driver * * Copyright (C) 2012 Greg Kroah-Hartman (gregkh@linuxfoundation.org) * Copyright (C) 2012 Linux Foundation @@ -21,11 +22,45 @@ #include <linux/usb/serial.h> #include <linux/serial_reg.h> -static const struct usb_device_id id_table[] = { - { USB_DEVICE(0x1934, 0x0706) }, +#define F81232_ID \ + { USB_DEVICE(0x1934, 0x0706) } /* 1 port UART device */ + +#define F81534A_SERIES_ID \ + { USB_DEVICE(0x2c42, 0x1602) }, /* In-Box 2 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1604) }, /* In-Box 4 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1605) }, /* In-Box 8 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1606) }, /* In-Box 12 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1608) }, /* Non-Flash type */ \ + { USB_DEVICE(0x2c42, 0x1632) }, /* 2 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1634) }, /* 4 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1635) }, /* 8 port UART device */ \ + { USB_DEVICE(0x2c42, 0x1636) } /* 12 port UART device */ + +#define F81534A_CTRL_ID \ + { USB_DEVICE(0x2c42, 0x16f8) } /* Global control device */ + +static const struct usb_device_id f81232_id_table[] = { + F81232_ID, { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE(usb, id_table); + +static const struct usb_device_id f81534a_id_table[] = { + F81534A_SERIES_ID, + { } /* Terminating entry */ +}; + +static const struct usb_device_id f81534a_ctrl_id_table[] = { + F81534A_CTRL_ID, + { } /* Terminating entry */ +}; + +static const struct usb_device_id combined_id_table[] = { + F81232_ID, + F81534A_SERIES_ID, + F81534A_CTRL_ID, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, combined_id_table); /* Maximum baudrate for F81232 */ #define F81232_MAX_BAUDRATE 1500000 @@ -35,6 +70,7 @@ MODULE_DEVICE_TABLE(usb, id_table); #define F81232_REGISTER_REQUEST 0xa0 #define F81232_GET_REGISTER 0xc0 #define F81232_SET_REGISTER 0x40 +#define F81534A_ACCESS_REG_RETRY 2 #define SERIAL_BASE_ADDRESS 0x0120 #define RECEIVE_BUFFER_REGISTER (0x00 + SERIAL_BASE_ADDRESS) @@ -61,6 +97,22 @@ MODULE_DEVICE_TABLE(usb, id_table); #define F81232_CLK_14_77_MHZ (BIT(1) | BIT(0)) #define F81232_CLK_MASK GENMASK(1, 0) +#define F81534A_MODE_REG 0x107 +#define F81534A_TRIGGER_MASK GENMASK(3, 2) +#define F81534A_TRIGGER_MULTIPLE_4X BIT(3) +#define F81534A_FIFO_128BYTE (BIT(1) | BIT(0)) + +/* Serial port self GPIO control, 2bytes [control&output data][input data] */ +#define F81534A_GPIO_REG 0x10e +#define F81534A_GPIO_MODE2_DIR BIT(6) /* 1: input, 0: output */ +#define F81534A_GPIO_MODE1_DIR BIT(5) +#define F81534A_GPIO_MODE0_DIR BIT(4) +#define F81534A_GPIO_MODE2_OUTPUT BIT(2) +#define F81534A_GPIO_MODE1_OUTPUT BIT(1) +#define F81534A_GPIO_MODE0_OUTPUT BIT(0) + +#define F81534A_CTRL_CMD_ENABLE_PORT 0x116 + struct f81232_private { struct mutex lock; u8 modem_control; @@ -322,10 +374,38 @@ exit: __func__, retval); } +static char f81232_handle_lsr(struct usb_serial_port *port, u8 lsr) +{ + struct f81232_private *priv = usb_get_serial_port_data(port); + char tty_flag = TTY_NORMAL; + + if (!(lsr & UART_LSR_BRK_ERROR_BITS)) + return tty_flag; + + if (lsr & UART_LSR_BI) { + tty_flag = TTY_BREAK; + port->icount.brk++; + usb_serial_handle_break(port); + } else if (lsr & UART_LSR_PE) { + tty_flag = TTY_PARITY; + port->icount.parity++; + } else if (lsr & UART_LSR_FE) { + tty_flag = TTY_FRAME; + port->icount.frame++; + } + + if (lsr & UART_LSR_OE) { + port->icount.overrun++; + schedule_work(&priv->lsr_work); + tty_insert_flip_char(&port->port, 0, TTY_OVERRUN); + } + + return tty_flag; +} + static void f81232_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct f81232_private *priv = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; char tty_flag; unsigned int i; @@ -341,29 +421,8 @@ static void f81232_process_read_urb(struct urb *urb) /* bulk-in data: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... */ for (i = 0; i < urb->actual_length; i += 2) { - tty_flag = TTY_NORMAL; lsr = data[i]; - - if (lsr & UART_LSR_BRK_ERROR_BITS) { - if (lsr & UART_LSR_BI) { - tty_flag = TTY_BREAK; - port->icount.brk++; - usb_serial_handle_break(port); - } else if (lsr & UART_LSR_PE) { - tty_flag = TTY_PARITY; - port->icount.parity++; - } else if (lsr & UART_LSR_FE) { - tty_flag = TTY_FRAME; - port->icount.frame++; - } - - if (lsr & UART_LSR_OE) { - port->icount.overrun++; - schedule_work(&priv->lsr_work); - tty_insert_flip_char(&port->port, 0, - TTY_OVERRUN); - } - } + tty_flag = f81232_handle_lsr(port, lsr); if (port->port.console && port->sysrq) { if (usb_serial_handle_sysrq_char(port, data[i + 1])) @@ -376,6 +435,47 @@ static void f81232_process_read_urb(struct urb *urb) tty_flip_buffer_push(&port->port); } +static void f81534a_process_read_urb(struct urb *urb) +{ + struct usb_serial_port *port = urb->context; + unsigned char *data = urb->transfer_buffer; + char tty_flag; + unsigned int i; + u8 lsr; + u8 len; + + if (urb->actual_length < 3) { + dev_err(&port->dev, "short message received: %d\n", + urb->actual_length); + return; + } + + len = data[0]; + if (len != urb->actual_length) { + dev_err(&port->dev, "malformed message received: %d (%d)\n", + urb->actual_length, len); + return; + } + + /* bulk-in data: [LEN][Data.....][LSR] */ + lsr = data[len - 1]; + tty_flag = f81232_handle_lsr(port, lsr); + + if (port->port.console && port->sysrq) { + for (i = 1; i < len - 1; ++i) { + if (!usb_serial_handle_sysrq_char(port, data[i])) { + tty_insert_flip_char(&port->port, data[i], + tty_flag); + } + } + } else { + tty_insert_flip_string_fixed_flag(&port->port, &data[1], + tty_flag, len - 2); + } + + tty_flip_buffer_push(&port->port); +} + static void f81232_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; @@ -659,6 +759,24 @@ static int f81232_open(struct tty_struct *tty, struct usb_serial_port *port) return 0; } +static int f81534a_open(struct tty_struct *tty, struct usb_serial_port *port) +{ + int status; + u8 mask; + u8 val; + + val = F81534A_TRIGGER_MULTIPLE_4X | F81534A_FIFO_128BYTE; + mask = F81534A_TRIGGER_MASK | F81534A_FIFO_128BYTE; + + status = f81232_set_mask_register(port, F81534A_MODE_REG, mask, val); + if (status) { + dev_err(&port->dev, "failed to set MODE_REG: %d\n", status); + return status; + } + + return f81232_open(tty, port); +} + static void f81232_close(struct usb_serial_port *port) { struct f81232_private *port_priv = usb_get_serial_port_data(port); @@ -678,6 +796,20 @@ static void f81232_dtr_rts(struct usb_serial_port *port, int on) f81232_set_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS); } +static bool f81232_tx_empty(struct usb_serial_port *port) +{ + int status; + u8 tmp; + + status = f81232_get_register(port, LINE_STATUS_REGISTER, &tmp); + if (!status) { + if ((tmp & UART_LSR_TEMT) != UART_LSR_TEMT) + return false; + } + + return true; +} + static int f81232_carrier_raised(struct usb_serial_port *port) { u8 msr; @@ -728,11 +860,98 @@ static void f81232_lsr_worker(struct work_struct *work) dev_warn(&port->dev, "read LSR failed: %d\n", status); } +static int f81534a_ctrl_set_register(struct usb_interface *intf, u16 reg, + u16 size, void *val) +{ + struct usb_device *dev = interface_to_usbdev(intf); + int retry = F81534A_ACCESS_REG_RETRY; + int status; + u8 *tmp; + + tmp = kmemdup(val, size, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + while (retry--) { + status = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + F81232_REGISTER_REQUEST, + F81232_SET_REGISTER, + reg, + 0, + tmp, + size, + USB_CTRL_SET_TIMEOUT); + if (status < 0) { + status = usb_translate_errors(status); + if (status == -EIO) + continue; + } else if (status != size) { + /* Retry on short transfers */ + status = -EIO; + continue; + } else { + status = 0; + } + + break; + } + + if (status) { + dev_err(&intf->dev, "failed to set register 0x%x: %d\n", + reg, status); + } + + kfree(tmp); + return status; +} + +static int f81534a_ctrl_enable_all_ports(struct usb_interface *intf, bool en) +{ + unsigned char enable[2] = {0}; + int status; + + /* + * Enable all available serial ports, define as following: + * bit 15 : Reset behavior (when HUB got soft reset) + * 0: maintain all serial port enabled state. + * 1: disable all serial port. + * bit 0~11 : Serial port enable bit. + */ + if (en) { + enable[0] = 0xff; + enable[1] = 0x8f; + } + + status = f81534a_ctrl_set_register(intf, F81534A_CTRL_CMD_ENABLE_PORT, + sizeof(enable), enable); + if (status) + dev_err(&intf->dev, "failed to enable ports: %d\n", status); + + return status; +} + +static int f81534a_ctrl_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return f81534a_ctrl_enable_all_ports(intf, true); +} + +static void f81534a_ctrl_disconnect(struct usb_interface *intf) +{ + f81534a_ctrl_enable_all_ports(intf, false); +} + +static int f81534a_ctrl_resume(struct usb_interface *intf) +{ + return f81534a_ctrl_enable_all_ports(intf, true); +} + static int f81232_port_probe(struct usb_serial_port *port) { struct f81232_private *priv; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&port->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -748,14 +967,17 @@ static int f81232_port_probe(struct usb_serial_port *port) return 0; } -static int f81232_port_remove(struct usb_serial_port *port) +static int f81534a_port_probe(struct usb_serial_port *port) { - struct f81232_private *priv; + int status; - priv = usb_get_serial_port_data(port); - kfree(priv); + /* tri-state with pull-high, default RS232 Mode */ + status = f81232_set_register(port, F81534A_GPIO_REG, + F81534A_GPIO_MODE2_DIR); + if (status) + return status; - return 0; + return f81232_port_probe(port); } static int f81232_suspend(struct usb_serial *serial, pm_message_t message) @@ -799,7 +1021,7 @@ static struct usb_serial_driver f81232_device = { .owner = THIS_MODULE, .name = "f81232", }, - .id_table = id_table, + .id_table = f81232_id_table, .num_ports = 1, .bulk_in_size = 256, .bulk_out_size = 256, @@ -813,22 +1035,82 @@ static struct usb_serial_driver f81232_device = { .tiocmget = f81232_tiocmget, .tiocmset = f81232_tiocmset, .tiocmiwait = usb_serial_generic_tiocmiwait, + .tx_empty = f81232_tx_empty, .process_read_urb = f81232_process_read_urb, .read_int_callback = f81232_read_int_callback, .port_probe = f81232_port_probe, - .port_remove = f81232_port_remove, + .suspend = f81232_suspend, + .resume = f81232_resume, +}; + +static struct usb_serial_driver f81534a_device = { + .driver = { + .owner = THIS_MODULE, + .name = "f81534a", + }, + .id_table = f81534a_id_table, + .num_ports = 1, + .open = f81534a_open, + .close = f81232_close, + .dtr_rts = f81232_dtr_rts, + .carrier_raised = f81232_carrier_raised, + .get_serial = f81232_get_serial_info, + .break_ctl = f81232_break_ctl, + .set_termios = f81232_set_termios, + .tiocmget = f81232_tiocmget, + .tiocmset = f81232_tiocmset, + .tiocmiwait = usb_serial_generic_tiocmiwait, + .tx_empty = f81232_tx_empty, + .process_read_urb = f81534a_process_read_urb, + .read_int_callback = f81232_read_int_callback, + .port_probe = f81534a_port_probe, .suspend = f81232_suspend, .resume = f81232_resume, }; static struct usb_serial_driver * const serial_drivers[] = { &f81232_device, + &f81534a_device, NULL, }; -module_usb_serial_driver(serial_drivers, id_table); +static struct usb_driver f81534a_ctrl_driver = { + .name = "f81534a_ctrl", + .id_table = f81534a_ctrl_id_table, + .probe = f81534a_ctrl_probe, + .disconnect = f81534a_ctrl_disconnect, + .resume = f81534a_ctrl_resume, +}; + +static int __init f81232_init(void) +{ + int status; + + status = usb_register_driver(&f81534a_ctrl_driver, THIS_MODULE, + KBUILD_MODNAME); + if (status) + return status; + + status = usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME, + combined_id_table); + if (status) { + usb_deregister(&f81534a_ctrl_driver); + return status; + } + + return 0; +} + +static void __exit f81232_exit(void) +{ + usb_serial_deregister_drivers(serial_drivers); + usb_deregister(&f81534a_ctrl_driver); +} + +module_init(f81232_init); +module_exit(f81232_exit); -MODULE_DESCRIPTION("Fintek F81232 USB to serial adaptor driver"); +MODULE_DESCRIPTION("Fintek F81232/532A/534A/535/536 USB to serial driver"); MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>"); MODULE_AUTHOR("Peter Hong <peter_hong@fintek.com.tw>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 1be8bea372a2..5cdf180cda23 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -417,7 +417,7 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb) /* * Make sure URB is marked as free before checking the throttled flag * to avoid racing with unthrottle() on another CPU. Matches the - * smp_mb() in unthrottle(). + * smp_mb__after_atomic() in unthrottle(). */ smp_mb__after_atomic(); @@ -489,7 +489,7 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty) * Matches the smp_mb__after_atomic() in * usb_serial_generic_read_bulk_callback(). */ - smp_mb(); + smp_mb__after_atomic(); usb_serial_generic_submit_read_urbs(port, GFP_KERNEL); } @@ -609,12 +609,10 @@ EXPORT_SYMBOL_GPL(usb_serial_handle_break); * @tty: tty for the port * @status: new carrier detect status, nonzero if active */ -void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port, +void usb_serial_handle_dcd_change(struct usb_serial_port *port, struct tty_struct *tty, unsigned int status) { - struct tty_port *port = &usb_port->port; - - dev_dbg(&usb_port->dev, "%s - status %d\n", __func__, status); + dev_dbg(&port->dev, "%s - status %d\n", __func__, status); if (tty) { struct tty_ldisc *ld = tty_ldisc_ref(tty); @@ -627,7 +625,7 @@ void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port, } if (status) - wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->port.open_wait); else if (tty && !C_CLOCAL(tty)) tty_hangup(tty); } diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 5737add6a2a4..4cca0b836f43 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -710,7 +710,7 @@ static void edge_interrupt_callback(struct urb *urb) /* grab the txcredits for the ports if available */ position = 2; portNumber = 0; - while ((position < length) && + while ((position < length - 1) && (portNumber < edge_serial->serial->num_ports)) { txCredits = data[position] | (data[position+1] << 8); if (txCredits) { diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 0b5dcf973d94..8bfffca3e4ae 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1992,8 +1992,14 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x7e11, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/A3 */ + { USB_DEVICE_INTERFACE_CLASS(0x1435, 0xd191, 0xff), /* Wistron Neweb D19Q1 */ + .driver_info = RSVD(1) | RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(0x1690, 0x7588, 0xff), /* ASKEY WWHC050 */ + .driver_info = RSVD(1) | RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(0x2020, 0x2031, 0xff), /* Olicard 600 */ .driver_info = RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(0x2020, 0x2033, 0xff), /* BroadMobi BM806U */ + .driver_info = RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(0x2020, 0x2060, 0xff), /* BroadMobi BM818 */ .driver_info = RSVD(4) }, { USB_DEVICE_INTERFACE_CLASS(0x2020, 0x4000, 0xff) }, /* OLICARD300 - MT6225 */ diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index dc7a65b9ec98..27e3bb58c872 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -288,7 +288,7 @@ static void serial_close(struct tty_struct *tty, struct file *filp) /** * serial_cleanup - free resources post close/hangup - * @port: port to free up + * @tty: tty to clean up * * Do the resource freeing and refcount dropping for the port. * Avoid freeing the console. |