Skip to content

Commit cd80a33

Browse files
authored
Merge pull request #156 from Jhynjhiruu/feature/uds
Implement UDS service with example
2 parents d308205 + fe85aa2 commit cd80a33

File tree

6 files changed

+1687
-1
lines changed

6 files changed

+1687
-1
lines changed

ctru-rs/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ shim-3ds = { git = "https://github.com/rust3ds/shim-3ds.git" }
2424
pthread-3ds = { git = "https://github.com/rust3ds/pthread-3ds.git" }
2525
libc = "0.2.121"
2626
bitflags = "2.3.3"
27+
macaddr = "1.0.1"
2728
widestring = "1.0.2"
2829

2930
[build-dependencies]

ctru-rs/examples/local-networking.rs

+301
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
//! Local networking example.
2+
//!
3+
//! This example showcases local networking using the UDS module.
4+
5+
use ctru::prelude::*;
6+
use ctru::services::uds::*;
7+
8+
fn handle_status_event(uds: &Uds, prev_node_mask: u16) -> ctru::Result<u16> {
9+
println!("Connection status event signalled");
10+
let status = uds.connection_status()?;
11+
println!("Status: {status:#02X?}");
12+
let left = prev_node_mask & (status.node_bitmask() ^ prev_node_mask);
13+
let joined = status.node_bitmask() & (status.node_bitmask() ^ prev_node_mask);
14+
for i in 0..16 {
15+
if left & (1 << i) != 0 {
16+
println!("Node {} disconnected", i + 1);
17+
}
18+
}
19+
for i in 0..16 {
20+
if joined & (1 << i) != 0 {
21+
println!(
22+
"Node {} connected: {:?}",
23+
i + 1,
24+
uds.node_info(NodeID::Node(i + 1))
25+
);
26+
}
27+
}
28+
Ok(status.node_bitmask())
29+
}
30+
31+
fn main() -> Result<(), Error> {
32+
let apt = Apt::new().unwrap();
33+
let mut hid = Hid::new().unwrap();
34+
let gfx = Gfx::new().unwrap();
35+
let console = Console::new(gfx.top_screen.borrow_mut());
36+
37+
println!("Local networking demo");
38+
39+
let mut uds = Uds::new(None).unwrap();
40+
41+
println!("UDS initialised");
42+
43+
enum State {
44+
Initialised,
45+
Scanning,
46+
DrawList,
47+
List,
48+
Connect,
49+
Connected,
50+
Create,
51+
Created,
52+
}
53+
54+
let mut state = State::Initialised;
55+
56+
println!("Press A to start scanning or B to create a new network");
57+
58+
let mut networks = vec![];
59+
let mut selected_network = 0;
60+
61+
let mut mode = ConnectionType::Client;
62+
63+
let mut channel = 0;
64+
let data_channel = 1;
65+
66+
let mut prev_node_mask = 0;
67+
68+
while apt.main_loop() {
69+
gfx.wait_for_vblank();
70+
71+
hid.scan_input();
72+
if hid.keys_down().contains(KeyPad::START) {
73+
break;
74+
}
75+
76+
match state {
77+
State::Initialised => {
78+
if hid.keys_down().contains(KeyPad::A) {
79+
state = State::Scanning;
80+
console.clear();
81+
prev_node_mask = 0;
82+
} else if hid.keys_down().contains(KeyPad::B) {
83+
state = State::Create;
84+
console.clear();
85+
prev_node_mask = 0;
86+
}
87+
}
88+
State::Scanning => {
89+
println!("Scanning...");
90+
91+
let nwks = uds.scan(b"HBW\x10", None, None);
92+
93+
match nwks {
94+
Ok(n) => {
95+
if n.is_empty() {
96+
state = State::Initialised;
97+
console.clear();
98+
println!("Scanned successfully; no networks found");
99+
println!("Press A to start scanning or B to create a new network");
100+
} else {
101+
networks = n;
102+
selected_network = 0;
103+
state = State::DrawList;
104+
}
105+
}
106+
Err(e) => {
107+
state = State::Initialised;
108+
console.clear();
109+
eprintln!("Error while scanning: {e}");
110+
println!("Press A to start scanning or B to create a new network");
111+
}
112+
}
113+
}
114+
State::DrawList => {
115+
console.clear();
116+
117+
println!(
118+
"Scanned successfully; {} network{} found",
119+
networks.len(),
120+
if networks.len() == 1 { "" } else { "s" }
121+
);
122+
123+
println!("D-Pad to select, A to connect as client, R + A to connect as spectator, B to create a new network");
124+
125+
for (index, n) in networks.iter().enumerate() {
126+
println!(
127+
"{} Username: {}",
128+
if index == selected_network { ">" } else { " " },
129+
n.nodes()[0].unwrap().username()
130+
);
131+
}
132+
133+
state = State::List;
134+
}
135+
State::List => {
136+
if hid.keys_down().contains(KeyPad::UP) && selected_network > 0 {
137+
selected_network -= 1;
138+
state = State::DrawList;
139+
} else if hid.keys_down().contains(KeyPad::DOWN)
140+
&& selected_network < networks.len() - 1
141+
{
142+
selected_network += 1;
143+
state = State::DrawList;
144+
} else if hid.keys_down().contains(KeyPad::A) {
145+
state = State::Connect;
146+
mode = if hid.keys_held().contains(KeyPad::R) {
147+
ConnectionType::Spectator
148+
} else {
149+
ConnectionType::Client
150+
};
151+
} else if hid.keys_down().contains(KeyPad::B) {
152+
state = State::Create;
153+
}
154+
}
155+
State::Connect => {
156+
let appdata = uds.network_appdata(&networks[selected_network], None)?;
157+
println!("App data: {:02X?}", appdata);
158+
159+
if let Err(e) = uds.connect_network(
160+
&networks[selected_network],
161+
b"udsdemo passphrase c186093cd2652741\0",
162+
mode,
163+
data_channel,
164+
) {
165+
console.clear();
166+
eprintln!("Error while connecting to network: {e}");
167+
state = State::Initialised;
168+
println!("Press A to start scanning or B to create a new network");
169+
} else {
170+
channel = uds.channel()?;
171+
println!("Connected using channel {}", channel);
172+
173+
let appdata = uds.appdata(None)?;
174+
println!("App data: {:02X?}", appdata);
175+
176+
if uds.wait_status_event(false, false)? {
177+
prev_node_mask = handle_status_event(&uds, prev_node_mask)?;
178+
}
179+
180+
println!("Press A to stop data transfer");
181+
state = State::Connected;
182+
}
183+
}
184+
State::Connected => {
185+
let packet = uds.pull_packet();
186+
187+
match packet {
188+
Ok(p) => {
189+
if let Some((pkt, node)) = p {
190+
println!(
191+
"{:02X}{:02X}{:02X}{:02X} from {:?}",
192+
pkt[0], pkt[1], pkt[2], pkt[3], node
193+
);
194+
}
195+
196+
if uds.wait_status_event(false, false)? {
197+
prev_node_mask = handle_status_event(&uds, prev_node_mask)?;
198+
}
199+
200+
if hid.keys_down().contains(KeyPad::A) {
201+
uds.disconnect_network()?;
202+
state = State::Initialised;
203+
console.clear();
204+
println!("Press A to start scanning or B to create a new network");
205+
} else if !hid.keys_down().is_empty() || !hid.keys_up().is_empty() {
206+
let transfer_data = hid.keys_held().bits();
207+
if mode != ConnectionType::Spectator {
208+
uds.send_packet(
209+
&transfer_data.to_le_bytes(),
210+
NodeID::Broadcast,
211+
data_channel,
212+
SendFlags::Default,
213+
)?;
214+
}
215+
}
216+
}
217+
Err(e) => {
218+
uds.disconnect_network()?;
219+
console.clear();
220+
eprintln!("Error while grabbing packet from network: {e}");
221+
state = State::Initialised;
222+
println!("Press A to start scanning or B to create a new network");
223+
}
224+
}
225+
}
226+
State::Create => {
227+
console.clear();
228+
println!("Creating network...");
229+
230+
match uds.create_network(
231+
b"HBW\x10",
232+
None,
233+
None,
234+
b"udsdemo passphrase c186093cd2652741\0",
235+
data_channel,
236+
) {
237+
Ok(_) => {
238+
let appdata = [0x69u8, 0x8a, 0x05, 0x5c]
239+
.into_iter()
240+
.chain((*b"Test appdata.").into_iter())
241+
.chain(std::iter::repeat(0).take(3))
242+
.collect::<Vec<_>>();
243+
244+
uds.set_appdata(&appdata)?;
245+
246+
println!("Press A to stop data transfer");
247+
state = State::Created;
248+
}
249+
Err(e) => {
250+
console.clear();
251+
eprintln!("Error while creating network: {e}");
252+
state = State::Initialised;
253+
println!("Press A to start scanning or B to create a new network");
254+
}
255+
}
256+
}
257+
State::Created => {
258+
let packet = uds.pull_packet();
259+
260+
match packet {
261+
Ok(p) => {
262+
if let Some((pkt, node)) = p {
263+
println!(
264+
"{:02X}{:02X}{:02X}{:02X} from {:?}",
265+
pkt[0], pkt[1], pkt[2], pkt[3], node
266+
);
267+
}
268+
269+
if uds.wait_status_event(false, false)? {
270+
prev_node_mask = handle_status_event(&uds, prev_node_mask)?;
271+
}
272+
273+
if hid.keys_down().contains(KeyPad::A) {
274+
uds.destroy_network()?;
275+
state = State::Initialised;
276+
console.clear();
277+
println!("Press A to start scanning or B to create a new network");
278+
} else if !hid.keys_down().is_empty() || !hid.keys_up().is_empty() {
279+
let transfer_data = hid.keys_held().bits();
280+
uds.send_packet(
281+
&transfer_data.to_le_bytes(),
282+
NodeID::Broadcast,
283+
data_channel,
284+
SendFlags::Default,
285+
)?;
286+
}
287+
}
288+
Err(e) => {
289+
uds.destroy_network()?;
290+
console.clear();
291+
eprintln!("Error while grabbing packet from network: {e}");
292+
state = State::Initialised;
293+
println!("Press A to start scanning or B to create a new network");
294+
}
295+
}
296+
}
297+
}
298+
}
299+
300+
Ok(())
301+
}

ctru-rs/src/lib.rs

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

ctru-rs/src/services/ir_user.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ impl IrUser {
150150
shared_mem.shared_memory_layout,
151151
);
152152

153-
Ok(())
153+
Ok::<_, Error>(())
154154
})()
155155
.unwrap();
156156
},

ctru-rs/src/services/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ mod reference;
2626
pub mod soc;
2727
pub mod sslc;
2828
pub mod svc;
29+
pub mod uds;
2930

3031
cfg_if::cfg_if! {
3132
if #[cfg(all(feature = "romfs", romfs_exists))] {

0 commit comments

Comments
 (0)