Browse Source

Add a rough ir:USER service wrapper and example

Works in the Citra emulator, but not on device.
pull/129/head
AzureMarker 2 years ago committed by Mark Drobnak
parent
commit
87bbc04dfd
No known key found for this signature in database
GPG Key ID: 47A133F3BF9D03D3
  1. 107
      ctru-rs/examples/ir-user.rs
  2. 318
      ctru-rs/src/services/ir_user.rs
  3. 1
      ctru-rs/src/services/mod.rs
  4. 1
      ctru-rs/src/services/reference.rs
  5. 12
      ctru-sys/src/lib.rs

107
ctru-rs/examples/ir-user.rs

@ -0,0 +1,107 @@ @@ -0,0 +1,107 @@
use ctru::prelude::*;
use ctru::services::ir_user::{IrDeviceId, IrUser};
use time::Duration;
use ctru::error::ResultCode;
const PACKET_INFO_SIZE: usize = 8;
const MAX_PACKET_SIZE: usize = 32;
const PACKET_COUNT: usize = 1;
const PACKET_BUFFER_SIZE: usize = PACKET_COUNT * (PACKET_INFO_SIZE + MAX_PACKET_SIZE);
fn main() {
ctru::init();
let apt = Apt::init().unwrap();
let hid = Hid::init().unwrap();
let gfx = Gfx::init().unwrap();
let console = Console::init(gfx.top_screen.borrow_mut());
let ir_user = IrUser::init(
PACKET_BUFFER_SIZE,
PACKET_COUNT,
PACKET_BUFFER_SIZE,
PACKET_COUNT,
)
.expect("Couldn't initialize ir:USER service");
let ir_user_connection_status_event = ir_user
.get_connection_status_event()
.expect("Couldn't get ir:USER connection status event");
ir_user
.require_connection(IrDeviceId::CirclePadPro)
.expect("Couldn't initialize circle pad pro connection");
let ir_user_recv_event = ir_user
.get_recv_event()
.expect("Couldn't get ir:USER recv event");
println!("StatusInfo:\n{:#?}", ir_user.get_status_info());
// Wait for the connection to establish
(|| unsafe {
ResultCode(ctru_sys::svcWaitSynchronization(
ir_user_connection_status_event,
Duration::seconds(10).whole_nanoseconds() as i64,
))?;
println!("Finished waiting on connection status event");
println!("StatusInfo:\n{:#?}", ir_user.get_status_info());
Ok(())
})().expect("Failed to connect to circle pad pro");
ir_user
.start_polling_input(20)
.expect("Couldn't configure circle pad pro polling interval");
while apt.main_loop() {
hid.scan_input();
// Check if we've received a packet from the circle pad pro
let check_ir_packet =
unsafe { ctru_sys::svcWaitSynchronization(ir_user_recv_event, 0) == 0 };
if check_ir_packet {
console.clear();
// Move the cursor back to the top of the screen
print!("\x1b[0;0H");
println!("StatusInfo:\n{:?}", ir_user.get_status_info());
ir_user.process_shared_memory(|ir_mem| {
println!("\nReceiveBufferInfo:");
for byte in &ir_mem[0x10..0x20] {
print!("{byte:02x} ");
}
println!("\nReceiveBuffer:");
let mut counter = 0;
for byte in &ir_mem[0x20..0x20 + PACKET_BUFFER_SIZE] {
print!("{byte:02x} ");
counter += 1;
if counter % 16 == 0 {
println!()
}
}
println!("\nSendBufferInfo:");
for byte in &ir_mem[0x20 + PACKET_BUFFER_SIZE..0x30 + PACKET_BUFFER_SIZE] {
print!("{byte:02x} ");
}
println!("\n(skipping send packet buffer)");
});
// Done handling the packet, release it
ir_user
.release_received_data(1)
.expect("Failed to release ir:USER packet");
println!("\x1b[29;16HPress Start to exit");
}
if hid.keys_held().intersects(KeyPad::KEY_START) {
break;
}
gfx.flush_buffers();
gfx.swap_buffers();
gfx.wait_for_vblank();
}
}

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

