Public bug reported:

Use 2 instances of an app that shows the pinout signals (DTR, DSR, RTS, CTS).
- 1: /dev/ttyS0  (some UART, integrated in the PC)
- 2: /dev/ttyUSB0 (Moxa UPort 1250, using Linux native driver)

Link both ports with a null-modem cable.

- Open 1 (ttyS0): DTR and RTS are ON.
- Open 2 (ttyUSB0): DSR and CTS are OFF.
- Close and reopen port for 1: on 2, DSR and CTS are ON.

You can also try to close and re-open 2: DSR and CTS are OFF.

Also try to close and re-open 1: DSR and CTS are ON.

Behavior of 1 (ttyS0) is correct.
Behavior of 2 (ttyUSB0) is wrong.

------------------------------------------------------------

In my case, once port is open, QSerialPort::pinoutSignals() is called.
The Unix implementation calls ioctl():

int arg = 0;
if (::ioctl(descriptor, TIOCMGET, &arg) == -1) {
    setError(getSystemError());
    return QSerialPort::NoSignal;
}

------------------------------------------------------------
Now look at the Linux native mxuport driver:
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/usb/serial/mxuport.c?h=v6.16.7

Line 1301, TIOCMGET should call mxuport_tiocmget():

static int mxuport_tiocmget(struct tty_struct *tty)
{
        struct mxuport_port *mxport;
        struct usb_serial_port *port = tty->driver_data;
        unsigned int result;
        unsigned long flags;
        unsigned int msr;
        unsigned int mcr;

        mxport = usb_get_serial_port_data(port);

        mutex_lock(&mxport->mutex);
        spin_lock_irqsave(&mxport->spinlock, flags);

        msr = mxport->msr_state;
        mcr = mxport->mcr_state;

        spin_unlock_irqrestore(&mxport->spinlock, flags);
        mutex_unlock(&mxport->mutex);

        result = (((mcr & UART_MCR_DTR) ? TIOCM_DTR : 0) |      /* 0x002 */
                  ((mcr & UART_MCR_RTS) ? TIOCM_RTS : 0) |      /* 0x004 */
                  ((msr & UART_MSR_CTS) ? TIOCM_CTS : 0) |      /* 0x020 */
                  ((msr & UART_MSR_DCD) ? TIOCM_CAR : 0) |      /* 0x040 */
                  ((msr & UART_MSR_RI) ? TIOCM_RI : 0) |        /* 0x080 */
                  ((msr & UART_MSR_DSR) ? TIOCM_DSR : 0));      /* 0x100 */

        dev_dbg(&port->dev, "%s - 0x%04x\n", __func__, result);

        return result;
}

I think that this function only gets cached values.

Now look briefly at mxuport_msr_event():
...
if (rcv_msr_hold & UART_MSR_CTS) {
        mxport->msr_state |= UART_MSR_CTS;
        dev_dbg(&port->dev, "%s - CTS high\n", __func__);
} else {
        mxport->msr_state &= ~UART_MSR_CTS;
        dev_dbg(&port->dev, "%s - CTS low\n", __func__);
}
// same for UART_MSR_DSR and UART_MSR_DCD
...
if (rcv_msr_event &
    (SERIAL_EV_CTS | SERIAL_EV_DSR | SERIAL_EV_RLSD)) {

        if (rcv_msr_event & SERIAL_EV_CTS) {
                port->icount.cts++;
                dev_dbg(&port->dev, "%s - CTS change\n", __func__);
        }

        if (rcv_msr_event & SERIAL_EV_DSR) {
                port->icount.dsr++;
                dev_dbg(&port->dev, "%s - DSR change\n", __func__);
        }

        if (rcv_msr_event & SERIAL_EV_RLSD) {
                port->icount.dcd++;
                dev_dbg(&port->dev, "%s - DCD change\n", __func__);
        }
        wake_up_interruptible(&port->port.delta_msr_wait);
}

This function is called by mxuport_process_read_urb_event():

/*
 * When something interesting happens, modem control lines XON/XOFF
 * etc, the device sends an event. Process these events.
 */
static void mxuport_process_read_urb_event(struct usb_serial_port *port,
                                           u8 buf[4], u32 event)
{
        dev_dbg(&port->dev, "%s - receive event : %04x\n", __func__, event);

        switch (event) {
        case UPORT_EVENT_SEND_NEXT:
                /*
                 * Sent as part of the flow control on device buffers.
                 * Not currently used.
                 */
                break;
        case UPORT_EVENT_MSR:
                mxuport_msr_event(port, buf);
                break;
        case UPORT_EVENT_LSR:
                mxuport_lsr_event(port, buf);
                break;
        case UPORT_EVENT_MCR:
                /*
                 * Event to indicate a change in XON/XOFF from the
                 * peer.  Currently not used. We just continue
                 * sending the device data and it will buffer it if
                 * needed. This event could be used for flow control
                 * between the host and the device.
                 */
                break;
        default:
                dev_dbg(&port->dev, "Unexpected event\n");
                break;
        }
}

Finally, an interesting comment in mxuport_open():

/*
 * TODO: use RQ_VENDOR_GET_MSR, once we know what it
 * returns.
 */
mxport->msr_state = 0;

--------------------------------------------------------
In the Moxa driver mxuport-kernel-6 in mx-uport.c, in mxuport_open():
...
msr_ret = mx_recv_ctrl_urb(port->serial->dev,
                RQ_VENDOR_GET_MSR,
                0,
                mx_port->portno,
                &msr_value,
                sizeof(msr_value));
if(msr_ret < 0)
        mx_port->msr_state = 0;
else
        mx_port->msr_state = msr_value;
...

I think that's the missing part in the Linux native driver.

Also, mx_recv_ctrl_urb() is a wrapper around usb_control_msg().
--------------------------------------------------------

To see a version with links and somewhat formatted code, you can look:
https://gitlab.com/scandyna/mdtterminal/-/issues/4

** Affects: ubuntu
     Importance: Undecided
         Status: New


** Tags: moxa mxuport uport

-- 
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2123856

Title:
  DSR and CTS pinout signals not reliable after port open with Moxa
  UPort

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+bug/2123856/+subscriptions


-- 
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to