Skip to content

Conversation

@PhazonicRidley
Copy link

@PhazonicRidley PhazonicRidley commented Jan 30, 2026

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.

@PhazonicRidley PhazonicRidley changed the title ✨ Usb enumeration ✨ (minor) Usb enumeration Jan 30, 2026
@PhazonicRidley PhazonicRidley marked this pull request as draft February 1, 2026 05:19
@PhazonicRidley PhazonicRidley force-pushed the usb-enumeration branch 2 times, most recently from 780ef28 to f41be54 Compare February 5, 2026 20:57
@kammce kammce self-requested a review February 7, 2026 22:59
Copy link
Member

@kammce kammce left a 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/*

@PhazonicRidley PhazonicRidley force-pushed the usb-enumeration branch 3 times, most recently from 6b474f8 to c9c62f1 Compare February 9, 2026 19:10
@PhazonicRidley PhazonicRidley marked this pull request as ready for review February 10, 2026 00:26
PhazonicRidley and others added 17 commits February 9, 2026 16:28
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
Copy link
Member

@kammce kammce left a 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.

#include <libhal/units.hpp>
#include <libhal/usb.hpp>

#include "libhal-util/usb/utils.hpp"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use the standard <> for this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can, I left it as "" because its the same repo that I am referencing and I am not a huge fan of relative path includes.

Comment on lines +245 to +255
std::array<u8, 9> m_packed_array = {
iface_desc_length,
iface_desc_type,
0, // interface_number
0, // alternate_setting
1, // num_endpoints
2, // interface_class
3, // interface_sub_class
4, // interface_protocol
0 // interface name index
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be using the constexpr length as the template argument and using the object's size to set the first element.

See this example of this working: https://godbolt.org/z/1j3rqzr1W

The code change would look like this:

Suggested change
std::array<u8, 9> m_packed_array = {
iface_desc_length,
iface_desc_type,
0, // interface_number
0, // alternate_setting
1, // num_endpoints
2, // interface_class
3, // interface_sub_class
4, // interface_protocol
0 // interface name index
};
std::array<u8, iface_desc_length> m_packed_array = {
sizeof(m_packed_array),
iface_desc_type,
0, // interface_number
0, // alternate_setting
1, // num_endpoints
2, // interface_class
3, // interface_sub_class
4, // interface_protocol
0 // interface name index
};

Now you don't have to worry about accidentially making a mistake which causes the sizes to be off. Like for example, accidentally putting 10 or 19 instead of 9 for the length.

Comment on lines +21 to +25
constexpr u8 iface_desc_length = 9;
constexpr u8 iface_desc_type = 0x4;
constexpr u8 str_desc_type = 0x3;
constexpr u8 iad_length = 0x08;
constexpr u8 iad_type = 0x0B;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer these written out fully. interface and string and I'm not sure what iad stands for off the top of my head, so spelling this out would be helpful

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even for just a mock?

Comment on lines +27 to +46
template<typename T>
bool span_eq(std::span<T> const lhs, std::span<T> const rhs)
{
if (lhs.size() != rhs.size()) {
return false;
}

for (size_t i = 0; i < lhs.size(); i++) {
if (lhs[i] != rhs[i]) {
return false;
}
}
return true;
}

template<typename T>
bool span_ne(std::span<T> const lhs, std::span<T> const rhs)
{
return !(lhs == rhs);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does std::ranges::equals(lhs, rhs) not work in this situation?

strong_ptr<std::array<configuration, num_configs>> const& p_configs,
u16 p_lang_str, // NOLINT
u8 p_starting_str_idx,
bool enumerate_immediately = true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: I'm not a fan of the bool parameter here. And this is a pretty large number of parameters, so this looks like a good option for a struct parameter.

strong_ptr<device> const& p_device,
strong_ptr<std::array<configuration, num_configs>> const& p_configs,
u16 p_lang_str, // NOLINT
u8 p_starting_str_idx,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should we allow the developer to choose this and why is it required? Is there some feature that gets unlocked from this depending on what value is set? Otherwise, this is laying a trap for someone when used. If you set this too low, they get an error. Set it too high, get an error. Or we can automate this and remove the error path completely.

}

std::u16string_view name;
std::pmr::vector<strong_ptr<interface>> interfaces;
Copy link
Member

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).

Comment on lines +125 to +127
if (enumerate_immediately) {
enumerate();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how I feel about this. My current opinion is that we should remove this as the operation isn't very async at the moment. Its like calling a scheduler and not expecting to get back control of the code beyond that point if the usb host is not connected.

}
}

void enumerate()
Copy link
Member

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:

Suggested change
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().

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

Successfully merging this pull request may close these issues.

2 participants