Implement event data trb handling#238
Conversation
5c81728 to
bcd3d40
Compare
bcd3d40 to
f46cde9
Compare
a96a6d4 to
35f5464
Compare
1f871b2 to
5e28c9f
Compare
67003f1 to
8067893
Compare
8067893 to
e0da4a4
Compare
e0da4a4 to
6b22267
Compare
snue
left a comment
There was a problem hiding this comment.
Did a partial review and left some comments. I really like the added unit tests.
Most importantly I'd like to get rid of most of the unreachable!() statements through code refactoring. And we also need to clarify what we do about the device overread / TD handling.
I also left lots of nits about naming and such. You really like long redundant variable names 😸
|
|
||
| let event_data_trb_data = match &transfer_trb.variant { | ||
| TransferTrbVariant::EventData(event_data_trb_data) => event_data_trb_data, | ||
| _ => unreachable!("checked variant before calling this handle"), |
There was a problem hiding this comment.
This redundant naming is exhausting.
| _ => unreachable!("checked variant before calling this handle"), | |
| let event_data = match &transfer_trb.variant { | |
| TransferTrbVariant::EventData(data) => data, |
Or even better, as you have all these unreachable!() branches in the called functions, can we modify the signature to take the embedded data directly and fix this in the caller?
6b22267 to
812139c
Compare
812139c to
a2e9a51
Compare
snue
left a comment
There was a problem hiding this comment.
I have a bunch of comments stored up. We are getting there, but I think you missed treating some of the error paths correctly? Could these be caught by unit tests as well?
A lot of my comments are nits for the documentation or name suggestions. I see some of my previously suggested renames are for pre-existing data structures (dropping the redundant Data part). I still think these are better names, but obviously it makes the diff larger. Sorry for that.
There are also some comments on outdated sections that I can't figure out how to edit/remove through the GitHub UI.
a2e9a51 to
5a603d6
Compare
|
Tests with a DummyControlEndpoint that would return specific errors are not yet included. |
f302339 to
efe1749
Compare
a7c7300 to
aeb5223
Compare
This enables a Data Stage to be composed of more than a single DataStageTrb.
Allow Event Data Trb and handle them accordingly when they are expected according to the spec.
The Event Data handle function has strong similarities between Control and each Normal Endpoint.
To use assert!() and similar derives have been added. Access to TransferEventTrbData were previously not necessary and are only public to enable the tests in the following commit. The MockInterrupter is using an async function to avoid having to implement any `wait_until_succeeds()` like utility in future tests where we might otherwise get race conditions between tasks. To better differentiate between release and test code the test only implementations are named Mock, because there are Dummy implementations used in the application.
The ControlEndpoint is tested with variations of trb input sequences with and without Event Data TRB. The NormalInEnpoint and NormalOutEndpoint are tested with a longer sequence each, including a section without Event Data TRB and a section with Event Data TRB.
aeb5223 to
8b7b352
Compare
snue
left a comment
There was a problem hiding this comment.
I have not done a full review, but it looks like we are getting there. The remaining unreachable statements are well explained and can be trusted. Left some comments, found typos etc.
| request: request.request, | ||
| value: request.value, | ||
| index: request.index, | ||
| data: &data, |
There was a problem hiding this comment.
| data: &request.data, |
Instead of the let data above?
| } | ||
| /// input is 17 Bit | ||
| const fn add(&mut self, addend: u32) { | ||
| self.edtla = MAX_VALUE_U24 & (self.edtla.wrapping_add(addend)); |
There was a problem hiding this comment.
The addend name confuses me 😅
| self.edtla = MAX_VALUE_U24 & (self.edtla.wrapping_add(addend)); | |
| self.edtla = MAX_VALUE_U24 & (self.edtla.wrapping_add(transfered_bytes)); |
| event_meta: EventDataTrbMetadata, | ||
| } | ||
| impl ControlTransferState { | ||
| // previous_completion_code should never be used as is, thus the error as a default value |
| let mut tmp = vec![0u8; transfer_length as usize]; | ||
| self.dma_bus.read_bulk(data_pointer, &mut tmp); | ||
|
|
||
| // SAFETY: is always set in the preceding setup stage |
| self.transfer_state.event_meta.zero(); | ||
| } | ||
| _ => { | ||
| error!("driver did not provide a spec ocmpliant control transfer trb chain"); |
There was a problem hiding this comment.
| error!("driver did not provide a spec ocmpliant control transfer trb chain"); | |
| error!("driver did not provide a spec compliant control transfer trb chain"); |
|
|
||
| let completion_code: CompletionCode; | ||
|
|
||
| // SACETY: in case the hardware_data.len() is bigger than u32 we take the u32 value regardless |
There was a problem hiding this comment.
typo:
| // SACETY: in case the hardware_data.len() is bigger than u32 we take the u32 value regardless | |
| // SAFETY: in case the hardware_data.len() is bigger than u32 we take the u32 value regardless |
| fn handle_status_stage_trb_hardware_response( | ||
| &mut self, | ||
| address: u64, | ||
| trb: StatusStageTrb, | ||
| ) -> anyhow::Result<()> { | ||
| match &mut self.transfer_state.data { | ||
| ControlTransferData::In(_) => { | ||
| unreachable!("internal error: ControlIn requests do the Hardware request in the SetupStage; a submission state to arrive here should never be used"); | ||
| } |
There was a problem hiding this comment.
Ah, we put so much effort into ensuring you can only call this with the right kind of TRB, but I think it would make much more sense if we had a single handle_hardware_response function that does the right thing depending on In/Out, instead of splitting it and having unreachable! cases in the respective functions. I may be missing something here, though.
Windows is never (as far as I saw in logs at least) utilizing the interrupt on completion bit. Windows will instead use event data trb.
The event data appeared on the Control Endpoint and the handler is initially written for the Control Endpoint.
When NormalIn and NormalOut Endpoints also needed event data handling I vendored the handle functions into the respective NormalEndpoint implementations because it seemed easiest. Combining this so that any endpoint can call the same handle_event_data seems better/less error prone.
Should that be added to this PR?