Skip to content

Conversation

@maribu
Copy link
Member

@maribu maribu commented Nov 18, 2025

Contribution description

  • Change return type of usbus_cdc_acm_submit from size_t to ssize_t
    to be able to report errors
  • Change stdio_cdc_acm to make use of the new API
  • Change stdio_cdc_acm to not loop until all data is flushed, but just
    report what could be added to the buffer now and post the flush event
    • The layers above should already loop until all data is written
    • The layers above could opt to not loop when in IRQ context. This
      would allow using stdio_usb_cdc_acm from IRQ context.

This also sneaks in a fix for our picolib integration: We didn't loop over stdio_write() in the flush implementation, as we should have.

Testing procedure

STDIO via USB CDC ACM should still work correctly with both newlib and picolibc as standard C lib.

Issues/PRs references

Follow up to: #21890

According to API doc, `stdio_write()` may write less data then requested
or report an error. This updates the code to loop in the flushing until
either all data is written or an error is returned.
- Change return type of `usbus_cdc_acm_submit` from `size_t` to `ssize_t`
  to be able to report errors
- Change `stdio_cdc_acm` to make use of the new API
- Change `stdio_cdc_acm` to not loop until all data is flushed, but just
  report what could be added to the buffer now and post the flush event
    - The layers above should already loop until all data is written
    - The layers above could opt to not loop when in IRQ context. This
      would allow using `stdio_usb_cdc_acm` from IRQ context.
@github-actions github-actions bot added Area: USB Area: Universal Serial Bus Area: sys Area: System labels Nov 18, 2025
@maribu
Copy link
Member Author

maribu commented Nov 18, 2025

Warning

This is completely untested

@crasbe crasbe added Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Nov 18, 2025
Comment on lines +185 to +192
while (pos < end) {
size_t left = (size_t)end - (size_t)pos;
ssize_t written = stdio_write(pos, left);
if (written < 0) {
/* failed to flush, drop data */
break;
}
pos += written;
Copy link
Contributor

Choose a reason for hiding this comment

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

Could this become an endless loop with high CPU load if stdio_write returns 0 (and does not write anything)? I'd have to look up if stdio_write could behave like that.

Copy link
Member Author

Choose a reason for hiding this comment

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

Returning 0 is very much valid. If the stdio would have flow control (e.g. UART with flow control or some network protocol), it may not be able to send out any data right now.

There is an expectation towards the transport that returning 0 is a temporary thing that will sort itself out within reasonable time. The layer above cannot reasonably judge when stuff won't work, so it just has to trust that at some point the transport will either make progress or give up.

@riot-ci
Copy link

riot-ci commented Nov 18, 2025

Murdock results

✔️ PASSED

ea94d1a Update sys/include/usb/usbus/cdc/acm.h

Success Failures Total Runtime
10931 0 10932 10m:41s

Artifacts

return (char *)buffer - start;
ssize_t retval = usbus_cdc_acm_submit(&cdcacm, buffer, len);
usbus_cdc_acm_flush(&cdcacm);
return retval;
Copy link
Contributor

@benpicco benpicco Nov 18, 2025

Choose a reason for hiding this comment

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

Handling the partial transfer in the upper layer won't work when stdio_dispatch is used.

Copy link
Member Author

Choose a reason for hiding this comment

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

We can do the looping in stdio_dispatch for now and have the option to later improve, e.g. be asking the transports what amount of data they can handle right away and dispatching only the minimum of those values.

If we force all transports to be smart and handle all the special cases and IRQ themselves, we easily run into duplicates code and transport specific behaviour.

Copy link
Contributor

@mguetschow mguetschow left a comment

Choose a reason for hiding this comment

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

Thanks for doing this! Looks reasonable to me, but I'm not deep enough into the stdio stacks to feel confident enough to approve :) Not tested yet either.

Co-authored-by: mguetschow <mikolai.guetschow@tu-dresden.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: sys Area: System Area: USB Area: Universal Serial Bus CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Type: enhancement The issue suggests enhanceable parts / The PR enhances parts of the codebase / documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants