ctru/services/
svc.rs

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