1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
//! Syscall APIs
//!
//! Not all APIs are wrapped in this module, since a lot are fundamentally unsafe.
//! Most APIs should be used directly from `ctru-sys`.
use crate::error::ResultCode;
use ctru_sys::Handle;
use std::time::Duration;
/// Extension trait for [Handle]
pub trait HandleExt {
/// Wait for an event to fire. If the timeout is reached, an error is returned. You can use
/// [`Error::is_timeout`] to check if the error is due to a timeout.
fn wait_for_event(self, timeout: Duration) -> crate::Result<()>;
/// Send a service request to the handle.
/// The request vector must contain the command header and any parameters.
/// The request vector is overwritten with the response and returned.
/// The error in the response is checked and returned as a `Result::Err` if the operation failed.
///
/// # Safety
/// This function is unsafe because it directly accesses the thread command buffer.
/// If the request vector or the expected response length is incorrect, or the handle is not a service that accepts
/// requests, the function may cause undefined behavior.
unsafe fn send_service_request(
self,
request: Vec<u32>,
expected_response_len: usize,
) -> crate::Result<Vec<u32>>;
}
impl HandleExt for Handle {
fn wait_for_event(self, timeout: Duration) -> crate::Result<()> {
let timeout = i64::try_from(timeout.as_nanos()).map_err(|e| {
crate::Error::Other(format!(
"Failed to convert timeout to 64-bit nanoseconds: {}",
e
))
})?;
unsafe {
ResultCode(ctru_sys::svcWaitSynchronization(self, timeout))?;
}
Ok(())
}
unsafe fn send_service_request(
self,
mut request: Vec<u32>,
expected_response_len: usize,
) -> crate::Result<Vec<u32>> {
// Copy over the request
let cmd_buffer_ptr = unsafe { ctru_sys::getThreadCommandBuffer() };
unsafe {
std::ptr::copy_nonoverlapping(request.as_ptr(), cmd_buffer_ptr, request.len());
// Send the request
ResultCode(ctru_sys::svcSendSyncRequest(self))?;
// Handle the result returned by the service
let result = std::ptr::read(cmd_buffer_ptr.add(1));
ResultCode(result as ctru_sys::Result)?;
}
// Copy back the response
request.clear();
request.resize(expected_response_len, 0);
unsafe {
std::ptr::copy_nonoverlapping(
cmd_buffer_ptr,
request.as_mut_ptr(),
expected_response_len,
);
}
Ok(request)
}
}
/// Creates a command header to be used for IPC. This is a const fn version of [`ctru_sys::IPC_MakeHeader`].
pub const fn make_ipc_header(command_id: u16, normal_params: u8, translate_params: u8) -> u32 {
((command_id as u32) << 16)
| (((normal_params as u32) & 0x3F) << 6)
| ((translate_params as u32) & 0x3F)
}