Skip to content
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

How to check if hardware buffers are flushed? #20

Open
dimpolo opened this issue Mar 24, 2021 · 1 comment
Open

How to check if hardware buffers are flushed? #20

dimpolo opened this issue Mar 24, 2021 · 1 comment

Comments

@dimpolo
Copy link

dimpolo commented Mar 24, 2021

The docs of SerialPort::flush metion that:

Note that even if this method returns Ok, data may still be in hardware buffers on either side.

I don't fully understand how USB works.
Would it be enough to check if write_state == WriteState::Idle?
Also do I understand correctly that usb_device.poll(&mut [&mut serial_port]) needs to be called to make progress in flushing the hardware buffers?

@ianrrees
Copy link

ianrrees commented May 31, 2021

Transactions on a USB are all driven by the host side, and usually the device side has dedicated hardware that handles some fraction of the work. The poll() method needs to be called periodically to check the USB hardware, to see if the host has done anything that might need to be handled by the device firmware. So, yes your understanding is correct, and the most efficient approach is probably to call poll() from within a USB ISR.

Yes, if write_state == WriteState::Idle, then there is no data waiting in the device's write buffer, however that field is private - you're looking for SerialPort::flush(), which returns Ok(()) when the write buffer is empty. Ed to add: I think this is the best that can be done in a device-agnostic way, at least on the ATSAMD parts I've been working on, I'm pretty sure it means that the data has been copied out of the device.

The reason for WriteState has to do with the implementation details of USB bulk transactions, which can involve more than one data packet; basically if you have a 64B bulk IN endpoint in a device (for moving data from the device to the host) the host knows that a transaction is complete when the device sends a packet with between 0 (a Zero-Length Packet aka ZLP) and 63B of data. If the device sends a full 64B packet, the host can't tell if the device meant to send exactly 64B in the whole transaction, or if it has more data to write. The application that initiated the USB read from the device will only see data after a transaction has completed, so the device limits the number of full packets it'll write in a given transaction to SHORT_PACKET_INTERVAL, once it hits the limit it will write one less than the endpoint size, say 63B, to signal an end of that particular transaction. It makes a sort of upper bound on the latency between some data being written in to SerialPort::write(), and coming out in the application running on the host - it's not a deterministic limit, because bulk transfers are scheduled according to available bandwidth on the bus.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants