Merge tag 'usb-serial-6.6-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 27 Aug 2023 11:11:05 +0000 (13:11 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 27 Aug 2023 11:11:05 +0000 (13:11 +0200)
Johan writes:

USB-serial updates for 6.6-rc1

Here are the USB-serial updates for 6.6-rc1, including:

 - support for the RS485 mode of XR devices
 - new modem device ids

All have been in linux-next with no reported issues.

* tag 'usb-serial-6.6-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial:
  USB: serial: option: add FOXCONN T99W368/T99W373 product
  USB: serial: option: add Quectel EM05G variant (0x030e)
  USB: serial: xr: add TIOCGRS485 and TIOCSRS485 ioctls

drivers/usb/serial/option.c
drivers/usb/serial/xr_serial.c

index 8ac98e6..7994a45 100644 (file)
@@ -259,6 +259,7 @@ static void option_instat_callback(struct urb *urb);
 #define QUECTEL_PRODUCT_EM05G                  0x030a
 #define QUECTEL_PRODUCT_EM060K                 0x030b
 #define QUECTEL_PRODUCT_EM05G_CS               0x030c
+#define QUECTEL_PRODUCT_EM05GV2                        0x030e
 #define QUECTEL_PRODUCT_EM05CN_SG              0x0310
 #define QUECTEL_PRODUCT_EM05G_SG               0x0311
 #define QUECTEL_PRODUCT_EM05CN                 0x0312
@@ -1188,6 +1189,8 @@ static const struct usb_device_id option_ids[] = {
          .driver_info = RSVD(6) | ZLP },
        { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G, 0xff),
          .driver_info = RSVD(6) | ZLP },
+       { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05GV2, 0xff),
+         .driver_info = RSVD(4) | ZLP },
        { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G_CS, 0xff),
          .driver_info = RSVD(6) | ZLP },
        { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G_GR, 0xff),
@@ -2232,6 +2235,10 @@ static const struct usb_device_id option_ids[] = {
          .driver_info = RSVD(0) | RSVD(1) | RSVD(6) },
        { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0db, 0xff),                     /* Foxconn T99W265 MBIM */
          .driver_info = RSVD(3) },
+       { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0ee, 0xff),                     /* Foxconn T99W368 MBIM */
+         .driver_info = RSVD(3) },
+       { USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe0f0, 0xff),                     /* Foxconn T99W373 MBIM */
+         .driver_info = RSVD(3) },
        { USB_DEVICE(0x1508, 0x1001),                                           /* Fibocom NL668 (IOT version) */
          .driver_info = RSVD(4) | RSVD(5) | RSVD(6) },
        { USB_DEVICE(0x1782, 0x4d10) },                                         /* Fibocom L610 (AT mode) */
