Browse Source

Merge pull request #156 from Jhynjhiruu/feature/uds

Implement UDS service with example
master
Meziu 9 months ago committed by GitHub
parent
commit
cd80a338fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      ctru-rs/Cargo.toml
  2. 301
      ctru-rs/examples/local-networking.rs
  3. 1
      ctru-rs/src/lib.rs
  4. 2
      ctru-rs/src/services/ir_user.rs
  5. 1
      ctru-rs/src/services/mod.rs
  6. 1382
      ctru-rs/src/services/uds.rs

1
ctru-rs/Cargo.toml

@ -24,6 +24,7 @@ shim-3ds = { git = "https://github.com/rust3ds/shim-3ds.git" } @@ -24,6 +24,7 @@ shim-3ds = { git = "https://github.com/rust3ds/shim-3ds.git" }
pthread-3ds = { git = "https://github.com/rust3ds/pthread-3ds.git" }
libc = "0.2.121"
bitflags = "2.3.3"
macaddr = "1.0.1"
widestring = "1.0.2"
[build-dependencies]

301
ctru-rs/examples/local-networking.rs

@ -0,0 +1,301 @@ @@ -0,0 +1,301 @@
//! Local networking example.
//!
//! This example showcases local networking using the UDS module.
use ctru::prelude::*;
use ctru::services::uds::*;
fn handle_status_event(uds: &Uds, prev_node_mask: u16) -> ctru::Result<u16> {
println!("Connection status event signalled");
let status = uds.connection_status()?;
println!("Status: {status:#02X?}");
let left = prev_node_mask & (status.node_bitmask() ^ prev_node_mask);
let joined = status.node_bitmask() & (status.node_bitmask() ^ prev_node_mask);
for i in 0..16 {
if left & (1 << i) != 0 {
println!("Node {} disconnected", i + 1);
}
}
for i in 0..16 {
if joined & (1 << i) != 0 {
println!(
"Node {} connected: {:?}",
i + 1,
uds.node_info(NodeID::Node(i + 1))
);
}
}
Ok(status.node_bitmask())
}
fn main() -> Result<(), Error> {
let apt = Apt::new().unwrap();
let mut hid = Hid::new().unwrap();
let gfx = Gfx::new().unwrap();
let console = Console::new(gfx.top_screen.borrow_mut());
println!("Local networking demo");
let mut uds = Uds::new(None).unwrap();
println!("UDS initialised");
enum State {
Initialised,
Scanning,
DrawList,
List,
Connect,
Connected,
Create,
Created,
}
let mut state = State::Initialised;
println!("Press A to start scanning or B to create a new network");
let mut networks = vec![];
let mut selected_network = 0;
let mut mode = ConnectionType::Client;
let mut channel = 0;
let data_channel = 1;
let mut prev_node_mask = 0;
while apt.main_loop() {
gfx.wait_for_vblank();
hid.scan_input();
if hid.keys_down().contains(KeyPad::START) {
break;
}
match state {
State::Initialised => {
if hid.keys_down().contains(KeyPad::A) {
state = State::Scanning;
console.clear();
prev_node_mask = 0;
} else if hid.keys_down().contains(KeyPad::B) {
state = State::Create;
console.clear();
prev_node_mask = 0;
}
}
State::Scanning => {
println!("Scanning...");
let nwks = uds.scan(b"HBW\x10", None, None);
match nwks {
Ok(n) => {
if n.is_empty() {
state = State::Initialised;
console.clear();
println!("Scanned successfully; no networks found");
println!("Press A to start scanning or B to create a new network");
} else {
networks = n;
selected_network = 0;
state = State::DrawList;
}
}
Err(e) => {
state = State::Initialised;
console.clear();
eprintln!("Error while scanning: {e}");
println!("Press A to start scanning or B to create a new network");
}
}
}
State::DrawList => {
console.clear();
println!(
"Scanned successfully; {} network{} found",
networks.len(),
if networks.len() == 1 { "" } else { "s" }
);
println!("D-Pad to select, A to connect as client, R + A to connect as spectator, B to create a new network");
for (index, n) in networks.iter().enumerate() {
println!(
"{} Username: {}",
if index == selected_network { ">" } else { " " },
n.nodes()[0].unwrap().username()
);
}
state = State::List;
}
State::List => {
if hid.keys_down().contains(KeyPad::UP) && selected_network > 0 {
selected_network -= 1;
state = State::DrawList;
} else if hid.keys_down().contains(KeyPad::DOWN)
&& selected_network < networks.len() - 1
{
selected_network += 1;
state = State::DrawList;
} else if hid.keys_down().contains(KeyPad::A) {
state = State::Connect;
mode = if hid.keys_held().contains(KeyPad::R) {
ConnectionType::Spectator
} else {
ConnectionType::Client
};
} else if hid.keys_down().contains(KeyPad::B) {
state = State::Create;
}
}
State::Connect => {
let appdata = uds.network_appdata(&networks[selected_network], None)?;
println!("App data: {:02X?}", appdata);
if let Err(e) = uds.connect_network(
&networks[selected_network],
b"udsdemo passphrase c186093cd2652741\0",
mode,
data_channel,
) {
console.clear();
eprintln!("Error while connecting to network: {e}");
state = State::Initialised;
println!("Press A to start scanning or B to create a new network");
} else {
channel = uds.channel()?;
println!("Connected using channel {}", channel);
let appdata = uds.appdata(None)?;
println!("App data: {:02X?}", appdata);
if uds.wait_status_event(false, false)? {
prev_node_mask = handle_status_event(&uds, prev_node_mask)?;
}
println!("Press A to stop data transfer");
state = State::Connected;
}
}
State::Connected => {
let packet = uds.pull_packet();
match packet {
Ok(p) => {
if let Some((pkt, node)) = p {
println!(
"{:02X}{:02X}{:02X}{:02X} from {:?}",
pkt[0], pkt[1], pkt[2], pkt[3], node
);
}
if uds.wait_status_event(false, false)? {
prev_node_mask = handle_status_event(&uds, prev_node_mask)?;
}
if hid.keys_down().contains(KeyPad::A) {
uds.disconnect_network()?;
state = State::Initialised;
console.clear();
println!("Press A to start scanning or B to create a new network");
} else if !hid.keys_down().is_empty() || !hid.keys_up().is_empty() {
let transfer_data = hid.keys_held().bits();
if mode != ConnectionType::Spectator {
uds.send_packet(
&transfer_data.to_le_bytes(),
NodeID::Broadcast,
data_channel,
SendFlags::Default,
)?;
}
}
}
Err(e) => {
uds.disconnect_network()?;
console.clear();
eprintln!("Error while grabbing packet from network: {e}");
state = State::Initialised;
println!("Press A to start scanning or B to create a new network");
}
}
}
State::Create => {
console.clear();
println!("Creating network...");
match uds.create_network(
b"HBW\x10",
None,
None,
b"udsdemo passphrase c186093cd2652741\0",
data_channel,
) {
Ok(_) => {
let appdata = [0x69u8, 0x8a, 0x05, 0x5c]
.into_iter()
.chain((*b"Test appdata.").into_iter())
.chain(std::iter::repeat(0).take(3))
.collect::<Vec<_>>();
uds.set_appdata(&appdata)?;
println!("Press A to stop data transfer");
state = State::Created;
}
Err(e) => {
console.clear();
eprintln!("Error while creating network: {e}");
state = State::Initialised;
println!("Press A to start scanning or B to create a new network");
}
}
}
State::Created => {
let packet = uds.pull_packet();
match packet {
Ok(p) => {
if let Some((pkt, node)) = p {
println!(
"{:02X}{:02X}{:02X}{:02X} from {:?}",
pkt[0], pkt[1], pkt[2], pkt[3], node
);
}
if uds.wait_status_event(false, false)? {
prev_node_mask = handle_status_event(&uds, prev_node_mask)?;
}
if hid.keys_down().contains(KeyPad::A) {
uds.destroy_network()?;
state = State::Initialised;
console.clear();
println!("Press A to start scanning or B to create a new network");
} else if !hid.keys_down().is_empty() || !hid.keys_up().is_empty() {
let transfer_data = hid.keys_held().bits();
uds.send_packet(
&transfer_data.to_le_bytes(),
NodeID::Broadcast,
data_channel,
SendFlags::Default,
)?;
}
}
Err(e) => {
uds.destroy_network()?;
console.clear();
eprintln!("Error while grabbing packet from network: {e}");
state = State::Initialised;
println!("Press A to start scanning or B to create a new network");
}
}
}
}
}
Ok(())
}

1
ctru-rs/src/lib.rs

@ -22,6 +22,7 @@ @@ -22,6 +22,7 @@
#![feature(custom_test_frameworks)]
#![feature(try_trait_v2)]
#![feature(allocator_api)]
#![feature(new_uninit)]
#![test_runner(test_runner::run_gdb)] // TODO: does this make sense to have configurable?
#![doc(
html_favicon_url = "https://user-images.githubusercontent.com/11131775/225929072-2fa1741c-93ae-4b47-9bdf-af70f3d59910.png"

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

@ -150,7 +150,7 @@ impl IrUser { @@ -150,7 +150,7 @@ impl IrUser {
shared_mem.shared_memory_layout,
);
Ok(())
Ok::<_, Error>(())
})()
.unwrap();
},

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

@ -26,6 +26,7 @@ mod reference; @@ -26,6 +26,7 @@ mod reference;
pub mod soc;
pub mod sslc;
pub mod svc;
pub mod uds;
cfg_if::cfg_if! {
if #[cfg(all(feature = "romfs", romfs_exists))] {

1382
ctru-rs/src/services/uds.rs

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save