1use crate::gamepad::GamepadManager;
2use crate::*;
3use alloc::boxed::Box;
4use core::cell::Cell;
5use core::fmt::Display;
6use core::marker::PhantomData;
7use core::net::{IpAddr, Ipv4Addr, SocketAddr};
8use std::io::{Read, Write};
9use std::net::{TcpListener, TcpStream, UdpSocket};
10use std::path::PathBuf;
11use std::sync::mpsc;
12
13const UDP_PORT_MIN: u16 = 3110;
14const UDP_PORT_MAX: u16 = 3117;
15const TCP_PORT_MIN: u16 = 3210;
16const TCP_PORT_MAX: u16 = 3217;
17const AUDIO_BUF_SIZE: usize = SAMPLE_RATE as usize / 6;
18
19#[derive(Clone)]
20pub struct DeviceConfig {
21 pub root: PathBuf,
23
24 pub tcp_ip: IpAddr,
26
27 pub udp_ip: IpAddr,
29
30 pub peers: Vec<IpAddr>,
32
33 pub wav: Option<PathBuf>,
35}
36
37impl Default for DeviceConfig {
38 fn default() -> Self {
39 let localhost = IpAddr::V4(Ipv4Addr::LOCALHOST);
40 Self {
41 root: PathBuf::new(),
42 tcp_ip: localhost,
43 udp_ip: localhost,
44 peers: vec![localhost],
45 wav: None,
46 }
47 }
48}
49
50pub struct DeviceImpl<'a> {
51 config: DeviceConfig,
52 start: std::time::Instant,
54 gamepad: GamepadManager,
56 audio: Option<AudioWriter>,
58 _life: &'a PhantomData<()>,
59}
60
61impl<'a> DeviceImpl<'a> {
62 pub fn new(config: DeviceConfig) -> Self {
63 let audio = start_audio(&config);
64 Self {
65 start: std::time::Instant::now(),
66 gamepad: GamepadManager::new(),
67 audio,
68 config,
69 _life: &PhantomData,
70 }
71 }
72
73 pub fn update_input(&mut self, input: InputState) {
75 self.gamepad.update_input(input)
76 }
77
78 pub fn alloc_psram(&self, size: usize) -> Vec<u8> {
79 Vec::with_capacity(size)
80 }
81}
82
83impl<'a> Device for DeviceImpl<'a> {
84 type Network = NetworkImpl<'a>;
85 type Serial = SerialImpl;
86 type Dir = DirImpl;
87
88 fn now(&self) -> Instant {
89 let now = std::time::Instant::now();
90 let dur = now.duration_since(self.start);
91 Instant {
92 us: dur.as_micros() as u32,
93 }
94 }
95
96 fn delay(&self, d: Duration) {
97 let dur = core::time::Duration::from_micros(d.us as u64);
98 std::thread::sleep(dur);
99 }
100
101 fn read_input(&mut self) -> Option<InputState> {
102 self.gamepad.read_input()
103 }
104
105 fn log_debug<D: Display>(&self, src: &str, msg: D) {
106 println!("DEBUG({src}): {msg}");
107 }
108
109 fn log_error<D: Display>(&self, src: &str, msg: D) {
110 eprintln!("ERROR({src}): {msg}");
111 }
112
113 fn random(&mut self) -> u32 {
114 rand::random()
115 }
116
117 fn open_dir(&mut self, path: &[&str]) -> Result<Self::Dir, FSError> {
118 let path: PathBuf = path.iter().collect();
119 let path = self.config.root.join(path);
120 if !path.exists() {
121 return Err(FSError::NotFound);
122 }
123 if !path.is_dir() {
124 return Err(FSError::OpenedFileAsDir);
125 }
126 Ok(DirImpl { path })
127 }
128
129 fn has_headphones(&mut self) -> bool {
130 false
131 }
132
133 fn get_audio_buffer(&mut self) -> &mut [i16] {
134 match &mut self.audio {
135 Some(audio) => audio.get_write_buf(),
136 None => &mut [][..],
137 }
138 }
139
140 fn get_battery_status(&mut self) -> Option<BatteryStatus> {
141 use battery::units::electric_potential::microvolt;
142 let manager = battery::Manager::new().ok()?;
143 let mut batteries = manager.batteries().ok()?;
144 let battery = batteries.next()?;
145 let battery = battery.ok()?;
146 let state = battery.state();
147 let voltage = battery.voltage().get::<microvolt>();
148 Some(BatteryStatus {
149 voltage: voltage as u16,
150 connected: state == battery::State::Charging,
151 full: state == battery::State::Full,
152 })
153 }
154
155 fn network(&mut self) -> Self::Network {
156 NetworkImpl::new(self.config.clone())
157 }
158
159 fn serial(&self) -> Self::Serial {
160 SerialImpl::new(self.config.tcp_ip)
161 }
162}
163
164pub struct DirImpl {
165 path: PathBuf,
166}
167
168impl Dir for DirImpl {
169 type Read = File;
170 type Write = File;
171
172 fn open_file(&mut self, name: &str) -> Result<Self::Read, FSError> {
173 let path = self.path.join(name);
174 let file = std::fs::File::open(path)?;
175 Ok(File { file })
176 }
177
178 fn create_file(&mut self, name: &str) -> Result<Self::Write, FSError> {
179 let path = self.path.join(name);
180 if let Some(parent) = path.parent() {
181 _ = std::fs::create_dir_all(parent);
182 }
183 let file = std::fs::File::create(path)?;
184 Ok(File { file })
185 }
186
187 fn append_file(&mut self, name: &str) -> Result<Self::Write, FSError> {
188 let path = self.path.join(name);
189 let mut opts = std::fs::OpenOptions::new();
190 let file = opts.append(true).open(path)?;
191 Ok(File { file })
192 }
193
194 fn get_file_size(&mut self, name: &str) -> Result<u32, FSError> {
195 let path = self.path.join(name);
196 let meta = std::fs::metadata(path)?;
197 Ok(meta.len() as u32)
198 }
199
200 fn remove_file(&mut self, name: &str) -> Result<(), FSError> {
201 let path = self.path.join(name);
202 let res = std::fs::remove_file(path);
203 match res {
204 Ok(_) => Ok(()),
205 Err(err) => match err.kind() {
206 std::io::ErrorKind::NotFound => Ok(()),
207 _ => Err(err.into()),
208 },
209 }
210 }
211
212 fn iter_dir<F>(&mut self, mut f: F) -> Result<(), FSError>
213 where
214 F: FnMut(EntryKind, &[u8]),
215 {
216 let entries = std::fs::read_dir(&self.path)?;
217 for entry in entries {
218 let entry = entry?;
219 let path = entry.path();
220 let kind = if path.is_dir() {
221 EntryKind::Dir
222 } else if path.is_file() {
223 EntryKind::File
224 } else {
225 continue;
226 };
227 let fname = entry.file_name();
228 let fname = fname.as_encoded_bytes();
229 f(kind, fname);
230 }
231 Ok(())
232 }
233}
234
235pub struct File {
236 file: std::fs::File,
237}
238
239impl embedded_io::ErrorType for File {
240 type Error = std::io::Error;
241}
242
243impl embedded_io::Read for File {
244 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
245 std::io::Read::read(&mut self.file, buf)
246 }
247}
248
249impl embedded_io::Write for File {
250 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
251 std::io::Write::write(&mut self.file, buf)
252 }
253
254 fn flush(&mut self) -> Result<(), Self::Error> {
255 std::io::Write::flush(&mut self.file)
256 }
257}
258
259pub struct NetworkImpl<'a> {
260 config: DeviceConfig,
261 worker: Cell<Option<UdpWorker>>,
262 r_in: mpsc::Receiver<NetMessage>,
263 s_out: mpsc::Sender<NetMessage>,
264 s_stop: mpsc::Sender<()>,
265 local_addr: Option<SocketAddr>,
266 _life: &'a PhantomData<()>,
267}
268
269impl<'a> NetworkImpl<'a> {
270 fn new(config: DeviceConfig) -> Self {
271 let (s_in, r_in) = mpsc::channel();
272 let (s_out, r_out) = mpsc::channel();
273 let (s_stop, r_stop) = mpsc::channel();
274 let worker = Cell::new(Some(UdpWorker {
275 s_in,
276 r_out,
277 r_stop,
278 }));
279 Self {
280 config,
281 worker,
282 r_in,
283 s_out,
284 s_stop,
285 local_addr: None,
286 _life: &PhantomData,
287 }
288 }
289}
290
291pub type Addr = SocketAddr;
292
293impl<'a> Network for NetworkImpl<'a> {
294 type Addr = SocketAddr;
295
296 fn local_addr(&self) -> SocketAddr {
297 self.local_addr.unwrap()
298 }
299
300 fn start(&mut self) -> NetworkResult<()> {
301 let worker = self.worker.replace(None);
302 let Some(worker) = worker else {
303 return Err(NetworkError::AlreadyInitialized);
304 };
305 let local_addr = worker.start(self.config.udp_ip)?;
306 self.local_addr = Some(local_addr);
307 Ok(())
308 }
309
310 fn stop(&mut self) -> NetworkResult<()> {
311 _ = self.s_stop.send(());
312 Ok(())
313 }
314
315 fn advertise(&mut self) -> NetworkResult<()> {
316 let hello = b"HELLO".to_owned();
317 let hello: Box<[u8]> = Box::new(hello);
318 for ip in &self.config.peers {
319 for port in UDP_PORT_MIN..=UDP_PORT_MAX {
320 let addr = SocketAddr::new(*ip, port);
321 let res = self.s_out.send((addr, hello.clone()));
322 if res.is_err() {
323 return Err(NetworkError::NetThreadDeallocated);
324 }
325 }
326 }
327 Ok(())
328 }
329
330 fn recv(&mut self) -> NetworkResult<Option<(Self::Addr, Box<[u8]>)>> {
331 Ok(self.r_in.try_recv().ok())
332 }
333
334 fn send(&mut self, addr: Self::Addr, data: &[u8]) -> NetworkResult<()> {
335 if data.len() >= 200 {
336 return Err(NetworkError::OutMessageTooBig);
337 };
338 let msg = data.to_vec().into_boxed_slice();
339 let res = self.s_out.send((addr, msg));
340 if res.is_err() {
341 return Err(NetworkError::NetThreadDeallocated);
342 }
343 Ok(())
344 }
345
346 fn send_status(&mut self, _: Self::Addr) -> NetworkResult<firefly_types::spi::SendStatus> {
347 Ok(firefly_types::spi::SendStatus::Empty)
348 }
349}
350
351pub struct SerialImpl {
352 ip: IpAddr,
353 worker: Cell<Option<TcpWorker>>,
354 r_in: mpsc::Receiver<SerialMessage>,
355 s_out: mpsc::Sender<SerialMessage>,
356 s_stop: mpsc::Sender<()>,
357}
358
359impl SerialImpl {
360 fn new(ip: IpAddr) -> Self {
361 let (s_in, r_in) = mpsc::channel();
362 let (s_out, r_out) = mpsc::channel();
363 let (s_stop, r_stop) = mpsc::channel();
364 let worker = TcpWorker {
365 s_in,
366 r_out,
367 r_stop,
368 };
369 let worker = Cell::new(Some(worker));
370 Self {
371 ip,
372 worker,
373 r_in,
374 s_out,
375 s_stop,
376 }
377 }
378}
379
380impl Serial for SerialImpl {
381 fn start(&mut self) -> NetworkResult<()> {
382 let worker = self.worker.replace(None);
383 let Some(worker) = worker else {
384 return Err(NetworkError::AlreadyInitialized);
385 };
386 worker.start(self.ip)?;
387 Ok(())
388 }
389
390 fn stop(&mut self) -> NetworkResult<()> {
391 _ = self.s_stop.send(());
392 Ok(())
393 }
394
395 fn recv(&mut self) -> NetworkResult<Option<Box<[u8]>>> {
396 Ok(self.r_in.try_recv().ok())
397 }
398
399 fn send(&mut self, data: &[u8]) -> NetworkResult<()> {
400 if data.len() >= 200 {
401 return Err(NetworkError::OutMessageTooBig);
402 };
403 let msg = data.to_vec().into_boxed_slice();
404 let res = self.s_out.send(msg);
405 if res.is_err() {
406 return Err(NetworkError::NetThreadDeallocated);
407 }
408 Ok(())
409 }
410}
411type NetMessage = (SocketAddr, Box<[u8]>);
412type SerialMessage = Box<[u8]>;
413
414struct UdpWorker {
415 s_in: mpsc::Sender<NetMessage>,
416 r_out: mpsc::Receiver<NetMessage>,
417 r_stop: mpsc::Receiver<()>,
418}
419
420impl UdpWorker {
421 fn start(self, ip: IpAddr) -> Result<SocketAddr, NetworkError> {
422 let addrs: Vec<_> = (UDP_PORT_MIN..=UDP_PORT_MAX)
423 .map(|port| SocketAddr::new(ip, port))
424 .collect();
425 let socket = match UdpSocket::bind(&addrs[..]) {
426 Ok(socket) => socket,
427 Err(_) => return Err(NetworkError::CannotBind),
428 };
429 let timeout = std::time::Duration::from_millis(10);
430 socket.set_read_timeout(Some(timeout)).unwrap();
431 if let Ok(addr) = socket.local_addr() {
432 println!("listening on {addr}/udp");
433 } else {
434 println!("listening a UDP port");
435 }
436 let local_addr = socket.local_addr().unwrap();
437 std::thread::spawn(move || {
438 loop {
439 match self.r_stop.try_recv() {
440 Ok(_) | Err(mpsc::TryRecvError::Disconnected) => {
441 break;
442 }
443 Err(mpsc::TryRecvError::Empty) => {}
444 }
445 let mut buf = vec![0; 64];
446 if let Ok((size, addr)) = socket.recv_from(&mut buf) {
447 if size == 0 {
448 continue;
449 }
450 buf.truncate(size);
451 let buf = buf.into_boxed_slice();
452 _ = self.s_in.send((addr, buf));
453 }
454 if let Ok((addr, buf)) = self.r_out.try_recv() {
455 if addr == local_addr {
456 continue;
457 }
458 _ = socket.send_to(&buf, addr);
459 }
460 }
461 });
462 Ok(local_addr)
463 }
464}
465
466struct TcpWorker {
467 s_in: mpsc::Sender<SerialMessage>,
468 r_out: mpsc::Receiver<SerialMessage>,
469 r_stop: mpsc::Receiver<()>,
470}
471
472impl TcpWorker {
473 fn start(self, ip: IpAddr) -> Result<(), NetworkError> {
474 let addrs: Vec<_> = (TCP_PORT_MIN..=TCP_PORT_MAX)
475 .map(|port| SocketAddr::new(ip, port))
476 .collect();
477 let socket = match TcpListener::bind(&addrs[..]) {
478 Ok(socket) => socket,
479 Err(_) => return Err(NetworkError::CannotBind),
480 };
481 socket.set_nonblocking(true).unwrap();
482 std::thread::spawn(move || {
483 let mut streams = RingBuf::new();
484 loop {
485 match self.r_stop.try_recv() {
486 Ok(_) | Err(mpsc::TryRecvError::Disconnected) => {
487 break;
488 }
489 Err(mpsc::TryRecvError::Empty) => {}
490 }
491
492 if let Ok((stream, _addr)) = socket.accept() {
493 stream.set_nonblocking(true).unwrap();
494 streams.push(stream);
495 };
496
497 for stream in streams.iter_mut() {
498 let mut buf = vec![0; 200];
499 let Ok(size) = stream.read(&mut buf) else {
500 continue;
501 };
502 if size == 0 {
503 continue;
504 }
505 buf.truncate(size);
506 let buf = buf.into_boxed_slice();
507 _ = self.s_in.send(buf);
508 }
509 if let Ok(buf) = self.r_out.try_recv() {
510 for stream in streams.iter_mut() {
511 _ = stream.write_all(&buf)
512 }
513 }
514 }
515 });
516 Ok(())
517 }
518}
519
520struct RingBuf {
525 data: [Option<TcpStream>; 4],
526 next: usize,
527}
528
529impl RingBuf {
530 fn new() -> Self {
531 Self {
532 data: [None, None, None, None],
533 next: 0,
534 }
535 }
536
537 fn push(&mut self, val: TcpStream) {
538 self.data[self.next] = Some(val);
539 self.next = (self.next + 1) % 4
540 }
541
542 fn iter_mut(&mut self) -> impl Iterator<Item = &mut TcpStream> {
543 self.data.iter_mut().filter_map(Option::as_mut)
544 }
545}
546
547fn start_audio(config: &DeviceConfig) -> Option<AudioWriter> {
548 let wav = if let Some(filename) = &config.wav {
549 let spec = hound::WavSpec {
550 channels: 2,
551 sample_rate: SAMPLE_RATE,
552 bits_per_sample: 16,
553 sample_format: hound::SampleFormat::Int,
554 };
555 let writer = hound::WavWriter::create(filename, spec).unwrap();
556 Some(writer)
557 } else {
558 None
559 };
560
561 let (send, recv) = mpsc::sync_channel(AUDIO_BUF_SIZE);
562 let Ok(mut stream) = rodio::OutputStreamBuilder::open_default_stream() else {
563 eprintln!("WARNING: audio device is not available, sound will be disabled");
564 return None;
565 };
566 stream.log_on_drop(false);
567 let mixer = stream.mixer();
568 let source = AudioReader { wav, recv };
569 mixer.add(source);
570 let audio = AudioWriter {
571 buf: [0; AUDIO_BUF_SIZE],
572 idx: 0,
573 send,
574 _stream: stream,
575 };
576 Some(audio)
577}
578
579struct AudioWriter {
580 buf: [i16; AUDIO_BUF_SIZE],
581 send: mpsc::SyncSender<i16>,
582 idx: usize,
584 _stream: rodio::OutputStream,
585}
586
587impl AudioWriter {
588 fn get_write_buf(&mut self) -> &mut [i16] {
589 if self.idx == AUDIO_BUF_SIZE {
590 self.idx = 0;
591 }
592 let start = self.idx;
593 let mut idx = self.idx;
594 while idx < AUDIO_BUF_SIZE {
596 let res = self.send.try_send(self.buf[idx]);
597 if res.is_err() {
598 break;
599 }
600 idx += 1;
601 }
602 self.idx = idx;
603 &mut self.buf[start..idx]
605 }
606}
607
608struct AudioReader {
609 wav: Option<hound::WavWriter<std::io::BufWriter<std::fs::File>>>,
610 recv: mpsc::Receiver<i16>,
611}
612
613impl rodio::Source for AudioReader {
614 fn current_span_len(&self) -> Option<usize> {
615 None
616 }
617
618 fn channels(&self) -> u16 {
619 2
620 }
621
622 fn sample_rate(&self) -> u32 {
623 SAMPLE_RATE
624 }
625
626 fn total_duration(&self) -> Option<core::time::Duration> {
627 None
628 }
629}
630
631impl Iterator for AudioReader {
632 type Item = f32;
633
634 fn next(&mut self) -> Option<Self::Item> {
635 let s = self.recv.try_recv().unwrap_or_default();
636 if let Some(wav) = self.wav.as_mut() {
637 wav.write_sample(s).unwrap()
638 }
639 let s = f32::from(s) / f32::from(i16::MAX);
640 Some(s)
641 }
642}