@ -0,0 +1,318 @@ @@ -0,0 +1,318 @@
use crate::error::ResultCode;
use crate::services::ServiceReference;
use ctru_sys::{Handle, MEMPERM_READ, MEMPERM_READWRITE};
use std::alloc::Layout;
use std::cmp::max;
use std::ffi::CString;
use std::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut};
use std::sync::Mutex;
static IR_USER_ACTIVE: Mutex<usize> = Mutex::new(0);
static IR_USER_STATE: Mutex<Option<IrUserState>> = Mutex::new(None);
#[non_exhaustive]
pub struct IrUser {
_service_reference: ServiceReference,
}
struct IrUserState {
service_handle: Handle,
shared_memory_handle: Handle,
shared_memory: &'static [u8],
// shared_memory: Box<[u8]>,
}
const INITIALIZE_IRNOP_SHARED_COMMAND_HEADER: u32 = 0x00180182;
const REQUIRE_CONNECTION_COMMAND_HEADER: u32 = 0x00060040;
const GET_CONNECTION_STATUS_EVENT_COMMAND_HEADER: u32 = 0x000C0000;
const GET_RECEIVE_EVENT_COMMAND_HEADER: u32 = 0x000A0000;
const SEND_IR_NOP_COMMAND_HEADER: u32 = 0x000D0042;
const RELEASE_RECEIVED_DATA_COMMAND_HEADER: u32 = 0x00190040;
impl IrUser {
pub fn init(recv_buffer_size: usize, recv_packet_count: usize, send_buffer_size: usize, send_packet_count: usize) -> crate::Result<Self> {
let service_reference = ServiceReference::new(
&IR_USER_ACTIVE,
true,
|| unsafe {
println!("Starting IrUser");
println!("Getting ir:USER service handle");
let mut service_handle = Handle::default();
let service_name = CString::new("ir:USER").unwrap();
ResultCode(ctru_sys::srvGetServiceHandle(
&mut service_handle,
service_name.as_ptr(),
))?;
println!("Getting shared memory pointer");
let info_sections_size = 0x30;
// let packet_count = 3;
// let max_packet_size = 32;
// let packet_info_size = 8;
// let recv_buffer_len = recv_packet_count * (packet_info_size + max_packet_size);
// let send_buffer_len = send_packet_count * (packet_info_size + max_packet_size);
let minimum_shared_memory_len = info_sections_size + recv_buffer_size + send_buffer_size;
let shared_memory_len = if minimum_shared_memory_len % 0x1000 != 0 {
(minimum_shared_memory_len / 0x1000) * 0x1000 + 0x1000
} else {
(minimum_shared_memory_len / 0x1000) * 0x1000
};
assert_eq!(shared_memory_len % 0x1000, 0);
// let shared_memory_len = info_sections_size + recv_buffer_size + send_buffer_size;
println!("Shared memory size: {shared_memory_len:#x}");
// let shared_memory_len = info_sections_size + packet_count * (packet_info_size + max_packet_size);
// let shared_memory = Box::new([0; 0x1000]);
// let shared_memory_ptr = ctru_sys::mappableAlloc(shared_memory_len) as *const u8;
let shared_memory_layout =
Layout::from_size_align(shared_memory_len, 0x1000).unwrap();
let shared_memory_ptr = std::alloc::alloc_zeroed(shared_memory_layout);
println!(
"Using shared memory address: {:#08x}",
shared_memory_ptr as usize
);
println!("Marking memory block as shared memory");
let mut shared_memory_handle = Handle::default();
ResultCode(ctru_sys::svcCreateMemoryBlock(
&mut shared_memory_handle,
shared_memory_ptr as u32,
shared_memory_len as u32,
MEMPERM_READ,
MEMPERM_READWRITE,
))?;
let shared_memory = &*slice_from_raw_parts(shared_memory_ptr, shared_memory_len);
println!("Initializing ir:USER service");
initialize_irnop_shared(InitializeIrnopSharedParams {
ir_user_handle: service_handle,
shared_memory_len: shared_memory_len as u32,
recv_packet_buffer_len: recv_buffer_size as u32,
recv_packet_count: recv_packet_count as u32,
send_packet_buffer_len: send_buffer_size as u32,
send_packet_count: send_packet_count as u32,
bit_rate: 4,
shared_memory_handle,
})?;
println!("Setting IrUserState in static");
let user_state = IrUserState {
service_handle,
shared_memory_handle,
shared_memory,
};
*IR_USER_STATE.lock().unwrap() = Some(user_state);
println!("Done starting IrUser");
Ok(())
},
|| {
println!("Close called for IrUser");
let mut shared_mem_guard = IR_USER_STATE.lock().unwrap();
let shared_mem = shared_mem_guard
.take()
.expect("ir:USER shared memory mutex shouldn't be empty");
// (|| unsafe {
// // println!("Unmapping the ir:USER shared memory");
// // ResultCode(ctru_sys::svcUnmapMemoryBlock(
// // shared_mem.shared_memory_handle,
// // shared_mem.shared_memory.as_ptr() as u32,
// // ))?;
//
// println!("Closing memory and service handles");
// // ResultCode(ctru_sys::svcCloseHandle(shared_mem.shared_memory_handle))?;
// ResultCode(ctru_sys::svcCloseHandle(shared_mem.service_handle))?;
//
// // println!("Freeing shared memory");
// // ctru_sys::mappableFree(shared_mem.shared_memory.as_ptr() as *mut libc::c_void);
//
// Ok(())
// })()
// .unwrap();
println!("Done closing IrUser");
},
)?;
Ok(IrUser {
_service_reference: service_reference,
})
}
pub fn require_connection(&self, device_id: IrDeviceId) -> crate::Result<()> {
println!("RequireConnection called");
self.send_service_request(
vec![REQUIRE_CONNECTION_COMMAND_HEADER, device_id.get_id()],
2,
)?;
println!("RequireConnection succeeded");
Ok(())
}
pub fn get_connection_status_event(&self) -> crate::Result<Handle> {
println!("GetConnectionStatusEvent called");
let response = self.send_service_request(vec![GET_CONNECTION_STATUS_EVENT_COMMAND_HEADER], 4)?;
let status_event = response[3] as Handle;
println!("GetConnectionStatusEvent succeeded");
Ok(status_event)
}
pub fn get_recv_event(&self) -> crate::Result<Handle> {
println!("GetReceiveEvent called");
let response = self.send_service_request(vec![GET_RECEIVE_EVENT_COMMAND_HEADER], 4)?;
let recv_event = response[3] as Handle;
println!("GetReceiveEvent succeeded");
Ok(recv_event)
}
pub fn start_polling_input(&self, period_ms: u8) -> crate::Result<()> {
println!("SendIrnop (start_polling_input) called");
let ir_request: [u8; 3] = [1, period_ms, 0];
self.send_service_request(
vec![
SEND_IR_NOP_COMMAND_HEADER,
ir_request.len() as u32,
2 + (ir_request.len() << 14) as u32,
ir_request.as_ptr() as u32,
],
2,
)?;
println!("SendIrnop (start_polling_input) succeeded");
Ok(())
}
pub fn release_received_data(&self, packet_count: u32) -> crate::Result<()> {
println!("ReleaseReceivedData called");
self.send_service_request(
vec![RELEASE_RECEIVED_DATA_COMMAND_HEADER, packet_count],
2
)?;
println!("ReleaseReceivedData succeeded");
Ok(())
}
pub fn process_shared_memory(&self, process_fn: impl FnOnce(&[u8])) {
println!("Process shared memory started");
let shared_mem_guard = IR_USER_STATE.lock().unwrap();
let shared_mem = shared_mem_guard.as_ref().unwrap();
process_fn(shared_mem.shared_memory);
println!("Process shared memory succeeded");
}
pub fn get_status_info(&self) -> IrUserStatusInfo {
let shared_mem_guard = IR_USER_STATE.lock().unwrap();
let shared_mem = shared_mem_guard.as_ref().unwrap().shared_memory;
IrUserStatusInfo {
recv_err_result: i32::from_ne_bytes([shared_mem[0], shared_mem[1], shared_mem[2], shared_mem[3]]),
send_err_result: i32::from_ne_bytes([shared_mem[4], shared_mem[5], shared_mem[6], shared_mem[7]]),
connection_status: shared_mem[8],
trying_to_connect_status: shared_mem[9],
connection_role: shared_mem[10],
machine_id: shared_mem[11],
unknown_field_1: shared_mem[12],
network_id: shared_mem[13],
unknown_field_2: shared_mem[14],
unknown_field_3: shared_mem[15],
}
}
fn send_service_request(
&self,
mut request: Vec<u32>,
expected_response_len: usize,
) -> crate::Result<Vec<u32>> {
let mut shared_mem_guard = IR_USER_STATE.lock().unwrap();
let shared_mem = shared_mem_guard.as_mut().unwrap();
let cmd_buffer = unsafe {
&mut *(slice_from_raw_parts_mut(
ctru_sys::getThreadCommandBuffer(),
max(request.len(), expected_response_len),
))
};
cmd_buffer[0..request.len()].copy_from_slice(&request);
// Send the request
unsafe {
ResultCode(ctru_sys::svcSendSyncRequest(shared_mem.service_handle))?;
}
// Handle the result returned by the service
ResultCode(cmd_buffer[1] as ctru_sys::Result)?;
// Copy back the response
request.clear();
request.extend_from_slice(&cmd_buffer[0..expected_response_len]);
Ok(request)
}
}
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,
}
unsafe fn initialize_irnop_shared(params: InitializeIrnopSharedParams) -> crate::Result<()> {
let cmd_buffer = &mut *(slice_from_raw_parts_mut(ctru_sys::getThreadCommandBuffer(), 9));
cmd_buffer[0] = INITIALIZE_IRNOP_SHARED_COMMAND_HEADER;
cmd_buffer[1] = params.shared_memory_len;
cmd_buffer[2] = params.recv_packet_buffer_len;
cmd_buffer[3] = params.recv_packet_count;
cmd_buffer[4] = params.send_packet_buffer_len;
cmd_buffer[5] = params.send_packet_count;
cmd_buffer[6] = params.bit_rate;
cmd_buffer[7] = 0;
cmd_buffer[8] = params.shared_memory_handle;
// Send the request
ResultCode(ctru_sys::svcSendSyncRequest(params.ir_user_handle))?;
// Handle the result returned by the service
ResultCode(cmd_buffer[1] as ctru_sys::Result)?;
Ok(())
}
pub enum IrDeviceId {
CirclePadPro,
// Pretty sure no other IDs are recognized, but just in case
Custom(u32),
}
impl IrDeviceId {
pub fn get_id(&self) -> u32 {
match *self {
IrDeviceId::CirclePadPro => 1,
IrDeviceId::Custom(id) => id,
}
}
}
#[derive(Debug)]
pub struct IrUserStatusInfo {
recv_err_result: ctru_sys::Result,
send_err_result: ctru_sys::Result,
connection_status: u8,
trying_to_connect_status: u8,
connection_role: u8,
machine_id: u8,
unknown_field_1: u8,
network_id: u8,
unknown_field_2: u8,
unknown_field_3: u8,
}

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

