redbear-ecmd, redbear-usbaudiod: fix endpoint numbering to xhcid global index

Same latent bug class as the acmd fix (f505d18a89): both drivers used
the USB endpoint ADDRESS number (ep.address & 0x0F) as the endpoint key,
but xhcid keys endpoints by global enumeration index across all
interfaces of the selected configuration. The two coincide only when
the device's endpoint addresses are sequential in enumeration order —
devices with non-sequential addresses (some modems/audio gear) would
open the wrong endpoint or fail to open.

Both now count endpoints in configuration order per selected
configuration, matching xhcid's PortState::get_endp_desc indexing.

Verified: cargo check -Z build-std --target x86_64-unknown-redox clean
for both crates.
This commit is contained in:
2026-07-20 21:05:26 +09:00
parent f505d18a89
commit 08097d5a2d
2 changed files with 43 additions and 27 deletions
@@ -110,29 +110,35 @@ fn main() {
let data_iface = data_iface.expect("No CDC ECM data interface found");
// Find bulk IN + bulk OUT on the data interface, and optional
// interrupt endpoint on the control interface.
// interrupt endpoint on the control interface. xhcid keys endpoints
// by GLOBAL index across all interfaces of the configuration (see
// xhcid PortState::get_endp_desc / endpoint_states) — the USB
// endpoint address number only coincides with that index when the
// device's addresses are sequential in enumeration order, so count
// endpoints in configuration order instead.
let mut bulk_in_num = 0u8;
let mut bulk_out_num = 0u8;
let mut int_num = 0u8;
for conf_desc in desc.config_descs.iter() {
// xhcid indexes endpoints within the selected configuration only;
// reset the counter per config and keep only the selected one.
let mut config_num = 0u8;
for ifd in conf_desc.interface_descs.iter() {
if ifd.number == data_iface {
for ep in ifd.endpoints.iter() {
if ep.attributes & 0x03 == 0x02 { // bulk transfer type
match ep.direction() {
EndpDirection::In => bulk_in_num = ep.address & 0x0F,
EndpDirection::Out => bulk_out_num = ep.address & 0x0F,
_ => {}
}
for ep in ifd.endpoints.iter() {
config_num += 1;
if conf_desc.configuration_value != conf_val {
continue;
}
if ifd.number == data_iface && ep.attributes & 0x03 == 0x02 { // bulk transfer type
match ep.direction() {
EndpDirection::In => bulk_in_num = config_num,
EndpDirection::Out => bulk_out_num = config_num,
_ => {}
}
}
}
if ifd.number == ctrl_iface {
for ep in ifd.endpoints.iter() {
if ep.attributes & 0x03 == 0x03 { // interrupt transfer type
int_num = ep.address & 0x0F;
}
if ifd.number == ctrl_iface && ep.attributes & 0x03 == 0x03 { // interrupt transfer type
int_num = config_num;
}
}
}
@@ -188,24 +188,34 @@ fn main() {
return;
}
// Find isoch endpoints on the streaming interface
// Find isoch endpoints on the streaming interface. xhcid keys
// endpoints by GLOBAL index across all interfaces of the
// configuration (see xhcid PortState::get_endp_desc /
// endpoint_states) — the USB endpoint address number only coincides
// with that index when the device's addresses are sequential in
// enumeration order, so count endpoints in configuration order.
let mut isoch_in_num = 0u8;
let mut isoch_out_num = 0u8;
let mut max_pkt_size = 0u16;
for conf_desc in desc.config_descs.iter() {
// xhcid indexes endpoints within the selected configuration only;
// reset the counter per config and keep only the selected one.
let mut config_num = 0u8;
for ifd in conf_desc.interface_descs.iter() {
if ifd.number == as_iface {
for ep in ifd.endpoints.iter() {
if ep.attributes & 0x03 == 0x01 { // isoch transfer type
if ep.max_packet_size > max_pkt_size {
max_pkt_size = ep.max_packet_size;
}
match ep.direction() {
EndpDirection::In => isoch_in_num = ep.address & 0x0F,
EndpDirection::Out => isoch_out_num = ep.address & 0x0F,
_ => {}
}
for ep in ifd.endpoints.iter() {
config_num += 1;
if conf_desc.configuration_value != conf_val {
continue;
}
if ifd.number == as_iface && ep.attributes & 0x03 == 0x01 { // isoch transfer type
if ep.max_packet_size > max_pkt_size {
max_pkt_size = ep.max_packet_size;
}
match ep.direction() {
EndpDirection::In => isoch_in_num = config_num,
EndpDirection::Out => isoch_out_num = config_num,
_ => {}
}
}
}