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