|
|
@ -2,6 +2,7 @@ |
|
|
|
|
|
|
|
|
|
|
|
use ctru::prelude::*; |
|
|
|
use ctru::prelude::*; |
|
|
|
use ctru::services::ir_user::{CirclePadProInputResponse, IrDeviceId, IrUser}; |
|
|
|
use ctru::services::ir_user::{CirclePadProInputResponse, IrDeviceId, IrUser}; |
|
|
|
|
|
|
|
use ctru_sys::Handle; |
|
|
|
use std::io::Write; |
|
|
|
use std::io::Write; |
|
|
|
use std::time::Duration; |
|
|
|
use std::time::Duration; |
|
|
|
|
|
|
|
|
|
|
@ -20,6 +21,59 @@ fn main() { |
|
|
|
let top_console = Console::init(gfx.top_screen.borrow_mut()); |
|
|
|
let top_console = Console::init(gfx.top_screen.borrow_mut()); |
|
|
|
let bottom_console = Console::init(gfx.bottom_screen.borrow_mut()); |
|
|
|
let bottom_console = Console::init(gfx.bottom_screen.borrow_mut()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let demo = CirclePadProDemo::new(top_console, bottom_console); |
|
|
|
|
|
|
|
demo.print_status_info(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
println!("Press A to connect to the CPP"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut is_connected = false; |
|
|
|
|
|
|
|
while apt.main_loop() { |
|
|
|
|
|
|
|
hid.scan_input(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check if we need to exit
|
|
|
|
|
|
|
|
if hid.keys_held().contains(KeyPad::KEY_START) { |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check if we've received a packet from the circle pad pro
|
|
|
|
|
|
|
|
let packet_received = |
|
|
|
|
|
|
|
IrUser::wait_for_event(demo.receive_packet_event, Duration::ZERO).is_ok(); |
|
|
|
|
|
|
|
if packet_received { |
|
|
|
|
|
|
|
demo.handle_packets(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check if we should start the connection
|
|
|
|
|
|
|
|
if hid.keys_down().contains(KeyPad::KEY_A) && !is_connected { |
|
|
|
|
|
|
|
println!("Attempting to connect to the CPP"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
match demo.connect_to_cpp(&hid) { |
|
|
|
|
|
|
|
ConnectionResult::Connected => is_connected = true, |
|
|
|
|
|
|
|
ConnectionResult::Canceled => break, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gfx.flush_buffers(); |
|
|
|
|
|
|
|
gfx.swap_buffers(); |
|
|
|
|
|
|
|
gfx.wait_for_vblank(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct CirclePadProDemo<'screen> { |
|
|
|
|
|
|
|
top_console: Console<'screen>, |
|
|
|
|
|
|
|
bottom_console: Console<'screen>, |
|
|
|
|
|
|
|
ir_user: IrUser, |
|
|
|
|
|
|
|
connection_status_event: Handle, |
|
|
|
|
|
|
|
receive_packet_event: Handle, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum ConnectionResult { |
|
|
|
|
|
|
|
Connected, |
|
|
|
|
|
|
|
Canceled, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl<'screen> CirclePadProDemo<'screen> { |
|
|
|
|
|
|
|
fn new(top_console: Console<'screen>, bottom_console: Console<'screen>) -> Self { |
|
|
|
|
|
|
|
bottom_console.select(); |
|
|
|
println!("Welcome to the ir:USER / Circle Pad Pro Demo"); |
|
|
|
println!("Welcome to the ir:USER / Circle Pad Pro Demo"); |
|
|
|
|
|
|
|
|
|
|
|
println!("Starting up ir:USER service"); |
|
|
|
println!("Starting up ir:USER service"); |
|
|
@ -30,78 +84,68 @@ fn main() { |
|
|
|
PACKET_COUNT, |
|
|
|
PACKET_COUNT, |
|
|
|
) |
|
|
|
) |
|
|
|
.expect("Couldn't initialize ir:USER service"); |
|
|
|
.expect("Couldn't initialize ir:USER service"); |
|
|
|
println!("ir:USER service initialized\nPress A to connect to the CPP"); |
|
|
|
println!("ir:USER service initialized"); |
|
|
|
|
|
|
|
|
|
|
|
let print_status_info = || { |
|
|
|
|
|
|
|
top_console.select(); |
|
|
|
|
|
|
|
top_console.clear(); |
|
|
|
|
|
|
|
println!("{:#x?}", ir_user.get_status_info()); |
|
|
|
|
|
|
|
bottom_console.select(); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Get event handles
|
|
|
|
// Get event handles
|
|
|
|
let conn_status_event = ir_user |
|
|
|
let connection_status_event = ir_user |
|
|
|
.get_connection_status_event() |
|
|
|
.get_connection_status_event() |
|
|
|
.expect("Couldn't get ir:USER connection status event"); |
|
|
|
.expect("Couldn't get ir:USER connection status event"); |
|
|
|
let recv_event = ir_user |
|
|
|
let receive_packet_event = ir_user |
|
|
|
.get_recv_event() |
|
|
|
.get_recv_event() |
|
|
|
.expect("Couldn't get ir:USER recv event"); |
|
|
|
.expect("Couldn't get ir:USER recv event"); |
|
|
|
print_status_info(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut is_connected = false; |
|
|
|
|
|
|
|
'main_loop: while apt.main_loop() { |
|
|
|
|
|
|
|
hid.scan_input(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check if we need to exit
|
|
|
|
Self { |
|
|
|
if hid.keys_held().contains(KeyPad::KEY_START) { |
|
|
|
top_console, |
|
|
|
break; |
|
|
|
bottom_console, |
|
|
|
|
|
|
|
ir_user, |
|
|
|
|
|
|
|
connection_status_event, |
|
|
|
|
|
|
|
receive_packet_event, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check if we've received a packet from the circle pad pro
|
|
|
|
|
|
|
|
let packet_received = IrUser::wait_for_event(recv_event, Duration::ZERO).is_ok(); |
|
|
|
|
|
|
|
if packet_received { |
|
|
|
|
|
|
|
handle_packet(&ir_user, &top_console, &bottom_console); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Check if we should start the connection
|
|
|
|
fn print_status_info(&self) { |
|
|
|
if hid.keys_down().contains(KeyPad::KEY_A) && !is_connected { |
|
|
|
self.top_console.select(); |
|
|
|
println!("Attempting to connect to the CPP"); |
|
|
|
self.top_console.clear(); |
|
|
|
|
|
|
|
println!("{:#x?}", self.ir_user.get_status_info()); |
|
|
|
|
|
|
|
self.bottom_console.select(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn connect_to_cpp(&self, hid: &Hid) -> ConnectionResult { |
|
|
|
// Connection loop
|
|
|
|
// Connection loop
|
|
|
|
loop { |
|
|
|
loop { |
|
|
|
hid.scan_input(); |
|
|
|
hid.scan_input(); |
|
|
|
if hid.keys_held().contains(KeyPad::KEY_START) { |
|
|
|
if hid.keys_held().contains(KeyPad::KEY_START) { |
|
|
|
break 'main_loop; |
|
|
|
return ConnectionResult::Canceled; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Start the connection process
|
|
|
|
// Start the connection process
|
|
|
|
ir_user |
|
|
|
self.ir_user |
|
|
|
.require_connection(IrDeviceId::CirclePadPro) |
|
|
|
.require_connection(IrDeviceId::CirclePadPro) |
|
|
|
.expect("Couldn't initialize circle pad pro connection"); |
|
|
|
.expect("Couldn't initialize circle pad pro connection"); |
|
|
|
|
|
|
|
|
|
|
|
// Wait for the connection to establish
|
|
|
|
// Wait for the connection to establish
|
|
|
|
if let Err(e) = |
|
|
|
if let Err(e) = |
|
|
|
IrUser::wait_for_event(conn_status_event, Duration::from_millis(100)) |
|
|
|
IrUser::wait_for_event(self.connection_status_event, Duration::from_millis(100)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if !e.is_timeout() { |
|
|
|
if !e.is_timeout() { |
|
|
|
panic!("Couldn't initialize circle pad pro connection: {e}"); |
|
|
|
panic!("Couldn't initialize circle pad pro connection: {e}"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
print_status_info(); |
|
|
|
self.print_status_info(); |
|
|
|
if ir_user.get_status_info().connection_status == 2 { |
|
|
|
if self.ir_user.get_status_info().connection_status == 2 { |
|
|
|
println!("Connected!"); |
|
|
|
println!("Connected!"); |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// If not connected (ex. timeout), disconnect so we can retry
|
|
|
|
// If not connected (ex. timeout), disconnect so we can retry
|
|
|
|
ir_user |
|
|
|
self.ir_user |
|
|
|
.disconnect() |
|
|
|
.disconnect() |
|
|
|
.expect("Failed to disconnect circle pad pro connection"); |
|
|
|
.expect("Failed to disconnect circle pad pro connection"); |
|
|
|
|
|
|
|
|
|
|
|
// Wait for the disconnect to go through
|
|
|
|
// Wait for the disconnect to go through
|
|
|
|
if let Err(e) = |
|
|
|
if let Err(e) = |
|
|
|
IrUser::wait_for_event(conn_status_event, Duration::from_millis(100)) |
|
|
|
IrUser::wait_for_event(self.connection_status_event, Duration::from_millis(100)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if !e.is_timeout() { |
|
|
|
if !e.is_timeout() { |
|
|
|
panic!("Couldn't initialize circle pad pro connection: {e}"); |
|
|
|
panic!("Couldn't initialize circle pad pro connection: {e}"); |
|
|
@ -113,45 +157,46 @@ fn main() { |
|
|
|
loop { |
|
|
|
loop { |
|
|
|
hid.scan_input(); |
|
|
|
hid.scan_input(); |
|
|
|
if hid.keys_held().contains(KeyPad::KEY_START) { |
|
|
|
if hid.keys_held().contains(KeyPad::KEY_START) { |
|
|
|
break 'main_loop; |
|
|
|
return ConnectionResult::Canceled; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Send a request for input to the CPP
|
|
|
|
// Send a request for input to the CPP
|
|
|
|
if let Err(e) = ir_user.request_input_polling(CPP_CONNECTION_POLLING_PERIOD_MS) { |
|
|
|
if let Err(e) = self |
|
|
|
|
|
|
|
.ir_user |
|
|
|
|
|
|
|
.request_input_polling(CPP_CONNECTION_POLLING_PERIOD_MS) |
|
|
|
|
|
|
|
{ |
|
|
|
println!("Error: {e:?}"); |
|
|
|
println!("Error: {e:?}"); |
|
|
|
} |
|
|
|
} |
|
|
|
print_status_info(); |
|
|
|
self.print_status_info(); |
|
|
|
|
|
|
|
|
|
|
|
// Wait for the response
|
|
|
|
// Wait for the response
|
|
|
|
let recv_event_result = |
|
|
|
let recv_event_result = |
|
|
|
IrUser::wait_for_event(recv_event, Duration::from_millis(100)); |
|
|
|
IrUser::wait_for_event(self.receive_packet_event, Duration::from_millis(100)); |
|
|
|
print_status_info(); |
|
|
|
self.print_status_info(); |
|
|
|
|
|
|
|
|
|
|
|
if recv_event_result.is_ok() { |
|
|
|
if recv_event_result.is_ok() { |
|
|
|
println!("Got first packet from CPP"); |
|
|
|
println!("Got first packet from CPP"); |
|
|
|
handle_packet(&ir_user, &top_console, &bottom_console); |
|
|
|
self.handle_packets(); |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// We didn't get a response in time, so loop and retry
|
|
|
|
// We didn't get a response in time, so loop and retry
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
is_connected = true; |
|
|
|
ConnectionResult::Connected |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
gfx.flush_buffers(); |
|
|
|
fn handle_packets(&self) { |
|
|
|
gfx.swap_buffers(); |
|
|
|
let packets = self.ir_user.get_packets(); |
|
|
|
gfx.wait_for_vblank(); |
|
|
|
let packet_count = packets.len(); |
|
|
|
} |
|
|
|
let Some(last_packet) = packets.last() else { return }; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn handle_packet(ir_user: &IrUser, top_console: &Console, bottom_console: &Console) { |
|
|
|
|
|
|
|
// Use a buffer to avoid flickering the screen (write all output at once)
|
|
|
|
// Use a buffer to avoid flickering the screen (write all output at once)
|
|
|
|
let mut output_buffer = Vec::with_capacity(0x1000); |
|
|
|
let mut output_buffer = Vec::with_capacity(0x1000); |
|
|
|
|
|
|
|
|
|
|
|
writeln!(&mut output_buffer, "{:x?}", ir_user.get_status_info()).unwrap(); |
|
|
|
writeln!(&mut output_buffer, "{:x?}", self.ir_user.get_status_info()).unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
ir_user.process_shared_memory(|ir_mem| { |
|
|
|
self.ir_user.process_shared_memory(|ir_mem| { |
|
|
|
writeln!(&mut output_buffer, "\nReceiveBufferInfo:").unwrap(); |
|
|
|
writeln!(&mut output_buffer, "\nReceiveBufferInfo:").unwrap(); |
|
|
|
write_buffer_as_hex(&ir_mem[0x10..0x20], &mut output_buffer); |
|
|
|
write_buffer_as_hex(&ir_mem[0x10..0x20], &mut output_buffer); |
|
|
|
|
|
|
|
|
|
|
@ -160,10 +205,7 @@ fn handle_packet(ir_user: &IrUser, top_console: &Console, bottom_console: &Conso |
|
|
|
writeln!(&mut output_buffer).unwrap(); |
|
|
|
writeln!(&mut output_buffer).unwrap(); |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
let packets = ir_user.get_packets(); |
|
|
|
|
|
|
|
let packet_count = packets.len(); |
|
|
|
|
|
|
|
writeln!(&mut output_buffer, "\nPacket count: {packet_count}").unwrap(); |
|
|
|
writeln!(&mut output_buffer, "\nPacket count: {packet_count}").unwrap(); |
|
|
|
let last_packet = packets.last().unwrap(); |
|
|
|
|
|
|
|
writeln!(&mut output_buffer, "{last_packet:02x?}").unwrap(); |
|
|
|
writeln!(&mut output_buffer, "{last_packet:02x?}").unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
let cpp_response = CirclePadProInputResponse::try_from(last_packet) |
|
|
|
let cpp_response = CirclePadProInputResponse::try_from(last_packet) |
|
|
@ -171,21 +213,22 @@ fn handle_packet(ir_user: &IrUser, top_console: &Console, bottom_console: &Conso |
|
|
|
writeln!(&mut output_buffer, "\n{cpp_response:#02x?}").unwrap(); |
|
|
|
writeln!(&mut output_buffer, "\n{cpp_response:#02x?}").unwrap(); |
|
|
|
|
|
|
|
|
|
|
|
// Write output to top screen
|
|
|
|
// Write output to top screen
|
|
|
|
top_console.select(); |
|
|
|
self.top_console.select(); |
|
|
|
top_console.clear(); |
|
|
|
self.top_console.clear(); |
|
|
|
std::io::stdout().write_all(&output_buffer).unwrap(); |
|
|
|
std::io::stdout().write_all(&output_buffer).unwrap(); |
|
|
|
bottom_console.select(); |
|
|
|
self.bottom_console.select(); |
|
|
|
|
|
|
|
|
|
|
|
// Done handling the packet, release it
|
|
|
|
// Done handling the packets, release them
|
|
|
|
ir_user |
|
|
|
self.ir_user |
|
|
|
.release_received_data(packet_count as u32) |
|
|
|
.release_received_data(packet_count as u32) |
|
|
|
.expect("Failed to release ir:USER packet"); |
|
|
|
.expect("Failed to release ir:USER packet"); |
|
|
|
|
|
|
|
|
|
|
|
// Remind the CPP that we're still listening
|
|
|
|
// Remind the CPP that we're still listening
|
|
|
|
if let Err(e) = ir_user.request_input_polling(CPP_POLLING_PERIOD_MS) { |
|
|
|
if let Err(e) = self.ir_user.request_input_polling(CPP_POLLING_PERIOD_MS) { |
|
|
|
println!("Error: {e:?}"); |
|
|
|
println!("Error: {e:?}"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn write_buffer_as_hex(buffer: &[u8], output: &mut Vec<u8>) { |
|
|
|
fn write_buffer_as_hex(buffer: &[u8], output: &mut Vec<u8>) { |
|
|
|
let mut counter = 0; |
|
|
|
let mut counter = 0; |
|
|
|