use bytes::{Bytes, BytesMut};
use nix::poll::{poll, PollFd, PollFlags, PollTimeout};
use proc_mounts::MountIter;
use std::{
collections::{hash_map::Entry, HashMap, HashSet},
ffi::{OsStr, OsString},
fmt, fs,
fs::File,
hash::Hash,
io::{Error, ErrorKind, Read, Result, Write},
os::fd::{AsFd, AsRawFd, RawFd},
path::{Path, PathBuf},
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex, Weak,
},
time::Duration,
};
use uuid::Uuid;
use super::{
util::{split_function_dir, value, FunctionDir, Status},
Function, Handle,
};
use crate::{Class, Language};
mod aio;
mod ffs;
pub(crate) fn driver() -> &'static OsStr {
OsStr::new("ffs")
}
pub use ffs::CustomDesc;
#[derive(Debug)]
#[non_exhaustive]
pub struct Interface {
pub interface_class: Class,
pub name: HashMap<Language, String>,
pub endpoints: Vec<Endpoint>,
pub association: Option<Association>,
pub os_ext_compat: Vec<OsExtCompat>,
pub os_ext_props: Vec<OsExtProp>,
pub custom_descs: Vec<CustomDesc>,
}
impl Interface {
pub fn new(interface_class: Class, name: impl AsRef<str>) -> Self {
Self {
interface_class,
name: [(Language::default(), name.as_ref().to_string())].into(),
endpoints: Vec::new(),
association: None,
os_ext_compat: Vec::new(),
os_ext_props: Vec::new(),
custom_descs: Vec::new(),
}
}
#[must_use]
pub fn with_endpoint(mut self, endpoint: Endpoint) -> Self {
self.endpoints.push(endpoint);
self
}
#[must_use]
pub fn with_association(mut self, association: &Association) -> Self {
self.association = Some(association.clone());
self
}
#[must_use]
pub fn with_os_ext_compat(mut self, os_ext_compat: OsExtCompat) -> Self {
self.os_ext_compat.push(os_ext_compat);
self
}
#[must_use]
pub fn with_os_ext_prop(mut self, os_ext_prop: OsExtProp) -> Self {
self.os_ext_props.push(os_ext_prop);
self
}
#[must_use]
pub fn with_custom_desc(mut self, custom_desc: CustomDesc) -> Self {
self.custom_descs.push(custom_desc);
self
}
}
#[derive(Debug, Clone)]
pub struct Association {
addr: Arc<()>,
pub function_class: Class,
pub name: HashMap<Language, String>,
}
impl PartialEq for Association {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.addr, &other.addr)
&& self.function_class == other.function_class
&& self.name == other.name
}
}
impl Eq for Association {}
impl Hash for Association {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
Arc::as_ptr(&self.addr).hash(state);
}
}
impl Association {
pub fn new(function_class: Class, name: impl AsRef<str>) -> Self {
Self {
addr: Arc::new(()),
function_class,
name: [(Language::default(), name.as_ref().to_string())].into(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Direction {
DeviceToHost,
HostToDevice,
}
pub struct EndpointDirection {
direction: Direction,
pub queue_len: u32,
tx: value::Sender<EndpointIo>,
}
impl fmt::Debug for EndpointDirection {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("EndpointDirection")
.field("direction", &self.direction)
.field("queue_len", &self.queue_len)
.finish()
}
}
impl EndpointDirection {
const DEFAULT_QUEUE_LEN: u32 = 16;
pub fn device_to_host() -> (EndpointSender, EndpointDirection) {
let (tx, rx) = value::channel();
let writer = EndpointSender(rx);
let this = Self { direction: Direction::DeviceToHost, tx, queue_len: Self::DEFAULT_QUEUE_LEN };
(writer, this)
}
pub fn host_to_device() -> (EndpointReceiver, EndpointDirection) {
let (tx, rx) = value::channel();
let reader = EndpointReceiver(rx);
let this = Self { direction: Direction::HostToDevice, tx, queue_len: Self::DEFAULT_QUEUE_LEN };
(reader, this)
}
#[must_use]
pub fn with_queue_len(mut self, queue_len: u32) -> Self {
self.queue_len = queue_len;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SyncType {
NoSync,
Async,
Adaptive,
Sync,
}
impl SyncType {
fn to_attributes(self) -> u8 {
(match self {
Self::NoSync => 0b00,
Self::Async => 0b01,
Self::Adaptive => 0b10,
Self::Sync => 0b11,
} << 2)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum UsageType {
Data,
Feedback,
ImplicitFeedback,
}
impl UsageType {
fn to_attributes(self) -> u8 {
(match self {
Self::Data => 0b00,
Self::Feedback => 0b01,
Self::ImplicitFeedback => 0b10,
} << 4)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TransferType {
Control,
Isochronous {
sync: SyncType,
usage: UsageType,
},
Bulk,
Interrupt,
}
impl TransferType {
fn to_attributes(self) -> u8 {
match self {
Self::Control => 0b00,
Self::Isochronous { sync, usage } => 0b01 | sync.to_attributes() | usage.to_attributes(),
Self::Bulk => 0b10,
Self::Interrupt => 0b11,
}
}
}
#[derive(Debug)]
#[non_exhaustive]
pub struct Endpoint {
pub direction: EndpointDirection,
pub transfer: TransferType,
pub max_packet_size_hs: u16,
pub max_packet_size_ss: u16,
pub max_burst_ss: u8,
pub bytes_per_interval_ss: u16,
pub interval: u8,
pub audio: Option<EndpointAudio>,
}
#[derive(Debug)]
pub struct EndpointAudio {
pub refresh: u8,
pub synch_address: u8,
}
impl Endpoint {
pub fn bulk(direction: EndpointDirection) -> Self {
Self::custom(direction, TransferType::Bulk)
}
pub fn custom(direction: EndpointDirection, transfer: TransferType) -> Self {
let transfer_direction = direction.direction;
Self {
direction,
transfer,
max_packet_size_hs: 512,
max_packet_size_ss: 1024,
max_burst_ss: 0,
bytes_per_interval_ss: 0,
interval: match transfer_direction {
Direction::DeviceToHost => 0,
Direction::HostToDevice => 1,
},
audio: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OsExtCompat {
pub compatible_id: [u8; 8],
pub sub_compatible_id: [u8; 8],
}
impl OsExtCompat {
pub const fn new(compatible_id: [u8; 8], sub_compatible_id: [u8; 8]) -> Self {
Self { compatible_id, sub_compatible_id }
}
pub const fn winusb() -> Self {
Self::new(*b"WINUSB\0\0", [0; 8])
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OsExtProp {
pub name: String,
pub value: OsRegValue,
}
impl OsExtProp {
pub fn new(name: impl AsRef<str>, value: impl Into<OsRegValue>) -> Self {
Self { name: name.as_ref().to_string(), value: value.into() }
}
pub fn device_interface_guid(guid: Uuid) -> Self {
Self::new("DeviceInterfaceGUID", guid.to_string())
}
fn as_os_ext_prop(&self) -> ffs::OsExtProp {
let mut name = self.name.clone();
name.push('\0');
ffs::OsExtProp { name, data_type: self.value.as_type(), data: self.value.as_bytes() }
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum OsRegValue {
Sz(String),
ExpandSz(String),
Binary(Vec<u8>),
DwordLe(u32),
DwordBe(u32),
Link(String),
MultiSz(Vec<String>),
}
impl OsRegValue {
fn as_type(&self) -> u32 {
match self {
Self::Sz(_) => 1,
Self::ExpandSz(_) => 2,
Self::Binary(_) => 3,
Self::DwordLe(_) => 4,
Self::DwordBe(_) => 5,
Self::Link(_) => 6,
Self::MultiSz(_) => 7,
}
}
fn as_bytes(&self) -> Vec<u8> {
match self {
Self::Sz(s) => [s.as_bytes(), &[0]].concat().to_vec(),
Self::ExpandSz(s) => [s.as_bytes(), &[0]].concat().to_vec(),
Self::Binary(s) => s.clone(),
Self::DwordLe(v) => v.to_le_bytes().to_vec(),
Self::DwordBe(v) => v.to_be_bytes().to_vec(),
Self::Link(s) => [s.as_bytes(), &[0]].concat().to_vec(),
Self::MultiSz(ss) => ss.iter().flat_map(|s| [s.as_bytes(), &[0]].concat()).collect(),
}
}
}
impl From<String> for OsRegValue {
fn from(value: String) -> Self {
Self::Sz(value)
}
}
impl From<&str> for OsRegValue {
fn from(value: &str) -> Self {
Self::Sz(value.to_string())
}
}
impl From<Vec<u8>> for OsRegValue {
fn from(value: Vec<u8>) -> Self {
Self::Binary(value)
}
}
impl From<&[u8]> for OsRegValue {
fn from(value: &[u8]) -> Self {
Self::Binary(value.to_vec())
}
}
impl From<u32> for OsRegValue {
fn from(value: u32) -> Self {
Self::DwordLe(value)
}
}
impl From<Vec<String>> for OsRegValue {
fn from(value: Vec<String>) -> Self {
Self::MultiSz(value)
}
}
#[derive(Debug)]
#[non_exhaustive]
pub struct CustomBuilder {
pub interfaces: Vec<Interface>,
pub all_ctrl_recipient: bool,
pub config0_setup: bool,
pub ffs_dir: Option<PathBuf>,
pub ffs_root_mode: Option<u32>,
pub ffs_file_mode: Option<u32>,
pub ffs_uid: Option<u32>,
pub ffs_gid: Option<u32>,
pub ffs_no_disconnect: bool,
pub ffs_no_init: bool,
pub ffs_no_mount: bool,
}
impl CustomBuilder {
pub fn build(self) -> (Custom, Handle) {
let dir = FunctionDir::new();
let (ep0_tx, ep0_rx) = value::channel();
let (ffs_dir_tx, ffs_dir_rx) = value::channel();
let ep_files = Arc::new(Mutex::new(Vec::new()));
(
Custom {
dir: dir.clone(),
ep0: ep0_rx,
setup_event: None,
ep_files: ep_files.clone(),
existing_ffs: false,
ffs_dir: ffs_dir_rx,
},
Handle::new(CustomFunction {
builder: self,
dir,
ep0_tx,
ep_files,
ffs_dir_created: AtomicBool::new(false),
ffs_dir_tx,
}),
)
}
pub fn existing(mut self, ffs_dir: impl AsRef<Path>) -> Result<Custom> {
self.ffs_dir = Some(ffs_dir.as_ref().to_path_buf());
let dir = FunctionDir::new();
let (ep0_tx, ep0_rx) = value::channel();
let (ffs_dir_tx, ffs_dir_rx) = value::channel();
let ep_files = Arc::new(Mutex::new(Vec::new()));
let func = CustomFunction {
builder: self,
dir: dir.clone(),
ep0_tx,
ep_files: ep_files.clone(),
ffs_dir_created: AtomicBool::new(false),
ffs_dir_tx,
};
func.init()?;
Ok(Custom { dir, ep0: ep0_rx, setup_event: None, ep_files, existing_ffs: true, ffs_dir: ffs_dir_rx })
}
#[must_use]
pub fn with_interface(mut self, interface: Interface) -> Self {
self.interfaces.push(interface);
self
}
fn ffs_descs(&self) -> Result<(ffs::Descs, ffs::Strings)> {
let mut strings = ffs::Strings(HashMap::new());
let mut add_strings = |strs: &HashMap<Language, String>| {
let all_langs: HashSet<_> = strings.0.keys().chain(strs.keys()).cloned().collect();
let str_cnt = strings.0.values().next().map(|s| s.len()).unwrap_or_default();
for lang in all_langs.into_iter() {
let lang_strs = strings.0.entry(lang).or_insert_with(|| vec![String::new(); str_cnt]);
lang_strs.push(strs.get(&lang).cloned().unwrap_or_default());
}
u8::try_from(str_cnt + 1).map_err(|_| Error::new(ErrorKind::InvalidInput, "too many strings"))
};
let mut fs_descrs = Vec::new();
let mut hs_descrs = Vec::new();
let mut ss_descrs = Vec::new();
let mut os_descrs = Vec::new();
let mut endpoint_num: u8 = 0;
let mut assocs: HashMap<Association, ffs::InterfaceAssocDesc> = HashMap::new();
for (interface_number, intf) in self.interfaces.iter().enumerate() {
let interface_number: u8 = interface_number
.try_into()
.map_err(|_| Error::new(ErrorKind::InvalidInput, "too many interfaces"))?;
let num_endpoints: u8 = intf
.endpoints
.len()
.try_into()
.map_err(|_| Error::new(ErrorKind::InvalidInput, "too many endpoints"))?;
let if_desc = ffs::InterfaceDesc {
interface_number,
alternate_setting: 0,
num_endpoints,
interface_class: intf.interface_class.class,
interface_sub_class: intf.interface_class.sub_class,
interface_protocol: intf.interface_class.protocol,
name_idx: add_strings(&intf.name)?,
};
fs_descrs.push(if_desc.clone().into());
hs_descrs.push(if_desc.clone().into());
ss_descrs.push(if_desc.clone().into());
for custom in &intf.custom_descs {
fs_descrs.push(custom.clone().into());
hs_descrs.push(custom.clone().into());
ss_descrs.push(custom.clone().into());
}
for ep in &intf.endpoints {
endpoint_num += 1;
if endpoint_num >= ffs::DIR_IN {
return Err(Error::new(ErrorKind::InvalidInput, "too many endpoints"));
}
let ep_desc = ffs::EndpointDesc {
endpoint_address: match ep.direction.direction {
Direction::DeviceToHost => endpoint_num | ffs::DIR_IN,
Direction::HostToDevice => endpoint_num | ffs::DIR_OUT,
},
attributes: ep.transfer.to_attributes(),
max_packet_size: 0,
interval: ep.interval,
audio: ep
.audio
.as_ref()
.map(|a| ffs::AudioEndpointDesc { refresh: a.refresh, synch_address: a.synch_address }),
};
let ss_comp_desc = ffs::SsEndpointComp {
max_burst: ep.max_burst_ss,
attributes: 0,
bytes_per_interval: ep.bytes_per_interval_ss,
};
fs_descrs.push(ep_desc.clone().into());
hs_descrs
.push(ffs::EndpointDesc { max_packet_size: ep.max_packet_size_hs, ..ep_desc.clone() }.into());
ss_descrs
.push(ffs::EndpointDesc { max_packet_size: ep.max_packet_size_ss, ..ep_desc.clone() }.into());
ss_descrs.push(ss_comp_desc.into());
}
if let Some(assoc) = &intf.association {
let iad = match assocs.entry(assoc.clone()) {
Entry::Occupied(ocu) => ocu.into_mut(),
Entry::Vacant(vac) => vac.insert(ffs::InterfaceAssocDesc {
first_interface: interface_number,
interface_count: 0,
function_class: assoc.function_class.class,
function_sub_class: assoc.function_class.sub_class,
function_protocol: assoc.function_class.protocol,
name_idx: add_strings(&assoc.name)?,
}),
};
if iad.first_interface + interface_number != interface_number {
return Err(Error::new(ErrorKind::InvalidInput, "associated interfaces must be adjacent"));
}
iad.interface_count += 1;
}
if !intf.os_ext_compat.is_empty() {
let os_desc = ffs::OsDesc {
interface: interface_number,
ext: ffs::OsDescExt::ExtCompat(
intf.os_ext_compat
.iter()
.map(|oe| ffs::OsExtCompat {
first_interface_number: interface_number,
compatible_id: oe.compatible_id,
sub_compatible_id: oe.sub_compatible_id,
})
.collect(),
),
};
os_descrs.push(os_desc);
}
if !intf.os_ext_props.is_empty() {
let os_desc = ffs::OsDesc {
interface: interface_number,
ext: ffs::OsDescExt::ExtProp(
intf.os_ext_props.iter().map(|oep| oep.as_os_ext_prop()).collect(),
),
};
os_descrs.push(os_desc);
}
}
for iad in assocs.into_values() {
fs_descrs.push(iad.clone().into());
hs_descrs.push(iad.clone().into());
ss_descrs.push(iad.clone().into());
}
let mut flags = ffs::Flags::empty();
flags.set(ffs::Flags::ALL_CTRL_RECIP, self.all_ctrl_recipient);
flags.set(ffs::Flags::CONFIG0_SETUP, self.config0_setup);
let descs = ffs::Descs { flags, eventfd: None, fs_descrs, hs_descrs, ss_descrs, os_descrs };
Ok((descs, strings))
}
pub fn ffs_descriptors_and_strings(&self) -> Result<(Vec<u8>, Vec<u8>)> {
let (descs, strs) = self.ffs_descs()?;
Ok((descs.to_bytes()?, strs.to_bytes()?))
}
}
fn default_ffs_dir(instance: &OsStr) -> PathBuf {
let mut name: OsString = "ffs-".into();
name.push(instance);
Path::new("/dev").join(name)
}
#[derive(Debug)]
struct CustomFunction {
builder: CustomBuilder,
dir: FunctionDir,
ep0_tx: value::Sender<Weak<File>>,
ep_files: Arc<Mutex<Vec<Arc<File>>>>,
ffs_dir_created: AtomicBool,
ffs_dir_tx: value::Sender<PathBuf>,
}
impl CustomFunction {
fn ffs_dir(&self) -> Result<PathBuf> {
match &self.builder.ffs_dir {
Some(ffs_dir) => Ok(ffs_dir.clone()),
None => Ok(default_ffs_dir(&self.dir.instance()?)),
}
}
fn init(&self) -> Result<()> {
let ffs_dir = self.ffs_dir()?;
if !self.builder.ffs_no_init {
let (descs, strs) = self.builder.ffs_descs()?;
log::trace!("functionfs descriptors: {descs:x?}");
log::trace!("functionfs strings: {strs:?}");
let ep0_path = ffs_dir.join("ep0");
let mut ep0 = File::options().read(true).write(true).open(&ep0_path)?;
log::debug!("writing functionfs descriptors to {}", ep0_path.display());
let descs_data = descs.to_bytes()?;
log::trace!("functionfs descriptor data: {descs_data:x?}");
if ep0.write(&descs_data)? != descs_data.len() {
return Err(Error::new(ErrorKind::UnexpectedEof, "short descriptor write"));
}
log::debug!("writing functionfs strings to {}", ep0_path.display());
let strs_data = strs.to_bytes()?;
log::trace!("functionfs strings data: {strs_data:x?}");
if ep0.write(&strs_data)? != strs_data.len() {
return Err(Error::new(ErrorKind::UnexpectedEof, "short strings write"));
}
log::debug!("functionfs initialized");
let mut endpoint_num = 0;
let mut ep_files = Vec::new();
for intf in &self.builder.interfaces {
for ep in &intf.endpoints {
endpoint_num += 1;
let ep_path = ffs_dir.join(format!("ep{endpoint_num}"));
let (ep_io, ep_file) = EndpointIo::new(ep_path, ep.direction.queue_len)?;
ep.direction.tx.send(ep_io).unwrap();
ep_files.push(ep_file);
}
}
let ep0 = Arc::new(ep0);
self.ep0_tx.send(Arc::downgrade(&ep0)).unwrap();
ep_files.push(ep0);
*self.ep_files.lock().unwrap() = ep_files;
}
self.ffs_dir_tx.send(ffs_dir).unwrap();
Ok(())
}
fn close(&self) {
self.ep_files.lock().unwrap().clear();
}
}
impl Function for CustomFunction {
fn driver(&self) -> OsString {
driver().to_os_string()
}
fn dir(&self) -> FunctionDir {
self.dir.clone()
}
fn register(&self) -> Result<()> {
if self.builder.ffs_no_mount {
return Ok(());
}
let ffs_dir = self.ffs_dir()?;
log::debug!("creating functionfs directory {}", ffs_dir.display());
match fs::create_dir(&ffs_dir) {
Ok(()) => self.ffs_dir_created.store(true, Ordering::SeqCst),
Err(err) if err.kind() == ErrorKind::AlreadyExists => (),
Err(err) => return Err(err),
}
let mount_opts = ffs::MountOptions {
no_disconnect: self.builder.ffs_no_disconnect,
rmode: self.builder.ffs_root_mode,
fmode: self.builder.ffs_file_mode,
mode: None,
uid: self.builder.ffs_uid,
gid: self.builder.ffs_gid,
};
log::debug!("mounting functionfs into {} using options {mount_opts:?}", ffs_dir.display());
ffs::mount(&self.dir.instance()?, &ffs_dir, &mount_opts)?;
self.init()
}
fn pre_removal(&self) -> Result<()> {
self.close();
Ok(())
}
fn post_removal(&self, _dir: &Path) -> Result<()> {
if self.ffs_dir_created.load(Ordering::SeqCst) {
if let Ok(ffs_dir) = self.ffs_dir() {
let _ = fs::remove_dir(ffs_dir);
}
}
Ok(())
}
}
pub(crate) fn remove_handler(dir: PathBuf) -> Result<()> {
let (_driver, instance) =
split_function_dir(&dir).ok_or_else(|| Error::new(ErrorKind::InvalidInput, "invalid configfs dir"))?;
for mount in MountIter::new()? {
let Ok(mount) = mount else { continue };
if mount.fstype == ffs::FS_TYPE && mount.source == instance {
log::debug!("unmounting functionfs {} from {}", instance.to_string_lossy(), mount.dest.display());
if let Err(err) = ffs::umount(&mount.dest, false) {
log::debug!("unmount failed, trying lazy unmount: {err}");
ffs::umount(&mount.dest, true)?;
}
if mount.dest == default_ffs_dir(instance) {
let _ = fs::remove_dir(&mount.dest);
}
}
}
Ok(())
}
#[derive(Debug)]
pub struct Custom {
dir: FunctionDir,
ep0: value::Receiver<Weak<File>>,
setup_event: Option<Direction>,
ep_files: Arc<Mutex<Vec<Arc<File>>>>,
existing_ffs: bool,
ffs_dir: value::Receiver<PathBuf>,
}
impl Custom {
pub fn builder() -> CustomBuilder {
CustomBuilder {
interfaces: Vec::new(),
all_ctrl_recipient: false,
config0_setup: false,
ffs_dir: None,
ffs_root_mode: None,
ffs_file_mode: None,
ffs_uid: None,
ffs_gid: None,
ffs_no_disconnect: false,
ffs_no_init: false,
ffs_no_mount: false,
}
}
pub fn status(&self) -> Option<Status> {
if !self.existing_ffs {
Some(self.dir.status())
} else {
None
}
}
fn ep0(&mut self) -> Result<Arc<File>> {
let ep0 = self.ep0.get()?;
ep0.upgrade().ok_or_else(|| Error::new(ErrorKind::BrokenPipe, "USB gadget was removed"))
}
pub fn real_address(&mut self, intf: u8) -> Result<u8> {
let ep0 = self.ep0()?;
let address = unsafe { ffs::interface_revmap(ep0.as_raw_fd(), intf.into()) }?;
Ok(address as u8)
}
fn clear_prev_event(&mut self) -> Result<()> {
let mut ep0 = self.ep0()?;
let mut buf = [0; 1];
match self.setup_event.take() {
Some(Direction::DeviceToHost) => {
let _ = ep0.read(&mut buf)?;
}
Some(Direction::HostToDevice) => {
let _ = ep0.write(&buf)?;
}
None => (),
}
Ok(())
}
fn read_event(&'_ mut self) -> Result<Event<'_>> {
let mut ep0 = self.ep0()?;
let mut buf = [0; ffs::Event::SIZE];
let n = ep0.read(&mut buf)?;
if n != ffs::Event::SIZE {
return Err(Error::new(ErrorKind::InvalidData, "invalid event size"));
}
let raw_event = ffs::Event::parse(&buf)?;
Ok(Event::from_ffs(raw_event, self))
}
fn wait_event_sync(&mut self, timeout: Option<Duration>) -> Result<bool> {
let ep0 = self.ep0()?;
let mut fds = [PollFd::new(ep0.as_fd(), PollFlags::POLLIN)];
poll(&mut fds, timeout.map(|d| d.as_millis().try_into().unwrap()).unwrap_or(PollTimeout::NONE))?;
Ok(fds[0].revents().map(|e| e.contains(PollFlags::POLLIN)).unwrap_or_default())
}
#[cfg(feature = "tokio")]
pub async fn wait_event(&mut self) -> Result<()> {
use tokio::io::{unix::AsyncFd, Interest};
let ep0 = self.ep0()?;
let async_fd = AsyncFd::with_interest(ep0.as_fd(), Interest::READABLE)?;
let mut guard = async_fd.readable().await?;
guard.clear_ready();
Ok(())
}
pub fn has_event(&mut self) -> bool {
self.wait_event_sync(Some(Duration::ZERO)).unwrap_or_default()
}
pub fn event(&'_ mut self) -> Result<Event<'_>> {
self.clear_prev_event()?;
self.read_event()
}
pub fn event_timeout(&'_ mut self, timeout: Duration) -> Result<Option<Event<'_>>> {
if self.wait_event_sync(Some(timeout))? {
Ok(Some(self.read_event()?))
} else {
Ok(None)
}
}
pub fn try_event(&'_ mut self) -> Result<Option<Event<'_>>> {
self.clear_prev_event()?;
if self.has_event() {
Ok(Some(self.read_event()?))
} else {
Ok(None)
}
}
pub fn fd(&mut self) -> Result<RawFd> {
let ep0 = self.ep0()?;
Ok(ep0.as_raw_fd())
}
pub fn ffs_dir(&mut self) -> Result<PathBuf> {
Ok(self.ffs_dir.get()?.clone())
}
}
impl Drop for Custom {
fn drop(&mut self) {
self.ep_files.lock().unwrap().clear();
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Event<'a> {
Bind,
Unbind,
Enable,
Disable,
Suspend,
Resume,
SetupHostToDevice(CtrlReceiver<'a>),
SetupDeviceToHost(CtrlSender<'a>),
Unknown(u8),
}
impl<'a> Event<'a> {
fn from_ffs(raw: ffs::Event, custom: &'a mut Custom) -> Self {
match raw.event_type {
ffs::event::BIND => Self::Bind,
ffs::event::UNBIND => Self::Unbind,
ffs::event::ENABLE => Self::Enable,
ffs::event::DISABLE => Self::Disable,
ffs::event::SUSPEND => Self::Suspend,
ffs::event::RESUME => Self::Resume,
ffs::event::SETUP => {
let ctrl_req = ffs::CtrlReq::parse(&raw.data).unwrap();
if (ctrl_req.request_type & ffs::DIR_IN) != 0 {
custom.setup_event = Some(Direction::DeviceToHost);
Self::SetupDeviceToHost(CtrlSender { ctrl_req, custom })
} else {
custom.setup_event = Some(Direction::HostToDevice);
Self::SetupHostToDevice(CtrlReceiver { ctrl_req, custom })
}
}
other => Self::Unknown(other),
}
}
}
pub use ffs::CtrlReq;
pub struct CtrlSender<'a> {
ctrl_req: CtrlReq,
custom: &'a mut Custom,
}
impl fmt::Debug for CtrlSender<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("CtrlSender").field("ctrl_req", &self.ctrl_req).finish()
}
}
impl CtrlSender<'_> {
pub const fn ctrl_req(&self) -> &CtrlReq {
&self.ctrl_req
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn len(&self) -> usize {
self.ctrl_req.length.into()
}
pub fn send(self, data: &[u8]) -> Result<usize> {
let mut file = self.custom.ep0()?;
let n = file.write(data)?;
self.custom.setup_event = None;
Ok(n)
}
pub fn halt(mut self) -> Result<()> {
self.do_halt()
}
fn do_halt(&mut self) -> Result<()> {
let mut file = self.custom.ep0()?;
let mut buf = [0; 1];
let _ = file.read(&mut buf)?;
self.custom.setup_event = None;
Ok(())
}
}
impl Drop for CtrlSender<'_> {
fn drop(&mut self) {
if self.custom.setup_event.is_some() {
let _ = self.do_halt();
}
}
}
pub struct CtrlReceiver<'a> {
ctrl_req: CtrlReq,
custom: &'a mut Custom,
}
impl fmt::Debug for CtrlReceiver<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("CtrlReceiver").field("ctrl_req", &self.ctrl_req).finish()
}
}
impl CtrlReceiver<'_> {
pub const fn ctrl_req(&self) -> &CtrlReq {
&self.ctrl_req
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn len(&self) -> usize {
self.ctrl_req.length.into()
}
pub fn recv_all(self) -> Result<Vec<u8>> {
let mut buf = vec![0; self.len()];
self.recv(&mut buf)?;
Ok(buf)
}
pub fn recv(self, data: &mut [u8]) -> Result<usize> {
let mut file = self.custom.ep0()?;
let n = file.read(data)?;
self.custom.setup_event = None;
Ok(n)
}
pub fn halt(mut self) -> Result<()> {
self.do_halt()
}
fn do_halt(&mut self) -> Result<()> {
let mut file = self.custom.ep0()?;
let buf = [0; 1];
let _ = file.write(&buf)?;
self.custom.setup_event = None;
Ok(())
}
}
impl Drop for CtrlReceiver<'_> {
fn drop(&mut self) {
if self.custom.setup_event.is_some() {
let _ = self.do_halt();
}
}
}
struct EndpointIo {
path: PathBuf,
file: Weak<File>,
aio: aio::Driver,
}
impl EndpointIo {
fn new(path: PathBuf, queue_len: u32) -> Result<(Self, Arc<File>)> {
log::debug!("opening endpoint file {} with queue length {queue_len}", path.display());
let file = Arc::new(File::options().read(true).write(true).open(&path)?);
let aio = aio::Driver::new(queue_len, Some(path.to_string_lossy().to_string()))?;
Ok((Self { path, file: Arc::downgrade(&file), aio }, file))
}
fn file(&self) -> Result<Arc<File>> {
self.file.upgrade().ok_or_else(|| Error::new(ErrorKind::BrokenPipe, "USB gadget was removed"))
}
}
impl fmt::Debug for EndpointIo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.path.display())
}
}
impl Drop for EndpointIo {
fn drop(&mut self) {
log::debug!("releasing endpoint file {}", self.path.display());
}
}
#[derive(Debug)]
pub struct EndpointControl<'a> {
io: &'a EndpointIo,
direction: Direction,
}
pub use ffs::{AudioEndpointDesc as RawAudioEndpointDesc, EndpointDesc as RawEndpointDesc};
impl<'a> EndpointControl<'a> {
fn new(io: &'a EndpointIo, direction: Direction) -> Self {
Self { io, direction }
}
pub fn unclaimed_fifo(&self) -> Result<usize> {
let file = self.io.file()?;
let bytes = unsafe { ffs::fifo_status(file.as_raw_fd()) }?;
Ok(bytes as usize)
}
pub fn discard_fifo(&self) -> Result<()> {
let file = self.io.file()?;
unsafe { ffs::fifo_flush(file.as_raw_fd()) }?;
Ok(())
}
pub fn halt(&self) -> Result<()> {
let mut file = self.io.file()?;
let mut buf = [0; 1];
match self.direction {
Direction::DeviceToHost => {
let _ = file.read(&mut buf)?;
}
Direction::HostToDevice => {
let _ = file.write(&buf)?;
}
}
Ok(())
}
pub fn clear_halt(&self) -> Result<()> {
let file = self.io.file()?;
unsafe { ffs::clear_halt(file.as_raw_fd()) }?;
Ok(())
}
pub fn real_address(&self) -> Result<u8> {
let file = self.io.file()?;
let address = unsafe { ffs::endpoint_revmap(file.as_raw_fd()) }?;
Ok(address as u8)
}
pub fn descriptor(&self) -> Result<RawEndpointDesc> {
let file = self.io.file()?;
let mut data = [0; ffs::EndpointDesc::AUDIO_SIZE];
unsafe { ffs::endpoint_desc(file.as_raw_fd(), &mut data) }?;
ffs::EndpointDesc::parse(&data)
}
pub fn fd(&mut self) -> Result<RawFd> {
let file = self.io.file()?;
Ok(file.as_raw_fd())
}
}
#[derive(Debug)]
pub struct EndpointSender(value::Receiver<EndpointIo>);
impl EndpointSender {
pub fn control(&'_ mut self) -> Result<EndpointControl<'_>> {
let io = self.0.get()?;
Ok(EndpointControl::new(io, Direction::DeviceToHost))
}
pub fn max_packet_size(&mut self) -> Result<usize> {
Ok(self.control()?.descriptor()?.max_packet_size.into())
}
pub fn send_and_flush(&mut self, data: Bytes) -> Result<()> {
self.send(data)?;
self.flush()
}
pub fn send_and_flush_timeout(&mut self, data: Bytes, timeout: Duration) -> Result<()> {
self.send(data)?;
let res = self.flush_timeout(timeout);
if res.is_err() {
self.cancel()?;
}
res
}
pub fn send(&mut self, data: Bytes) -> Result<()> {
self.ready()?;
self.try_send(data)
}
#[cfg(feature = "tokio")]
pub async fn send_async(&mut self, data: Bytes) -> Result<()> {
self.wait_ready().await?;
self.try_send(data)
}
pub fn send_timeout(&mut self, data: Bytes, timeout: Duration) -> Result<()> {
self.ready_timeout(timeout)?;
self.try_send(data)
}
pub fn try_send(&mut self, data: Bytes) -> Result<()> {
self.try_ready()?;
let io = self.0.get()?;
let file = io.file()?;
io.aio.submit(aio::opcode::PWRITE, file.as_raw_fd(), data)?;
Ok(())
}
pub fn is_ready(&mut self) -> bool {
let Ok(io) = self.0.get() else { return false };
!io.aio.is_full()
}
pub fn is_empty(&mut self) -> bool {
let Ok(io) = self.0.get() else { return true };
io.aio.is_empty()
}
#[cfg(feature = "tokio")]
pub async fn wait_ready(&mut self) -> Result<()> {
let io = self.0.get()?;
while io.aio.is_full() {
let comp = io.aio.wait_completed().await.unwrap();
comp.result()?;
}
Ok(())
}
pub fn ready(&mut self) -> Result<()> {
let io = self.0.get()?;
while io.aio.is_full() {
let comp = io.aio.completed().unwrap();
comp.result()?;
}
Ok(())
}
pub fn ready_timeout(&mut self, timeout: Duration) -> Result<()> {
let io = self.0.get()?;
while io.aio.is_full() {
let comp = io
.aio
.completed_timeout(timeout)
.ok_or_else(|| Error::new(ErrorKind::TimedOut, "timeout waiting for send space"))?;
comp.result()?;
}
Ok(())
}
pub fn try_ready(&mut self) -> Result<()> {
let io = self.0.get()?;
while let Some(comp) = io.aio.try_completed() {
comp.result()?;
}
Ok(())
}
pub fn flush(&mut self) -> Result<()> {
let io = self.0.get()?;
while let Some(comp) = io.aio.completed() {
comp.result()?;
}
Ok(())
}
#[cfg(feature = "tokio")]
pub async fn flush_async(&mut self) -> Result<()> {
let io = self.0.get()?;
while let Some(comp) = io.aio.wait_completed().await {
comp.result()?;
}
Ok(())
}
pub fn flush_timeout(&mut self, timeout: Duration) -> Result<()> {
let io = self.0.get()?;
while let Some(comp) = io.aio.completed_timeout(timeout) {
comp.result()?;
}
if io.aio.is_empty() {
Ok(())
} else {
Err(Error::new(ErrorKind::TimedOut, "timeout waiting for send to complete"))
}
}
pub fn cancel(&mut self) -> Result<()> {
let io = self.0.get()?;
io.aio.cancel_all();
while io.aio.completed().is_some() {}
Ok(())
}
}
#[derive(Debug)]
pub struct EndpointReceiver(value::Receiver<EndpointIo>);
impl EndpointReceiver {
pub fn control(&'_ mut self) -> Result<EndpointControl<'_>> {
let io = self.0.get()?;
Ok(EndpointControl::new(io, Direction::HostToDevice))
}
pub fn max_packet_size(&mut self) -> Result<usize> {
Ok(self.control()?.descriptor()?.max_packet_size.into())
}
pub fn recv_and_fetch(&mut self, buf: BytesMut) -> Result<BytesMut> {
self.try_recv(buf)?;
Ok(self.fetch()?.unwrap())
}
pub fn recv_and_fetch_timeout(&mut self, buf: BytesMut, timeout: Duration) -> Result<BytesMut> {
self.try_recv(buf)?;
let res = self.fetch_timeout(timeout);
match res {
Ok(data) => Ok(data.unwrap()),
Err(err) => {
self.cancel()?;
Err(err)
}
}
}
pub fn recv(&mut self, buf: BytesMut) -> Result<Option<BytesMut>> {
let data = if self.is_ready() { self.try_fetch()? } else { self.fetch()? };
self.try_recv(buf)?;
Ok(data)
}
#[cfg(feature = "tokio")]
pub async fn recv_async(&mut self, buf: BytesMut) -> Result<Option<BytesMut>> {
let data = if self.is_ready() { self.try_fetch()? } else { self.fetch_async().await? };
self.try_recv(buf)?;
Ok(data)
}
pub fn recv_timeout(&mut self, buf: BytesMut, timeout: Duration) -> Result<Option<BytesMut>> {
let data = if self.is_ready() { self.try_fetch()? } else { self.fetch_timeout(timeout)? };
if self.is_ready() {
self.try_recv(buf)?;
}
Ok(data)
}
pub fn try_recv(&mut self, buf: BytesMut) -> Result<()> {
let io = self.0.get()?;
let file = io.file()?;
io.aio.submit(aio::opcode::PREAD, file.as_raw_fd(), buf)?;
Ok(())
}
pub fn is_ready(&mut self) -> bool {
let Ok(io) = self.0.get() else { return false };
!io.aio.is_full()
}
pub fn is_empty(&mut self) -> bool {
let Ok(io) = self.0.get() else { return true };
io.aio.is_empty()
}
pub fn fetch(&mut self) -> Result<Option<BytesMut>> {
let io = self.0.get()?;
let Some(comp) = io.aio.completed() else {
return Ok(None);
};
Ok(Some(comp.result()?.try_into().unwrap()))
}
#[cfg(feature = "tokio")]
pub async fn fetch_async(&mut self) -> Result<Option<BytesMut>> {
let io = self.0.get()?;
let Some(comp) = io.aio.wait_completed().await else {
return Ok(None);
};
Ok(Some(comp.result()?.try_into().unwrap()))
}
pub fn fetch_timeout(&mut self, timeout: Duration) -> Result<Option<BytesMut>> {
let io = self.0.get()?;
let Some(comp) = io.aio.completed_timeout(timeout) else {
return Ok(None);
};
Ok(Some(comp.result()?.try_into().unwrap()))
}
pub fn try_fetch(&mut self) -> Result<Option<BytesMut>> {
let io = self.0.get()?;
let Some(comp) = io.aio.try_completed() else { return Ok(None) };
let data = comp.result()?;
Ok(Some(data.try_into().unwrap()))
}
pub fn cancel(&mut self) -> Result<()> {
let io = self.0.get()?;
io.aio.cancel_all();
while io.aio.completed().is_some() {}
Ok(())
}
}