-
Notifications
You must be signed in to change notification settings - Fork 3
✨ (minor) Usb enumeration #92
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
base: main
Are you sure you want to change the base?
Conversation
d2cc07b to
c8f1daa
Compare
780ef28 to
f41be54
Compare
kammce
left a comment
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.
Please rebase your project and fix your merge conflicts. Also, we moved the libhal-util code into v5 in preperation for v6 which will use libhal-v5. So we can remove the include files within the include directory and only keep the ones within v5/*
6b474f8 to
c9c62f1
Compare
For devices that use the libhal usb interface high level class. Handles configuration at the device and usb configuration level Handles multiple configurations and multiple interfaces.
Simulated host by sending enumerator requests and having it respond. Correct payloads were verified
Changed usb.hpp to be a shorthand for including all usb utils
For devices that use the libhal usb interface high level class. Handles configuration at the device and usb configuration level Handles multiple configurations and multiple interfaces.
Simulated host by sending enumerator requests and having it respond. Correct payloads were verified
Changed usb.hpp to be a shorthand for including all usb utils
c9c62f1 to
ff91bbf
Compare
kammce
left a comment
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 haven't reviewed everything. I'm posting what I have now because I'm not sure if github will lose my info.
| } | ||
|
|
||
| std::u16string_view name; | ||
| std::pmr::vector<strong_ptr<interface>> interfaces; |
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.
Consider using hal::allocated_buffer<strong_ptr<interface>> instead of vector. We don't need to resize the array after construction, and thus all of its code needed to handle pushing and resizing can be eliminated. This way we pay for only what we needed which is a pointer, a length, and the allocator that created.
#include <libhal/allocated_buffer.hpp>It was meant for just buffers but its usable for any type. (maybe I should make an alias for it thats dynamic_array).
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 just realized a concern I have with std::pmr::vector. And apologies for bringing this up on late notice, but I think we should design with the intention that our allocator is monotonic and thus doesn't free memory. Meaning, if we make a copy of this configuration and destroy the old one, we don't get that memory back. That makes copying this type problematic. I don't have an answer. We may want to talk over voice chat to discuss this further.
| } | ||
| } | ||
|
|
||
| void enumerate() |
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.
This is purely a suggestion but I think we might benefit from this rename:
| void enumerate() | |
| void enumerate_sync_prototype0() |
Why? We can provide this temporary API that we plan to deprecate in the future. This way we can work with the experimental stuff while preventing the API from being broken. This will allow us to publish the code and leave space for a better implementation in the future. Because making this async may require a breaking API change. Or a new API called enumerate2().
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.
How about enumerate_sync()
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.
No, because I don't think this API is going to stick around. I don't know if enumeration_sync() is usable without an RTOS. I'm hoping we don't need an RTOS for our APIs.
Added required copyright notices Changed over from span_eq to ranges::equal and removed function Renamed truncated constant names to their full names Changed mock interface's packed array to use a predefined constant instead of a magic number Changed headers in the same library to be relative paths Factored out ctor arguments for device, config, and enumeration Enforced endianess with device descriptor Changed ref getters to return by value and add setters where needed Documented make_sub_scatter_array and changed return type to a struct for clarity Hardcoded string indexes for device descriptors, removed the ablity to set a start for string descriptors
kammce
left a comment
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.
This is a partial review of the changes you've made. Things are looking really good. I plan to dig into the details of the enumerator next. Thank you for your effort on this!
| #pragma once | ||
|
|
||
| #include <array> | ||
| #include <libhal/allocated_buffer.hpp> |
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.
Move this with the other libhal includes. Keep it away from the C++ headers.
| } | ||
|
|
||
| std::u16string_view name; | ||
| std::pmr::vector<strong_ptr<interface>> interfaces; |
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 just realized a concern I have with std::pmr::vector. And apologies for bringing this up on late notice, but I think we should design with the intention that our allocator is monotonic and thus doesn't free memory. Meaning, if we make a copy of this configuration and destroy the old one, we don't get that memory back. That makes copying this type problematic. I don't have an answer. We may want to talk over voice chat to discuss this further.
| if (determine_standard_request(req) == | ||
| standard_request_types::get_descriptor && | ||
| static_cast<descriptor_type>((req.value() & 0xFF << 8) >> 8) == | ||
| static_cast<descriptor_type>(req.value() >> 8 & 0xFF) == |
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.
Suggestion: I see this value() >> 8 & 0xFF a few times in the code? Maybe this warrants a free function or class method for getting this specific value from the request?
| @@ -74,11 +74,17 @@ boost::ut::suite<"enumeration_test"> enumeration_test = [] { | |||
| make_strong_ptr<std::array<configuration, 1>>(&pool, conf_arr); | |||
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'm a bit concerned that this results in a copy. It should be a move. I'm wondering if we should make configuration a move-only object.
kammce
left a comment
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.
alright, this is the review of the enumerator it self. There is a lot of extra code hanging around that needs some cleanup. And I think we should think very critically about errors and how we expect developers to handle them. No error should come without a means to handle it. And I think in most cases, the exceptions can be eliminated entirely.
| // | ||
| // for (auto const& el : raw_req) { | ||
| // | ||
| // } | ||
| // |
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.
| // | |
| // for (auto const& el : raw_req) { | |
| // | |
| // } | |
| // |
|
|
||
| // TODO: Handle exception | ||
| handle_standard_device_request(req); | ||
| // m_ctrl_ep->write({}); // Send ZLP to complete Data |
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.
| // m_ctrl_ep->write({}); // Send ZLP to complete Data |
Was that still needed?
| } while (!finished_enumeration); | ||
| } | ||
|
|
||
| [[nodiscard]] configuration& get_active_configuration() |
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.
Is this meant to be public? And if so, what is the expected use case for devs?
| while (!m_has_setup_packet) { | ||
| continue; | ||
| } |
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.
Any reason to not just return from this point? Why must we halt until a setup package comes in?
| standard_request_types::get_descriptor && | ||
| static_cast<descriptor_type>(req.value() >> 8 & 0xFF) == | ||
| descriptor_type::string) { | ||
| handle_str_descriptors(req.value() & 0xFF, req.length() > 2); |
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.
This API accepts a length as the second parameter, but you've only passed in a bool which is true/false based on the request length. Is that what you wanted?
| auto s_hdr = | ||
| std::to_array({ static_cast<byte>(4), | ||
| static_cast<byte>(descriptor_type::string) }); | ||
| auto lang = setup_packet::to_le_u16(m_lang_str); | ||
| auto scatter_arr_pair = make_scatter_bytes(s_hdr, lang); |
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.
Maybe this should be its own descriptor to manage all of this?
| } | ||
| } | ||
|
|
||
| void handle_str_descriptors(u8 const target_idx, u16 p_len) |
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.
| void handle_str_descriptors(u8 const target_idx, u16 p_len) | |
| void handle_str_descriptors(u8 target_idx, u16 p_len) |
No need for const here.
| std::mbstate_t state{}; | ||
| for (wchar_t const wc : sv) { | ||
| std::array<char, 1024> tmp; | ||
| size_t len = std::wcrtomb(tmp.data(), wc, &state); | ||
| if (len == static_cast<size_t>(-1)) { | ||
|
|
||
| continue; | ||
| } | ||
|
|
||
| for (size_t i = 0; i < len; i++) { | ||
| } | ||
| } |
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.
What is the intention of this code?
| auto const conf_sv_span = hal::as_bytes(opt_conf_sv.value()); | ||
| auto desc_len = static_cast<hal::byte>((conf_sv_span.size() + 2)); | ||
|
|
||
| auto hdr_arr = std::to_array( | ||
| { desc_len, static_cast<hal::byte>(descriptor_type::string) }); | ||
|
|
||
| auto scatter_arr_pair = | ||
| make_sub_scatter_bytes(p_len, hdr_arr, conf_sv_span); | ||
|
|
||
| auto p = scatter_span<byte const>(scatter_arr_pair.spans) | ||
| .first(scatter_arr_pair.count); |
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.
Feels like a string_descriptor like thing would be useful. One that provides sub scatter spans based on length.
| if (!req_handled) { | ||
| safe_throw(hal::argument_out_of_domain(this)); | ||
| } |
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.
What should the caller of this function do when they get this exception? This is more of an issue regarding the host. And if so, is reporting this to the caller important or should the enumerator handle it itself. Like using STALL.
Implemented a USB enumerator that can be used in various projects. This is meant as a default and reference implementation of an enumerator using the libhal USB system.