AzureMarker
2 years ago
committed by
Mark Drobnak
5 changed files with 439 additions and 0 deletions
@ -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(); |
||||||
|
} |
||||||
|
} |
@ -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, |
||||||
|
} |
Loading…
Reference in new issue