USB UART output buffer remains full after serial disconnect

Windows 11, NCS v2.3.0, VSCode

Hi, I am using the uart_poll_out API for communicating via USB serial.

For each character I want to output, I do this:

uint32_t dtr;
uart_line_ctrl_get(DEV_UART_USB, UART_LINE_CTRL_DTR, &dtr);
if (dtr)
{
    uart_poll_out(DEV_UART_USB, c);
}

The other side of the serial connection can disconnect at any time.

I don't want my code to hang trying to write, that's why I checked the DTS beforehand, and basically throw away any data that won't get sent immediately.

The problem I have is:

- I am writing continuously

- The other side disconnects

- I attempt to detect that, and do, eventually, but not before the uart_poll_out puts a few characters into what I assume is an output buffer which fills up a short time later

Then, when the other side re-connects, the bytes that are sitting in the output buffer get sent.  This corrupts the datastream to the receiver as those bytes were for a different connection.

Is there a way to deal with this?

I would basically like to detect that I can't send data, and clear any existing output buffer at that time.

Or any other way to deal with this situation?

I'm not a serial expert and I only had the line_ctrl code because I saw it in an example.  Please let me know.

Thanks.

  • Hello,

    I assume the genereal theory here is that you are feeding the USB with data (by using the poll api) after USB is disconnected. Considering you are only reading the dtr register one time before you send several bytes, and I believe the poll() api is blocking for each bytes sent (while waiting for previous to be sent), you will very likely experience the problem you are. How to workaround this? I expect it will slightly depend on the priority you have of both interrupts and threads here. It is at least very likely that the disconnect will happen asynchonrous to your current scheme of things. Here are a few things I would have looked into:

    - Ideally you want to make sure that the previous byte is sent before you try to send the next, a combination of checking that it is indeed connected and that the previous transfer have occured before writing the next byte seems like something I would have looked into. Though I suggest you may also consider using the uart_tx_abort() in addition if you find that it just disconnected.

    Kenneth

  • Hi,

    I assume the genereal theory here is that you are feeding the USB with data (by using the poll api) after USB is disconnected

    That is my theory, yes.  And once that occurs, I want to clear out the buffer that will ultimately be sent on the next connection, because the bytes that are in that buffer are not valid anymore.

    . Considering you are only reading the dtr register one time before you send several bytes

    No.

    I need you to read the code and understand it.

    The sending of 6 bytes of "\n" is a ONE-TIME event upon each RE-CONNECTION as a WORKAROUND for the problem I'm trying to resolve with you.

    Just as the comment above it spells out, this is a workaround.

    The ThreadFnUARTUSBOutput is a THREAD, which runs FOREVER.  There is a forever while loop in that thread.  The loop pulls a single character at a time from an input buffer, and uses uart_poll_out to send that character.  Before every uart_poll_out like that, the dtr is checked.

    That is the normal behavior during a connection.  Only IF DURING A CONNECTION does the other side disconnect, then upon RE-CONNECTION will I send 6 newline characters to effectively flush the output buffer.

    Does this make sense to you?  Do you see how this is not the scenario you are explaining me to above?

    Ideally you want to make sure that the previous byte is sent before you try to send the next, a combination of checking that it is indeed connected and that the previous transfer have occured before writing the next byte seems like something I would have looked into

    Yes the code explicitly and clearly does this already.

    Though I suggest you may also consider using the uart_tx_abort() in addition if you find that it just disconnected

    I directly asked you above if using uart_tx_abort would resolve the issue I'm discussing here.  You choose to not answer and simply repeat it, along with not answering other direct questions.

    We are making NEGATIVE PROGRESS in this thread where I'm asking for help.  I am not closer to resolving this 10 days later.  I am sending you code and then needing to read the code to you.  You aren't answering my questions.  Not one single time have you explained how any of your suggestions would resolve the root cause of the issue, or even attempted to diagnose or explain the root cause.  What actual help have I gained from this interaction?

    How do we escalate this to involve other people on the Nordic side for help?  This isn't working and I need help from other people.

    Thanks.

  • douglas.malnati said:
    I directly asked you above if using uart_tx_abort would resolve the issue I'm discussing here.  You choose to not answer and simply repeat it, along with not answering other direct questions.

    I find it difficult to give a good answer to that question, because it will depend on when you call it. If you call it in the USB callback handler, then it should abort the transfer, but considering this can happen between you checking dtr and calling poll api, then you will end up in the same situation is my assumption. So, maybe you need to instead add a flag in the USB callback handler, such that you after calling the poll api, you can abort the transfer if it disconnected in the meantime. Can you try that?

    Kenneth

  • Can you try on USB_DC_DISCONNECTED on event in:
    https://github.com/nrfconnect/sdk-zephyr/blob/main/subsys/usb/device/class/cdc_acm.c#L384 

    Add the line: ring_buf_reset(dev_data->tx_ringbuf);

    And see if that helps?

    And/or try to set CONFIG_USB_CDC_ACM_RINGBUF_SIZE to 1 and see if that also can work.

    Kenneth

  • Hello,

    How did this go? Were you able to find a solution?

    Kenneth

Related