blob: 5a1d22767f6b947cf996eec87b7350c70654ddeb [file]
// Copyright 2025 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#![no_main]
#![no_std]
use handler_codegen::handle;
use pw_status::{Error, Result};
use userspace::process_entry;
use userspace::syscall::{self, Signals};
use userspace::time::Instant;
fn handle_uppercase_ipcs() -> Result<()> {
loop {
// Wait for an IPC or a USER signal from the initiator.
let wait_return =
syscall::object_wait(handle::IPC, Signals::READABLE | Signals::USER, Instant::MAX)
.map_err(|_| Error::Internal)?;
// USER signal means the initiator is pinging us; echo it back and lower it
// (level-triggered: the sender owns both raise and lower).
if wait_return.pending_signals.contains(Signals::USER) {
syscall::object_set_peer_user_signal(handle::IPC, true).map_err(|_| Error::Internal)?;
syscall::object_set_peer_user_signal(handle::IPC, false)
.map_err(|_| Error::Internal)?;
return Ok(());
}
if !wait_return.pending_signals.contains(Signals::READABLE) || wait_return.user_data != 0 {
return Err(Error::Internal);
}
// Read the payload.
let mut buffer = [0u8; size_of::<char>()];
let len = syscall::channel_read(handle::IPC, 0, &mut buffer)?;
if len != size_of::<char>() {
return Err(Error::OutOfRange);
};
// Convert the payload to a character and make it uppercase.
let Some(c) = char::from_u32(u32::from_ne_bytes(buffer)) else {
return Err(Error::InvalidArgument);
};
if c == '!' {
syscall::object_set_peer_user_signal(handle::IPC, true)?;
} else if c == '#' {
syscall::object_set_peer_user_signal(handle::IPC, false)?;
}
let upper_c = c.to_ascii_uppercase();
// Respond to the IPC with the uppercase character.
let mut response_buffer = [0u8; size_of::<char>() * 2];
upper_c.encode_utf8(&mut response_buffer[0..size_of::<char>()]);
c.encode_utf8(&mut response_buffer[size_of::<char>()..]);
syscall::channel_respond(handle::IPC, &response_buffer)?;
}
}
fn handle_iovec_ipc() -> Result<()> {
let wait_return = syscall::object_wait(handle::IPC, Signals::READABLE, Instant::MAX)
.map_err(|_| Error::Internal)?;
if !wait_return.pending_signals.contains(Signals::READABLE) || wait_return.user_data != 0 {
return Err(Error::Internal);
}
// Read the payload.
let mut buffer = [0u8; 32];
let len = syscall::channel_read(handle::IPC, 0, &mut buffer)?;
let s = core::str::from_utf8(&buffer[..len]).expect("utf8 string");
test_logger::step_info!("IPC received: {}", s as &str);
// Convert the message to lowercase.
for b in buffer[..len].iter_mut() {
*b = b.to_ascii_lowercase();
}
// Send the reply as a iovec, just because we can.
let (x, y) = buffer[..len].split_at(len / 2);
syscall::channel_respond(handle::IPC, &[x, y])?;
Ok(())
}
fn handle_ipcs() -> Result<()> {
handle_uppercase_ipcs()?;
handle_iovec_ipc()?;
Ok(())
}
#[process_entry("handler")]
fn entry() -> Result<()> {
handle_ipcs().inspect_err(|e| {
// On error, log that it occurred and, since this is written as a test,
// shut down the system with the error code.
test_logger::step_failed!("IPC service error: {}", *e as u32);
let _ = syscall::debug_shutdown(Err(*e));
})
}