Skip to content

serial: uart_native_pty: ASYNC API support #87874

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 25, 2025

Conversation

JordanYates
Copy link
Collaborator

@JordanYates JordanYates commented Mar 30, 2025

Add support for the ASYNC API to the native PTY uart driver.
Asynchronous transmission is performed from the system workqueue.
Asynchronous reception is performed from a dedicated thread.

I would love to add testing for testing this to tests/drivers/uart/uart_async_api, but it depends on loopback support.
As far as I can tell the basic polling API is not tested either due to the lack of this support. Does anyone have ideas for enabling this in CI?

@github-actions github-actions bot added area: native port Host native arch port (native_sim) area: UART Universal Asynchronous Receiver-Transmitter labels Mar 30, 2025
@github-actions github-actions bot requested review from aescolar and dcpleung March 30, 2025 23:38
@JordanYates JordanYates force-pushed the 250330_pty_async branch 2 times, most recently from d408832 to 95f5aab Compare March 31, 2025 00:14
Copy link
Member

@aescolar aescolar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this is not an approach we can use, due to several issues, of which the main are:

  1. A signal is delivered fully asynchronously to the native_simulator execution
  2. The signal handler will by default be run by any native simulator thread
    That means between other things that the signal handler will be run, for ex., while the CPU is sleeping corrupting the kernel state; for AMP targets, from any other CPU thread, causing mayhem in both CPUs kernels state, and other messy situations.

In general in native_sim we do not have really asynchronous behaviors. What we do is that we have a deterministic synchronous one that pretends to inject an asynchronous like event at a very deterministic time (see for example the TTY UART interrupt driven mechanism).

@github-actions github-actions bot added the area: Samples Samples label Apr 16, 2025
@github-actions github-actions bot requested review from kartben and nashif April 16, 2025 08:25
@JordanYates
Copy link
Collaborator Author

Unfortunately this is not an approach we can use, due to several issues, of which the main are:

1. A signal is delivered fully asynchronously to the native_simulator execution

2. The signal handler will by default be run by _any_ native simulator thread
   That means between other things that the signal handler will be run, for ex., while the CPU is sleeping corrupting the kernel state; for AMP targets, from any other CPU thread, causing mayhem in both CPUs kernels state, and other messy situations.

In general in native_sim we do not have really asynchronous behaviors. What we do is that we have a deterministic synchronous one that pretends to inject an asynchronous like event at a very deterministic time (see for example the TTY UART interrupt driven mechanism).

Thanks for the feedback, I have reworked the PR in line with the TTY driver.
I also added an ASYNC API sample that can be used with the new driver.

@JordanYates JordanYates force-pushed the 250330_pty_async branch 3 times, most recently from 8e9e255 to a7ca231 Compare April 16, 2025 10:06
Copy link
Member

@aescolar aescolar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @JordanYates
What I see now is much more minor.
And probably people who is more familiar with the UART API is better than me to provide good input for what remains (CC @dcpleung @nordic-krch )


static int np_uart_tx(const struct device *dev, const uint8_t *buf, size_t len, int32_t timeout)
{
struct native_pty_status *data = dev->data;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this check if the callback is set and return -ENOTSUP otherwise?
(Same for rx and np_uart_tx_abort)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That isn't done in the other drivers that I have inspected

Copy link
Member

@aescolar aescolar Apr 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I based my comment on the API header comments. I leave it up to the driver class owner @dcpleung to decide if anything needs doing

Add a length parameter to the poll in read function so that data can be
read in larger chunks.

Signed-off-by: Jordan Yates <[email protected]>
@JordanYates JordanYates force-pushed the 250330_pty_async branch 2 times, most recently from 3bbfee8 to 3f3d17f Compare April 22, 2025 23:24
@JordanYates
Copy link
Collaborator Author

@aescolar thanks for the feedback, halfway through the implementation I switched from a persistent thread to one that only runs while RX is enabled and missed some things.

Add support for transmitting using the asynchronous API. The
asynchronous portion is simulated through the system workqueue.

Signed-off-by: Jordan Yates <[email protected]>
Add support for transmitting using the asynchronous API. The
asynchronous portion is simulated through a dedicated polling thread.

Signed-off-by: Jordan Yates <[email protected]>
Add a sample that utilises the ASYNC API to queue packets in bursts.

Signed-off-by: Jordan Yates <[email protected]>
data->async.tx_buf = NULL;

if (data->async.user_callback) {
data->async.user_callback(data->async.dev, &evt, data->async.user_data);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe not critical for native but normally user callback should not be called with interrupts locked.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal here was to prevent the system workqueue item being preempted by some other thread while this function was running, since in a "normal" driver this logic would be running from an interrupt context. Happy to use a different solution if you have one.

}

/* Generate TX_DONE event with number of bytes transmitted */
evt.type = UART_TX_DONE;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UART_TX_ABORTED

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UART documentation contradicts itself on which event should be generated:

* Transmitting can be aborted using @ref uart_tx_abort, after calling that
* function #UART_TX_ABORTED event will be generated.

* #UART_TX_DONE event will be generated with amount of data sent.

ARG_UNUSED(arg2);
ARG_UNUSED(arg3);

while (data->async.rx_len) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the expected receiver behavior is that RX_RDY is reported when buffer is filled or when timeout occurs (and no new data arrives). When buffer is filled and new buffer is not provided then RX_BUF_RELEASED followed by RX_DISABLED events will be generated.

case UART_RX_BUF_REQUEST:
/* Return the next buffer index */
LOG_DBG("Providing buffer index %d", async_rx_buffer_idx);
rc = uart_rx_buf_rsp(dev, async_rx_buffer[async_rx_buffer_idx],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it will assert with current implementation

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It won't, because the native driver never issues UART_RX_BUF_REQUEST, since it has no need for multiple buffers.

There could be an argument to be made to simulate the ping-pong buffers though to more closely behave like a real hardware device.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It won't, because the native driver never issues UART_RX_BUF_REQUEST, since it has no need for multiple buffers.

But this is a generic sample for all UART drivers supporting the ASYNC API, or?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My comment was in reference to what I thought @nordic-krch was referring to, which was that uart_rx_buf_rsp would return -ENOTSUP for the native driver, and thus trigger the assertion. If that is not the case I would need clarification on the "will assert" comment.

Copy link
Member

@aescolar aescolar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+2 for the native/host integration side
+1 for driver API/behaviour side

@aescolar aescolar removed their assignment Apr 25, 2025
@kartben kartben merged commit bb0f192 into zephyrproject-rtos:main Apr 25, 2025
24 checks passed
@JordanYates JordanYates deleted the 250330_pty_async branch April 25, 2025 22:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: native port Host native arch port (native_sim) area: Samples Samples area: UART Universal Asynchronous Receiver-Transmitter
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants