Browse Source

Rename srv module to svc and add Handle::send_service_request

pull/86/head
AzureMarker 1 year ago
parent
commit
df50702c13
  1. 2
      ctru-rs/examples/ir-user-circle-pad-pro.rs
  2. 108
      ctru-rs/src/services/ir_user.rs
  3. 2
      ctru-rs/src/services/mod.rs
  4. 37
      ctru-rs/src/services/srv.rs
  5. 76
      ctru-rs/src/services/svc.rs

2
ctru-rs/examples/ir-user-circle-pad-pro.rs

@ -3,7 +3,7 @@
use ctru::prelude::*; use ctru::prelude::*;
use ctru::services::gfx::{Flush, Swap}; use ctru::services::gfx::{Flush, Swap};
use ctru::services::ir_user::{CirclePadProInputResponse, ConnectionStatus, IrDeviceId, IrUser}; use ctru::services::ir_user::{CirclePadProInputResponse, ConnectionStatus, IrDeviceId, IrUser};
use ctru::services::srv::HandleExt; use ctru::services::svc::HandleExt;
use ctru_sys::Handle; use ctru_sys::Handle;
use std::time::Duration; use std::time::Duration;

108
ctru-rs/src/services/ir_user.rs

@ -11,13 +11,13 @@
#![doc(alias = "gamepad")] #![doc(alias = "gamepad")]
use crate::error::ResultCode; use crate::error::ResultCode;
use crate::services::srv::make_ipc_header; use crate::services::svc::{make_ipc_header, HandleExt};
use crate::services::ServiceReference; use crate::services::ServiceReference;
use crate::Error; use crate::Error;
use ctru_sys::{Handle, MEMPERM_READ, MEMPERM_READWRITE}; use ctru_sys::{Handle, MEMPERM_READ, MEMPERM_READWRITE};
use std::alloc::Layout; use std::alloc::Layout;
use std::ffi::CString; use std::ffi::CString;
use std::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut}; use std::ptr::slice_from_raw_parts;
use std::sync::Mutex; use std::sync::Mutex;
static IR_USER_ACTIVE: Mutex<()> = Mutex::new(()); static IR_USER_ACTIVE: Mutex<()> = Mutex::new(());
@ -100,16 +100,18 @@ impl IrUser {
))?; ))?;
// Initialize the ir:USER service with the shared memory // Initialize the ir:USER service with the shared memory
initialize_irnop_shared(InitializeIrnopSharedParams { let request = vec![
ir_user_handle: service_handle, INITIALIZE_IRNOP_SHARED_COMMAND_HEADER,
shared_memory_len: shared_memory_len as u32, shared_memory_len as u32,
recv_packet_buffer_len: recv_buffer_size as u32, recv_buffer_size as u32,
recv_packet_count: recv_packet_count as u32, recv_packet_count as u32,
send_packet_buffer_len: send_buffer_size as u32, send_buffer_size as u32,
send_packet_count: send_packet_count as u32, send_packet_count as u32,
bit_rate: IR_BITRATE, IR_BITRATE,
0,
shared_memory_handle, shared_memory_handle,
})?; ];
service_handle.send_service_request(request, 2)?;
// Set up our service state // Set up our service state
let user_state = IrUserState { let user_state = IrUserState {
@ -161,23 +163,28 @@ impl IrUser {
/// Try to connect to the device with the provided ID. /// Try to connect to the device with the provided ID.
pub fn require_connection(&self, device_id: IrDeviceId) -> crate::Result<()> { pub fn require_connection(&self, device_id: IrDeviceId) -> crate::Result<()> {
unsafe {
self.send_service_request( self.send_service_request(
vec![REQUIRE_CONNECTION_COMMAND_HEADER, device_id.get_id()], vec![REQUIRE_CONNECTION_COMMAND_HEADER, device_id.get_id()],
2, 2,
)?; )?;
}
Ok(()) Ok(())
} }
/// Close the current IR connection. /// Close the current IR connection.
pub fn disconnect(&self) -> crate::Result<()> { pub fn disconnect(&self) -> crate::Result<()> {
unsafe {
self.send_service_request(vec![DISCONNECT_COMMAND_HEADER], 2)?; self.send_service_request(vec![DISCONNECT_COMMAND_HEADER], 2)?;
}
Ok(()) Ok(())
} }
/// Get an event handle that activates on connection status changes. /// Get an event handle that activates on connection status changes.
pub fn get_connection_status_event(&self) -> crate::Result<Handle> { pub fn get_connection_status_event(&self) -> crate::Result<Handle> {
let response = let response = unsafe {
self.send_service_request(vec![GET_CONNECTION_STATUS_EVENT_COMMAND_HEADER], 4)?; self.send_service_request(vec![GET_CONNECTION_STATUS_EVENT_COMMAND_HEADER], 4)
}?;
let status_event = response[3] as Handle; let status_event = response[3] as Handle;
Ok(status_event) Ok(status_event)
@ -185,7 +192,8 @@ impl IrUser {
/// Get an event handle that activates when a packet is received. /// Get an event handle that activates when a packet is received.
pub fn get_recv_event(&self) -> crate::Result<Handle> { pub fn get_recv_event(&self) -> crate::Result<Handle> {
let response = self.send_service_request(vec![GET_RECEIVE_EVENT_COMMAND_HEADER], 4)?; let response =
unsafe { self.send_service_request(vec![GET_RECEIVE_EVENT_COMMAND_HEADER], 4) }?;
let recv_event = response[3] as Handle; let recv_event = response[3] as Handle;
Ok(recv_event) Ok(recv_event)
@ -197,6 +205,7 @@ impl IrUser {
/// with the current device input values. /// with the current device input values.
pub fn request_input_polling(&self, period_ms: u8) -> crate::Result<()> { pub fn request_input_polling(&self, period_ms: u8) -> crate::Result<()> {
let ir_request: [u8; 3] = [1, period_ms, (period_ms + 2) << 2]; let ir_request: [u8; 3] = [1, period_ms, (period_ms + 2) << 2];
unsafe {
self.send_service_request( self.send_service_request(
vec![ vec![
SEND_IR_NOP_COMMAND_HEADER, SEND_IR_NOP_COMMAND_HEADER,
@ -206,6 +215,7 @@ impl IrUser {
], ],
2, 2,
)?; )?;
}
Ok(()) Ok(())
} }
@ -213,7 +223,9 @@ impl IrUser {
/// Mark the last `packet_count` packets as processed, so their memory in /// Mark the last `packet_count` packets as processed, so their memory in
/// the receive buffer can be reused. /// the receive buffer can be reused.
pub fn release_received_data(&self, packet_count: u32) -> crate::Result<()> { pub fn release_received_data(&self, packet_count: u32) -> crate::Result<()> {
unsafe {
self.send_service_request(vec![RELEASE_RECEIVED_DATA_COMMAND_HEADER, packet_count], 2)?; self.send_service_request(vec![RELEASE_RECEIVED_DATA_COMMAND_HEADER, packet_count], 2)?;
}
Ok(()) Ok(())
} }
@ -349,37 +361,17 @@ impl IrUser {
} }
/// Internal helper for calling ir:USER service methods. /// Internal helper for calling ir:USER service methods.
fn send_service_request( unsafe fn send_service_request(
&self, &self,
mut request: Vec<u32>, request: Vec<u32>,
expected_response_len: usize, expected_response_len: usize,
) -> crate::Result<Vec<u32>> { ) -> crate::Result<Vec<u32>> {
let mut shared_mem_guard = IR_USER_STATE.lock().unwrap(); let mut shared_mem_guard = IR_USER_STATE.lock().unwrap();
let shared_mem = shared_mem_guard.as_mut().unwrap(); let shared_mem = shared_mem_guard.as_mut().unwrap();
unsafe { shared_mem
// Set up the request .service_handle
let thread_command_buffer = unsafe { ctru_sys::getThreadCommandBuffer() }; .send_service_request(request, expected_response_len)
std::ptr::copy(request.as_ptr(), thread_command_buffer, request.len());
// Send the request
ResultCode(ctru_sys::svcSendSyncRequest(shared_mem.service_handle))?;
// Handle the result returned by the service
let result = unsafe { std::ptr::read(thread_command_buffer.add(1)) };
ResultCode(result as ctru_sys::Result)?;
// Copy back the response
request.clear();
request.resize(expected_response_len, 0);
std::ptr::copy(
thread_command_buffer,
request.as_mut_ptr(),
expected_response_len,
);
}
Ok(request)
} }
} }
@ -392,44 +384,6 @@ fn round_up(value: usize, multiple: usize) -> usize {
} }
} }
struct InitializeIrnopSharedParams {
ir_user_handle: Handle,
shared_memory_len: u32,
recv_packet_buffer_len: u32,
recv_packet_count: u32,
send_packet_buffer_len: u32,
send_packet_count: u32,
bit_rate: u32,
shared_memory_handle: Handle,
}
/// Internal helper for initializing the ir:USER service
unsafe fn initialize_irnop_shared(params: InitializeIrnopSharedParams) -> crate::Result<()> {
// Set up the request
let request = [
INITIALIZE_IRNOP_SHARED_COMMAND_HEADER,
params.shared_memory_len,
params.recv_packet_buffer_len,
params.recv_packet_count,
params.send_packet_buffer_len,
params.send_packet_count,
params.bit_rate,
0,
params.shared_memory_handle,
];
let cmd_buffer_ptr = ctru_sys::getThreadCommandBuffer();
std::ptr::copy_nonoverlapping(request.as_ptr(), cmd_buffer_ptr, request.len());
// Send the request
ResultCode(ctru_sys::svcSendSyncRequest(params.ir_user_handle))?;
// Handle the result returned by the service
let result = std::ptr::read(cmd_buffer_ptr.add(1));
ResultCode(result as ctru_sys::Result)?;
Ok(())
}
/// An enum which represents the different IR devices the 3DS can connect to via /// An enum which represents the different IR devices the 3DS can connect to via
/// the ir:USER service. /// the ir:USER service.
pub enum IrDeviceId { pub enum IrDeviceId {

2
ctru-rs/src/services/mod.rs

@ -24,8 +24,8 @@ pub mod ndsp;
pub mod ps; pub mod ps;
mod reference; mod reference;
pub mod soc; pub mod soc;
pub mod srv;
pub mod sslc; pub mod sslc;
pub mod svc;
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(all(feature = "romfs", romfs_exists))] { if #[cfg(all(feature = "romfs", romfs_exists))] {

37
ctru-rs/src/services/srv.rs

@ -1,37 +0,0 @@
//! Service API
//!
//! 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<()>;
}
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(())
}
}
/// 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)
}

76
ctru-rs/src/services/svc.rs

@ -0,0 +1,76 @@
//! 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() };
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 = unsafe { 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);
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)
}
Loading…
Cancel
Save