A UART can return more bytes than the console asked for. This doesn't cause a crash because of how the console capsule copies into the appslice:
If rx_len is longer than the size of app_buffer, the app_buffer.iter_mut() makes sure we don't copy too many bytes. However, in the callback, we use the rx_len.
I'm not _entirely_ sure what the correct fix is. We can certainly check for how many bytes we copied, and report that instead. Should there be an error? Is it ok if we drop any extra bytes?
Since an app is calling down with a fixed buffer length, I think the solution would be to not allow the underlying UART hardware to receive too much. That is, instead of passing the whole console capsule buffer here, pass a slice up to the length asked for by the app.
In that way, we match the semantics of the underlying UART hardware (which I believe will drop bytes in the case of buffer overrun).
But then we lose access to the rest of that buffer forever, right?
Aha..sounds like a job for LeasableBuffer!
The other problem, is what happens if the hardware _does_ receive too much? For example, serial over USB isn't a real UART bus, and we might get longer packets from the host than the console asked for. Or a UART implementation might just want to return all of the bytes it has received into a buffer by the time it reads from the HW buffer.
At a certain point, there's just nothing you can do about this case if the bus doesn't have flow control.
Technically, the serial standard includes RTS/CTS [ready-to-send / clear-to-send] pins for this purpose. I assume that's therefore part of the serial-over-USB protocol.
In the absence of flow control, I think the best/only thing that the underlying driver can do is report to the upper layers when bytes were dropped due to buffer overrun.
That's not the only thing, the driver can also just pass up more bytes than were asked for (of course until the buffer runs out of space). That is what can happen right now.
the driver can also just pass up more bytes than were asked for (of course until the buffer runs out of space). That is what can happen right now.
Well that's slightly terrifying. I don't think it's a good behavior for the kernel to modify parts of the buffer that the app didn't ask it to. We should.. not do that.
Well that's slightly terrifying. I don't think it's a good behavior for the kernel to modify parts of the buffer that the app didn't ask it to. We should.. not do that.
Not the original issue, but related, is I was (am?) actually planning on using this trick to implement UartReceiveAdvanced on top of the CDC-USB stack for the bootloader. The bootloader does its UART reads based on timeouts (hence using receive advanced trait).
The problem with uart receive over USB is that the host just sends us data whenever it wants. My idea was to have a shim that starts a receive by asking CDC for 1 byte, with the expectation that it would get probably a lot more than 1 byte. Then, set a timer and ask for another 1 byte. And repeat this until the actual timer expires, signaling that the host has stopped sending us data.
If the CDC layer cannot fill the receive buffer with more bytes than were requested, then I'm not sure what to do with the extra bytes that the host sends us at any given time.
Most helpful comment
Aha..sounds like a job for LeasableBuffer!