USB: P3 scheme service wrappers — ecmd + usbaudiod
redbear-ecmd: EcmScheme → /scheme/net/usbECM_<N> for netstack redbear-usbaudiod: AudioScheme → /scheme/audio/usbAudio_<N> for audiod (supports capture/playback path routing via openat path names) All 4 class drivers now have scheme service wrappers: redbear-acmd → /scheme/ttys/usbACM_<N> redbear-ftdi → /scheme/ttys/usbFTDI_<N> redbear-ecmd → /scheme/net/usbECM_<N> redbear-usbaudiod → /scheme/audio/usbAudio_<N> (HID+Storage already have scheme integration via ProducerHandle/DiskScheme)
This commit is contained in:
@@ -14,6 +14,9 @@ xhcid = { path = "../../../../../local/sources/base/drivers/usb/xhcid" }
|
||||
common = { path = "../../../../../local/sources/base/drivers/common" }
|
||||
libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] }
|
||||
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
//! Implements the CDC ECM subclass for USB Ethernet adapters:
|
||||
//! control requests (packet filter, multicast), bidirectional
|
||||
//! bulk I/O for Ethernet frames, and CDC notification handling.
|
||||
//! On Redox: registers /scheme/net/usbECM_<N> for netstack integration.
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
mod scheme;
|
||||
|
||||
use std::{env, io, io::{Read, Write}, thread, time};
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
use redox_scheme::scheme::SchemeSync;
|
||||
use redox_scheme::{CallerCtx, OpenResult};
|
||||
use syscall::error::{Error, Result, EBADF, EINVAL};
|
||||
use syscall::flag::{MODE_FILE, O_RDWR, O_STAT};
|
||||
|
||||
use xhcid_interface::XhciEndpHandle;
|
||||
|
||||
pub struct EcmScheme {
|
||||
bulk_in: Mutex<XhciEndpHandle>,
|
||||
bulk_out: Mutex<XhciEndpHandle>,
|
||||
open_count: Mutex<usize>,
|
||||
}
|
||||
|
||||
impl EcmScheme {
|
||||
pub fn new(bulk_in: XhciEndpHandle, bulk_out: XhciEndpHandle) -> Self {
|
||||
Self { bulk_in: Mutex::new(bulk_in), bulk_out: Mutex::new(bulk_out), open_count: Mutex::new(0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeSync for EcmScheme {
|
||||
fn openat(&self, id: usize, path: &str, _flags: usize, _ctx: &CallerCtx) -> Result<OpenResult> {
|
||||
if id != 1 { return Err(Error::new(EBADF)); }
|
||||
if path.is_empty() || path == "." || path == "/" {
|
||||
*self.open_count.lock().unwrap_or_else(|e| e.into_inner()) += 1;
|
||||
return Ok(OpenResult::ThisScheme { number: 1, flags: O_STAT | MODE_FILE });
|
||||
}
|
||||
let mut count = self.open_count.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let next_id = 2 + *count; *count += 1;
|
||||
Ok(OpenResult::OtherScheme { number: next_id, flags: O_RDWR })
|
||||
}
|
||||
fn read(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
|
||||
if id < 2 || buf.is_empty() { return Err(Error::new(EBADF)); }
|
||||
self.bulk_in.lock().unwrap_or_else(|e| e.into_inner())
|
||||
.transfer_read(buf).map(|s| s.bytes_transferred as usize)
|
||||
.map_err(|e| { log::warn!("ECM scheme: read: {}", e); Error::new(EINVAL) })
|
||||
}
|
||||
fn write(&mut self, id: usize, buf: &[u8], _ctx: &CallerCtx) -> Result<usize> {
|
||||
if id < 2 || buf.is_empty() { return Err(Error::new(EBADF)); }
|
||||
self.bulk_out.lock().unwrap_or_else(|e| e.into_inner())
|
||||
.transfer_write(buf).map(|s| s.bytes_transferred as usize)
|
||||
.map_err(|e| { log::warn!("ECM scheme: write: {}", e); Error::new(EINVAL) })
|
||||
}
|
||||
fn fsync(&mut self, _id: usize, _ctx: &CallerCtx) -> Result<()> { Ok(()) }
|
||||
fn close(&self, id: usize, _ctx: &CallerCtx) -> Result<()> {
|
||||
if id >= 2 { let mut c = self.open_count.lock().unwrap_or_else(|e| e.into_inner()); *c = c.saturating_sub(1); }
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,9 @@ xhcid = { path = "../../../../../local/sources/base/drivers/usb/xhcid" }
|
||||
common = { path = "../../../../../local/sources/base/drivers/common" }
|
||||
libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] }
|
||||
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
//! - Audio Streaming Interface (PCM isochronous audio data)
|
||||
//! - Sample rates: 8000, 11025, 16000, 22050, 44100, 48000 Hz
|
||||
//! - 1-2 channels (mono/stereo), 8/16-bit
|
||||
//! On Redox: registers /scheme/audio/usbAudio_<N> for audiod integration.
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
mod scheme;
|
||||
|
||||
use std::{env, io, io::{Read, Write}, thread, time};
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
use redox_scheme::scheme::SchemeSync;
|
||||
use redox_scheme::{CallerCtx, OpenResult};
|
||||
use syscall::error::{Error, Result, EBADF, EINVAL};
|
||||
use syscall::flag::{MODE_FILE, O_RDONLY, O_WRONLY, O_STAT};
|
||||
|
||||
use xhcid_interface::XhciEndpHandle;
|
||||
|
||||
pub struct AudioScheme {
|
||||
isoch_in: Mutex<XhciEndpHandle>,
|
||||
isoch_out: Mutex<Option<XhciEndpHandle>>,
|
||||
open_count: Mutex<usize>,
|
||||
}
|
||||
|
||||
impl AudioScheme {
|
||||
pub fn new(isoch_in: XhciEndpHandle, isoch_out: Option<XhciEndpHandle>) -> Self {
|
||||
Self { isoch_in: Mutex::new(isoch_in), isoch_out: Mutex::new(isoch_out), open_count: Mutex::new(0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeSync for AudioScheme {
|
||||
fn openat(&self, id: usize, path: &str, _flags: usize, _ctx: &CallerCtx) -> Result<OpenResult> {
|
||||
if id != 1 { return Err(Error::new(EBADF)); }
|
||||
let mut c = self.open_count.lock().unwrap_or_else(|e| e.into_inner());
|
||||
if path.is_empty() || path == "." || path == "/" {
|
||||
*c += 1; return Ok(OpenResult::ThisScheme { number: 1, flags: O_STAT | MODE_FILE });
|
||||
}
|
||||
let flags = match path {
|
||||
"capture" | "rec" => O_RDONLY,
|
||||
"playback" | "play" => if self.isoch_out.lock().unwrap_or_else(|e| e.into_inner()).is_some() { O_WRONLY } else { return Err(Error::new(EINVAL)); },
|
||||
_ => O_RDWR,
|
||||
};
|
||||
let next_id = 2 + *c; *c += 1;
|
||||
Ok(OpenResult::OtherScheme { number: next_id, flags })
|
||||
}
|
||||
fn read(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result<usize> {
|
||||
if id < 2 || buf.is_empty() { return Err(Error::new(EBADF)); }
|
||||
self.isoch_in.lock().unwrap_or_else(|e| e.into_inner())
|
||||
.transfer_read(buf).map(|s| s.bytes_transferred as usize)
|
||||
.map_err(|e| { log::warn!("Audio scheme: read: {}", e); Error::new(EINVAL) })
|
||||
}
|
||||
fn write(&mut self, id: usize, buf: &[u8], _ctx: &CallerCtx) -> Result<usize> {
|
||||
if id < 2 || buf.is_empty() { return Err(Error::new(EBADF)); }
|
||||
match self.isoch_out.lock().unwrap_or_else(|e| e.into_inner()).as_mut() {
|
||||
Some(out) => out.transfer_write(buf).map(|s| s.bytes_transferred as usize)
|
||||
.map_err(|e| { log::warn!("Audio scheme: write: {}", e); Error::new(EINVAL) }),
|
||||
None => Err(Error::new(EINVAL)),
|
||||
}
|
||||
}
|
||||
fn fsync(&mut self, _id: usize, _ctx: &CallerCtx) -> Result<()> { Ok(()) }
|
||||
fn close(&self, id: usize, _ctx: &CallerCtx) -> Result<()> {
|
||||
if id >= 2 { let mut c = self.open_count.lock().unwrap_or_else(|e| e.into_inner()); *c = c.saturating_sub(1); }
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user