@ -4,6 +4,7 @@ pub mod cfgu; @@ -4,6 +4,7 @@ pub mod cfgu;
pub mod fs;
pub mod gspgpu;
pub mod hid;
pub mod ir_user;
pub mod ps;
mod reference;
pub mod soc;

1
ctru-rs/src/services/reference.rs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
use crate::Error;
use std::sync::Mutex;
pub(crate) struct ServiceReference {
counter: &'static Mutex<usize>,
close: Box<dyn Fn() + Send + Sync>,

12
ctru-sys/src/lib.rs

@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
#![allow(non_snake_case)]
#![allow(clippy::all)]
use core::arch::asm;
pub mod result;
mod bindings;
@ -17,3 +19,13 @@ pub use result::*; @@ -17,3 +19,13 @@ pub use result::*;
pub unsafe fn errno() -> s32 {
(*__getreent())._errno
}
pub unsafe fn getThreadLocalStorage() -> *mut libc::c_void {
let return_value: *mut libc::c_void;
asm!("mrc p15, 0, {}, c13, c0, 3", out(reg) return_value);
return_value
}
pub unsafe fn getThreadCommandBuffer() -> *mut u32 {
(getThreadLocalStorage() as *mut u8).add(0x80) as *mut u32
}

Loading…
Cancel
Save