diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2021-02-10 15:58:04 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2021-02-10 15:58:04 +0100 |
commit | c85bfed171aaa91a32dcecd7962a4c880bf9d0ab (patch) | |
tree | 28fe832bc1a8206e0b0ca15bea7a869f1e0c12d4 /drivers/usb | |
parent | 43861d29c0810a70792bf69d37482efb7bb6677d (diff) | |
parent | 1542d1324be1191d970f69c55e885af5dd810b84 (diff) |
Merge tag 'usb-serial-5.12-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial into usb-next
Johan writes:
USB-serial updates for 5.12-rc1
Here are the USB-serial updates for 5.12-rc1, including:
- a line-speed fix for newer pl2303 devices
- a line-speed fix for FTDI FT-X devices
- a new xr_serial driver for MaxLinear/Exar devices (non-ACM mode)
- a cdc-acm blacklist entry for when the xr_serial driver is enabled
- cp210x support for software flow control
- various cp210x modem-control fixes
- an updated ZTE P685M modem entry to stop claiming the QMI interface
- an update to drop the port_remove() driver-callback return value
Included are also various clean ups.
All have been in linux-next with no reported issues.
* tag 'usb-serial-5.12-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial: (41 commits)
USB: serial: drop bogus to_usb_serial_port() checks
USB: serial: make remove callback return void
USB: serial: drop if with an always false condition
USB: serial: option: update interface mapping for ZTE P685M
USB: serial: ftdi_sio: restore divisor-encoding comments
USB: serial: ftdi_sio: fix FTX sub-integer prescaler
USB: serial: cp210x: clean up auto-RTS handling
USB: serial: cp210x: fix RTS handling
USB: serial: cp210x: clean up printk zero padding
USB: serial: cp210x: clean up flow-control debug message
USB: serial: cp210x: drop shift macros
USB: serial: cp210x: fix modem-control handling
USB: serial: cp210x: suppress modem-control errors
USB: serial: mos7720: fix error code in mos7720_write()
USB: serial: xr: fix B0 handling
USB: serial: xr: fix pin configuration
USB: serial: xr: fix gpio-mode handling
USB: serial: xr: simplify line-speed logic
USB: serial: xr: clean up line-settings handling
USB: serial: xr: document vendor-request recipient
...
Diffstat (limited to 'drivers/usb')
43 files changed, 887 insertions, 224 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 781905745812..37f824b59daa 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1901,6 +1901,12 @@ static const struct usb_device_id acm_ids[] = { }, #endif +#if IS_ENABLED(CONFIG_USB_SERIAL_XR) + { USB_DEVICE(0x04e2, 0x1410), /* Ignore XR21V141X USB to Serial converter */ + .driver_info = IGNORE_DEVICE, + }, +#endif + /*Samsung phone in firmware update mode */ { USB_DEVICE(0x04e8, 0x685d), .driver_info = IGNORE_DEVICE, diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index a21ff5ab6df9..de5c01257060 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -633,6 +633,15 @@ config USB_SERIAL_UPD78F0730 To compile this driver as a module, choose M here: the module will be called upd78f0730. +config USB_SERIAL_XR + tristate "USB MaxLinear/Exar USB to Serial driver" + help + Say Y here if you want to use MaxLinear/Exar USB to Serial converter + devices. + + To compile this driver as a module, choose M here: the + module will be called xr_serial. + config USB_SERIAL_DEBUG tristate "USB Debugging Device" help diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index 50c53aed787a..c7bb1a88173e 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -61,4 +61,5 @@ 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 +obj-$(CONFIG_USB_SERIAL_XR) += xr_serial.o obj-$(CONFIG_USB_SERIAL_XSENS_MT) += xsens_mt.o diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 71a9206ea1e2..f0ac7bb07ac1 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -178,15 +178,13 @@ static int ark3116_port_probe(struct usb_serial_port *port) return 0; } -static int ark3116_port_remove(struct usb_serial_port *port) +static void ark3116_port_remove(struct usb_serial_port *port) { struct ark3116_private *priv = usb_get_serial_port_data(port); /* device is closed, so URBs and DMA should be down */ mutex_destroy(&priv->hw_lock); kfree(priv); - - return 0; } static void ark3116_set_termios(struct tty_struct *tty, diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 9bb123ab9bc9..ed9193f3bb1a 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -37,7 +37,7 @@ /* function prototypes for a Belkin USB Serial Adapter F5U103 */ static int belkin_sa_port_probe(struct usb_serial_port *port); -static int belkin_sa_port_remove(struct usb_serial_port *port); +static void belkin_sa_port_remove(struct usb_serial_port *port); static int belkin_sa_open(struct tty_struct *tty, struct usb_serial_port *port); static void belkin_sa_close(struct usb_serial_port *port); @@ -134,14 +134,12 @@ static int belkin_sa_port_probe(struct usb_serial_port *port) return 0; } -static int belkin_sa_port_remove(struct usb_serial_port *port) +static void belkin_sa_port_remove(struct usb_serial_port *port) { struct belkin_sa_private *priv; priv = usb_get_serial_port_data(port); kfree(priv); - - return 0; } static int belkin_sa_open(struct tty_struct *tty, diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index eb0195cf37dd..7133818a58b9 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -16,19 +16,13 @@ static int usb_serial_device_match(struct device *dev, struct device_driver *drv) { - struct usb_serial_driver *driver; - const struct usb_serial_port *port; + const struct usb_serial_port *port = to_usb_serial_port(dev); + struct usb_serial_driver *driver = to_usb_serial_driver(drv); /* * drivers are already assigned to ports in serial_probe so it's * a simple check here. */ - port = to_usb_serial_port(dev); - if (!port) - return 0; - - driver = to_usb_serial_driver(drv); - if (driver == port->serial->type) return 1; @@ -37,16 +31,12 @@ static int usb_serial_device_match(struct device *dev, static int usb_serial_device_probe(struct device *dev) { + struct usb_serial_port *port = to_usb_serial_port(dev); struct usb_serial_driver *driver; - struct usb_serial_port *port; struct device *tty_dev; int retval = 0; int minor; - port = to_usb_serial_port(dev); - if (!port) - return -ENODEV; - /* make sure suspend/resume doesn't race against port_probe */ retval = usb_autopm_get_interface(port->serial->interface); if (retval) @@ -86,16 +76,11 @@ err_autopm_put: static int usb_serial_device_remove(struct device *dev) { + struct usb_serial_port *port = to_usb_serial_port(dev); struct usb_serial_driver *driver; - struct usb_serial_port *port; - int retval = 0; int minor; int autopm_err; - port = to_usb_serial_port(dev); - if (!port) - return -ENODEV; - /* * Make sure suspend/resume doesn't race against port_remove. * @@ -109,7 +94,7 @@ static int usb_serial_device_remove(struct device *dev) driver = port->serial->type; if (driver->port_remove) - retval = driver->port_remove(port); + driver->port_remove(port); dev_info(dev, "%s converter now disconnected from ttyUSB%d\n", driver->description, minor); @@ -117,7 +102,7 @@ static int usb_serial_device_remove(struct device *dev) if (!autopm_err) usb_autopm_put_interface(port->serial->interface); - return retval; + return 0; } static ssize_t new_id_store(struct device_driver *driver, diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 28deaaec581f..8d997b71056f 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -419,14 +419,12 @@ error: kfree(priv); return r; } -static int ch341_port_remove(struct usb_serial_port *port) +static void ch341_port_remove(struct usb_serial_port *port) { struct ch341_private *priv; priv = usb_get_serial_port_data(port); kfree(priv); - - return 0; } static int ch341_carrier_raised(struct usb_serial_port *port) diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 7bec1e730b20..9e1c609792fb 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -3,6 +3,7 @@ * Silicon Laboratories CP210x USB to RS232 serial adaptor driver * * Copyright (C) 2005 Craig Shelley (craig@microtron.org.uk) + * Copyright (C) 2010-2021 Johan Hovold (johan@kernel.org) * * Support to set flow control line levels using TIOCMGET and TIOCMSET * thanks to Karl Hiramoto karl@hiramoto.org. RTSCTS hardware flow @@ -16,9 +17,7 @@ #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/usb.h> -#include <linux/uaccess.h> #include <linux/usb/serial.h> #include <linux/gpio/driver.h> #include <linux/bitops.h> @@ -45,7 +44,7 @@ static int cp210x_attach(struct usb_serial *); static void cp210x_disconnect(struct usb_serial *); static void cp210x_release(struct usb_serial *); static int cp210x_port_probe(struct usb_serial_port *); -static int cp210x_port_remove(struct usb_serial_port *); +static void cp210x_port_remove(struct usb_serial_port *); static void cp210x_dtr_rts(struct usb_serial_port *port, int on); static void cp210x_process_read_urb(struct urb *urb); static void cp210x_enable_event_mode(struct usb_serial_port *port); @@ -268,7 +267,12 @@ struct cp210x_port_private { u8 bInterfaceNumber; bool event_mode; enum cp210x_event_state event_state; - u8 lsr; + u8 lsr; + + struct mutex mutex; + bool crtscts; + bool dtr; + bool rts; }; static struct usb_serial_driver cp210x_device = { @@ -379,6 +383,16 @@ static struct usb_serial_driver * const serial_drivers[] = { #define CONTROL_WRITE_DTR 0x0100 #define CONTROL_WRITE_RTS 0x0200 +/* CP210X_(GET|SET)_CHARS */ +struct cp210x_special_chars { + u8 bEofChar; + u8 bErrorChar; + u8 bBreakChar; + u8 bEventChar; + u8 bXonChar; + u8 bXoffChar; +}; + /* CP210X_VENDOR_SPECIFIC values */ #define CP210X_READ_2NCONFIG 0x000E #define CP210X_READ_LATCH 0x00C2 @@ -437,17 +451,14 @@ struct cp210x_flow_ctl { /* cp210x_flow_ctl::ulControlHandshake */ #define CP210X_SERIAL_DTR_MASK GENMASK(1, 0) -#define CP210X_SERIAL_DTR_SHIFT(_mode) (_mode) +#define CP210X_SERIAL_DTR_INACTIVE (0 << 0) +#define CP210X_SERIAL_DTR_ACTIVE (1 << 0) +#define CP210X_SERIAL_DTR_FLOW_CTL (2 << 0) #define CP210X_SERIAL_CTS_HANDSHAKE BIT(3) #define CP210X_SERIAL_DSR_HANDSHAKE BIT(4) #define CP210X_SERIAL_DCD_HANDSHAKE BIT(5) #define CP210X_SERIAL_DSR_SENSITIVITY BIT(6) -/* values for cp210x_flow_ctl::ulControlHandshake::CP210X_SERIAL_DTR_MASK */ -#define CP210X_SERIAL_DTR_INACTIVE 0 -#define CP210X_SERIAL_DTR_ACTIVE 1 -#define CP210X_SERIAL_DTR_FLOW_CTL 2 - /* cp210x_flow_ctl::ulFlowReplace */ #define CP210X_SERIAL_AUTO_TRANSMIT BIT(0) #define CP210X_SERIAL_AUTO_RECEIVE BIT(1) @@ -455,14 +466,11 @@ struct cp210x_flow_ctl { #define CP210X_SERIAL_NULL_STRIPPING BIT(3) #define CP210X_SERIAL_BREAK_CHAR BIT(4) #define CP210X_SERIAL_RTS_MASK GENMASK(7, 6) -#define CP210X_SERIAL_RTS_SHIFT(_mode) (_mode << 6) +#define CP210X_SERIAL_RTS_INACTIVE (0 << 6) +#define CP210X_SERIAL_RTS_ACTIVE (1 << 6) +#define CP210X_SERIAL_RTS_FLOW_CTL (2 << 6) #define CP210X_SERIAL_XOFF_CONTINUE BIT(31) -/* values for cp210x_flow_ctl::ulFlowReplace::CP210X_SERIAL_RTS_MASK */ -#define CP210X_SERIAL_RTS_INACTIVE 0 -#define CP210X_SERIAL_RTS_ACTIVE 1 -#define CP210X_SERIAL_RTS_FLOW_CTL 2 - /* CP210X_VENDOR_SPECIFIC, CP210X_GET_DEVICEMODE call reads these 0x2 bytes. */ struct cp210x_pin_mode { u8 eci; @@ -666,16 +674,13 @@ static int cp210x_write_reg_block(struct usb_serial_port *port, u8 req, kfree(dmabuf); - if (result == bufsize) { - result = 0; - } else { + if (result < 0) { dev_err(&port->dev, "failed set req 0x%x size %d status: %d\n", req, bufsize, result); - if (result >= 0) - result = -EIO; + return result; } - return result; + return 0; } /* @@ -712,17 +717,14 @@ static int cp210x_write_vendor_block(struct usb_serial *serial, u8 type, kfree(dmabuf); - if (result == bufsize) { - result = 0; - } else { + if (result < 0) { dev_err(&serial->interface->dev, "failed to set vendor val 0x%04x size %d: %d\n", val, bufsize, result); - if (result >= 0) - result = -EIO; + return result; } - return result; + return 0; } #endif @@ -1076,30 +1078,80 @@ static void cp210x_disable_event_mode(struct usb_serial_port *port) port_priv->event_mode = false; } +static int cp210x_set_chars(struct usb_serial_port *port, + struct cp210x_special_chars *chars) +{ + struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); + struct usb_serial *serial = port->serial; + void *dmabuf; + int result; + + dmabuf = kmemdup(chars, sizeof(*chars), GFP_KERNEL); + if (!dmabuf) + return -ENOMEM; + + result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + CP210X_SET_CHARS, REQTYPE_HOST_TO_INTERFACE, 0, + port_priv->bInterfaceNumber, + dmabuf, sizeof(*chars), USB_CTRL_SET_TIMEOUT); + + kfree(dmabuf); + + if (result < 0) { + dev_err(&port->dev, "failed to set special chars: %d\n", result); + return result; + } + + return 0; +} + static bool cp210x_termios_change(const struct ktermios *a, const struct ktermios *b) { - bool iflag_change; + bool iflag_change, cc_change; - iflag_change = ((a->c_iflag ^ b->c_iflag) & INPCK); + iflag_change = ((a->c_iflag ^ b->c_iflag) & (INPCK | IXON | IXOFF)); + cc_change = a->c_cc[VSTART] != b->c_cc[VSTART] || + a->c_cc[VSTOP] != b->c_cc[VSTOP]; - return tty_termios_hw_change(a, b) || iflag_change; + return tty_termios_hw_change(a, b) || iflag_change || cc_change; } static void cp210x_set_flow_control(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { + struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); + struct cp210x_special_chars chars; struct cp210x_flow_ctl flow_ctl; u32 flow_repl; u32 ctl_hs; int ret; - if (old_termios && C_CRTSCTS(tty) == (old_termios->c_cflag & CRTSCTS)) + if (old_termios && + C_CRTSCTS(tty) == (old_termios->c_cflag & CRTSCTS) && + I_IXON(tty) == (old_termios->c_iflag & IXON) && + I_IXOFF(tty) == (old_termios->c_iflag & IXOFF) && + START_CHAR(tty) == old_termios->c_cc[VSTART] && + STOP_CHAR(tty) == old_termios->c_cc[VSTOP]) { return; + } + + if (I_IXON(tty) || I_IXOFF(tty)) { + memset(&chars, 0, sizeof(chars)); + + chars.bXonChar = START_CHAR(tty); + chars.bXoffChar = STOP_CHAR(tty); + + ret = cp210x_set_chars(port, &chars); + if (ret) + return; + } + + mutex_lock(&port_priv->mutex); ret = cp210x_read_reg_block(port, CP210X_GET_FLOW, &flow_ctl, sizeof(flow_ctl)); if (ret) - return; + goto out_unlock; ctl_hs = le32_to_cpu(flow_ctl.ulControlHandshake); flow_repl = le32_to_cpu(flow_ctl.ulFlowReplace); @@ -1108,26 +1160,51 @@ static void cp210x_set_flow_control(struct tty_struct *tty, ctl_hs &= ~CP210X_SERIAL_DCD_HANDSHAKE; ctl_hs &= ~CP210X_SERIAL_DSR_SENSITIVITY; ctl_hs &= ~CP210X_SERIAL_DTR_MASK; - ctl_hs |= CP210X_SERIAL_DTR_SHIFT(CP210X_SERIAL_DTR_ACTIVE); + if (port_priv->dtr) + ctl_hs |= CP210X_SERIAL_DTR_ACTIVE; + else + ctl_hs |= CP210X_SERIAL_DTR_INACTIVE; + flow_repl &= ~CP210X_SERIAL_RTS_MASK; if (C_CRTSCTS(tty)) { ctl_hs |= CP210X_SERIAL_CTS_HANDSHAKE; - flow_repl &= ~CP210X_SERIAL_RTS_MASK; - flow_repl |= CP210X_SERIAL_RTS_SHIFT(CP210X_SERIAL_RTS_FLOW_CTL); + if (port_priv->rts) + flow_repl |= CP210X_SERIAL_RTS_FLOW_CTL; + else + flow_repl |= CP210X_SERIAL_RTS_INACTIVE; + port_priv->crtscts = true; } else { ctl_hs &= ~CP210X_SERIAL_CTS_HANDSHAKE; - flow_repl &= ~CP210X_SERIAL_RTS_MASK; - flow_repl |= CP210X_SERIAL_RTS_SHIFT(CP210X_SERIAL_RTS_ACTIVE); + if (port_priv->rts) + flow_repl |= CP210X_SERIAL_RTS_ACTIVE; + else + flow_repl |= CP210X_SERIAL_RTS_INACTIVE; + port_priv->crtscts = false; } - dev_dbg(&port->dev, "%s - ulControlHandshake=0x%08x, ulFlowReplace=0x%08x\n", - __func__, ctl_hs, flow_repl); + if (I_IXOFF(tty)) + flow_repl |= CP210X_SERIAL_AUTO_RECEIVE; + else + flow_repl &= ~CP210X_SERIAL_AUTO_RECEIVE; + + if (I_IXON(tty)) + flow_repl |= CP210X_SERIAL_AUTO_TRANSMIT; + else + flow_repl &= ~CP210X_SERIAL_AUTO_TRANSMIT; + + flow_ctl.ulXonLimit = cpu_to_le32(128); + flow_ctl.ulXoffLimit = cpu_to_le32(128); + + dev_dbg(&port->dev, "%s - ctrl = 0x%02x, flow = 0x%02x\n", __func__, + ctl_hs, flow_repl); flow_ctl.ulControlHandshake = cpu_to_le32(ctl_hs); flow_ctl.ulFlowReplace = cpu_to_le32(flow_repl); cp210x_write_reg_block(port, CP210X_SET_FLOW, &flow_ctl, sizeof(flow_ctl)); +out_unlock: + mutex_unlock(&port_priv->mutex); } static void cp210x_set_termios(struct tty_struct *tty, @@ -1212,28 +1289,77 @@ static int cp210x_tiocmset(struct tty_struct *tty, static int cp210x_tiocmset_port(struct usb_serial_port *port, unsigned int set, unsigned int clear) { + struct cp210x_port_private *port_priv = usb_get_serial_port_data(port); + struct cp210x_flow_ctl flow_ctl; + u32 ctl_hs, flow_repl; u16 control = 0; + int ret; + + mutex_lock(&port_priv->mutex); if (set & TIOCM_RTS) { + port_priv->rts = true; control |= CONTROL_RTS; control |= CONTROL_WRITE_RTS; } if (set & TIOCM_DTR) { + port_priv->dtr = true; control |= CONTROL_DTR; control |= CONTROL_WRITE_DTR; } if (clear & TIOCM_RTS) { + port_priv->rts = false; control &= ~CONTROL_RTS; control |= CONTROL_WRITE_RTS; } if (clear & TIOCM_DTR) { + port_priv->dtr = false; control &= ~CONTROL_DTR; control |= CONTROL_WRITE_DTR; } - dev_dbg(&port->dev, "%s - control = 0x%.4x\n", __func__, control); + /* + * Use SET_FLOW to set DTR and enable/disable auto-RTS when hardware + * flow control is enabled. + */ + if (port_priv->crtscts && control & CONTROL_WRITE_RTS) { + ret = cp210x_read_reg_block(port, CP210X_GET_FLOW, &flow_ctl, + sizeof(flow_ctl)); + if (ret) + goto out_unlock; + + ctl_hs = le32_to_cpu(flow_ctl.ulControlHandshake); + flow_repl = le32_to_cpu(flow_ctl.ulFlowReplace); + + ctl_hs &= ~CP210X_SERIAL_DTR_MASK; + if (port_priv->dtr) + ctl_hs |= CP210X_SERIAL_DTR_ACTIVE; + else + ctl_hs |= CP210X_SERIAL_DTR_INACTIVE; - return cp210x_write_u16_reg(port, CP210X_SET_MHS, control); + flow_repl &= ~CP210X_SERIAL_RTS_MASK; + if (port_priv->rts) + flow_repl |= CP210X_SERIAL_RTS_FLOW_CTL; + else + flow_repl |= CP210X_SERIAL_RTS_INACTIVE; + + flow_ctl.ulControlHandshake = cpu_to_le32(ctl_hs); + flow_ctl.ulFlowReplace = cpu_to_le32(flow_repl); + + dev_dbg(&port->dev, "%s - ctrl = 0x%02x, flow = 0x%02x\n", + __func__, ctl_hs, flow_repl); + + ret = cp210x_write_reg_block(port, CP210X_SET_FLOW, &flow_ctl, + sizeof(flow_ctl)); + } else { + dev_dbg(&port->dev, "%s - control = 0x%04x\n", __func__, control); + + ret = cp210x_write_u16_reg(port, CP210X_SET_MHS, control); + } +out_unlock: + mutex_unlock(&port_priv->mutex); + + return ret; } static void cp210x_dtr_rts(struct usb_serial_port *port, int on) @@ -1261,7 +1387,7 @@ static int cp210x_tiocmget(struct tty_struct *tty) |((control & CONTROL_RING)? TIOCM_RI : 0) |((control & CONTROL_DCD) ? TIOCM_CD : 0); - dev_dbg(&port->dev, "%s - control = 0x%.2x\n", __func__, control); + dev_dbg(&port->dev, "%s - control = 0x%02x\n", __func__, control); return result; } @@ -1710,20 +1836,19 @@ static int cp210x_port_probe(struct usb_serial_port *port) return -ENOMEM; port_priv->bInterfaceNumber = cp210x_interface_num(serial); + mutex_init(&port_priv->mutex); usb_set_serial_port_data(port, port_priv); return 0; } -static int cp210x_port_remove(struct usb_serial_port *port) +static void cp210x_port_remove(struct usb_serial_port *port) { struct cp210x_port_private *port_priv; port_priv = usb_get_serial_port_data(port); kfree(port_priv); - - return 0; } static void cp210x_init_max_speed(struct usb_serial *serial) diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 2e40908963da..cf389224d528 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -47,7 +47,7 @@ /* Function prototypes */ static int cyberjack_port_probe(struct usb_serial_port *port); -static int cyberjack_port_remove(struct usb_serial_port *port); +static void cyberjack_port_remove(struct usb_serial_port *port); static int cyberjack_open(struct tty_struct *tty, struct usb_serial_port *port); static void cyberjack_close(struct usb_serial_port *port); @@ -120,7 +120,7 @@ static int cyberjack_port_probe(struct usb_serial_port *port) return 0; } -static int cyberjack_port_remove(struct usb_serial_port *port) +static void cyberjack_port_remove(struct usb_serial_port *port) { struct cyberjack_private *priv; @@ -128,8 +128,6 @@ static int cyberjack_port_remove(struct usb_serial_port *port) priv = usb_get_serial_port_data(port); kfree(priv); - - return 0; } static int cyberjack_open(struct tty_struct *tty, diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index cc028601c388..166ee2286fda 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -115,7 +115,7 @@ struct cypress_private { static int cypress_earthmate_port_probe(struct usb_serial_port *port); static int cypress_hidcom_port_probe(struct usb_serial_port *port); static int cypress_ca42v2_port_probe(struct usb_serial_port *port); -static int cypress_port_remove(struct usb_serial_port *port); +static void cypress_port_remove(struct usb_serial_port *port); static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port); static void cypress_close(struct usb_serial_port *port); static void cypress_dtr_rts(struct usb_serial_port *port, int on); @@ -564,7 +564,7 @@ static int cypress_ca42v2_port_probe(struct usb_serial_port *port) return 0; } -static int cypress_port_remove(struct usb_serial_port *port) +static void cypress_port_remove(struct usb_serial_port *port) { struct cypress_private *priv; @@ -572,8 +572,6 @@ static int cypress_port_remove(struct usb_serial_port *port) kfifo_free(&priv->write_fifo); kfree(priv); - - return 0; } static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port) diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 0ecd5316d85f..8b2f06539f2c 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -233,7 +233,7 @@ static int digi_startup(struct usb_serial *serial); static void digi_disconnect(struct usb_serial *serial); static void digi_release(struct usb_serial *serial); static int digi_port_probe(struct usb_serial_port *port); -static int digi_port_remove(struct usb_serial_port *port); +static void digi_port_remove(struct usb_serial_port *port); static void digi_read_bulk_callback(struct urb *urb); static int digi_read_inb_callback(struct urb *urb); static int digi_read_oob_callback(struct urb *urb); @@ -1281,14 +1281,12 @@ static int digi_port_probe(struct usb_serial_port *port) return digi_port_init(port, port->port_number); } -static int digi_port_remove(struct usb_serial_port *port) +static void digi_port_remove(struct usb_serial_port *port) { struct digi_port *priv; priv = usb_get_serial_port_data(port); kfree(priv); - - return 0; } static void digi_read_bulk_callback(struct urb *urb) diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 0c7eacc630e0..6a8f39147d8e 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -192,13 +192,9 @@ static int f81232_set_register(struct usb_serial_port *port, u16 reg, u8 val) tmp, sizeof(val), USB_CTRL_SET_TIMEOUT); - if (status != sizeof(val)) { + if (status < 0) { dev_err(&port->dev, "%s failed status: %d\n", __func__, status); - - if (status < 0) - status = usb_translate_errors(status); - else - status = -EIO; + status = usb_translate_errors(status); } else { status = 0; } @@ -886,10 +882,6 @@ static int f81534a_ctrl_set_register(struct usb_interface *intf, u16 reg, status = usb_translate_errors(status); if (status == -EIO) continue; - } else if (status != size) { - /* Retry on short transfers */ - status = -EIO; - continue; } else { status = 0; } diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 5661fd03e545..a763b362f081 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -235,11 +235,9 @@ static int f81534_set_register(struct usb_serial *serial, u16 reg, u8 data) USB_TYPE_VENDOR | USB_DIR_OUT, reg, 0, tmp, sizeof(u8), F81534_USB_TIMEOUT); - if (status > 0) { + if (status == sizeof(u8)) { status = 0; break; - } else if (status == 0) { - status = -EIO; } } @@ -1432,12 +1430,11 @@ static int f81534_port_probe(struct usb_serial_port *port) return f81534_set_port_output_pin(port); } -static int f81534_port_remove(struct usb_serial_port *port) +static void f81534_port_remove(struct usb_serial_port *port) { struct f81534_port_private *port_priv = usb_get_serial_port_data(port); flush_work(&port_priv->lsr_work); - return 0; } static int f81534_tiocmget(struct tty_struct *tty) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 94398f89e600..c867592477c9 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1069,7 +1069,7 @@ static const char *ftdi_chip_name[] = { static int ftdi_sio_probe(struct usb_serial *serial, const struct usb_device_id *id); static int ftdi_sio_port_probe(struct usb_serial_port *port); -static int ftdi_sio_port_remove(struct usb_serial_port *port); +static void ftdi_sio_port_remove(struct usb_serial_port *port); static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port); static void ftdi_dtr_rts(struct usb_serial_port *port, int on); static void ftdi_process_read_urb(struct urb *urb); @@ -1153,13 +1153,13 @@ static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base) divisor = divisor3 >> 3; divisor3 &= 0x7; if (divisor3 == 1) - divisor |= 0xc000; + divisor |= 0xc000; /* +0.125 */ else if (divisor3 >= 4) - divisor |= 0x4000; + divisor |= 0x4000; /* +0.5 */ else if (divisor3 != 0) - divisor |= 0x8000; + divisor |= 0x8000; /* +0.25 */ else if (divisor == 1) - divisor = 0; /* special case for maximum baud rate */ + divisor = 0; /* special case for maximum baud rate */ return divisor; } @@ -1177,9 +1177,9 @@ static u32 ftdi_232bm_baud_base_to_divisor(int baud, int base) divisor = divisor3 >> 3; divisor |= (u32)divfrac[divisor3 & 0x7] << 14; /* Deal with special cases for highest baud rates. */ - if (divisor == 1) + if (divisor == 1) /* 1.0 */ divisor = 0; - else if (divisor == 0x4001) + else if (divisor == 0x4001) /* 1.5 */ divisor = 1; return divisor; } @@ -1201,9 +1201,9 @@ static u32 ftdi_2232h_baud_base_to_divisor(int baud, int base) divisor = divisor3 >> 3; divisor |= (u32)divfrac[divisor3 & 0x7] << 14; /* Deal with special cases for highest baud rates. */ - if (divisor == 1) + if (divisor == 1) /* 1.0 */ divisor = 0; - else if (divisor == 0x4001) + else if (divisor == 0x4001) /* 1.5 */ divisor = 1; /* * Set this bit to turn off a divide by 2.5 on baud rate generator @@ -1386,8 +1386,9 @@ static int change_speed(struct tty_struct *tty, struct usb_serial_port *port) index_value = get_ftdi_divisor(tty, port); value = (u16)index_value; index = (u16)(index_value >> 16); - if ((priv->chip_type == FT2232C) || (priv->chip_type == FT2232H) || - (priv->chip_type == FT4232H) || (priv->chip_type == FT232H)) { + if (priv->chip_type == FT2232C || priv->chip_type == FT2232H || + priv->chip_type == FT4232H || priv->chip_type == FT232H || + priv->chip_type == FTX) { /* Probably the BM type needs the MSB of the encoded fractional * divider also moved like for the chips above. Any infos? */ index = (u16)((index << 8) | priv->interface); @@ -2399,7 +2400,7 @@ static int ftdi_stmclite_probe(struct usb_serial *serial) return 0; } -static int ftdi_sio_port_remove(struct usb_serial_port *port) +static void ftdi_sio_port_remove(struct usb_serial_port *port) { struct ftdi_private *priv = usb_get_serial_port_data(port); @@ -2408,8 +2409,6 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port) remove_sysfs_attrs(port); kfree(priv); - - return 0; } static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port) diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index c02c19bb1183..50e8bdc77e71 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1401,7 +1401,7 @@ err_free: } -static int garmin_port_remove(struct usb_serial_port *port) +static void garmin_port_remove(struct usb_serial_port *port) { struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); @@ -1409,7 +1409,6 @@ static int garmin_port_remove(struct usb_serial_port *port) usb_kill_urb(port->interrupt_in_urb); del_timer_sync(&garmin_data_p->timer); kfree(garmin_data_p); - return 0; } diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index ba5d8df69518..a493670c06e6 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -293,7 +293,7 @@ static int edge_startup(struct usb_serial *serial); static void edge_disconnect(struct usb_serial *serial); static void edge_release(struct usb_serial *serial); static int edge_port_probe(struct usb_serial_port *port); -static int edge_port_remove(struct usb_serial_port *port); +static void edge_port_remove(struct usb_serial_port *port); /* function prototypes for all of our local functions */ @@ -3078,14 +3078,12 @@ static int edge_port_probe(struct usb_serial_port *port) return 0; } -static int edge_port_remove(struct usb_serial_port *port) +static void edge_port_remove(struct usb_serial_port *port) { struct edgeport_port *edge_port; edge_port = usb_get_serial_port_data(port); kfree(edge_port); - - return 0; } static struct usb_serial_driver edgeport_2port_device = { diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index c327d4cf7928..e800547be9e0 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -266,7 +266,7 @@ static int ti_vread_sync(struct usb_device *dev, __u8 request, if (status < 0) return status; if (status != size) { - dev_dbg(&dev->dev, "%s - wanted to write %d, but only wrote %d\n", + dev_dbg(&dev->dev, "%s - wanted to read %d, but only read %d\n", __func__, size, status); return -ECOMM; } @@ -283,11 +283,7 @@ static int ti_vsend_sync(struct usb_device *dev, u8 request, u16 value, value, index, data, size, timeout); if (status < 0) return status; - if (status != size) { - dev_dbg(&dev->dev, "%s - wanted to write %d, but only wrote %d\n", - __func__, size, status); - return -ECOMM; - } + return 0; } @@ -2629,15 +2625,13 @@ err: return ret; } -static int edge_port_remove(struct usb_serial_port *port) +static void edge_port_remove(struct usb_serial_port *port) { struct edgeport_port *edge_port; edge_port = usb_get_serial_port_data(port); edge_remove_sysfs_attrs(port); kfree(edge_port); - - return 0; } /* Sysfs Attributes */ diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index e8f06b41a503..093afd67a664 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -100,7 +100,7 @@ static int iuu_port_probe(struct usb_serial_port *port) return 0; } -static int iuu_port_remove(struct usb_serial_port *port) +static void iuu_port_remove(struct usb_serial_port *port) { struct iuu_private *priv = usb_get_serial_port_data(port); @@ -108,8 +108,6 @@ static int iuu_port_remove(struct usb_serial_port *port) kfree(priv->writebuf); kfree(priv->buf); kfree(priv); - - return 0; } static int iuu_tiocmset(struct tty_struct *tty, diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index aa3dbce22cfb..622077dcc344 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -49,7 +49,7 @@ static int keyspan_startup(struct usb_serial *serial); static void keyspan_disconnect(struct usb_serial *serial); static void keyspan_release(struct usb_serial *serial); static int keyspan_port_probe(struct usb_serial_port *port); -static int keyspan_port_remove(struct usb_serial_port *port); +static void keyspan_port_remove(struct usb_serial_port *port); static int keyspan_write_room(struct tty_struct *tty); static int keyspan_write(struct tty_struct *tty, struct usb_serial_port *port, const unsigned char *buf, int count); @@ -2985,7 +2985,7 @@ err_in_buffer: return -ENOMEM; } -static int keyspan_port_remove(struct usb_serial_port *port) +static void keyspan_port_remove(struct usb_serial_port *port) { struct keyspan_port_private *p_priv; int i; @@ -3014,8 +3014,6 @@ static int keyspan_port_remove(struct usb_serial_port *port) kfree(p_priv->in_buffer[i]); kfree(p_priv); - - return 0; } /* Structs for the devices, pre and post renumeration. */ diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index e6f933e8d25f..39b0f5f344c2 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -672,14 +672,12 @@ static int keyspan_pda_port_probe(struct usb_serial_port *port) return 0; } -static int keyspan_pda_port_remove(struct usb_serial_port *port) +static void keyspan_pda_port_remove(struct usb_serial_port *port) { struct keyspan_pda_private *priv; priv = usb_get_serial_port_data(port); kfree(priv); - - return 0; } static struct usb_serial_driver keyspan_pda_fake_device = { diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 5f6b82ebccc5..f1e9628a9907 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -52,7 +52,7 @@ * Function prototypes */ static int klsi_105_port_probe(struct usb_serial_port *port); -static int klsi_105_port_remove(struct usb_serial_port *port); +static void klsi_105_port_remove(struct usb_serial_port *port); static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port); static void klsi_105_close(struct usb_serial_port *port); static void klsi_105_set_termios(struct tty_struct *tty, @@ -231,14 +231,12 @@ static int klsi_105_port_probe(struct usb_serial_port *port) return 0; } -static int klsi_105_port_remove(struct usb_serial_port *port) +static void klsi_105_port_remove(struct usb_serial_port *port) { struct klsi_105_private *priv; priv = usb_get_serial_port_data(port); kfree(priv); - - return 0; } static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port) diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 49aacb0a327c..a9bc546626ab 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -48,7 +48,7 @@ /* Function prototypes */ static int kobil_port_probe(struct usb_serial_port *probe); -static int kobil_port_remove(struct usb_serial_port *probe); +static void kobil_port_remove(struct usb_serial_port *probe); static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port); static void kobil_close(struct usb_serial_port *port); static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port, @@ -143,14 +143,12 @@ static int kobil_port_probe(struct usb_serial_port *port) } -static int kobil_port_remove(struct usb_serial_port *port) +static void kobil_port_remove(struct usb_serial_port *port) { struct kobil_private *priv; priv = usb_get_serial_port_data(port); kfree(priv); - - return 0; } static void kobil_init_termios(struct tty_struct *tty) diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 7887c312d9a9..ecd5b921e374 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -39,7 +39,7 @@ * Function prototypes */ static int mct_u232_port_probe(struct usb_serial_port *port); -static int mct_u232_port_remove(struct usb_serial_port *remove); +static void mct_u232_port_remove(struct usb_serial_port *remove); static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port); static void mct_u232_close(struct usb_serial_port *port); static void mct_u232_dtr_rts(struct usb_serial_port *port, int on); @@ -400,14 +400,12 @@ static int mct_u232_port_probe(struct usb_serial_port *port) return 0; } -static int mct_u232_port_remove(struct usb_serial_port *port) +static void mct_u232_port_remove(struct usb_serial_port *port) { struct mct_u232_private *priv; priv = usb_get_serial_port_data(port); kfree(priv); - - return 0; } static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port) diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index e63cea02cfd8..0bfe4459c37f 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -256,14 +256,12 @@ static int metrousb_port_probe(struct usb_serial_port *port) return 0; } -static int metrousb_port_remove(struct usb_serial_port *port) +static void metrousb_port_remove(struct usb_serial_port *port) { struct metrousb_private *metro_priv; metro_priv = usb_get_serial_port_data(port); kfree(metro_priv); - - return 0; } static void metrousb_throttle(struct tty_struct *tty) diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 41ee2984a0df..701dfb32b129 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -215,8 +215,10 @@ static int read_mos_reg(struct usb_serial *serial, unsigned int serial_portnum, int status; buf = kmalloc(1, GFP_KERNEL); - if (!buf) + if (!buf) { + *data = 0; return -ENOMEM; + } status = usb_control_msg(usbdev, pipe, request, requesttype, value, index, buf, 1, MOS_WDR_TIMEOUT); @@ -1092,8 +1094,10 @@ static int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port, if (urb->transfer_buffer == NULL) { urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_ATOMIC); - if (!urb->transfer_buffer) + if (!urb->transfer_buffer) { + bytes_sent = -ENOMEM; goto exit; + } } transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE); @@ -1756,14 +1760,12 @@ static int mos7720_port_probe(struct usb_serial_port *port) return 0; } -static int mos7720_port_remove(struct usb_serial_port *port) +static void mos7720_port_remove(struct usb_serial_port *port) { struct moschip_port *mos7720_port; mos7720_port = usb_get_serial_port_data(port); kfree(mos7720_port); - - return 0; } static struct usb_serial_driver moschip7720_2port_driver = { diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 23f91d658cb4..1bf0d066f55a 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -883,8 +883,10 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port, if (urb->transfer_buffer == NULL) { urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_ATOMIC); - if (!urb->transfer_buffer) + if (!urb->transfer_buffer) { + bytes_sent = -ENOMEM; goto exit; + } } transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE); @@ -1743,7 +1745,7 @@ error: return status; } -static int mos7840_port_remove(struct usb_serial_port *port) +static void mos7840_port_remove(struct usb_serial_port *port) { struct moschip_port *mos7840_port = usb_get_serial_port_data(port); @@ -1760,8 +1762,6 @@ static int mos7840_port_remove(struct usb_serial_port *port) } kfree(mos7840_port); - - return 0; } static struct usb_serial_driver moschip7840_4port_device = { diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index 5d38c2a0f590..eb45a9b0005c 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -261,13 +261,6 @@ static int mxuport_send_ctrl_data_urb(struct usb_serial *serial, return status; } - if (status != size) { - dev_err(&serial->interface->dev, - "%s - short write (%d / %zd)\n", - __func__, status, size); - return -EIO; - } - return 0; } diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 5b6e982a9376..83c62f920c50 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -36,7 +36,7 @@ static int omninet_prepare_write_buffer(struct usb_serial_port *port, static int omninet_calc_num_ports(struct usb_serial *serial, struct usb_serial_endpoints *epds); static int omninet_port_probe(struct usb_serial_port *port); -static int omninet_port_remove(struct usb_serial_port *port); +static void omninet_port_remove(struct usb_serial_port *port); static const struct usb_device_id id_table[] = { { USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) }, @@ -121,14 +121,12 @@ static int omninet_port_probe(struct usb_serial_port *port) return 0; } -static int omninet_port_remove(struct usb_serial_port *port) +static void omninet_port_remove(struct usb_serial_port *port) { struct omninet_data *od; od = usb_get_serial_port_data(port); kfree(od); - - return 0; } #define OMNINET_HEADERLEN 4 diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 0af76800bd78..eecb72aef83e 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -385,13 +385,11 @@ static int opticon_port_probe(struct usb_serial_port *port) return 0; } -static int opticon_port_remove(struct usb_serial_port *port) +static void opticon_port_remove(struct usb_serial_port *port) { struct opticon_private *priv = usb_get_serial_port_data(port); kfree(priv); - - return 0; } static struct usb_serial_driver opticon_device = { diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 2049e66f34a3..c6969ca72839 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1569,7 +1569,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1272, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1273, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1274, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1275, 0xff, 0xff, 0xff) }, + { USB_DEVICE(ZTE_VENDOR_ID, 0x1275), /* ZTE P685M */ + .driver_info = RSVD(3) | RSVD(4) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1276, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1277, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1278, 0xff, 0xff, 0xff) }, diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 8151dd7a45e8..65cd0341fa78 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -132,7 +132,7 @@ static int oti6858_tiocmget(struct tty_struct *tty); static int oti6858_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); static int oti6858_port_probe(struct usb_serial_port *port); -static int oti6858_port_remove(struct usb_serial_port *port); +static void oti6858_port_remove(struct usb_serial_port *port); /* device info */ static struct usb_serial_driver oti6858_device = { @@ -344,14 +344,12 @@ static int oti6858_port_probe(struct usb_serial_port *port) return 0; } -static int oti6858_port_remove(struct usb_serial_port *port) +static void oti6858_port_remove(struct usb_serial_port *port) { struct oti6858_private *priv; priv = usb_get_serial_port_data(port); kfree(priv); - - return 0; } static int oti6858_write(struct tty_struct *tty, struct usb_serial_port *port, diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index be8067017eaa..eed9acd1ae08 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -183,6 +183,7 @@ struct pl2303_type_data { speed_t max_baud_rate; unsigned long quirks; unsigned int no_autoxonxoff:1; + unsigned int no_divisors:1; }; struct pl2303_serial_private { @@ -209,6 +210,7 @@ static const struct pl2303_type_data pl2303_type_data[TYPE_COUNT] = { }, [TYPE_HXN] = { .max_baud_rate = 12000000, + .no_divisors = true, }, }; @@ -448,13 +450,11 @@ static int pl2303_port_probe(struct usb_serial_port *port) return 0; } -static int pl2303_port_remove(struct usb_serial_port *port) +static void pl2303_port_remove(struct usb_serial_port *port) { struct pl2303_private *priv = usb_get_serial_port_data(port); kfree(priv); - - return 0; } static int pl2303_set_control_lines(struct usb_serial_port *port, u8 value) @@ -571,8 +571,12 @@ static void pl2303_encode_baud_rate(struct tty_struct *tty, baud = min_t(speed_t, baud, spriv->type->max_baud_rate); /* * Use direct method for supported baud rates, otherwise use divisors. + * Newer chip types do not support divisor encoding. */ - baud_sup = pl2303_get_supported_baud_rate(baud); + if (spriv->type->no_divisors) + baud_sup = baud; + else + baud_sup = pl2303_get_supported_baud_rate(baud); if (baud == baud_sup) baud = pl2303_encode_baud_rate_direct(buf, baud); diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 872d1bc86ab4..599dcb2e374d 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -727,7 +727,7 @@ err_buf: return -ENOMEM; } -static int qt2_port_remove(struct usb_serial_port *port) +static void qt2_port_remove(struct usb_serial_port *port) { struct qt2_port_private *port_priv; @@ -735,8 +735,6 @@ static int qt2_port_remove(struct usb_serial_port *port) usb_free_urb(port_priv->write_urb); kfree(port_priv->write_buffer); kfree(port_priv); - - return 0; } static int qt2_tiocmget(struct tty_struct *tty) diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 57fc3c31712e..54e16ffc30a0 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -901,15 +901,13 @@ static int sierra_port_probe(struct usb_serial_port *port) return 0; } -static int sierra_port_remove(struct usb_serial_port *port) +static void sierra_port_remove(struct usb_serial_port *port) { struct sierra_port_private *portdata; portdata = usb_get_serial_port_data(port); usb_set_serial_port_data(port, NULL); kfree(portdata); - - return 0; } #ifdef CONFIG_PM diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 3bac55bd9bd9..7039dc918827 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -169,14 +169,12 @@ static int spcp8x5_port_probe(struct usb_serial_port *port) return 0; } -static int spcp8x5_port_remove(struct usb_serial_port *port) +static void spcp8x5_port_remove(struct usb_serial_port *port) { struct spcp8x5_private *priv; priv = usb_get_serial_port_data(port); kfree(priv); - - return 0; } static int spcp8x5_set_ctrl_line(struct usb_serial_port *port, u8 mcr) diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 7d39d35e52a1..89fdc5c19285 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -366,14 +366,12 @@ static int ssu100_port_probe(struct usb_serial_port *port) return 0; } -static int ssu100_port_remove(struct usb_serial_port *port) +static void ssu100_port_remove(struct usb_serial_port *port) { struct ssu100_port_private *priv; priv = usb_get_serial_port_data(port); kfree(priv); - - return 0; } static int ssu100_tiocmget(struct tty_struct *tty) diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index 6ca24e86f686..d7f73ad6e778 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -160,13 +160,11 @@ static int symbol_port_probe(struct usb_serial_port *port) return 0; } -static int symbol_port_remove(struct usb_serial_port *port) +static void symbol_port_remove(struct usb_serial_port *port) { struct symbol_private *priv = usb_get_serial_port_data(port); kfree(priv); - - return 0; } static struct usb_serial_driver symbol_device = { diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 73075b9351c5..7252b0ce75a6 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -303,7 +303,7 @@ struct ti_device { static int ti_startup(struct usb_serial *serial); static void ti_release(struct usb_serial *serial); static int ti_port_probe(struct usb_serial_port *port); -static int ti_port_remove(struct usb_serial_port *port); +static void ti_port_remove(struct usb_serial_port *port); static int ti_open(struct tty_struct *tty, struct usb_serial_port *port); static void ti_close(struct usb_serial_port *port); static int ti_write(struct tty_struct *tty, struct usb_serial_port *port, @@ -629,14 +629,12 @@ static int ti_port_probe(struct usb_serial_port *port) return 0; } -static int ti_port_remove(struct usb_serial_port *port) +static void ti_port_remove(struct usb_serial_port *port) { struct ti_port *tport; tport = usb_get_serial_port_data(port); kfree(tport); - - return 0; } static int ti_open(struct tty_struct *tty, struct usb_serial_port *port) diff --git a/drivers/usb/serial/upd78f0730.c b/drivers/usb/serial/upd78f0730.c index 0a2268c479af..26d7b003b7e3 100644 --- a/drivers/usb/serial/upd78f0730.c +++ b/drivers/usb/serial/upd78f0730.c @@ -145,14 +145,11 @@ static int upd78f0730_send_ctl(struct usb_serial_port *port, kfree(buf); - if (res != size) { + if (res < 0) { 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; } @@ -174,15 +171,13 @@ static int upd78f0730_port_probe(struct usb_serial_port *port) return 0; } -static int upd78f0730_port_remove(struct usb_serial_port *port) +static void 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) diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h index 934e9361cf6b..79dafd98e0a1 100644 --- a/drivers/usb/serial/usb-wwan.h +++ b/drivers/usb/serial/usb-wwan.h @@ -10,7 +10,7 @@ extern void usb_wwan_dtr_rts(struct usb_serial_port *port, int on); extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port); extern void usb_wwan_close(struct usb_serial_port *port); extern int usb_wwan_port_probe(struct usb_serial_port *port); -extern int usb_wwan_port_remove(struct usb_serial_port *port); +extern void usb_wwan_port_remove(struct usb_serial_port *port); extern int usb_wwan_write_room(struct tty_struct *tty); extern int usb_wwan_tiocmget(struct tty_struct *tty); extern int usb_wwan_tiocmset(struct tty_struct *tty, diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 4b9845807bee..46d46a4f99c9 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -544,7 +544,7 @@ bail_out_error: } EXPORT_SYMBOL_GPL(usb_wwan_port_probe); -int usb_wwan_port_remove(struct usb_serial_port *port) +void usb_wwan_port_remove(struct usb_serial_port *port) { int i; struct usb_wwan_port_private *portdata; @@ -562,8 +562,6 @@ int usb_wwan_port_remove(struct usb_serial_port *port) } kfree(portdata); - - return 0; } EXPORT_SYMBOL(usb_wwan_port_remove); diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index ca3bd58f2025..ccfd5ed652cd 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -79,7 +79,7 @@ static int whiteheat_firmware_attach(struct usb_serial *serial); static int whiteheat_attach(struct usb_serial *serial); static void whiteheat_release(struct usb_serial *serial); static int whiteheat_port_probe(struct usb_serial_port *port); -static int whiteheat_port_remove(struct usb_serial_port *port); +static void whiteheat_port_remove(struct usb_serial_port *port); static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port); static void whiteheat_close(struct usb_serial_port *port); @@ -345,14 +345,12 @@ static int whiteheat_port_probe(struct usb_serial_port *port) return 0; } -static int whiteheat_port_remove(struct usb_serial_port *port) +static void whiteheat_port_remove(struct usb_serial_port *port) { struct whiteheat_private *info; info = usb_get_serial_port_data(port); kfree(info); - - return 0; } static int whiteheat_open(struct tty_struct *tty, struct usb_serial_port *port) diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c new file mode 100644 index 000000000000..483d07dee19d --- /dev/null +++ b/drivers/usb/serial/xr_serial.c @@ -0,0 +1,611 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * MaxLinear/Exar USB to Serial driver + * + * Copyright (c) 2020 Manivannan Sadhasivam <mani@kernel.org> + * + * Based on the initial driver written by Patong Yang: + * + * https://lore.kernel.org/r/20180404070634.nhspvmxcjwfgjkcv@advantechmxl-desktop + * + * Copyright (c) 2018 Patong Yang <patong.mxl@gmail.com> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/usb.h> +#include <linux/usb/serial.h> + +struct xr_txrx_clk_mask { + u16 tx; + u16 rx0; + u16 rx1; +}; + +#define XR_INT_OSC_HZ 48000000U +#define XR21V141X_MIN_SPEED 46U +#define XR21V141X_MAX_SPEED XR_INT_OSC_HZ + +/* USB Requests */ +#define XR21V141X_SET_REQ 0 +#define XR21V141X_GET_REQ 1 + +#define XR21V141X_CLOCK_DIVISOR_0 0x04 +#define XR21V141X_CLOCK_DIVISOR_1 0x05 +#define XR21V141X_CLOCK_DIVISOR_2 0x06 +#define XR21V141X_TX_CLOCK_MASK_0 0x07 +#define XR21V141X_TX_CLOCK_MASK_1 0x08 +#define XR21V141X_RX_CLOCK_MASK_0 0x09 +#define XR21V141X_RX_CLOCK_MASK_1 0x0a + +/* XR21V141X register blocks */ +#define XR21V141X_UART_REG_BLOCK 0 +#define XR21V141X_UM_REG_BLOCK 4 +#define XR21V141X_UART_CUSTOM_BLOCK 0x66 + +/* XR21V141X UART Manager Registers */ +#define XR21V141X_UM_FIFO_ENABLE_REG 0x10 +#define XR21V141X_UM_ENABLE_TX_FIFO 0x01 +#define XR21V141X_UM_ENABLE_RX_FIFO 0x02 + +#define XR21V141X_UM_RX_FIFO_RESET 0x18 +#define XR21V141X_UM_TX_FIFO_RESET 0x1c + +#define XR21V141X_UART_ENABLE_TX 0x1 +#define XR21V141X_UART_ENABLE_RX 0x2 + +#define XR21V141X_UART_MODE_RI BIT(0) +#define XR21V141X_UART_MODE_CD BIT(1) +#define XR21V141X_UART_MODE_DSR BIT(2) +#define XR21V141X_UART_MODE_DTR BIT(3) +#define XR21V141X_UART_MODE_CTS BIT(4) +#define XR21V141X_UART_MODE_RTS BIT(5) + +#define XR21V141X_UART_BREAK_ON 0xff +#define XR21V141X_UART_BREAK_OFF 0 + +#define XR21V141X_UART_DATA_MASK GENMASK(3, 0) +#define XR21V141X_UART_DATA_7 0x7 +#define XR21V141X_UART_DATA_8 0x8 + +#define XR21V141X_UART_PARITY_MASK GENMASK(6, 4) +#define XR21V141X_UART_PARITY_SHIFT 4 +#define XR21V141X_UART_PARITY_NONE (0x0 << XR21V141X_UART_PARITY_SHIFT) +#define XR21V141X_UART_PARITY_ODD (0x1 << XR21V141X_UART_PARITY_SHIFT) +#define XR21V141X_UART_PARITY_EVEN (0x2 << XR21V141X_UART_PARITY_SHIFT) +#define XR21V141X_UART_PARITY_MARK (0x3 << XR21V141X_UART_PARITY_SHIFT) +#define XR21V141X_UART_PARITY_SPACE (0x4 << XR21V141X_UART_PARITY_SHIFT) + +#define XR21V141X_UART_STOP_MASK BIT(7) +#define XR21V141X_UART_STOP_SHIFT 7 +#define XR21V141X_UART_STOP_1 (0x0 << XR21V141X_UART_STOP_SHIFT) +#define XR21V141X_UART_STOP_2 (0x1 << XR21V141X_UART_STOP_SHIFT) + +#define XR21V141X_UART_FLOW_MODE_NONE 0x0 +#define XR21V141X_UART_FLOW_MODE_HW 0x1 +#define XR21V141X_UART_FLOW_MODE_SW 0x2 + +#define XR21V141X_UART_MODE_GPIO_MASK GENMASK(2, 0) +#define XR21V141X_UART_MODE_RTS_CTS 0x1 +#define XR21V141X_UART_MODE_DTR_DSR 0x2 +#define XR21V141X_UART_MODE_RS485 0x3 +#define XR21V141X_UART_MODE_RS485_ADDR 0x4 + +#define XR21V141X_REG_ENABLE 0x03 +#define XR21V141X_REG_FORMAT 0x0b +#define XR21V141X_REG_FLOW_CTRL 0x0c +#define XR21V141X_REG_XON_CHAR 0x10 +#define XR21V141X_REG_XOFF_CHAR 0x11 +#define XR21V141X_REG_LOOPBACK 0x12 +#define XR21V141X_REG_TX_BREAK 0x14 +#define XR21V141X_REG_RS845_DELAY 0x15 +#define XR21V141X_REG_GPIO_MODE 0x1a +#define XR21V141X_REG_GPIO_DIR 0x1b +#define XR21V141X_REG_GPIO_INT_MASK 0x1c +#define XR21V141X_REG_GPIO_SET 0x1d +#define XR21V141X_REG_GPIO_CLR 0x1e +#define XR21V141X_REG_GPIO_STATUS 0x1f + +static int xr_set_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 val) +{ + struct usb_serial *serial = port->serial; + int ret; + + ret = usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + XR21V141X_SET_REQ, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + val, reg | (block << 8), NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (ret < 0) { + dev_err(&port->dev, "Failed to set reg 0x%02x: %d\n", reg, ret); + return ret; + } + + return 0; +} + +static int xr_get_reg(struct usb_serial_port *port, u8 block, u8 reg, u8 *val) +{ + struct usb_serial *serial = port->serial; + u8 *dmabuf; + int ret; + + dmabuf = kmalloc(1, GFP_KERNEL); + if (!dmabuf) + return -ENOMEM; + + ret = usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + XR21V141X_GET_REQ, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, reg | (block << 8), dmabuf, 1, + USB_CTRL_GET_TIMEOUT); + if (ret == 1) { + *val = *dmabuf; + ret = 0; + } else { + dev_err(&port->dev, "Failed to get reg 0x%02x: %d\n", reg, ret); + if (ret >= 0) + ret = -EIO; + } + + kfree(dmabuf); + + return ret; +} + +static int xr_set_reg_uart(struct usb_serial_port *port, u8 reg, u8 val) +{ + return xr_set_reg(port, XR21V141X_UART_REG_BLOCK, reg, val); +} + +static int xr_get_reg_uart(struct usb_serial_port *port, u8 reg, u8 *val) +{ + return xr_get_reg(port, XR21V141X_UART_REG_BLOCK, reg, val); +} + +static int xr_set_reg_um(struct usb_serial_port *port, u8 reg, u8 val) +{ + return xr_set_reg(port, XR21V141X_UM_REG_BLOCK, reg, val); +} + +/* + * According to datasheet, below is the recommended sequence for enabling UART + * module in XR21V141X: + * + * Enable Tx FIFO + * Enable Tx and Rx + * Enable Rx FIFO + */ +static int xr_uart_enable(struct usb_serial_port *port) +{ + int ret; + + ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, + XR21V141X_UM_ENABLE_TX_FIFO); + if (ret) + return ret; + + ret = xr_set_reg_uart(port, XR21V141X_REG_ENABLE, + XR21V141X_UART_ENABLE_TX | XR21V141X_UART_ENABLE_RX); + if (ret) + return ret; + + ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, + XR21V141X_UM_ENABLE_TX_FIFO | XR21V141X_UM_ENABLE_RX_FIFO); + + if (ret) + xr_set_reg_uart(port, XR21V141X_REG_ENABLE, 0); + + return ret; +} + +static int xr_uart_disable(struct usb_serial_port *port) +{ + int ret; + + ret = xr_set_reg_uart(port, XR21V141X_REG_ENABLE, 0); + if (ret) + return ret; + + ret = xr_set_reg_um(port, XR21V141X_UM_FIFO_ENABLE_REG, 0); + + return ret; +} + +static int xr_tiocmget(struct tty_struct *tty) +{ + struct usb_serial_port *port = tty->driver_data; + u8 status; + int ret; + + ret = xr_get_reg_uart(port, XR21V141X_REG_GPIO_STATUS, &status); + if (ret) + return ret; + + /* + * Modem control pins are active low, so reading '0' means it is active + * and '1' means not active. + */ + ret = ((status & XR21V141X_UART_MODE_DTR) ? 0 : TIOCM_DTR) | + ((status & XR21V141X_UART_MODE_RTS) ? 0 : TIOCM_RTS) | + ((status & XR21V141X_UART_MODE_CTS) ? 0 : TIOCM_CTS) | + ((status & XR21V141X_UART_MODE_DSR) ? 0 : TIOCM_DSR) | + ((status & XR21V141X_UART_MODE_RI) ? 0 : TIOCM_RI) | + ((status & XR21V141X_UART_MODE_CD) ? 0 : TIOCM_CD); + + return ret; +} + +static int xr_tiocmset_port(struct usb_serial_port *port, + unsigned int set, unsigned int clear) +{ + u8 gpio_set = 0; + u8 gpio_clr = 0; + int ret = 0; + + /* Modem control pins are active low, so set & clr are swapped */ + if (set & TIOCM_RTS) + gpio_clr |= XR21V141X_UART_MODE_RTS; + if (set & TIOCM_DTR) + gpio_clr |= XR21V141X_UART_MODE_DTR; + if (clear & TIOCM_RTS) + gpio_set |= XR21V141X_UART_MODE_RTS; + if (clear & TIOCM_DTR) + gpio_set |= XR21V141X_UART_MODE_DTR; + + /* Writing '0' to gpio_{set/clr} bits has no effect, so no need to do */ + if (gpio_clr) + ret = xr_set_reg_uart(port, XR21V141X_REG_GPIO_CLR, gpio_clr); + + if (gpio_set) + ret = xr_set_reg_uart(port, XR21V141X_REG_GPIO_SET, gpio_set); + + return ret; +} + +static int xr_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct usb_serial_port *port = tty->driver_data; + + return xr_tiocmset_port(port, set, clear); +} + +static void xr_dtr_rts(struct usb_serial_port *port, int on) +{ + if (on) + xr_tiocmset_port(port, TIOCM_DTR | TIOCM_RTS, 0); + else + xr_tiocmset_port(port, 0, TIOCM_DTR | TIOCM_RTS); +} + +static void xr_break_ctl(struct tty_struct *tty, int break_state) +{ + struct usb_serial_port *port = tty->driver_data; + u8 state; + + if (break_state == 0) + state = XR21V141X_UART_BREAK_OFF; + else + state = XR21V141X_UART_BREAK_ON; + + dev_dbg(&port->dev, "Turning break %s\n", + state == XR21V141X_UART_BREAK_OFF ? "off" : "on"); + xr_set_reg_uart(port, XR21V141X_REG_TX_BREAK, state); +} + +/* Tx and Rx clock mask values obtained from section 3.3.4 of datasheet */ +static const struct xr_txrx_clk_mask xr21v141x_txrx_clk_masks[] = { + { 0x000, 0x000, 0x000 }, + { 0x000, 0x000, 0x000 }, + { 0x100, 0x000, 0x100 }, + { 0x020, 0x400, 0x020 }, + { 0x010, 0x100, 0x010 }, + { 0x208, 0x040, 0x208 }, + { 0x104, 0x820, 0x108 }, + { 0x844, 0x210, 0x884 }, + { 0x444, 0x110, 0x444 }, + { 0x122, 0x888, 0x224 }, + { 0x912, 0x448, 0x924 }, + { 0x492, 0x248, 0x492 }, + { 0x252, 0x928, 0x292 }, + { 0x94a, 0x4a4, 0xa52 }, + { 0x52a, 0xaa4, 0x54a }, + { 0xaaa, 0x954, 0x4aa }, + { 0xaaa, 0x554, 0xaaa }, + { 0x555, 0xad4, 0x5aa }, + { 0xb55, 0xab4, 0x55a }, + { 0x6b5, 0x5ac, 0xb56 }, + { 0x5b5, 0xd6c, 0x6d6 }, + { 0xb6d, 0xb6a, 0xdb6 }, + { 0x76d, 0x6da, 0xbb6 }, + { 0xedd, 0xdda, 0x76e }, + { 0xddd, 0xbba, 0xeee }, + { 0x7bb, 0xf7a, 0xdde }, + { 0xf7b, 0xef6, 0x7de }, + { 0xdf7, 0xbf6, 0xf7e }, + { 0x7f7, 0xfee, 0xefe }, + { 0xfdf, 0xfbe, 0x7fe }, + { 0xf7f, 0xefe, 0xffe }, + { 0xfff, 0xffe, 0xffd }, +}; + +static int xr_set_baudrate(struct tty_struct *tty, + struct usb_serial_port *port) +{ + u32 divisor, baud, idx; + u16 tx_mask, rx_mask; + int ret; + + baud = tty->termios.c_ospeed; + if (!baud) + return 0; + + baud = clamp(baud, XR21V141X_MIN_SPEED, XR21V141X_MAX_SPEED); + divisor = XR_INT_OSC_HZ / baud; + idx = ((32 * XR_INT_OSC_HZ) / baud) & 0x1f; + tx_mask = xr21v141x_txrx_clk_masks[idx].tx; + + if (divisor & 0x01) + rx_mask = xr21v141x_txrx_clk_masks[idx].rx1; + else + rx_mask = xr21v141x_txrx_clk_masks[idx].rx0; + + dev_dbg(&port->dev, "Setting baud rate: %u\n", baud); + /* + * XR21V141X uses fractional baud rate generator with 48MHz internal + * oscillator and 19-bit programmable divisor. So theoretically it can + * generate most commonly used baud rates with high accuracy. + */ + ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_0, + divisor & 0xff); + if (ret) + return ret; + + ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_1, + (divisor >> 8) & 0xff); + if (ret) + return ret; + + ret = xr_set_reg_uart(port, XR21V141X_CLOCK_DIVISOR_2, + (divisor >> 16) & 0xff); + if (ret) + return ret; + + ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_0, + tx_mask & 0xff); + if (ret) + return ret; + + ret = xr_set_reg_uart(port, XR21V141X_TX_CLOCK_MASK_1, + (tx_mask >> 8) & 0xff); + if (ret) + return ret; + + ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_0, + rx_mask & 0xff); + if (ret) + return ret; + + ret = xr_set_reg_uart(port, XR21V141X_RX_CLOCK_MASK_1, + (rx_mask >> 8) & 0xff); + if (ret) + return ret; + + tty_encode_baud_rate(tty, baud, baud); + + return 0; +} + +static void xr_set_flow_mode(struct tty_struct *tty, + struct usb_serial_port *port, + struct ktermios *old_termios) +{ + u8 flow, gpio_mode; + int ret; + + ret = xr_get_reg_uart(port, XR21V141X_REG_GPIO_MODE, &gpio_mode); + if (ret) + return; + + /* Set GPIO mode for controlling the pins manually by default. */ + gpio_mode &= ~XR21V141X_UART_MODE_GPIO_MASK; + + if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) { + dev_dbg(&port->dev, "Enabling hardware flow ctrl\n"); + gpio_mode |= XR21V141X_UART_MODE_RTS_CTS; + flow = XR21V141X_UART_FLOW_MODE_HW; + } else if (I_IXON(tty)) { + u8 start_char = START_CHAR(tty); + u8 stop_char = STOP_CHAR(tty); + + dev_dbg(&port->dev, "Enabling sw flow ctrl\n"); + flow = XR21V141X_UART_FLOW_MODE_SW; + + xr_set_reg_uart(port, XR21V141X_REG_XON_CHAR, start_char); + xr_set_reg_uart(port, XR21V141X_REG_XOFF_CHAR, stop_char); + } else { + dev_dbg(&port->dev, "Disabling flow ctrl\n"); + flow = XR21V141X_UART_FLOW_MODE_NONE; + } + + /* + * As per the datasheet, UART needs to be disabled while writing to + * FLOW_CONTROL register. + */ + xr_uart_disable(port); + xr_set_reg_uart(port, XR21V141X_REG_FLOW_CTRL, flow); + xr_uart_enable(port); + + xr_set_reg_uart(port, XR21V141X_REG_GPIO_MODE, gpio_mode); + + if (C_BAUD(tty) == B0) + xr_dtr_rts(port, 0); + else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) + xr_dtr_rts(port, 1); +} + +static void xr_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, + struct ktermios *old_termios) +{ + struct ktermios *termios = &tty->termios; + u8 bits = 0; + int ret; + + if (!old_termios || (tty->termios.c_ospeed != old_termios->c_ospeed)) + xr_set_baudrate(tty, port); + + switch (C_CSIZE(tty)) { + case CS5: + case CS6: + /* CS5 and CS6 are not supported, so just restore old setting */ + termios->c_cflag &= ~CSIZE; + if (old_termios) + termios->c_cflag |= old_termios->c_cflag & CSIZE; + else + bits |= XR21V141X_UART_DATA_8; + break; + case CS7: + bits |= XR21V141X_UART_DATA_7; + break; + case CS8: + default: + bits |= XR21V141X_UART_DATA_8; + break; + } + + if (C_PARENB(tty)) { + if (C_CMSPAR(tty)) { + if (C_PARODD(tty)) + bits |= XR21V141X_UART_PARITY_MARK; + else + bits |= XR21V141X_UART_PARITY_SPACE; + } else { + if (C_PARODD(tty)) + bits |= XR21V141X_UART_PARITY_ODD; + else + bits |= XR21V141X_UART_PARITY_EVEN; + } + } + + if (C_CSTOPB(tty)) + bits |= XR21V141X_UART_STOP_2; + else + bits |= XR21V141X_UART_STOP_1; + + ret = xr_set_reg_uart(port, XR21V141X_REG_FORMAT, bits); + if (ret) + return; + + xr_set_flow_mode(tty, port, old_termios); +} + +static int xr_open(struct tty_struct *tty, struct usb_serial_port *port) +{ + u8 gpio_dir; + int ret; + + ret = xr_uart_enable(port); + if (ret) { + dev_err(&port->dev, "Failed to enable UART\n"); + return ret; + } + + /* + * Configure DTR and RTS as outputs and RI, CD, DSR and CTS as + * inputs. + */ + gpio_dir = XR21V141X_UART_MODE_DTR | XR21V141X_UART_MODE_RTS; + xr_set_reg_uart(port, XR21V141X_REG_GPIO_DIR, gpio_dir); + + /* Setup termios */ + if (tty) + xr_set_termios(tty, port, NULL); + + ret = usb_serial_generic_open(tty, port); + if (ret) { + xr_uart_disable(port); + return ret; + } + + return 0; +} + +static void xr_close(struct usb_serial_port *port) +{ + usb_serial_generic_close(port); + + xr_uart_disable(port); +} + +static int xr_probe(struct usb_serial *serial, const struct usb_device_id *id) +{ + struct usb_driver *driver = serial->type->usb_driver; + struct usb_interface *control_interface; + int ret; + + /* Don't bind to control interface */ + if (serial->interface->cur_altsetting->desc.bInterfaceNumber == 0) + return -ENODEV; + + /* But claim the control interface during data interface probe */ + control_interface = usb_ifnum_to_if(serial->dev, 0); + if (!control_interface) + return -ENODEV; + + ret = usb_driver_claim_interface(driver, control_interface, NULL); + if (ret) { + dev_err(&serial->interface->dev, "Failed to claim control interface\n"); + return ret; + } + + return 0; +} + +static void xr_disconnect(struct usb_serial *serial) +{ + struct usb_driver *driver = serial->type->usb_driver; + struct usb_interface *control_interface; + + control_interface = usb_ifnum_to_if(serial->dev, 0); + usb_driver_release_interface(driver, control_interface); +} + +static const struct usb_device_id id_table[] = { + { USB_DEVICE(0x04e2, 0x1410) }, /* XR21V141X */ + { } +}; +MODULE_DEVICE_TABLE(usb, id_table); + +static struct usb_serial_driver xr_device = { + .driver = { + .owner = THIS_MODULE, + .name = "xr_serial", + }, + .id_table = id_table, + .num_ports = 1, + .probe = xr_probe, + .disconnect = xr_disconnect, + .open = xr_open, + .close = xr_close, + .break_ctl = xr_break_ctl, + .set_termios = xr_set_termios, + .tiocmget = xr_tiocmget, + .tiocmset = xr_tiocmset, + .dtr_rts = xr_dtr_rts +}; + +static struct usb_serial_driver * const serial_drivers[] = { + &xr_device, NULL +}; + +module_usb_serial_driver(serial_drivers, id_table); + +MODULE_AUTHOR("Manivannan Sadhasivam <mani@kernel.org>"); +MODULE_DESCRIPTION("MaxLinear/Exar USB to Serial driver"); +MODULE_LICENSE("GPL"); |