Files
RedBear-OS/drivers/gpio/gpiod/src/main.rs
T

497 lines
17 KiB
Rust

use std::collections::BTreeMap;
use std::process;
use anyhow::{Context, Result};
use redox_scheme::scheme::SchemeSync;
use redox_scheme::{CallerCtx, OpenResult, Socket};
use scheme_utils::{Blocking, HandleMap};
use serde::{Deserialize, Serialize};
use syscall::schemev2::NewFdFlags;
use syscall::{Error as SysError, EACCES, EBADF, EINVAL, ENOENT};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct GpioControllerInfo {
pub id: u32,
pub name: String,
pub pin_count: usize,
pub supports_interrupt: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum GpioControlRequest {
RegisterController { info: GpioControllerInfo },
ReadPin { controller_id: u32, pin: u32 },
WritePin { controller_id: u32, pin: u32, value: bool },
ConfigurePin { controller_id: u32, pin: u32, config: PinConfig },
ListControllers,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PinConfig {
pub direction: PinDirection,
pub pull: PullMode,
pub interrupt_mode: Option<InterruptMode>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum PinDirection {
Input,
Output,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum PullMode {
None,
Up,
Down,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum InterruptMode {
EdgeRising,
EdgeFalling,
EdgeBoth,
LevelHigh,
LevelLow,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
enum GpioControlResponse {
ControllerRegistered { id: u32 },
Controllers(Vec<GpioControllerInfo>),
Controller(GpioControllerInfo),
PinValue(bool),
Ack,
Error(String),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum PinOpKind {
Read,
Write,
Configure,
}
enum Handle {
SchemeRoot,
Register { pending: Vec<u8> },
Provider { controller_id: u32, pending: Vec<u8> },
ControllersDir { pending: Vec<u8> },
ControllerDetail { id: u32, pending: Vec<u8> },
PinOp { kind: PinOpKind, pending: Vec<u8> },
}
struct ControllerEntry {
info: GpioControllerInfo,
provider_handle: usize,
}
struct GpioDaemon {
handles: HandleMap<Handle>,
controllers: BTreeMap<u32, ControllerEntry>,
next_id: u32,
}
impl GpioDaemon {
fn new() -> Self {
Self {
handles: HandleMap::new(),
controllers: BTreeMap::new(),
next_id: 0,
}
}
fn controller_list(&self) -> Vec<GpioControllerInfo> {
self.controllers
.values()
.map(|entry| entry.info.clone())
.collect()
}
fn serialize_response(response: &GpioControlResponse) -> syscall::Result<Vec<u8>> {
ron::ser::to_string(response)
.map(|text| text.into_bytes())
.map_err(|err| {
log::error!("gpiod: failed to serialize control response: {err}");
SysError::new(EINVAL)
})
}
fn deserialize_request(buf: &[u8]) -> syscall::Result<GpioControlRequest> {
let text = std::str::from_utf8(buf).map_err(|err| {
log::warn!("gpiod: invalid UTF-8 request payload: {err}");
SysError::new(EINVAL)
})?;
ron::from_str(text).map_err(|err| {
log::warn!("gpiod: failed to decode control request: {err}");
SysError::new(EINVAL)
})
}
fn set_pending_response(handle: &mut Handle, response: GpioControlResponse) -> syscall::Result<()> {
let pending = Self::serialize_response(&response)?;
Self::set_pending_bytes(handle, pending)
}
fn set_pending_bytes(handle: &mut Handle, pending: Vec<u8>) -> syscall::Result<()> {
match handle {
Handle::Register { pending: slot }
| Handle::Provider { pending: slot, .. }
| Handle::ControllersDir { pending: slot }
| Handle::ControllerDetail { pending: slot, .. }
| Handle::PinOp { pending: slot, .. } => {
*slot = pending;
Ok(())
}
Handle::SchemeRoot => Err(SysError::new(EBADF)),
}
}
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::ControllersDir { pending }
| Handle::ControllerDetail { pending, .. }
| Handle::PinOp { 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)
}
fn validate_pin_target(
&self,
controller_id: u32,
pin: u32,
) -> std::result::Result<GpioControllerInfo, String> {
let entry = self
.controllers
.get(&controller_id)
.ok_or_else(|| format!("unknown controller {controller_id}"))?;
if usize::try_from(pin)
.ok()
.filter(|pin| *pin < entry.info.pin_count)
.is_none()
{
return Err(format!(
"pin {pin} is out of range for controller {} (pin_count={})",
entry.info.name, entry.info.pin_count
));
}
Ok(entry.info.clone())
}
}
impl SchemeSync for GpioDaemon {
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("controllers") => match parts.next() {
None => Handle::ControllersDir {
pending: Vec::new(),
},
Some(id) if parts.next().is_none() => Handle::ControllerDetail {
id: id.parse::<u32>().map_err(|_| SysError::new(EINVAL))?,
pending: Vec::new(),
},
_ => return Err(SysError::new(EINVAL)),
},
Some("read_pin") if parts.next().is_none() => Handle::PinOp {
kind: PinOpKind::Read,
pending: Vec::new(),
},
Some("write_pin") if parts.next().is_none() => Handle::PinOp {
kind: PinOpKind::Write,
pending: Vec::new(),
},
Some("configure_pin") if parts.next().is_none() => Handle::PinOp {
kind: PinOpKind::Configure,
pending: Vec::new(),
},
_ => return Err(SysError::new(ENOENT)),
}
}
Handle::ControllersDir { .. } => {
if segments.is_empty() {
return Err(SysError::new(EINVAL));
}
Handle::ControllerDetail {
id: segments.parse::<u32>().map_err(|_| SysError::new(EINVAL))?,
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 controllers = self.controller_list();
let detail = match self.handles.get(id)? {
Handle::ControllerDetail { id, .. } => self.controllers.get(id).map(|entry| entry.info.clone()),
_ => None,
};
let handle = self.handles.get_mut(id)?;
match handle {
Handle::ControllersDir { pending } if pending.is_empty() => {
*pending = Self::serialize_response(&GpioControlResponse::Controllers(controllers))?;
}
Handle::ControllerDetail { id, pending } if pending.is_empty() => {
let info = detail.ok_or(SysError::new(ENOENT))?;
*pending = Self::serialize_response(&GpioControlResponse::Controller(info))?;
log::debug!("gpiod: served controller detail for id={id}");
}
_ => {}
}
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 {
GpioControlRequest::RegisterController { mut info } => {
if !matches!(self.handles.get(id)?, Handle::Register { .. }) {
return Err(SysError::new(EINVAL));
}
let controller_id = self.next_id;
self.next_id = self.next_id.checked_add(1).ok_or(SysError::new(EINVAL))?;
info.id = controller_id;
self.controllers.insert(
controller_id,
ControllerEntry {
info: info.clone(),
provider_handle: id,
},
);
let handle = self.handles.get_mut(id)?;
*handle = Handle::Provider {
controller_id,
pending: Self::serialize_response(&GpioControlResponse::ControllerRegistered {
id: controller_id,
})?,
};
log::info!(
"RB_GPIOD_CONTROLLER_REGISTERED id={} name={} pin_count={} supports_interrupt={}",
info.id,
info.name,
info.pin_count,
info.supports_interrupt,
);
Ok(buf.len())
}
GpioControlRequest::ListControllers => {
let controllers = self.controller_list();
let handle = self.handles.get_mut(id)?;
Self::set_pending_response(handle, GpioControlResponse::Controllers(controllers))?;
Ok(buf.len())
}
GpioControlRequest::ReadPin { controller_id, pin } => {
let validation = self.validate_pin_target(controller_id, pin);
let handle = self.handles.get_mut(id)?;
match handle {
Handle::PinOp {
kind: PinOpKind::Read,
..
} => {
match validation {
Ok(info) => {
log::info!(
"RB_GPIOD_PIN_READ controller_id={} name={} pin={} routed=stub",
controller_id,
info.name,
pin,
);
Self::set_pending_response(handle, GpioControlResponse::PinValue(false))?;
}
Err(message) => {
Self::set_pending_response(handle, GpioControlResponse::Error(message))?;
}
}
Ok(buf.len())
}
_ => Err(SysError::new(EINVAL)),
}
}
GpioControlRequest::WritePin {
controller_id,
pin,
value,
} => {
let validation = self.validate_pin_target(controller_id, pin);
let handle = self.handles.get_mut(id)?;
match handle {
Handle::PinOp {
kind: PinOpKind::Write,
..
} => {
match validation {
Ok(info) => {
log::info!(
"RB_GPIOD_PIN_WRITE controller_id={} name={} pin={} value={} routed=stub",
controller_id,
info.name,
pin,
value,
);
Self::set_pending_response(handle, GpioControlResponse::Ack)?;
}
Err(message) => {
Self::set_pending_response(handle, GpioControlResponse::Error(message))?;
}
}
Ok(buf.len())
}
_ => Err(SysError::new(EINVAL)),
}
}
GpioControlRequest::ConfigurePin {
controller_id,
pin,
config,
} => {
let validation = self.validate_pin_target(controller_id, pin);
let handle = self.handles.get_mut(id)?;
match handle {
Handle::PinOp {
kind: PinOpKind::Configure,
..
} => {
match validation {
Ok(info) => {
log::info!(
"RB_GPIOD_PIN_CONFIG controller_id={} name={} pin={} direction={:?} pull={:?} interrupt={:?} routed=stub",
controller_id,
info.name,
pin,
config.direction,
config.pull,
config.interrupt_mode,
);
Self::set_pending_response(handle, GpioControlResponse::Ack)?;
}
Err(message) => {
Self::set_pending_response(handle, GpioControlResponse::Error(message))?;
}
}
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 { controller_id, .. } = handle {
if let Some(entry) = self.controllers.remove(&controller_id) {
log::info!(
"RB_GPIOD_CONTROLLER_REMOVED id={} name={} provider_handle={}",
controller_id,
entry.info.name,
entry.provider_handle,
);
}
}
}
}
fn run_daemon(daemon: daemon::SchemeDaemon) -> Result<()> {
let socket = Socket::create().context("failed to create gpio scheme socket")?;
let mut scheme = GpioDaemon::new();
let handler = Blocking::new(&socket, 16);
daemon
.ready_sync_scheme(&socket, &mut scheme)
.context("failed to publish gpio scheme root")?;
log::info!("RB_GPIOD_SCHEMA version=1");
libredox::call::setrens(0, 0).context("failed to enter null namespace")?;
handler
.process_requests_blocking(scheme)
.context("failed to process gpiod requests")?;
}
fn daemon_runner(daemon: daemon::SchemeDaemon) -> ! {
if let Err(err) = run_daemon(daemon) {
log::error!("gpiod: {err:#}");
process::exit(1);
}
process::exit(0);
}
fn main() {
common::setup_logging(
"gpio",
"gpio",
"gpiod",
common::output_level(),
common::file_level(),
);
daemon::SchemeDaemon::new(daemon_runner);
}