index 4ec7c58..1d9a126 100644 (file)
@@ -93,6 +93,7 @@ struct xr_txrx_clk_mask {
 #define XR_GPIO_MODE_SEL_DTR_DSR       0x2
 #define XR_GPIO_MODE_SEL_RS485         0x3
 #define XR_GPIO_MODE_SEL_RS485_ADDR    0x4
+#define XR_GPIO_MODE_RS485_TX_H                0x8
 #define XR_GPIO_MODE_TX_TOGGLE         0x100
 #define XR_GPIO_MODE_RX_TOGGLE         0x200
 
@@ -237,6 +238,7 @@ static const struct xr_type xr_types[] = {
 struct xr_data {
        const struct xr_type *type;
        u8 channel;                     /* zero-based index or interface number */
+       struct serial_rs485 rs485;
 };
 
 static int xr_set_reg(struct usb_serial_port *port, u8 channel, u16 reg, u16 val)
@@ -629,6 +631,7 @@ static void xr_set_flow_mode(struct tty_struct *tty,
        struct xr_data *data = usb_get_serial_port_data(port);
        const struct xr_type *type = data->type;
        u16 flow, gpio_mode;
+       bool rs485_enabled;
        int ret;
 
        ret = xr_get_reg_uart(port, type->gpio_mode, &gpio_mode);
@@ -645,7 +648,17 @@ static void xr_set_flow_mode(struct tty_struct *tty,
        /* Set GPIO mode for controlling the pins manually by default. */
        gpio_mode &= ~XR_GPIO_MODE_SEL_MASK;
 
-       if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) {
+       rs485_enabled = !!(data->rs485.flags & SER_RS485_ENABLED);
+       if (rs485_enabled) {
+               dev_dbg(&port->dev, "Enabling RS-485\n");
+               gpio_mode |= XR_GPIO_MODE_SEL_RS485;
+               if (data->rs485.flags & SER_RS485_RTS_ON_SEND)
+                       gpio_mode &= ~XR_GPIO_MODE_RS485_TX_H;
+               else
+                       gpio_mode |= XR_GPIO_MODE_RS485_TX_H;
+       }
+
+       if (C_CRTSCTS(tty) && C_BAUD(tty) != B0 && !rs485_enabled) {
                dev_dbg(&port->dev, "Enabling hardware flow ctrl\n");
                gpio_mode |= XR_GPIO_MODE_SEL_RTS_CTS;
                flow = XR_UART_FLOW_MODE_HW;
@@ -809,6 +822,79 @@ static void xr_cdc_set_line_coding(struct tty_struct *tty,
        kfree(lc);
 }
 
+static void xr_sanitize_serial_rs485(struct serial_rs485 *rs485)
+{
+       if (!(rs485->flags & SER_RS485_ENABLED)) {
+               memset(rs485, 0, sizeof(*rs485));
+               return;
+       }
+
+       /* RTS always toggles after TX */
+       if (rs485->flags & SER_RS485_RTS_ON_SEND)
+               rs485->flags &= ~SER_RS485_RTS_AFTER_SEND;
+       else
+               rs485->flags |= SER_RS485_RTS_AFTER_SEND;
+
+       /* Only the flags are implemented at the moment */
+       rs485->flags &= SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
+                       SER_RS485_RTS_AFTER_SEND;
+       rs485->delay_rts_before_send = 0;
+       rs485->delay_rts_after_send = 0;
+       memset(rs485->padding, 0, sizeof(rs485->padding));
+}
+
+static int xr_get_rs485_config(struct tty_struct *tty,
+                              struct serial_rs485 __user *argp)
+{
+       struct usb_serial_port *port = tty->driver_data;
+       struct xr_data *data = usb_get_serial_port_data(port);
+
+       down_read(&tty->termios_rwsem);
+       if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) {
+               up_read(&tty->termios_rwsem);
+               return -EFAULT;
+       }
+       up_read(&tty->termios_rwsem);
+
+       return 0;
+}
+
+static int xr_set_rs485_config(struct tty_struct *tty,
+                              struct serial_rs485 __user *argp)
+{
+       struct usb_serial_port *port = tty->driver_data;
+       struct xr_data *data = usb_get_serial_port_data(port);
+       struct serial_rs485 rs485;
+
+       if (copy_from_user(&rs485, argp, sizeof(rs485)))
+               return -EFAULT;
+       xr_sanitize_serial_rs485(&rs485);
+
+       down_write(&tty->termios_rwsem);
+       data->rs485 = rs485;
+       xr_set_flow_mode(tty, port, NULL);
+       up_write(&tty->termios_rwsem);
+
+       if (copy_to_user(argp, &rs485, sizeof(rs485)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int xr_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
+{
+       void __user *argp = (void __user *)arg;
+
+       switch (cmd) {
+       case TIOCGRS485:
+               return xr_get_rs485_config(tty, argp);
+       case TIOCSRS485:
+               return xr_set_rs485_config(tty, argp);
+       }
+
+       return -ENOIOCTLCMD;
+}
+
 static void xr_set_termios(struct tty_struct *tty,
                           struct usb_serial_port *port,
                           const struct ktermios *old_termios)
@@ -1010,6 +1096,7 @@ static struct usb_serial_driver xr_device = {
        .set_termios            = xr_set_termios,
        .tiocmget               = xr_tiocmget,
        .tiocmset               = xr_tiocmset,
+       .ioctl                  = xr_ioctl,
        .dtr_rts                = xr_dtr_rts
 };