Advance Wayland and KDE package bring-up
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
diff --git a/drivers/inputd/src/main.rs b/drivers/inputd/src/main.rs
|
||||
--- a/drivers/inputd/src/main.rs
|
||||
+++ b/drivers/inputd/src/main.rs
|
||||
@@ -35,6 +35,9 @@
|
||||
|
||||
enum Handle {
|
||||
Producer,
|
||||
+ NamedProducer {
|
||||
+ name: String,
|
||||
+ },
|
||||
Consumer {
|
||||
events: EventFlags,
|
||||
pending: Vec<u8>,
|
||||
@@ -58,9 +61,11 @@
|
||||
|
||||
struct InputScheme {
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
+ devices: BTreeMap<String, u32>,
|
||||
|
||||
next_id: AtomicUsize,
|
||||
next_vt_id: AtomicUsize,
|
||||
+ next_device_id: AtomicUsize,
|
||||
|
||||
display: Option<String>,
|
||||
vts: BTreeSet<usize>,
|
||||
@@ -73,13 +78,30 @@
|
||||
has_new_events: bool,
|
||||
}
|
||||
|
||||
+const RESERVED_DEVICE_NAMES: [&str; 7] = [
|
||||
+ "producer",
|
||||
+ "consumer",
|
||||
+ "consumer_bootlog",
|
||||
+ "events",
|
||||
+ "handle",
|
||||
+ "handle_early",
|
||||
+ "control",
|
||||
+];
|
||||
+
|
||||
+enum ProducerKind {
|
||||
+ Legacy,
|
||||
+ Named(String),
|
||||
+}
|
||||
+
|
||||
impl InputScheme {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
handles: BTreeMap::new(),
|
||||
+ devices: BTreeMap::new(),
|
||||
|
||||
next_id: AtomicUsize::new(0),
|
||||
next_vt_id: AtomicUsize::new(2), // VT 1 is reserved for the bootlog
|
||||
+ next_device_id: AtomicUsize::new(0),
|
||||
|
||||
display: None,
|
||||
vts: BTreeSet::new(),
|
||||
@@ -91,6 +113,46 @@
|
||||
rshift: false,
|
||||
has_new_events: false,
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ fn register_named_producer(&mut self, name: &str) -> syscall::Result<Handle> {
|
||||
+ if name.is_empty() || RESERVED_DEVICE_NAMES.contains(&name) {
|
||||
+ return Err(SysError::new(EINVAL));
|
||||
+ }
|
||||
+
|
||||
+ if self.devices.contains_key(name) {
|
||||
+ return Err(SysError::new(EEXIST));
|
||||
+ }
|
||||
+
|
||||
+ let device_id = self.next_device_id.fetch_add(1, Ordering::SeqCst) as u32;
|
||||
+ self.devices.insert(name.to_owned(), device_id);
|
||||
+ Ok(Handle::NamedProducer {
|
||||
+ name: name.to_owned(),
|
||||
+ })
|
||||
+ }
|
||||
+
|
||||
+ fn route_legacy_consumer_events(&mut self, buf: &[u8]) {
|
||||
+ if let Some(active_vt) = self.active_vt {
|
||||
+ for handle in self.handles.values_mut() {
|
||||
+ match handle {
|
||||
+ Handle::Consumer {
|
||||
+ pending,
|
||||
+ notified,
|
||||
+ vt,
|
||||
+ ..
|
||||
+ } if *vt == active_vt => {
|
||||
+ pending.extend_from_slice(buf);
|
||||
+ *notified = false;
|
||||
+ }
|
||||
+ _ => continue,
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn route_named_producer_events(&mut self, _name: &str, _buf: &[u8]) {
|
||||
+ // DeviceConsumer routing is added in a follow-up patch. Named producers already share the
|
||||
+ // legacy consumer path, so existing callers continue to receive these events.
|
||||
}
|
||||
|
||||
fn switch_vt(&mut self, new_active: usize) {
|
||||
@@ -175,7 +237,13 @@
|
||||
let command = path_parts.next().ok_or(SysError::new(EINVAL))?;
|
||||
|
||||
let handle_ty = match command {
|
||||
- "producer" => Handle::Producer,
|
||||
+ "producer" => match path_parts.next() {
|
||||
+ None => Handle::Producer,
|
||||
+ Some(name) if !name.is_empty() && path_parts.next().is_none() => {
|
||||
+ self.register_named_producer(name)?
|
||||
+ }
|
||||
+ _ => return Err(SysError::new(EINVAL)),
|
||||
+ },
|
||||
"consumer" => {
|
||||
let vt = self.next_vt_id.fetch_add(1, Ordering::Relaxed);
|
||||
self.vts.insert(vt);
|
||||
@@ -338,7 +406,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
- Handle::Producer => {
|
||||
+ Handle::Producer | Handle::NamedProducer { .. } => {
|
||||
log::error!("producer tried to read");
|
||||
return Err(SysError::new(EINVAL));
|
||||
}
|
||||
@@ -360,9 +428,7 @@
|
||||
) -> syscall::Result<usize> {
|
||||
self.has_new_events = true;
|
||||
|
||||
- let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
|
||||
-
|
||||
- match handle {
|
||||
+ let producer_kind = match self.handles.get(&id).ok_or(SysError::new(EINVAL))? {
|
||||
Handle::Control => {
|
||||
if buf.len() != size_of::<ControlEvent>() {
|
||||
log::error!("control tried to write incorrectly sized command");
|
||||
@@ -391,9 +457,10 @@
|
||||
log::error!("display tried to write");
|
||||
return Err(SysError::new(EINVAL));
|
||||
}
|
||||
- Handle::Producer => {}
|
||||
+ Handle::Producer => ProducerKind::Legacy,
|
||||
+ Handle::NamedProducer { name } => ProducerKind::Named(name.clone()),
|
||||
Handle::SchemeRoot => return Err(SysError::new(EBADF)),
|
||||
- }
|
||||
+ };
|
||||
|
||||
if buf.len() == 1 && buf[0] > 0xf4 {
|
||||
return Ok(1);
|
||||
@@ -445,9 +512,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
- let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
|
||||
- assert!(matches!(handle, Handle::Producer));
|
||||
-
|
||||
let buf = unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
(events.as_ptr()) as *const u8,
|
||||
@@ -455,26 +519,11 @@
|
||||
)
|
||||
};
|
||||
|
||||
- if let Some(active_vt) = self.active_vt {
|
||||
- for handle in self.handles.values_mut() {
|
||||
- match handle {
|
||||
- Handle::Consumer {
|
||||
- pending,
|
||||
- notified,
|
||||
- vt,
|
||||
- ..
|
||||
- } => {
|
||||
- if *vt != active_vt {
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- pending.extend_from_slice(buf);
|
||||
- *notified = false;
|
||||
- }
|
||||
- _ => continue,
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
+ if let ProducerKind::Named(name) = &producer_kind {
|
||||
+ self.route_named_producer_events(name, buf);
|
||||
+ }
|
||||
+
|
||||
+ self.route_legacy_consumer_events(buf);
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
@@ -506,7 +555,7 @@
|
||||
*notified = false;
|
||||
Ok(EventFlags::empty())
|
||||
}
|
||||
- Handle::Producer | Handle::Control => {
|
||||
+ Handle::Producer | Handle::NamedProducer { .. } | Handle::Control => {
|
||||
log::error!("producer or control tried to use an event queue");
|
||||
Err(SysError::new(EINVAL))
|
||||
}
|
||||
@@ -515,9 +564,15 @@
|
||||
}
|
||||
|
||||
fn on_close(&mut self, id: usize) {
|
||||
- let handle = self.handles.remove(&id).unwrap();
|
||||
+ let Some(handle) = self.handles.remove(&id) else {
|
||||
+ log::warn!("received close for unknown input handle {id}");
|
||||
+ return;
|
||||
+ };
|
||||
|
||||
match handle {
|
||||
+ Handle::NamedProducer { name } => {
|
||||
+ self.devices.remove(&name);
|
||||
+ }
|
||||
Handle::Consumer { vt, .. } => {
|
||||
self.vts.remove(&vt);
|
||||
if self.active_vt == Some(vt) {
|
||||
diff --git a/drivers/inputd/src/lib.rs b/drivers/inputd/src/lib.rs
|
||||
--- a/drivers/inputd/src/lib.rs
|
||||
+++ b/drivers/inputd/src/lib.rs
|
||||
@@ -197,6 +197,38 @@
|
||||
pub vt: usize,
|
||||
}
|
||||
|
||||
+/// Handle for opening a named producer on the input scheme.
|
||||
+/// Opens /scheme/input/producer/{name}
|
||||
+pub struct NamedProducerHandle {
|
||||
+ fd: File,
|
||||
+}
|
||||
+
|
||||
+impl NamedProducerHandle {
|
||||
+ pub fn new(name: &str) -> io::Result<Self> {
|
||||
+ if name.is_empty() {
|
||||
+ return Err(io::Error::new(
|
||||
+ io::ErrorKind::InvalidInput,
|
||||
+ "input producer name must not be empty",
|
||||
+ ));
|
||||
+ }
|
||||
+
|
||||
+ if name.contains('/') {
|
||||
+ return Err(io::Error::new(
|
||||
+ io::ErrorKind::InvalidInput,
|
||||
+ "input producer name must not contain '/'",
|
||||
+ ));
|
||||
+ }
|
||||
+
|
||||
+ let path = format!("/scheme/input/producer/{name}");
|
||||
+ File::open(path).map(|fd| Self { fd })
|
||||
+ }
|
||||
+
|
||||
+ pub fn write_event(&mut self, event: &orbclient::Event) -> io::Result<()> {
|
||||
+ self.fd.write(unsafe { any_as_u8_slice(event) })?;
|
||||
+ Ok(())
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
pub struct ProducerHandle(File);
|
||||
|
||||
impl ProducerHandle {
|
||||
@@ -0,0 +1,450 @@
|
||||
diff --git a/drivers/inputd/src/main.rs b/drivers/inputd/src/main.rs
|
||||
--- a/drivers/inputd/src/main.rs
|
||||
+++ b/drivers/inputd/src/main.rs
|
||||
@@ -17,7 +17,7 @@
|
||||
use std::mem::transmute;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
-use inputd::{ControlEvent, VtEvent, VtEventKind};
|
||||
+use inputd::{ControlEvent, HotplugEventHeader, VtEvent, VtEventKind};
|
||||
|
||||
use libredox::errno::ESTALE;
|
||||
use redox_scheme::scheme::{SchemeState, SchemeSync};
|
||||
@@ -47,6 +47,17 @@
|
||||
notified: bool,
|
||||
vt: usize,
|
||||
},
|
||||
+ DeviceConsumer {
|
||||
+ device_name: String,
|
||||
+ events: EventFlags,
|
||||
+ pending: Vec<u8>,
|
||||
+ notified: bool,
|
||||
+ },
|
||||
+ HotplugEvents {
|
||||
+ events: EventFlags,
|
||||
+ pending: Vec<u8>,
|
||||
+ notified: bool,
|
||||
+ },
|
||||
Display {
|
||||
events: EventFlags,
|
||||
pending: Vec<VtEvent>,
|
||||
@@ -88,6 +99,9 @@
|
||||
"control",
|
||||
];
|
||||
|
||||
+const DEVICE_ADD: u32 = 1;
|
||||
+const DEVICE_REMOVE: u32 = 2;
|
||||
+
|
||||
enum ProducerKind {
|
||||
Legacy,
|
||||
Named(String),
|
||||
@@ -116,7 +130,7 @@
|
||||
}
|
||||
|
||||
fn register_named_producer(&mut self, name: &str) -> syscall::Result<Handle> {
|
||||
- if name.is_empty() || RESERVED_DEVICE_NAMES.contains(&name) {
|
||||
+ if name.is_empty() || name.contains('/') || RESERVED_DEVICE_NAMES.contains(&name) {
|
||||
return Err(SysError::new(EINVAL));
|
||||
}
|
||||
|
||||
@@ -126,11 +140,57 @@
|
||||
|
||||
let device_id = self.next_device_id.fetch_add(1, Ordering::SeqCst) as u32;
|
||||
self.devices.insert(name.to_owned(), device_id);
|
||||
+ self.queue_hotplug_event(DEVICE_ADD, device_id, name)?;
|
||||
Ok(Handle::NamedProducer {
|
||||
name: name.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
+ fn drain_pending_bytes(pending: &mut Vec<u8>, buf: &mut [u8]) -> usize {
|
||||
+ let copy = core::cmp::min(pending.len(), buf.len());
|
||||
+
|
||||
+ for (i, byte) in pending.drain(..copy).enumerate() {
|
||||
+ buf[i] = byte;
|
||||
+ }
|
||||
+
|
||||
+ copy
|
||||
+ }
|
||||
+
|
||||
+ fn queue_hotplug_event(
|
||||
+ &mut self,
|
||||
+ kind: u32,
|
||||
+ device_id: u32,
|
||||
+ name: &str,
|
||||
+ ) -> syscall::Result<()> {
|
||||
+ let name_len = u32::try_from(name.len()).map_err(|_| SysError::new(EINVAL))?;
|
||||
+ let header = HotplugEventHeader {
|
||||
+ kind,
|
||||
+ device_id,
|
||||
+ name_len,
|
||||
+ _reserved: 0,
|
||||
+ };
|
||||
+ let header_bytes = unsafe {
|
||||
+ core::slice::from_raw_parts(
|
||||
+ (&header as *const HotplugEventHeader).cast::<u8>(),
|
||||
+ size_of::<HotplugEventHeader>(),
|
||||
+ )
|
||||
+ };
|
||||
+
|
||||
+ for handle in self.handles.values_mut() {
|
||||
+ if let Handle::HotplugEvents {
|
||||
+ pending, notified, ..
|
||||
+ } = handle
|
||||
+ {
|
||||
+ pending.extend_from_slice(header_bytes);
|
||||
+ pending.extend_from_slice(name.as_bytes());
|
||||
+ *notified = false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ self.has_new_events = true;
|
||||
+ Ok(())
|
||||
+ }
|
||||
+
|
||||
fn route_legacy_consumer_events(&mut self, buf: &[u8]) {
|
||||
if let Some(active_vt) = self.active_vt {
|
||||
for handle in self.handles.values_mut() {
|
||||
@@ -150,9 +210,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
- fn route_named_producer_events(&mut self, _name: &str, _buf: &[u8]) {
|
||||
- // DeviceConsumer routing is added in a follow-up patch. Named producers already share the
|
||||
- // legacy consumer path, so existing callers continue to receive these events.
|
||||
+ fn route_named_producer_events(&mut self, name: &str, buf: &[u8]) {
|
||||
+ for handle in self.handles.values_mut() {
|
||||
+ match handle {
|
||||
+ Handle::DeviceConsumer {
|
||||
+ device_name,
|
||||
+ pending,
|
||||
+ notified,
|
||||
+ ..
|
||||
+ } if device_name == name => {
|
||||
+ pending.extend_from_slice(buf);
|
||||
+ *notified = false;
|
||||
+ }
|
||||
+ _ => continue,
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
fn switch_vt(&mut self, new_active: usize) {
|
||||
@@ -324,7 +396,24 @@
|
||||
is_earlyfb: command == "handle_early",
|
||||
}
|
||||
}
|
||||
+ "events" if path_parts.next().is_none() => Handle::HotplugEvents {
|
||||
+ events: EventFlags::empty(),
|
||||
+ pending: Vec::new(),
|
||||
+ notified: false,
|
||||
+ },
|
||||
"control" => Handle::Control,
|
||||
+ device_name
|
||||
+ if !device_name.is_empty()
|
||||
+ && !RESERVED_DEVICE_NAMES.contains(&device_name)
|
||||
+ && path_parts.next().is_none() =>
|
||||
+ {
|
||||
+ Handle::DeviceConsumer {
|
||||
+ device_name: device_name.to_owned(),
|
||||
+ events: EventFlags::empty(),
|
||||
+ pending: Vec::new(),
|
||||
+ notified: false,
|
||||
+ }
|
||||
+ }
|
||||
|
||||
_ => {
|
||||
log::error!("invalid path '{path}'");
|
||||
@@ -380,13 +469,11 @@
|
||||
return Err(SysError::new(ESTALE));
|
||||
}
|
||||
|
||||
- let copy = core::cmp::min(pending.len(), buf.len());
|
||||
-
|
||||
- for (i, byte) in pending.drain(..copy).enumerate() {
|
||||
- buf[i] = byte;
|
||||
- }
|
||||
+ Ok(Self::drain_pending_bytes(pending, buf))
|
||||
+ }
|
||||
|
||||
- Ok(copy)
|
||||
+ Handle::DeviceConsumer { pending, .. } | Handle::HotplugEvents { pending, .. } => {
|
||||
+ Ok(Self::drain_pending_bytes(pending, buf))
|
||||
}
|
||||
|
||||
Handle::Display { pending, .. } => {
|
||||
@@ -453,6 +540,10 @@
|
||||
log::error!("consumer tried to write");
|
||||
return Err(SysError::new(EINVAL));
|
||||
}
|
||||
+ Handle::DeviceConsumer { .. } | Handle::HotplugEvents { .. } => {
|
||||
+ log::error!("consumer or hotplug handle tried to write");
|
||||
+ return Err(SysError::new(EINVAL));
|
||||
+ }
|
||||
Handle::Display { .. } => {
|
||||
log::error!("display tried to write");
|
||||
return Err(SysError::new(EINVAL));
|
||||
@@ -541,6 +632,16 @@
|
||||
ref mut events,
|
||||
ref mut notified,
|
||||
..
|
||||
+ }
|
||||
+ | Handle::DeviceConsumer {
|
||||
+ ref mut events,
|
||||
+ ref mut notified,
|
||||
+ ..
|
||||
+ }
|
||||
+ | Handle::HotplugEvents {
|
||||
+ ref mut events,
|
||||
+ ref mut notified,
|
||||
+ ..
|
||||
} => {
|
||||
*events = flags;
|
||||
*notified = false;
|
||||
@@ -571,7 +672,12 @@
|
||||
|
||||
match handle {
|
||||
Handle::NamedProducer { name } => {
|
||||
- self.devices.remove(&name);
|
||||
+ if let Some(device_id) = self.devices.remove(&name) {
|
||||
+ self.queue_hotplug_event(DEVICE_REMOVE, device_id, &name)
|
||||
+ .unwrap_or_else(|err| {
|
||||
+ log::error!("failed to queue removal hotplug event for {name}: {err}");
|
||||
+ });
|
||||
+ }
|
||||
}
|
||||
Handle::Consumer { vt, .. } => {
|
||||
self.vts.remove(&vt);
|
||||
@@ -658,6 +764,28 @@
|
||||
socket_file.write_response(
|
||||
Response::post_fevent(*id, EventFlags::EVENT_READ.bits()),
|
||||
SignalBehavior::Restart,
|
||||
+ )?;
|
||||
+
|
||||
+ *notified = true;
|
||||
+ }
|
||||
+ Handle::DeviceConsumer {
|
||||
+ events,
|
||||
+ pending,
|
||||
+ ref mut notified,
|
||||
+ ..
|
||||
+ }
|
||||
+ | Handle::HotplugEvents {
|
||||
+ events,
|
||||
+ pending,
|
||||
+ ref mut notified,
|
||||
+ } => {
|
||||
+ if pending.is_empty() || *notified || !events.contains(EventFlags::EVENT_READ) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ socket_file.write_response(
|
||||
+ Response::post_fevent(*id, EventFlags::EVENT_READ.bits()),
|
||||
+ SignalBehavior::Restart,
|
||||
)?;
|
||||
|
||||
*notified = true;
|
||||
diff --git a/drivers/inputd/src/lib.rs b/drivers/inputd/src/lib.rs
|
||||
--- a/drivers/inputd/src/lib.rs
|
||||
+++ b/drivers/inputd/src/lib.rs
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::fs::{File, OpenOptions};
|
||||
-use std::io::{self, Read, Write};
|
||||
+use std::io::{self, ErrorKind, Read, Write};
|
||||
use std::mem::size_of;
|
||||
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd};
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
@@ -31,6 +31,24 @@
|
||||
slice::from_raw_parts_mut((p as *mut T) as *mut u8, size_of::<T>())
|
||||
}
|
||||
|
||||
+fn validate_input_name(kind: &str, name: &str) -> io::Result<()> {
|
||||
+ if name.is_empty() {
|
||||
+ return Err(io::Error::new(
|
||||
+ io::ErrorKind::InvalidInput,
|
||||
+ format!("input {kind} name must not be empty"),
|
||||
+ ));
|
||||
+ }
|
||||
+
|
||||
+ if name.contains('/') {
|
||||
+ return Err(io::Error::new(
|
||||
+ io::ErrorKind::InvalidInput,
|
||||
+ format!("input {kind} name must not contain '/'"),
|
||||
+ ));
|
||||
+ }
|
||||
+
|
||||
+ Ok(())
|
||||
+}
|
||||
+
|
||||
pub struct ConsumerHandle(File);
|
||||
|
||||
pub enum ConsumerHandleEvent<'a> {
|
||||
@@ -197,6 +215,22 @@
|
||||
pub vt: usize,
|
||||
}
|
||||
|
||||
+#[derive(Debug, Clone, Copy)]
|
||||
+#[repr(C)]
|
||||
+pub struct HotplugEventHeader {
|
||||
+ pub kind: u32,
|
||||
+ pub device_id: u32,
|
||||
+ pub name_len: u32,
|
||||
+ pub _reserved: u32,
|
||||
+}
|
||||
+
|
||||
+#[derive(Debug, Clone)]
|
||||
+pub struct HotplugEvent {
|
||||
+ pub kind: u32,
|
||||
+ pub device_id: u32,
|
||||
+ pub name: String,
|
||||
+}
|
||||
+
|
||||
/// Handle for opening a named producer on the input scheme.
|
||||
/// Opens /scheme/input/producer/{name}
|
||||
pub struct NamedProducerHandle {
|
||||
@@ -205,19 +239,7 @@
|
||||
|
||||
impl NamedProducerHandle {
|
||||
pub fn new(name: &str) -> io::Result<Self> {
|
||||
- if name.is_empty() {
|
||||
- return Err(io::Error::new(
|
||||
- io::ErrorKind::InvalidInput,
|
||||
- "input producer name must not be empty",
|
||||
- ));
|
||||
- }
|
||||
-
|
||||
- if name.contains('/') {
|
||||
- return Err(io::Error::new(
|
||||
- io::ErrorKind::InvalidInput,
|
||||
- "input producer name must not contain '/'",
|
||||
- ));
|
||||
- }
|
||||
+ validate_input_name("producer", name)?;
|
||||
|
||||
let path = format!("/scheme/input/producer/{name}");
|
||||
File::open(path).map(|fd| Self { fd })
|
||||
@@ -229,6 +251,124 @@
|
||||
}
|
||||
}
|
||||
|
||||
+pub struct DeviceConsumerHandle {
|
||||
+ fd: File,
|
||||
+}
|
||||
+
|
||||
+impl DeviceConsumerHandle {
|
||||
+ pub fn new(device_name: &str) -> io::Result<Self> {
|
||||
+ validate_input_name("device", device_name)?;
|
||||
+
|
||||
+ let fd = OpenOptions::new()
|
||||
+ .read(true)
|
||||
+ .custom_flags(O_NONBLOCK as i32)
|
||||
+ .open(format!("/scheme/input/{device_name}"))?;
|
||||
+
|
||||
+ Ok(Self { fd })
|
||||
+ }
|
||||
+
|
||||
+ pub fn event_handle(&self) -> BorrowedFd<'_> {
|
||||
+ self.fd.as_fd()
|
||||
+ }
|
||||
+
|
||||
+ pub fn read_event(&mut self) -> io::Result<Option<orbclient::Event>> {
|
||||
+ let mut raw = [0_u8; size_of::<orbclient::Event>()];
|
||||
+
|
||||
+ match self.fd.read(&mut raw) {
|
||||
+ Ok(0) => Ok(None),
|
||||
+ Ok(read) => {
|
||||
+ assert_eq!(read, raw.len());
|
||||
+ Ok(Some(unsafe {
|
||||
+ core::ptr::read_unaligned(raw.as_ptr().cast::<orbclient::Event>())
|
||||
+ }))
|
||||
+ }
|
||||
+ Err(err) if err.kind() == ErrorKind::WouldBlock => Ok(None),
|
||||
+ Err(err) => Err(err),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+pub struct HotplugHandle {
|
||||
+ fd: File,
|
||||
+ partial: Vec<u8>,
|
||||
+}
|
||||
+
|
||||
+impl HotplugHandle {
|
||||
+ pub fn new() -> io::Result<Self> {
|
||||
+ let fd = OpenOptions::new()
|
||||
+ .read(true)
|
||||
+ .custom_flags(O_NONBLOCK as i32)
|
||||
+ .open("/scheme/input/events")?;
|
||||
+
|
||||
+ Ok(Self {
|
||||
+ fd,
|
||||
+ partial: Vec::new(),
|
||||
+ })
|
||||
+ }
|
||||
+
|
||||
+ pub fn event_handle(&self) -> BorrowedFd<'_> {
|
||||
+ self.fd.as_fd()
|
||||
+ }
|
||||
+
|
||||
+ pub fn read_event(&mut self) -> io::Result<Option<HotplugEvent>> {
|
||||
+ let mut buf = [0_u8; 1024];
|
||||
+
|
||||
+ loop {
|
||||
+ if let Some(event) = Self::try_parse_event(&mut self.partial)? {
|
||||
+ return Ok(Some(event));
|
||||
+ }
|
||||
+
|
||||
+ match self.fd.read(&mut buf) {
|
||||
+ Ok(0) => return Ok(None),
|
||||
+ Ok(read) => self.partial.extend_from_slice(&buf[..read]),
|
||||
+ Err(err) if err.kind() == ErrorKind::WouldBlock => return Ok(None),
|
||||
+ Err(err) => return Err(err),
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn try_parse_event(partial: &mut Vec<u8>) -> io::Result<Option<HotplugEvent>> {
|
||||
+ if partial.len() < size_of::<HotplugEventHeader>() {
|
||||
+ return Ok(None);
|
||||
+ }
|
||||
+
|
||||
+ let header =
|
||||
+ unsafe { core::ptr::read_unaligned(partial.as_ptr().cast::<HotplugEventHeader>()) };
|
||||
+ let name_len = usize::try_from(header.name_len).map_err(|_| {
|
||||
+ io::Error::new(
|
||||
+ io::ErrorKind::InvalidData,
|
||||
+ "invalid input hotplug name length",
|
||||
+ )
|
||||
+ })?;
|
||||
+ let total_len = size_of::<HotplugEventHeader>()
|
||||
+ .checked_add(name_len)
|
||||
+ .ok_or_else(|| {
|
||||
+ io::Error::new(io::ErrorKind::InvalidData, "input hotplug event too large")
|
||||
+ })?;
|
||||
+
|
||||
+ if partial.len() < total_len {
|
||||
+ return Ok(None);
|
||||
+ }
|
||||
+
|
||||
+ let name = std::str::from_utf8(&partial[size_of::<HotplugEventHeader>()..total_len])
|
||||
+ .map_err(|_| {
|
||||
+ io::Error::new(
|
||||
+ io::ErrorKind::InvalidData,
|
||||
+ "input hotplug name is not UTF-8",
|
||||
+ )
|
||||
+ })?
|
||||
+ .to_owned();
|
||||
+
|
||||
+ partial.drain(..total_len);
|
||||
+
|
||||
+ Ok(Some(HotplugEvent {
|
||||
+ kind: header.kind,
|
||||
+ device_id: header.device_id,
|
||||
+ name,
|
||||
+ }))
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
pub struct ProducerHandle(File);
|
||||
|
||||
impl ProducerHandle {
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/drivers/acpid/src/acpi.rs b/drivers/acpid/src/acpi.rs
|
||||
index 94a1eb17..c3a5bfdc 100644
|
||||
index 94a1eb17..3fd91156 100644
|
||||
--- a/drivers/acpid/src/acpi.rs
|
||||
+++ b/drivers/acpid/src/acpi.rs
|
||||
@@ -25,6 +25,8 @@ use amlserde::{AmlSerde, AmlSerdeValue};
|
||||
@@ -24,7 +24,7 @@ index 94a1eb17..c3a5bfdc 100644
|
||||
|
||||
aml_symbols: RwLock<AmlSymbols>,
|
||||
|
||||
@@ -424,6 +432,62 @@ impl AcpiContext {
|
||||
@@ -424,6 +432,63 @@ impl AcpiContext {
|
||||
.flatten()
|
||||
}
|
||||
|
||||
@@ -35,7 +35,8 @@ index 94a1eb17..c3a5bfdc 100644
|
||||
+ args: &[u64],
|
||||
+ ) -> Result<Vec<u64>, AmlEvalError> {
|
||||
+ let full_path = format!("{path}.{method}");
|
||||
+ let aml_name = AmlName::from_str(&full_path).map_err(|_| AmlEvalError::DeserializationError)?;
|
||||
+ let aml_name =
|
||||
+ AmlName::from_str(&full_path).map_err(|_| AmlEvalError::DeserializationError)?;
|
||||
+ let args = args
|
||||
+ .iter()
|
||||
+ .copied()
|
||||
@@ -87,7 +88,7 @@ index 94a1eb17..c3a5bfdc 100644
|
||||
pub fn init(
|
||||
rxsdt_physaddrs: impl Iterator<Item = u64>,
|
||||
ec: Vec<(RegionSpace, Box<dyn RegionHandler>)>,
|
||||
@@ -444,6 +508,12 @@ impl AcpiContext {
|
||||
@@ -444,6 +509,12 @@ impl AcpiContext {
|
||||
tables,
|
||||
dsdt: None,
|
||||
fadt: None,
|
||||
@@ -100,7 +101,7 @@ index 94a1eb17..c3a5bfdc 100644
|
||||
|
||||
// Temporary values
|
||||
aml_symbols: RwLock::new(AmlSymbols::new(ec)),
|
||||
@@ -458,7 +528,10 @@ impl AcpiContext {
|
||||
@@ -458,7 +529,10 @@ impl AcpiContext {
|
||||
}
|
||||
|
||||
Fadt::init(&mut this);
|
||||
@@ -112,7 +113,7 @@ index 94a1eb17..c3a5bfdc 100644
|
||||
|
||||
this
|
||||
}
|
||||
@@ -562,92 +635,83 @@ impl AcpiContext {
|
||||
@@ -562,92 +636,83 @@ impl AcpiContext {
|
||||
aml_symbols.symbol_cache = FxHashMap::default();
|
||||
}
|
||||
|
||||
@@ -268,7 +269,7 @@ index 94a1eb17..c3a5bfdc 100644
|
||||
loop {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
@@ -707,7 +771,7 @@ unsafe impl plain::Plain for FadtStruct {}
|
||||
@@ -707,7 +772,7 @@ unsafe impl plain::Plain for FadtStruct {}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
@@ -277,7 +278,7 @@ index 94a1eb17..c3a5bfdc 100644
|
||||
address_space: u8,
|
||||
bit_width: u8,
|
||||
bit_offset: u8,
|
||||
@@ -715,11 +779,67 @@ pub struct GenericAddressStructure {
|
||||
@@ -715,11 +780,77 @@ pub struct GenericAddressStructure {
|
||||
address: u64,
|
||||
}
|
||||
|
||||
@@ -290,14 +291,23 @@ index 94a1eb17..c3a5bfdc 100644
|
||||
+ pub fn write_u8(&self, value: u8) {
|
||||
+ match self.address_space {
|
||||
+ 0 => {
|
||||
+ let Ok(address) = usize::try_from(self.address) else {
|
||||
+ log::error!("Reset register physical address is invalid: {:#X}", self.address);
|
||||
+ let address_val = self.address;
|
||||
+ let Ok(address) = usize::try_from(address_val) else {
|
||||
+ log::error!(
|
||||
+ "Reset register physical address is invalid: {:#X}",
|
||||
+ address_val
|
||||
+ );
|
||||
+ return;
|
||||
+ };
|
||||
+ let page = address / PAGE_SIZE * PAGE_SIZE;
|
||||
+ let offset = address % PAGE_SIZE;
|
||||
+ let virt = unsafe {
|
||||
+ common::physmap(page, PAGE_SIZE, common::Prot::RW, common::MemoryType::default())
|
||||
+ common::physmap(
|
||||
+ page,
|
||||
+ PAGE_SIZE,
|
||||
+ common::Prot::RW,
|
||||
+ common::MemoryType::default(),
|
||||
+ )
|
||||
+ };
|
||||
+
|
||||
+ match virt {
|
||||
@@ -315,7 +325,8 @@ index 94a1eb17..c3a5bfdc 100644
|
||||
+ Pio::<u8>::new(port).write(value);
|
||||
+ }
|
||||
+ Err(_) => {
|
||||
+ log::error!("Reset register I/O port is invalid: {:#X}", self.address);
|
||||
+ let address_val = self.address;
|
||||
+ log::error!("Reset register I/O port is invalid: {:#X}", address_val);
|
||||
+ }
|
||||
+ },
|
||||
+ address_space => {
|
||||
@@ -346,7 +357,7 @@ index 94a1eb17..c3a5bfdc 100644
|
||||
|
||||
pub reset_value: u8,
|
||||
reserved3: [u8; 3],
|
||||
@@ -728,14 +848,14 @@ pub struct FadtAcpi2Struct {
|
||||
@@ -728,14 +859,14 @@ pub struct FadtAcpi2Struct {
|
||||
pub x_firmware_control: u64,
|
||||
pub x_dsdt: u64,
|
||||
|
||||
@@ -369,7 +380,7 @@ index 94a1eb17..c3a5bfdc 100644
|
||||
}
|
||||
unsafe impl plain::Plain for FadtAcpi2Struct {}
|
||||
|
||||
@@ -793,9 +913,25 @@ impl Fadt {
|
||||
@@ -793,9 +924,27 @@ impl Fadt {
|
||||
None => usize::try_from(fadt.dsdt).expect("expected any given u32 to fit within usize"),
|
||||
};
|
||||
|
||||
@@ -379,7 +390,9 @@ index 94a1eb17..c3a5bfdc 100644
|
||||
+ let pm1a_cnt_blk = u64::from(fadt.pm1a_control_block);
|
||||
+ let pm1b_cnt_blk = u64::from(fadt.pm1b_control_block);
|
||||
+ let (reset_reg, reset_value) = match fadt.acpi_2_struct() {
|
||||
+ Some(fadt2) if !fadt2.reset_reg.is_empty() => (Some(fadt2.reset_reg), fadt2.reset_value),
|
||||
+ Some(fadt2) if !fadt2.reset_reg.is_empty() => {
|
||||
+ (Some(fadt2.reset_reg), fadt2.reset_value)
|
||||
+ }
|
||||
+ _ => (None, 0),
|
||||
+ };
|
||||
|
||||
@@ -397,22 +410,24 @@ index 94a1eb17..c3a5bfdc 100644
|
||||
Ok(dsdt) => dsdt,
|
||||
Err(error) => {
|
||||
log::error!("Failed to load DSDT: {}", error);
|
||||
@@ -803,8 +939,46 @@ impl Fadt {
|
||||
@@ -803,8 +952,48 @@ impl Fadt {
|
||||
}
|
||||
};
|
||||
|
||||
+ let (slp_typa_s5, slp_typb_s5) = match AmlName::from_str("\\_S5") {
|
||||
+ Ok(s5_name) => match context.aml_eval(s5_name, Vec::new()) {
|
||||
+ Ok(AmlSerdeValue::Package { contents }) => match (contents.get(0), contents.get(1)) {
|
||||
+ (Some(AmlSerdeValue::Integer(slp_typa)), Some(AmlSerdeValue::Integer(slp_typb))) => {
|
||||
+ match (u8::try_from(*slp_typa), u8::try_from(*slp_typb)) {
|
||||
+ (Ok(slp_typa_s5), Ok(slp_typb_s5)) => (slp_typa_s5, slp_typb_s5),
|
||||
+ _ => {
|
||||
+ log::warn!("\\_S5 values do not fit in u8: {:?}", contents);
|
||||
+ (0, 0)
|
||||
+ }
|
||||
+ Ok(AmlSerdeValue::Package { contents }) => match (contents.get(0), contents.get(1))
|
||||
+ {
|
||||
+ (
|
||||
+ Some(AmlSerdeValue::Integer(slp_typa)),
|
||||
+ Some(AmlSerdeValue::Integer(slp_typb)),
|
||||
+ ) => match (u8::try_from(*slp_typa), u8::try_from(*slp_typb)) {
|
||||
+ (Ok(slp_typa_s5), Ok(slp_typb_s5)) => (slp_typa_s5, slp_typb_s5),
|
||||
+ _ => {
|
||||
+ log::warn!("\\_S5 values do not fit in u8: {:?}", contents);
|
||||
+ (0, 0)
|
||||
+ }
|
||||
+ }
|
||||
+ },
|
||||
+ _ => {
|
||||
+ log::warn!("\\_S5 package did not contain two integers: {:?}", contents);
|
||||
+ (0, 0)
|
||||
@@ -557,7 +572,7 @@ index 0933f638..d4b0f3d0 100644
|
||||
|
||||
fn main() {
|
||||
diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs
|
||||
index ce55b33f..c06bdec4 100644
|
||||
index c2caf804..95acdb57 100644
|
||||
--- a/drivers/pcid/src/scheme.rs
|
||||
+++ b/drivers/pcid/src/scheme.rs
|
||||
@@ -21,6 +21,7 @@ enum Handle {
|
||||
@@ -591,7 +606,7 @@ index ce55b33f..c06bdec4 100644
|
||||
}
|
||||
fn is_scheme_root(&self) -> bool {
|
||||
matches!(self, Self::SchemeRoot)
|
||||
@@ -153,6 +160,7 @@ impl SchemeSync for PciScheme {
|
||||
@@ -132,6 +139,7 @@ impl SchemeSync for PciScheme {
|
||||
let (len, mode) = match handle.inner {
|
||||
Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755),
|
||||
Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755),
|
||||
@@ -599,7 +614,7 @@ index ce55b33f..c06bdec4 100644
|
||||
Handle::Access | Handle::Channel { .. } => (0, MODE_CHR | 0o600),
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
};
|
||||
@@ -177,6 +185,18 @@ impl SchemeSync for PciScheme {
|
||||
@@ -156,6 +164,18 @@ impl SchemeSync for PciScheme {
|
||||
match handle.inner {
|
||||
Handle::TopLevel { .. } => Err(Error::new(EISDIR)),
|
||||
Handle::Device => Err(Error::new(EISDIR)),
|
||||
@@ -618,7 +633,7 @@ index ce55b33f..c06bdec4 100644
|
||||
Handle::Channel {
|
||||
addr: _,
|
||||
ref mut st,
|
||||
@@ -214,7 +234,9 @@ impl SchemeSync for PciScheme {
|
||||
@@ -193,7 +213,9 @@ impl SchemeSync for PciScheme {
|
||||
return Ok(buf);
|
||||
}
|
||||
Handle::Device => DEVICE_CONTENTS,
|
||||
@@ -629,7 +644,7 @@ index ce55b33f..c06bdec4 100644
|
||||
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
||||
};
|
||||
|
||||
@@ -244,6 +266,20 @@ impl SchemeSync for PciScheme {
|
||||
@@ -223,6 +245,20 @@ impl SchemeSync for PciScheme {
|
||||
}
|
||||
|
||||
match handle.inner {
|
||||
@@ -650,7 +665,7 @@ index ce55b33f..c06bdec4 100644
|
||||
Handle::Channel { addr, ref mut st } => {
|
||||
Self::write_channel(&self.pcie, &mut self.tree, addr, st, buf)
|
||||
}
|
||||
@@ -339,6 +375,10 @@ impl PciScheme {
|
||||
@@ -318,6 +354,10 @@ impl PciScheme {
|
||||
func.enabled = false;
|
||||
}
|
||||
}
|
||||
@@ -661,7 +676,7 @@ index ce55b33f..c06bdec4 100644
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -365,6 +405,7 @@ impl PciScheme {
|
||||
@@ -343,6 +383,7 @@ impl PciScheme {
|
||||
let path = &after[1..];
|
||||
|
||||
match path {
|
||||
@@ -669,3 +684,34 @@ index ce55b33f..c06bdec4 100644
|
||||
"channel" => {
|
||||
if func.enabled {
|
||||
return Err(Error::new(ENOLCK));
|
||||
diff --git a/init/src/scheduler.rs b/init/src/scheduler.rs
|
||||
index 670a5526..24ce3d68 100644
|
||||
--- a/init/src/scheduler.rs
|
||||
+++ b/init/src/scheduler.rs
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
-use crate::InitConfig;
|
||||
use crate::script::Command;
|
||||
use crate::unit::{Unit, UnitId, UnitKind, UnitStore};
|
||||
+use crate::InitConfig;
|
||||
|
||||
pub struct Scheduler {
|
||||
pending: VecDeque<Job>,
|
||||
@@ -96,14 +96,9 @@ fn run(unit: &mut Unit, config: &mut InitConfig) {
|
||||
eprintln!("Skipping '{} {}'", service.cmd, service.args.join(" "));
|
||||
return;
|
||||
}
|
||||
- if config.log_debug {
|
||||
- eprintln!(
|
||||
- "Starting {} ({})",
|
||||
- unit.info.description.as_ref().unwrap_or(&unit.id.0),
|
||||
- service.cmd,
|
||||
- );
|
||||
- }
|
||||
+ eprintln!("init: starting {} ({:?})", service.cmd, &service.type_,);
|
||||
service.spawn(&config.envs);
|
||||
+ eprintln!("init: started {} done", service.cmd,);
|
||||
}
|
||||
UnitKind::Target {} => {
|
||||
if config.log_debug {
|
||||
|
||||
Reference in New Issue
Block a user