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}