USB: P3 — wire scheme into main loop for ecmd + usbaudiod

redbear-ecmd: scheme loop replaces stdout on Redox
  (/scheme/net/usbECM_<N>), notification reader kept for
  CDC link state / speed change events on both paths.

redbear-usbaudiod: scheme loop replaces stdout on Redox
  (/scheme/audio/usbAudio_<N>), stdin→isoch_OUT playback
  thread kept for host stdout path only.

All 4 class drivers now fully wired:
  acmd  → scheme event loop active on Redox 
  ftdi  → scheme event loop active on Redox 
  ecmd  → scheme event loop active on Redox 
  usbaudiod → scheme event loop active on Redox 
This commit is contained in:
2026-07-07 19:55:40 +03:00
parent 827cc0223d
commit 5b4f2ac984
2 changed files with 77 additions and 46 deletions
@@ -168,67 +168,83 @@ fn main() {
log::info!("CDC ECM ready on {} port {} — bulk_in={} bulk_out={} int={} filter={:04X}",
scheme, port_id, bulk_in_num, bulk_out_num, int_num, filter);
// Spawn stdin→bulk OUT writer thread for Ethernet Tx.
// ── Scheme IPC path (Redox) ──
#[cfg(target_os = "redox")]
{
use redox_scheme::scheme::SchemeSync;
use redox_scheme::{Socket, SignalBehavior};
let scheme_name = format!("net/usbECM_{}", port_id);
log::info!("ECM: registering /scheme/{}", scheme_name);
let socket = Socket::create().expect("ECM: scheme socket");
let mut scheme_state = redox_scheme::scheme::SchemeState::new();
let mut ecm_scheme = crate::scheme::EcmScheme::new(dev.bulk_in, dev.bulk_out);
socket.register(&scheme_name).expect("ECM: scheme register");
if let Some(mut int_ep) = dev.int_ep {
thread::spawn(move || {
let mut buf = [0u8; 16];
loop {
match int_ep.transfer_read(&mut buf) {
Ok(s) if s.bytes_transferred >= 8 => {
let b = buf[1];
match b {
CDC_NOTIFY_NETWORK_CONNECTION => log::info!("ECM: link {}", if u16::from_le_bytes([buf[2],buf[3]]) != 0 { "UP" } else { "DOWN" }),
CDC_NOTIFY_SPEED_CHANGE => log::info!("ECM: speed up={} down={}", u32::from_le_bytes([buf[8],buf[9],buf[10],buf[11]]), u32::from_le_bytes([buf[12],buf[13],buf[14],buf[15]])),
_ => log::debug!("ECM: notify type={:02X}", b),
}
}
Ok(_) => {}
Err(e) => log::warn!("ECM: int: {}", e),
}
thread::sleep(time::Duration::from_millis(100));
}
});
}
loop {
let req = socket.next_request(SignalBehavior::Restart).expect("ECM: scheme request");
if let Err(e) = req.handle_scheme_mut(&mut ecm_scheme, &mut scheme_state) {
log::warn!("ECM: scheme error: {}", e);
}
}
}
// ── Stdout path (host/Linux testing) ──
#[cfg(not(target_os = "redox"))]
{
let mut bulk_out = dev.bulk_out;
let _tx_thread = thread::spawn(move || {
let mut buf = [0u8; 2048];
loop {
match io::stdin().read(&mut buf) {
Ok(0) => break,
Ok(n) => { let _ = bulk_out.transfer_write(&buf[..n]); }
Ok(0) => break, Ok(n) => { let _ = bulk_out.transfer_write(&buf[..n]); }
Err(e) => { log::warn!("ECM: stdin: {}", e); break; }
}
}
});
// Optional: spawn notification reader for network connection / speed changes.
if let Some(mut int_ep) = dev.int_ep {
thread::spawn(move || {
let mut buf = [0u8; 16];
loop {
match int_ep.transfer_read(&mut buf) {
Ok(status) if status.bytes_transferred >= 8 => {
let bm_request_type = buf[0];
let b_notification = buf[1];
let w_value = u16::from_le_bytes([buf[2], buf[3]]);
let w_index = u16::from_le_bytes([buf[4], buf[5]]);
let w_length = u16::from_le_bytes([buf[6], buf[7]]);
match b_notification {
CDC_NOTIFY_NETWORK_CONNECTION => {
let connected = w_value != 0;
log::info!("ECM: network connection: {}", if connected { "UP" } else { "DOWN" });
}
CDC_NOTIFY_SPEED_CHANGE => {
let up: u32 = u32::from_le_bytes([buf[8], buf[9], buf[10], buf[11]]);
let down: u32 = u32::from_le_bytes([buf[12], buf[13], buf[14], buf[15]]);
log::info!("ECM: speed change: up={}bps down={}bps", up, down);
}
_ => {
log::debug!("ECM: notification type={:02X} request_type={:02X} value={:04X} index={:04X} len={}",
b_notification, bm_request_type, w_value, w_index, w_length);
}
Ok(s) if s.bytes_transferred >= 8 => {
match buf[1] {
CDC_NOTIFY_NETWORK_CONNECTION => log::info!("ECM: link {}", if u16::from_le_bytes([buf[2],buf[3]]) != 0 { "UP" } else { "DOWN" }),
CDC_NOTIFY_SPEED_CHANGE => log::info!("ECM: speed up={} down={}", u32::from_le_bytes([buf[8],buf[9],buf[10],buf[11]]), u32::from_le_bytes([buf[12],buf[13],buf[14],buf[15]])),
_ => {}
}
}
Ok(_) => {}
Err(e) => log::warn!("ECM: interrupt: {}", e),
Ok(_) => {} Err(e) => log::warn!("ECM: int: {}", e),
}
thread::sleep(time::Duration::from_millis(100));
}
});
}
// Main loop: bulk IN → stdout (Ethernet Rx).
let mut buf = [0u8; 2048];
loop {
match dev.bulk_in.transfer_read(&mut buf) {
Ok(status) if status.bytes_transferred > 0 => {
let n = status.bytes_transferred as usize;
let _ = io::stdout().write_all(&buf[..n]);
let _ = io::stdout().flush();
}
Ok(_) => {}
Err(e) => log::warn!("ECM: read: {}", e),
Ok(status) if status.bytes_transferred > 0 => { let _ = io::stdout().write_all(&buf[..status.bytes_transferred as usize]); let _ = io::stdout().flush(); }
Ok(_) => {} Err(e) => log::warn!("ECM: read: {}", e),
}
thread::sleep(time::Duration::from_millis(1));
}
}
}
@@ -248,7 +248,28 @@ fn main() {
let _ = dev.set_mute(0, false);
log::info!("UAC: sample rate={} unmuted", sample_rate);
// Spawn stdin→isoch OUT writer thread for playback
// ── Scheme IPC path (Redox) ──
#[cfg(target_os = "redox")]
{
use redox_scheme::scheme::SchemeSync;
use redox_scheme::{Socket, SignalBehavior};
let scheme_name = format!("audio/usbAudio_{}", port_id);
log::info!("UAC: registering /scheme/{}", scheme_name);
let socket = Socket::create().expect("UAC: scheme socket");
let mut scheme_state = redox_scheme::scheme::SchemeState::new();
let mut audio_scheme = crate::scheme::AudioScheme::new(dev.isoch_in, dev.isoch_out);
socket.register(&scheme_name).expect("UAC: scheme register");
loop {
let req = socket.next_request(SignalBehavior::Restart).expect("UAC: scheme request");
if let Err(e) = req.handle_scheme_mut(&mut audio_scheme, &mut scheme_state) {
log::warn!("UAC: scheme error: {}", e);
}
}
}
// ── Stdout path (host/Linux testing) ──
#[cfg(not(target_os = "redox"))]
{
let frame_size = dev.frame_size();
if let Some(isoch_out) = dev.isoch_out {
let mut isoch_out = isoch_out;
@@ -263,20 +284,14 @@ fn main() {
}
});
}
// Main loop: isoch IN → stdout (recording)
let buf_size = frame_size * 480;
let mut buf = vec![0u8; buf_size];
loop {
match dev.isoch_in.transfer_read(&mut buf) {
Ok(status) if status.bytes_transferred > 0 => {
let n = status.bytes_transferred as usize;
let _ = io::stdout().write_all(&buf[..n]);
let _ = io::stdout().flush();
}
Ok(_) => {}
Err(e) => log::warn!("UAC: read: {}", e),
Ok(s) if s.bytes_transferred > 0 => { let _ = io::stdout().write_all(&buf[..s.bytes_transferred as usize]); let _ = io::stdout().flush(); }
Ok(_) => {} Err(e) => log::warn!("UAC: read: {}", e),
}
thread::sleep(time::Duration::from_millis(1));
}
}
}