[PATCH v2 4/4] USB: serial: mxuport: support RS485 mode configuration

From: Crescent Hsieh

Date: Tue Jun 23 2026 - 04:05:50 EST


Add support for TIOCSRS485 and TIOCGRS485 so that userspace can
configure the serial interface mode using struct serial_rs485.

Map the serial_rs485 flags to the device interface modes as follows:

- RS232 = no flags set
- RS422 = SER_RS485_ENABLED | SER_RS485_MODE_RS422
- RS485_2W (half-duplex) = SER_RS485_ENABLED
- RS485_4W (full-duplex) = SER_RS485_ENABLED | SER_RS485_RX_DURING_TX

Unsupported flags and fields are cleared before applying the
configuration. Cache the sanitized per-port configuration and serialize
access with the per-port mutex.

Signed-off-by: Crescent Hsieh <crescentcy.hsieh@xxxxxxxx>
---
drivers/usb/serial/mxuport.c | 98 +++++++++++++++++++++++++++++++++++-
1 file changed, 97 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c
index f3be2b3bd95b..79085595345c 100644
--- a/drivers/usb/serial/mxuport.c
+++ b/drivers/usb/serial/mxuport.c
@@ -199,7 +199,8 @@ struct mxuport_port {
u8 hold_reason;
u8 mcr_state; /* Last MCR state */
u8 msr_state; /* Last MSR state */
- struct mutex mutex; /* Protects mcr_state */
+ struct serial_rs485 rs485;
+ struct mutex mutex; /* Protects per-port control state */
spinlock_t spinlock; /* Protects msr_state */
};

@@ -983,6 +984,100 @@ static int mxuport_tiocmget(struct tty_struct *tty)
return result;
}

+static void mxuport_sanitize_serial_rs485(struct serial_rs485 *rs485)
+{
+ if (!(rs485->flags & SER_RS485_ENABLED)) {
+ memset(rs485, 0, sizeof(*rs485));
+ return;
+ }
+
+ if (rs485->flags & SER_RS485_MODE_RS422)
+ rs485->flags &= SER_RS485_ENABLED | SER_RS485_MODE_RS422;
+ else
+ rs485->flags &= SER_RS485_ENABLED | SER_RS485_RX_DURING_TX;
+
+ rs485->delay_rts_before_send = 0;
+ rs485->delay_rts_after_send = 0;
+ memset(rs485->padding, 0, sizeof(rs485->padding));
+}
+
+static int mxuport_rs485_config(struct usb_serial_port *port,
+ const struct serial_rs485 *rs485)
+{
+ struct usb_serial *serial = port->serial;
+ u16 mode = MX_INT_RS232;
+
+ if (rs485->flags & SER_RS485_ENABLED) {
+ if (rs485->flags & SER_RS485_MODE_RS422)
+ mode = MX_INT_RS422;
+ else if (rs485->flags & SER_RS485_RX_DURING_TX)
+ mode = MX_INT_4W_RS485;
+ else
+ mode = MX_INT_2W_RS485;
+ }
+
+ return mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_INTERFACE, mode,
+ port->port_number);
+}
+
+static int mxuport_get_rs485_config(struct usb_serial_port *port,
+ struct serial_rs485 __user *argp)
+{
+ struct mxuport_port *mxport = usb_get_serial_port_data(port);
+ struct serial_rs485 rs485;
+
+ mutex_lock(&mxport->mutex);
+ rs485 = mxport->rs485;
+ mutex_unlock(&mxport->mutex);
+
+ if (copy_to_user(argp, &rs485, sizeof(rs485)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int mxuport_set_rs485_config(struct usb_serial_port *port,
+ struct serial_rs485 __user *argp)
+{
+ struct mxuport_port *mxport = usb_get_serial_port_data(port);
+ struct serial_rs485 rs485;
+ int err;
+
+ if (copy_from_user(&rs485, argp, sizeof(rs485)))
+ return -EFAULT;
+
+ mxuport_sanitize_serial_rs485(&rs485);
+
+ mutex_lock(&mxport->mutex);
+ err = mxuport_rs485_config(port, &rs485);
+ if (!err)
+ mxport->rs485 = rs485;
+ mutex_unlock(&mxport->mutex);
+ if (err)
+ return err;
+
+ if (copy_to_user(argp, &rs485, sizeof(rs485)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int mxuport_ioctl(struct tty_struct *tty, unsigned int cmd,
+ unsigned long arg)
+{
+ struct usb_serial_port *port = tty->driver_data;
+ void __user *argp = (void __user *)arg;
+
+ switch (cmd) {
+ case TIOCGRS485:
+ return mxuport_get_rs485_config(port, argp);
+ case TIOCSRS485:
+ return mxuport_set_rs485_config(port, argp);
+ }
+
+ return -ENOIOCTLCMD;
+}
+
static int mxuport_set_termios_flow(struct tty_struct *tty,
const struct ktermios *old_termios,
struct usb_serial_port *port,
@@ -1625,6 +1720,7 @@ static struct usb_serial_driver mxuport_device = {
.tx_empty = mxuport_tx_empty,
.write = mxuport_write,
.write_bulk_callback = mxuport_write_bulk_callback,
+ .ioctl = mxuport_ioctl,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
.throttle = mxuport_throttle,
--
2.43.0