]> Git Repo - linux.git/commitdiff
Merge tag 'usb-serial-5.12-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git...
authorGreg Kroah-Hartman <[email protected]>
Wed, 10 Feb 2021 14:58:04 +0000 (15:58 +0100)
committerGreg Kroah-Hartman <[email protected]>
Wed, 10 Feb 2021 14:58:04 +0000 (15:58 +0100)
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
  ...

1  2 
drivers/usb/serial/cp210x.c
drivers/usb/serial/option.c

index 7bec1e730b2093147feda765c189f5cfc349a5b1,665798043d3c7e9a80e09846522b9cdc00ceb953..9e1c609792fb0241370db38e58a647f6c26a5dce
@@@ -3,6 -3,7 +3,7 @@@
   * Silicon Laboratories CP210x USB to RS232 serial adaptor driver
   *
   * Copyright (C) 2005 Craig Shelley ([email protected])
+  * Copyright (C) 2010-2021 Johan Hovold ([email protected])
   *
   * Support to set flow control line levels using TIOCMGET and TIOCMSET
   * thanks to Karl Hiramoto [email protected]. RTSCTS hardware flow
@@@ -16,9 -17,7 +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 +44,7 @@@ static int cp210x_attach(struct usb_ser
  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);
@@@ -61,7 -60,6 +60,7 @@@ static const struct usb_device_id id_ta
        { USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */
        { USB_DEVICE(0x08FD, 0x000A) }, /* Digianswer A/S , ZigBee/802.15.4 MAC Device */
        { USB_DEVICE(0x0908, 0x01FF) }, /* Siemens RUGGEDCOM USB Serial Console */
 +      { USB_DEVICE(0x0988, 0x0578) }, /* Teraoka AD2000 */
        { USB_DEVICE(0x0B00, 0x3070) }, /* Ingenico 3070 */
        { USB_DEVICE(0x0BED, 0x1100) }, /* MEI (TM) Cashflow-SC Bill/Voucher Acceptor */
        { USB_DEVICE(0x0BED, 0x1101) }, /* MEI series 2000 Combo Acceptor */
        { 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(0x199B, 0xBA30) }, /* LORD WSDA-200-USB */
        { USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */
        { USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */
        { USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */
@@@ -268,7 -265,12 +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 -381,16 +383,16 @@@ static struct usb_serial_driver * cons
  #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 -449,14 +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)
  #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 -672,13 +674,13 @@@ static int cp210x_write_reg_block(struc
  
        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 -715,14 +717,14 @@@ static int cp210x_write_vendor_block(st
  
        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 -1076,80 +1078,80 @@@ static void cp210x_disable_event_mode(s
        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);
        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 -1287,77 +1289,77 @@@ static int cp210x_tiocmset(struct tty_s
  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 -1385,7 +1387,7 @@@ static int cp210x_tiocmget(struct tty_s
                |((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 -1834,19 +1836,19 @@@ static int cp210x_port_probe(struct usb
                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)
index 2049e66f34a3fd1c73c832ffd1866b0864869544,6716dfcf2610350ee09b7def848eb81152b86d68..c6969ca72839083ebac780ee31dc5b6304bbde12
@@@ -425,8 -425,6 +425,8 @@@ static void option_instat_callback(stru
  #define CINTERION_PRODUCT_AHXX_2RMNET         0x0084
  #define CINTERION_PRODUCT_AHXX_AUDIO          0x0085
  #define CINTERION_PRODUCT_CLS8                        0x00b0
 +#define CINTERION_PRODUCT_MV31_MBIM           0x00b3
 +#define CINTERION_PRODUCT_MV31_RMNET          0x00b7
  
  /* Olivetti products */
  #define OLIVETTI_VENDOR_ID                    0x0b3c
@@@ -1569,7 -1567,8 +1569,8 @@@ static const struct usb_device_id optio
        { 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) },
        { USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC25_MDMNET) },
        { USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) }, /* HC28 enumerates with Siemens or Cinterion VID depending on FW revision */
        { USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC28_MDMNET) },
 +      { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_MV31_MBIM, 0xff),
 +        .driver_info = RSVD(3)},
 +      { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_MV31_RMNET, 0xff),
 +        .driver_info = RSVD(0)},
        { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100),
          .driver_info = RSVD(4) },
        { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD120),
This page took 0.080429 seconds and 4 git commands to generate.