-
Notifications
You must be signed in to change notification settings - Fork 10
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
UART: Add wrapper around RIOT's UART-interface #39
base: main
Are you sure you want to change the base?
Conversation
Thanks for the PR. I've cleared it for a first CI run, but haven't yet had time to look at it. Given this needs a uart_t at construction, it might be good to look at #37 in parallel. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding this. It looks good over-all, and very comprehensive. I have several details and questions annotated across the source. Quite a few of them might easily be explained by inspiration from existing code; I'd still hope to not continue on the bad examples I've set in the past.
First of all, thank you for this comprehensive review. I will try to resolve your issues in the next few days :) |
CI tests are failing because RIOT's version of the riot-sys crate wasn't updated to the latest nightly yet -- but that's on the RIOT repository side of things. |
When all open issues are addressed (I think it's only the matter of safety and scoping), as with the DAC PR, I'll want to have a test around to ensure that the code is actually built during CI. Let me know whether you want to give that a try in the style of c9cf3f2, otherwise I'll do it as part of the final review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is approaching completion.
Given the many fixes and merges, please rebase onto current master and squash into one or some logically structured commits.
Would you add some small runnable test?
@kbarning hey! I accidentally started a competing PR, I overlooked your work. Do you intend of picking this PR up again? |
Sorry for having let this sit unduly long, especially after you've brought it all this way – and not having remembered that this is sitting when Teufelchen started up his PR didn't help make the situation better, yet here we are. Before I start looking through both PRs to see whether there's one that clearly outperforms (on usability, safe usability, features or clarity), would the two of you like to have a look over each other's PRs and/or a short exchange on whether there is one you both prefer or aspects missing from both? |
Okay, I gave it a thought and I believe this PR is better, in the sense that it is much more mature (documentation, api completeness, etc.). What is missing compared to my draft is the implementation of This PR needs a rebase. I did a shitty one locally to test this PR, it was annoying but not too bad. The code still works as intended. @kbarning I can imagine you want this done sooner than later, please rebase and squash. I'll poke @chrysn to review it in a timely manner :) (I can also offer to rebase & squash for you, if you like) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Final round of comments while going through this again after the duplicate PR situation is being resolved.
Most of them should be easy to apply. The soundness one will need another round of review, but given that there was a lifetime in the type in earlier iterations, it should be managable.
) -> Result<RMain, UartDeviceError> | ||
where | ||
F: FnMut(u8) + Send + 'scope, | ||
Main: FnOnce(&mut Self) -> RMain, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, just found a soundness hole in this:
A user might do something like this:
new_scoped(…, |_char| (), |self_| {
let racy = 0;
let shortlived_callback = |_char| { *racy = char };
new_scoped(…, shortlived_callback, |self2_| {
core::mem::swap(self_, self2_);
});
// at drop time it's effectively self_ that is dropped
do_something_with(&mut racy);
// wait for input
// goes to self2_, calls shortlived_callback, writes into racy
});
The two classical workarounds that occur to me off my head are using Pin (which we don't fully need but resolves the issue because they can't be swapped), or making 'scope part of Self (where for _without_rx and _static that'd be 'static), and then Main is for<'a> Main: FnOnce(&mut UartDevice<'a>)
so it becomes a brand, and this module will by construction ensure that there are never two UartDevice that can be swapped.
A scope was even in the type back in #39 (comment), which from how I view it now was right, but we just didn't manage to figure out why it was. The new_scoped etc. types could still live on the <'static>
version of this, which should make it unambiguous for both the user and the compiler which to pick.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, which version would you recommend? I have to admit, I've reached the end of my knowledge here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the version currently on chrysn-impl_uart_wrapper
would work. That the example still works unmodified even though I altered the lifetimes accordingly is a good sign.
[edit: Oups, that one so far does not apply the branding; I'm redoing it…]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed now: The proposed 479d663 introduces a lifetime on a UART, which states that if any callback has been configured, that callback is valid for that lifetime. Constructors that don't set a callback (or the one that sets one for a shorter time) are implemented on the 'static
because that's what corresponds to "no callback has been registered (therefore all registered callbacks live for the longest possible time)".
src/uart.rs
Outdated
/// static mut CB: fn(u8) = |new_data| { | ||
/// //do something here with the received data | ||
/// }; | ||
/// let mut uart = UartDevice::new_with_static_cb(0, 115200, unsafe { &mut CB }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The better example would be to use static_cell
or maybe crate::mutex::Mutex
, or even println!("Received {:02x}", new_data);
, but unless you feel super motivate to update this, we can leave that as is for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added the println!
in the examples :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then could almost just be fn cb(new_data: &[u8) { println!(...) }
, no need for a mut static that'll need unsafe and makes the linter scream.
Almost, because we don't get a &mut cb
, which hints at that user_callback should be F and not &'scope mut F … which again we can't do because we don't have a size limit.
We may need to iterate over that later on, but that'll work better when we see actual use cases. So let's have this in for now. I've just updated the example to not use static muts any more.
|
||
/// Change the pins back to plain GPIO functionality | ||
#[cfg(riot_module_periph_uart_reconfigure)] | ||
pub unsafe fn deinit_pins(&mut self) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When is this safe to call?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I'm not so sure about this. Is it an option to consume the object here via taking self
, so that the is destructed, and the user can use the pins as normal GPIO?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the object is consumed, it can't be reinited with the same settings later; it's an option, but I don't think that's the intention of deinit_pins. That seems to be more of a "let's pause this for a moment, do funky stuff with the UART, and return to using it later".
If we're not sure (I am not either), maybe let's remove that API for now.
If we keep this and the other removals in a separate commit when squashing, it'll be easy to selectively revert it.
I will have a look in the following days :) |
Co-authored-by: chrysn <[email protected]>
Co-authored-by: chrysn <[email protected]>
Co-authored-by: chrysn <[email protected]>
Co-authored-by: chrysn <[email protected]>
Co-authored-by: chrysn <[email protected]>
Co-authored-by: chrysn <[email protected]>
Co-authored-by: chrysn <[email protected]>
Co-authored-by: chrysn <[email protected]>
Co-authored-by: chrysn <[email protected]>
405bfc8
to
455291e
Compare
I have rebased my branch. The squashing is done directly via this PR right? (at least I think so, if not I think @chrysn can enable it). It would be great if you can add the embedded_io stuff @Teufelchen1 because I hardly have any time at the moment 😞. |
Yes, squashing can be done via GitHub UI by chrysn (I do not have permissions). |
I wouldn't insist on squashing in general; if you prefer things squashed to a single commit, that can be done at merge time. (My general approach is that I like to have history visible in the tree, and merges from master into the feature branches, because people can always get a view as-if-squashed from git using The latest version you pushed was not tested locally, was it? I've found a few compile errors, but am fixing them locally and pushing to the branch as part of a final checking round. |
I've added a few fixes to the branch |
When you're done with the fixes, should I rebase this branch from yours? |
Here is my attempt to create a wrapper for RIOT's UART-interface :)