bd595851e2
251 files: init, acpid, ipcd, netcfg, ihdgd, virtio-gpud, scheme-utils, inputd, block driver, ptyd, ramfs, randd, initfs bootstrap, path deps, version +rb0.3.1, author attribution
378 lines
12 KiB
Rust
378 lines
12 KiB
Rust
use std::collections::BTreeMap;
|
|
use std::process;
|
|
|
|
use anyhow::{Context, Result};
|
|
use i2c_interface::{
|
|
I2cAdapterInfo, I2cControlRequest, I2cControlResponse, I2cTransferRequest,
|
|
I2cTransferResponse,
|
|
};
|
|
use redox_scheme::scheme::SchemeSync;
|
|
use redox_scheme::{CallerCtx, OpenResult, Socket};
|
|
use scheme_utils::{Blocking, HandleMap};
|
|
use syscall::schemev2::NewFdFlags;
|
|
use syscall::{Error as SysError, EACCES, EBADF, EINVAL, ENOENT};
|
|
|
|
enum Handle {
|
|
SchemeRoot,
|
|
Register { pending: Vec<u8> },
|
|
Provider { adapter_id: u32, pending: Vec<u8> },
|
|
Adapters { pending: Vec<u8> },
|
|
AdapterDetail { id: u32, pending: Vec<u8> },
|
|
Transfer { pending: Vec<u8> },
|
|
}
|
|
|
|
struct AdapterEntry {
|
|
info: I2cAdapterInfo,
|
|
provider_handle: usize,
|
|
}
|
|
|
|
struct I2cDaemon {
|
|
handles: HandleMap<Handle>,
|
|
adapters: BTreeMap<u32, AdapterEntry>,
|
|
next_id: u32,
|
|
}
|
|
|
|
impl I2cDaemon {
|
|
fn new() -> Self {
|
|
Self {
|
|
handles: HandleMap::new(),
|
|
adapters: BTreeMap::new(),
|
|
next_id: 0,
|
|
}
|
|
}
|
|
|
|
fn adapter_list(&self) -> Vec<I2cAdapterInfo> {
|
|
self.adapters.values().map(|entry| entry.info.clone()).collect()
|
|
}
|
|
|
|
fn serialize_response(response: &I2cControlResponse) -> syscall::Result<Vec<u8>> {
|
|
ron::ser::to_string(response)
|
|
.map(|text| text.into_bytes())
|
|
.map_err(|err| {
|
|
log::error!("i2cd: failed to serialize control response: {err}");
|
|
SysError::new(EINVAL)
|
|
})
|
|
}
|
|
|
|
fn deserialize_request(buf: &[u8]) -> syscall::Result<I2cControlRequest> {
|
|
let text = std::str::from_utf8(buf).map_err(|err| {
|
|
log::warn!("i2cd: invalid UTF-8 control payload: {err}");
|
|
SysError::new(EINVAL)
|
|
})?;
|
|
|
|
ron::from_str(text).map_err(|err| {
|
|
log::warn!("i2cd: failed to decode control request: {err}");
|
|
SysError::new(EINVAL)
|
|
})
|
|
}
|
|
|
|
fn set_pending_response(handle: &mut Handle, response: I2cControlResponse) -> syscall::Result<()> {
|
|
let pending = Self::serialize_response(&response)?;
|
|
match handle {
|
|
Handle::Register { pending: slot }
|
|
| Handle::Provider { pending: slot, .. }
|
|
| Handle::Adapters { pending: slot }
|
|
| Handle::AdapterDetail { pending: slot, .. }
|
|
| Handle::Transfer { pending: slot } => {
|
|
*slot = pending;
|
|
Ok(())
|
|
}
|
|
Handle::SchemeRoot => Err(SysError::new(EBADF)),
|
|
}
|
|
}
|
|
|
|
fn queue_adapter_list(handle: &mut Handle, adapters: Vec<I2cAdapterInfo>) -> syscall::Result<()> {
|
|
Self::set_pending_response(handle, I2cControlResponse::AdapterList(adapters))
|
|
}
|
|
|
|
fn queue_transfer_stub(
|
|
handle: &mut Handle,
|
|
adapter: &I2cAdapterInfo,
|
|
request: &I2cTransferRequest,
|
|
) -> syscall::Result<()> {
|
|
let write_bytes = request
|
|
.segments
|
|
.iter()
|
|
.filter_map(|segment| match &segment.op {
|
|
i2c_interface::I2cTransferOp::Write(bytes) => Some(bytes.len()),
|
|
i2c_interface::I2cTransferOp::Read(_) => None,
|
|
})
|
|
.sum::<usize>();
|
|
let read_segments = request
|
|
.segments
|
|
.iter()
|
|
.filter(|segment| matches!(segment.op, i2c_interface::I2cTransferOp::Read(_)))
|
|
.count();
|
|
|
|
log::info!(
|
|
"i2cd: routing transfer to adapter {} ({}) name={} segments={} write_bytes={} read_segments={} stop={} (stubbed)",
|
|
adapter.id,
|
|
adapter.name,
|
|
request.adapter,
|
|
request.segments.len(),
|
|
write_bytes,
|
|
read_segments,
|
|
request.stop,
|
|
);
|
|
|
|
Self::set_pending_response(
|
|
handle,
|
|
I2cControlResponse::TransferResult(I2cTransferResponse {
|
|
ok: false,
|
|
read_data: Vec::new(),
|
|
error: Some(String::from("I2C controller transfer path is not implemented yet")),
|
|
}),
|
|
)
|
|
}
|
|
|
|
fn copy_pending(handle: &mut Handle, buf: &mut [u8], offset: u64) -> syscall::Result<usize> {
|
|
let pending = match handle {
|
|
Handle::Register { pending }
|
|
| Handle::Provider { pending, .. }
|
|
| Handle::Adapters { pending }
|
|
| Handle::AdapterDetail { pending, .. }
|
|
| Handle::Transfer { pending } => pending,
|
|
Handle::SchemeRoot => return Err(SysError::new(EBADF)),
|
|
};
|
|
|
|
let offset = usize::try_from(offset).map_err(|_| SysError::new(EINVAL))?;
|
|
if offset >= pending.len() {
|
|
return Ok(0);
|
|
}
|
|
|
|
let copy_len = buf.len().min(pending.len() - offset);
|
|
buf[..copy_len].copy_from_slice(&pending[offset..offset + copy_len]);
|
|
Ok(copy_len)
|
|
}
|
|
}
|
|
|
|
impl SchemeSync for I2cDaemon {
|
|
fn scheme_root(&mut self) -> syscall::Result<usize> {
|
|
Ok(self.handles.insert(Handle::SchemeRoot))
|
|
}
|
|
|
|
fn openat(
|
|
&mut self,
|
|
dirfd: usize,
|
|
path: &str,
|
|
_flags: usize,
|
|
_fcntl_flags: u32,
|
|
_ctx: &CallerCtx,
|
|
) -> syscall::Result<OpenResult> {
|
|
let handle = self.handles.get(dirfd)?;
|
|
let segments = path.trim_matches('/');
|
|
|
|
let new_handle = match handle {
|
|
Handle::SchemeRoot => {
|
|
if segments.is_empty() {
|
|
return Err(SysError::new(EINVAL));
|
|
}
|
|
|
|
let mut parts = segments.split('/');
|
|
match parts.next() {
|
|
Some("register") if parts.next().is_none() => Handle::Register {
|
|
pending: Vec::new(),
|
|
},
|
|
Some("adapters") => match parts.next() {
|
|
None => Handle::Adapters {
|
|
pending: Vec::new(),
|
|
},
|
|
Some(id) if parts.next().is_none() => {
|
|
let id = id.parse::<u32>().map_err(|_| SysError::new(EINVAL))?;
|
|
Handle::AdapterDetail {
|
|
id,
|
|
pending: Vec::new(),
|
|
}
|
|
}
|
|
_ => return Err(SysError::new(EINVAL)),
|
|
},
|
|
Some("transfer") if parts.next().is_none() => Handle::Transfer {
|
|
pending: Vec::new(),
|
|
},
|
|
_ => return Err(SysError::new(ENOENT)),
|
|
}
|
|
}
|
|
Handle::Adapters { .. } => {
|
|
if segments.is_empty() {
|
|
return Err(SysError::new(EINVAL));
|
|
}
|
|
|
|
let id = segments.parse::<u32>().map_err(|_| SysError::new(EINVAL))?;
|
|
Handle::AdapterDetail {
|
|
id,
|
|
pending: Vec::new(),
|
|
}
|
|
}
|
|
_ => return Err(SysError::new(EACCES)),
|
|
};
|
|
|
|
let fd = self.handles.insert(new_handle);
|
|
Ok(OpenResult::ThisScheme {
|
|
number: fd,
|
|
flags: NewFdFlags::empty(),
|
|
})
|
|
}
|
|
|
|
fn read(
|
|
&mut self,
|
|
id: usize,
|
|
buf: &mut [u8],
|
|
offset: u64,
|
|
_fcntl_flags: u32,
|
|
_ctx: &CallerCtx,
|
|
) -> syscall::Result<usize> {
|
|
let adapters = self.adapter_list();
|
|
let handle = self.handles.get_mut(id)?;
|
|
|
|
match handle {
|
|
Handle::Adapters { pending } if pending.is_empty() => {
|
|
*pending = Self::serialize_response(&I2cControlResponse::AdapterList(adapters))?;
|
|
}
|
|
Handle::AdapterDetail { id, pending } if pending.is_empty() => {
|
|
let info = self
|
|
.adapters
|
|
.get(id)
|
|
.map(|entry| entry.info.clone())
|
|
.ok_or(SysError::new(ENOENT))?;
|
|
*pending = Self::serialize_response(&I2cControlResponse::AdapterList(vec![info]))?;
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
Self::copy_pending(handle, buf, offset)
|
|
}
|
|
|
|
fn write(
|
|
&mut self,
|
|
id: usize,
|
|
buf: &[u8],
|
|
_offset: u64,
|
|
_fcntl_flags: u32,
|
|
_ctx: &CallerCtx,
|
|
) -> syscall::Result<usize> {
|
|
let request = Self::deserialize_request(buf)?;
|
|
|
|
match request {
|
|
I2cControlRequest::RegisterAdapter { mut info } => {
|
|
let adapter_id = self.next_id;
|
|
self.next_id = self.next_id.checked_add(1).ok_or(SysError::new(EINVAL))?;
|
|
info.id = adapter_id;
|
|
|
|
self.adapters.insert(
|
|
adapter_id,
|
|
AdapterEntry {
|
|
info: info.clone(),
|
|
provider_handle: id,
|
|
},
|
|
);
|
|
|
|
let handle = self.handles.get_mut(id)?;
|
|
*handle = Handle::Provider {
|
|
adapter_id,
|
|
pending: Self::serialize_response(&I2cControlResponse::AdapterRegistered {
|
|
id: adapter_id,
|
|
})?,
|
|
};
|
|
|
|
log::info!(
|
|
"RB_I2CD_ADAPTER_REGISTERED id={} name={} max_transaction_size={} supports_10bit_addr={}",
|
|
info.id,
|
|
info.name,
|
|
info.max_transaction_size,
|
|
info.supports_10bit_addr,
|
|
);
|
|
Ok(buf.len())
|
|
}
|
|
I2cControlRequest::ListAdapters => {
|
|
let adapters = self.adapter_list();
|
|
let handle = self.handles.get_mut(id)?;
|
|
Self::queue_adapter_list(handle, adapters)?;
|
|
Ok(buf.len())
|
|
}
|
|
I2cControlRequest::OpenAdapter { id: adapter_id } => {
|
|
if !self.adapters.contains_key(&adapter_id) {
|
|
return Err(SysError::new(ENOENT));
|
|
}
|
|
|
|
let handle = self.handles.get_mut(id)?;
|
|
match handle {
|
|
Handle::Adapters { .. } | Handle::AdapterDetail { .. } => {
|
|
Self::set_pending_response(handle, I2cControlResponse::AdapterOpened)?;
|
|
Ok(buf.len())
|
|
}
|
|
_ => Err(SysError::new(EINVAL)),
|
|
}
|
|
}
|
|
I2cControlRequest::Transfer {
|
|
adapter_id,
|
|
request,
|
|
} => {
|
|
let entry = self.adapters.get(&adapter_id).ok_or(SysError::new(ENOENT))?;
|
|
log::debug!(
|
|
"i2cd: transfer requested for adapter {} via provider fd {}",
|
|
adapter_id,
|
|
entry.provider_handle,
|
|
);
|
|
|
|
let adapter_info = entry.info.clone();
|
|
let handle = self.handles.get_mut(id)?;
|
|
match handle {
|
|
Handle::Transfer { .. } => {
|
|
Self::queue_transfer_stub(handle, &adapter_info, &request)?;
|
|
Ok(buf.len())
|
|
}
|
|
_ => Err(SysError::new(EINVAL)),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn on_close(&mut self, id: usize) {
|
|
let Some(handle) = self.handles.remove(id) else {
|
|
return;
|
|
};
|
|
if let Handle::Provider { adapter_id, .. } = handle {
|
|
self.adapters.remove(&adapter_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn run_daemon(daemon: daemon::SchemeDaemon) -> Result<()> {
|
|
let socket = Socket::create().context("failed to create i2c scheme socket")?;
|
|
let mut scheme = I2cDaemon::new();
|
|
let handler = Blocking::new(&socket, 16);
|
|
|
|
daemon
|
|
.ready_sync_scheme(&socket, &mut scheme)
|
|
.context("failed to publish i2c scheme root")?;
|
|
|
|
log::info!("RB_I2CD_SCHEMA");
|
|
|
|
libredox::call::setrens(0, 0).context("failed to enter null namespace")?;
|
|
|
|
handler
|
|
.process_requests_blocking(scheme)
|
|
.context("failed to process i2cd requests")?;
|
|
}
|
|
|
|
fn daemon_runner(daemon: daemon::SchemeDaemon) -> ! {
|
|
if let Err(err) = run_daemon(daemon) {
|
|
log::error!("i2cd: {err:#}");
|
|
process::exit(1);
|
|
}
|
|
|
|
process::exit(0);
|
|
}
|
|
|
|
fn main() {
|
|
common::setup_logging(
|
|
"bus",
|
|
"i2c",
|
|
"i2cd",
|
|
common::output_level(),
|
|
common::file_level(),
|
|
);
|
|
|
|
daemon::SchemeDaemon::new(daemon_runner);
|
|
}
|