Skip to main content

firefly_hal/
hosted.rs

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::{SocketAddrV4, 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    /// The full path to the VFS.
22    pub root: PathBuf,
23
24    /// The TCP IP address where to listen for serial events.
25    pub tcp_ip: IpAddr,
26
27    /// The UDP IP address where to listen for netplay events.
28    pub udp_ip: IpAddr,
29
30    /// The UDP IP addresses where to send netplay advertisements.
31    pub peers: Vec<IpAddr>,
32
33    /// If provided, the path where to save the audio output (as a WAV file).
34    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    /// The time at which the device instance was created.
53    start: std::time::Instant,
54    /// The shared logic for reading the gamepad input.
55    gamepad: GamepadManager,
56    /// The audio buffer
57    audio: Option<AudioWriter>,
58    wifi_status: u8,
59    network: NetworkImpl<'a>,
60    serial: SerialImpl,
61    tcp_conn: Option<TcpStream>,
62}
63
64impl<'a> DeviceImpl<'a> {
65    pub fn new(config: DeviceConfig) -> Self {
66        #[cfg(not(target_os = "android"))]
67        let audio = start_audio(&config);
68        #[cfg(target_os = "android")]
69        let audio = None;
70        Self {
71            start: std::time::Instant::now(),
72            gamepad: GamepadManager::new(),
73            audio,
74            config,
75            wifi_status: 2,
76            network: NetworkImpl::new(),
77            serial: SerialImpl::new(),
78            tcp_conn: None,
79        }
80    }
81
82    /// Called by the GUI to set input from UI and keyboard.
83    pub fn update_input(&mut self, input: InputState) {
84        self.gamepad.update_input(input)
85    }
86
87    pub fn alloc_psram(&self, size: usize) -> Vec<u8> {
88        Vec::with_capacity(size)
89    }
90}
91
92impl<'a> Device for DeviceImpl<'a> {
93    type Dir = DirImpl;
94
95    fn now(&self) -> Instant {
96        let now = std::time::Instant::now();
97        let dur = now.duration_since(self.start);
98        Instant {
99            us: dur.as_micros() as u32,
100        }
101    }
102
103    fn delay(&self, d: Duration) {
104        let dur = core::time::Duration::from_micros(d.us as u64);
105        std::thread::sleep(dur);
106    }
107
108    fn read_input(&mut self) -> Option<InputState> {
109        self.gamepad.read_input()
110    }
111
112    fn log_debug<D: Display>(&mut self, src: &str, msg: D) {
113        println!("DEBUG({src}): {msg}");
114    }
115
116    fn log_error<D: Display>(&mut self, src: &str, msg: D) {
117        eprintln!("ERROR({src}): {msg}");
118    }
119
120    fn random(&mut self) -> u32 {
121        rand::random()
122    }
123
124    fn open_dir(&mut self, path: &[&str]) -> Result<Self::Dir, FSError> {
125        let path: PathBuf = path.iter().collect();
126        let path = self.config.root.join(path);
127        if !path.exists() {
128            return Err(FSError::NotFound);
129        }
130        if !path.is_dir() {
131            return Err(FSError::OpenedFileAsDir);
132        }
133        Ok(DirImpl { path })
134    }
135
136    fn has_headphones(&mut self) -> bool {
137        false
138    }
139
140    fn get_audio_buffer(&mut self) -> &mut [i16] {
141        match &mut self.audio {
142            Some(audio) => audio.get_write_buf(),
143            None => &mut [][..],
144        }
145    }
146
147    #[cfg(target_os = "android")]
148    fn get_battery_status(&mut self) -> Option<BatteryStatus> {
149        None
150    }
151
152    #[cfg(not(target_os = "android"))]
153    fn get_battery_status(&mut self) -> Option<BatteryStatus> {
154        use battery::units::electric_potential::microvolt;
155        let manager = battery::Manager::new().ok()?;
156        let mut batteries = manager.batteries().ok()?;
157        let battery = batteries.next()?;
158        let battery = battery.ok()?;
159        let state = battery.state();
160        let voltage = battery.voltage().get::<microvolt>();
161        Some(BatteryStatus {
162            voltage: voltage as u16,
163            connected: state == battery::State::Charging,
164            full: state == battery::State::Full,
165        })
166    }
167}
168
169pub struct DirImpl {
170    path: PathBuf,
171}
172
173impl Dir for DirImpl {
174    type Read = File;
175    type Write = File;
176
177    fn open_file(&mut self, name: &str) -> Result<Self::Read, FSError> {
178        let path = self.path.join(name);
179        let file = std::fs::File::open(path)?;
180        Ok(File { file })
181    }
182
183    fn create_file(&mut self, name: &str) -> Result<Self::Write, FSError> {
184        let path = self.path.join(name);
185        if let Some(parent) = path.parent() {
186            _ = std::fs::create_dir_all(parent);
187        }
188        let file = std::fs::File::create(path)?;
189        Ok(File { file })
190    }
191
192    fn append_file(&mut self, name: &str) -> Result<Self::Write, FSError> {
193        let path = self.path.join(name);
194        let mut opts = std::fs::OpenOptions::new();
195        let file = opts.append(true).open(path)?;
196        Ok(File { file })
197    }
198
199    fn get_file_size(&mut self, name: &str) -> Result<u32, FSError> {
200        let path = self.path.join(name);
201        let meta = std::fs::metadata(path)?;
202        Ok(meta.len() as u32)
203    }
204
205    fn remove_file(&mut self, name: &str) -> Result<(), FSError> {
206        let path = self.path.join(name);
207        let res = std::fs::remove_file(path);
208        match res {
209            Ok(_) => Ok(()),
210            Err(err) => match err.kind() {
211                std::io::ErrorKind::NotFound => Ok(()),
212                _ => Err(err.into()),
213            },
214        }
215    }
216
217    fn create_dir(&mut self, name: &str) -> Result<(), FSError> {
218        let path = self.path.join(name);
219        std::fs::create_dir(path)?;
220        Ok(())
221    }
222
223    fn remove_dir(self) -> Result<(), FSError> {
224        let res = std::fs::remove_dir_all(&self.path);
225        match res {
226            Ok(_) => Ok(()),
227            Err(err) => match err.kind() {
228                std::io::ErrorKind::NotFound => Ok(()),
229                _ => Err(err.into()),
230            },
231        }
232    }
233
234    fn iter_dir<F>(&mut self, mut f: F) -> Result<(), FSError>
235    where
236        F: FnMut(EntryKind, &[u8]),
237    {
238        let entries = std::fs::read_dir(&self.path)?;
239        for entry in entries {
240            let entry = entry?;
241            let path = entry.path();
242            let kind = if path.is_dir() {
243                EntryKind::Dir
244            } else if path.is_file() {
245                EntryKind::File
246            } else {
247                continue;
248            };
249            let fname = entry.file_name();
250            let fname = fname.as_encoded_bytes();
251            f(kind, fname);
252        }
253        Ok(())
254    }
255}
256
257pub struct File {
258    file: std::fs::File,
259}
260
261impl embedded_io::ErrorType for File {
262    type Error = std::io::Error;
263}
264
265impl embedded_io::Read for File {
266    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
267        std::io::Read::read(&mut self.file, buf)
268    }
269}
270
271impl embedded_io::Write for File {
272    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
273        std::io::Write::write(&mut self.file, buf)
274    }
275
276    fn flush(&mut self) -> Result<(), Self::Error> {
277        std::io::Write::flush(&mut self.file)
278    }
279}
280
281pub struct NetworkImpl<'a> {
282    worker: Cell<Option<UdpWorker>>,
283    r_in: mpsc::Receiver<NetMessage>,
284    s_out: mpsc::Sender<NetMessage>,
285    s_stop: mpsc::Sender<()>,
286    local_addr: Option<SocketAddr>,
287    _life: &'a PhantomData<()>,
288}
289
290impl<'a> NetworkImpl<'a> {
291    fn new() -> Self {
292        let (s_in, r_in) = mpsc::channel();
293        let (s_out, r_out) = mpsc::channel();
294        let (s_stop, r_stop) = mpsc::channel();
295        let worker = Cell::new(Some(UdpWorker {
296            s_in,
297            r_out,
298            r_stop,
299        }));
300        Self {
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 DeviceImpl<'a> {
314    type Addr = SocketAddr;
315
316    fn net_local_addr(&self) -> SocketAddr {
317        self.network.local_addr.unwrap()
318    }
319
320    fn net_start(&mut self) -> NetworkResult<()> {
321        let worker = self.network.worker.replace(None);
322        let Some(worker) = worker else {
323            return Ok(());
324        };
325        let local_addr = worker.start(self.config.udp_ip)?;
326        self.network.local_addr = Some(local_addr);
327        Ok(())
328    }
329
330    fn net_stop(&mut self) -> NetworkResult<()> {
331        _ = self.network.s_stop.send(());
332        Ok(())
333    }
334
335    fn net_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.network.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 net_recv(&mut self) -> NetworkResult<Option<(Self::Addr, Box<[u8]>)>> {
351        Ok(self.network.r_in.try_recv().ok())
352    }
353
354    fn net_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.network.s_out.send((addr, msg));
360        if res.is_err() {
361            return Err(NetworkError::NetThreadDeallocated);
362        }
363        Ok(())
364    }
365
366    fn net_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    worker: Cell<Option<TcpWorker>>,
373    r_in: mpsc::Receiver<SerialMessage>,
374    s_out: mpsc::Sender<SerialMessage>,
375    s_stop: mpsc::Sender<()>,
376}
377
378impl SerialImpl {
379    fn new() -> Self {
380        let (s_in, r_in) = mpsc::channel();
381        let (s_out, r_out) = mpsc::channel();
382        let (s_stop, r_stop) = mpsc::channel();
383        let worker = TcpWorker {
384            s_in,
385            r_out,
386            r_stop,
387        };
388        let worker = Cell::new(Some(worker));
389        Self {
390            worker,
391            r_in,
392            s_out,
393            s_stop,
394        }
395    }
396}
397
398impl Serial for DeviceImpl<'_> {
399    fn serial_start(&mut self) -> NetworkResult<()> {
400        let worker = self.serial.worker.replace(None);
401        let Some(worker) = worker else {
402            return Ok(());
403        };
404        worker.start(self.config.tcp_ip)?;
405        Ok(())
406    }
407
408    fn serial_stop(&mut self) -> NetworkResult<()> {
409        _ = self.serial.s_stop.send(());
410        Ok(())
411    }
412
413    fn serial_recv(&mut self) -> NetworkResult<Option<Box<[u8]>>> {
414        Ok(self.serial.r_in.try_recv().ok())
415    }
416
417    fn serial_send(&mut self, data: &[u8]) -> NetworkResult<()> {
418        if data.len() >= 200 {
419            return Err(NetworkError::OutMessageTooBig);
420        };
421        let msg = data.to_vec().into_boxed_slice();
422        let res = self.serial.s_out.send(msg);
423        if res.is_err() {
424            return Err(NetworkError::NetThreadDeallocated);
425        }
426        Ok(())
427    }
428}
429
430impl Wifi for DeviceImpl<'_> {
431    fn wifi_scan(&mut self) -> NetworkResult<[String; 6]> {
432        let points = ["Default Network", "", "", "", "", ""];
433        let points = points.map(|s| s.to_string());
434        Ok(points)
435    }
436
437    fn wifi_connect(&mut self, ssid: &str, pass: &str) -> NetworkResult<()> {
438        if ssid != "Default Network" {
439            self.wifi_status = 2; // disconnected
440        } else if pass == "invalid" {
441            self.wifi_status = 1; // error
442        } else {
443            self.wifi_status = 3; // initializing
444        }
445        Ok(())
446    }
447
448    fn wifi_status(&mut self) -> NetworkResult<u8> {
449        if self.wifi_status == 3 {
450            self.wifi_status = 4; // connected
451            Ok(3)
452        } else {
453            Ok(self.wifi_status)
454        }
455    }
456
457    fn wifi_disconnect(&mut self) -> NetworkResult<()> {
458        self.wifi_status = 2;
459        Ok(())
460    }
461
462    fn tcp_connect(&mut self, ip: u32, port: u16) -> NetworkResult<()> {
463        if self.wifi_status != 4 {
464            return Err(NetworkError::Error("not connected to wifi"));
465        }
466        let ip = Ipv4Addr::new(
467            (ip >> 24) as u8,
468            (ip >> 16) as u8,
469            (ip >> 8) as u8,
470            ip as u8,
471        );
472        let addr = SocketAddrV4::new(ip, port);
473        let Ok(stream) = TcpStream::connect(addr) else {
474            return Err(NetworkError::CannotBind);
475        };
476        _ = stream.set_nonblocking(true);
477        self.tcp_conn = Some(stream);
478        Ok(())
479    }
480
481    fn tcp_status(&mut self) -> NetworkResult<u8> {
482        let Some(stream) = &mut self.tcp_conn else {
483            return Ok(1);
484        };
485        if stream.peer_addr().is_err() {
486            return Ok(1);
487        }
488        Ok(5)
489    }
490
491    fn tcp_send(&mut self, data: &[u8]) -> NetworkResult<()> {
492        let Some(stream) = &mut self.tcp_conn else {
493            return Err(NetworkError::NotInitialized);
494        };
495        let res = stream.write_all(data);
496        if let Err(err) = res {
497            let err = alloc::format!("{err}");
498            return Err(NetworkError::OwnedError(err));
499        }
500        Ok(())
501    }
502
503    fn tcp_recv(&mut self) -> NetworkResult<Box<[u8]>> {
504        let Some(stream) = &mut self.tcp_conn else {
505            return Err(NetworkError::NotInitialized);
506        };
507        let mut buf = vec![0; 80];
508        let n = match stream.read(&mut buf) {
509            Ok(n) => n,
510            Err(err) => {
511                if err.kind() == std::io::ErrorKind::WouldBlock {
512                    0
513                } else {
514                    return Err(NetworkError::Error("failed to read incoming TCP data"));
515                }
516            }
517        };
518        buf.truncate(n);
519        Ok(buf.into_boxed_slice())
520    }
521
522    fn tcp_close(&mut self) -> NetworkResult<()> {
523        let Some(stream) = self.tcp_conn.take() else {
524            return Ok(());
525        };
526        _ = stream.shutdown(std::net::Shutdown::Both);
527        Ok(())
528    }
529}
530
531type NetMessage = (SocketAddr, Box<[u8]>);
532type SerialMessage = Box<[u8]>;
533
534struct UdpWorker {
535    s_in: mpsc::Sender<NetMessage>,
536    r_out: mpsc::Receiver<NetMessage>,
537    r_stop: mpsc::Receiver<()>,
538}
539
540impl UdpWorker {
541    fn start(self, ip: IpAddr) -> Result<SocketAddr, NetworkError> {
542        let addrs: Vec<_> = (UDP_PORT_MIN..=UDP_PORT_MAX)
543            .map(|port| SocketAddr::new(ip, port))
544            .collect();
545        let socket = match UdpSocket::bind(&addrs[..]) {
546            Ok(socket) => socket,
547            Err(_) => return Err(NetworkError::CannotBind),
548        };
549        let timeout = std::time::Duration::from_millis(10);
550        socket.set_read_timeout(Some(timeout)).unwrap();
551        if let Ok(addr) = socket.local_addr() {
552            println!("listening on {addr}/udp");
553        } else {
554            println!("listening a UDP port");
555        }
556        let local_addr = socket.local_addr().unwrap();
557        std::thread::spawn(move || {
558            loop {
559                match self.r_stop.try_recv() {
560                    Ok(_) | Err(mpsc::TryRecvError::Disconnected) => {
561                        break;
562                    }
563                    Err(mpsc::TryRecvError::Empty) => {}
564                }
565                let mut buf = vec![0; 64];
566                if let Ok((size, addr)) = socket.recv_from(&mut buf) {
567                    if size == 0 {
568                        continue;
569                    }
570                    buf.truncate(size);
571                    let buf = buf.into_boxed_slice();
572                    _ = self.s_in.send((addr, buf));
573                }
574                if let Ok((addr, buf)) = self.r_out.try_recv() {
575                    if addr == local_addr {
576                        continue;
577                    }
578                    _ = socket.send_to(&buf, addr);
579                }
580            }
581        });
582        Ok(local_addr)
583    }
584}
585
586struct TcpWorker {
587    s_in: mpsc::Sender<SerialMessage>,
588    r_out: mpsc::Receiver<SerialMessage>,
589    r_stop: mpsc::Receiver<()>,
590}
591
592impl TcpWorker {
593    fn start(self, ip: IpAddr) -> Result<(), NetworkError> {
594        let addrs: Vec<_> = (TCP_PORT_MIN..=TCP_PORT_MAX)
595            .map(|port| SocketAddr::new(ip, port))
596            .collect();
597        let Ok(socket) = TcpListener::bind(&addrs[..]) else {
598            return Err(NetworkError::CannotBind);
599        };
600        socket.set_nonblocking(true).unwrap();
601        std::thread::spawn(move || {
602            let mut streams = RingBuf::new();
603            loop {
604                match self.r_stop.try_recv() {
605                    Ok(_) | Err(mpsc::TryRecvError::Disconnected) => {
606                        break;
607                    }
608                    Err(mpsc::TryRecvError::Empty) => {}
609                }
610
611                if let Ok((stream, _addr)) = socket.accept() {
612                    stream.set_nonblocking(true).unwrap();
613                    streams.push(stream);
614                };
615
616                for stream in streams.iter_mut() {
617                    let mut buf = vec![0; 200];
618                    let Ok(size) = stream.read(&mut buf) else {
619                        continue;
620                    };
621                    if size == 0 {
622                        continue;
623                    }
624                    buf.truncate(size);
625                    let buf = buf.into_boxed_slice();
626                    _ = self.s_in.send(buf);
627                }
628                if let Ok(buf) = self.r_out.try_recv() {
629                    for stream in streams.iter_mut() {
630                        _ = stream.write_all(&buf)
631                    }
632                }
633            }
634        });
635        Ok(())
636    }
637}
638
639/// A collection that holds 4 latest TCP connections.
640///
641/// If there are already 4 TCP connections and a new one comes in,
642/// the oldest one is dropped.
643struct RingBuf {
644    data: [Option<TcpStream>; 4],
645    next: usize,
646}
647
648impl RingBuf {
649    fn new() -> Self {
650        Self {
651            data: [None, None, None, None],
652            next: 0,
653        }
654    }
655
656    fn push(&mut self, val: TcpStream) {
657        self.data[self.next] = Some(val);
658        self.next = (self.next + 1) % 4
659    }
660
661    fn iter_mut(&mut self) -> impl Iterator<Item = &mut TcpStream> {
662        self.data.iter_mut().filter_map(Option::as_mut)
663    }
664}
665
666#[cfg(not(target_os = "android"))]
667fn start_audio(config: &DeviceConfig) -> Option<AudioWriter> {
668    let wav = if let Some(filename) = &config.wav {
669        let spec = hound::WavSpec {
670            channels: 2,
671            sample_rate: SAMPLE_RATE,
672            bits_per_sample: 16,
673            sample_format: hound::SampleFormat::Int,
674        };
675        let writer = hound::WavWriter::create(filename, spec).unwrap();
676        Some(writer)
677    } else {
678        None
679    };
680
681    let (send, recv) = mpsc::sync_channel(AUDIO_BUF_SIZE);
682    let Ok(mut stream) = rodio::OutputStreamBuilder::open_default_stream() else {
683        eprintln!("WARNING: audio device is not available, sound will be disabled");
684        return None;
685    };
686    stream.log_on_drop(false);
687    let mixer = stream.mixer();
688    let source = AudioReader { wav, recv };
689    mixer.add(source);
690    let audio = AudioWriter {
691        buf: [0; AUDIO_BUF_SIZE],
692        idx: 0,
693        send,
694        _stream: stream,
695    };
696    Some(audio)
697}
698
699struct AudioWriter {
700    buf: [i16; AUDIO_BUF_SIZE],
701    send: mpsc::SyncSender<i16>,
702    /// The index of the next sample that we'll need to try sending.
703    idx: usize,
704    #[cfg(not(target_os = "android"))]
705    _stream: rodio::OutputStream,
706}
707
708impl AudioWriter {
709    fn get_write_buf(&mut self) -> &mut [i16] {
710        if self.idx == AUDIO_BUF_SIZE {
711            self.idx = 0;
712        }
713        let start = self.idx;
714        let mut idx = self.idx;
715        // write as much as we can from the buffer into the channel
716        while idx < AUDIO_BUF_SIZE {
717            let res = self.send.try_send(self.buf[idx]);
718            if res.is_err() {
719                break;
720            }
721            idx += 1;
722        }
723        self.idx = idx;
724        // fill the now empty part of the buffer with audio data
725        &mut self.buf[start..idx]
726    }
727}
728
729#[cfg(not(target_os = "android"))]
730struct AudioReader {
731    wav: Option<hound::WavWriter<std::io::BufWriter<std::fs::File>>>,
732    recv: mpsc::Receiver<i16>,
733}
734
735#[cfg(not(target_os = "android"))]
736impl rodio::Source for AudioReader {
737    fn current_span_len(&self) -> Option<usize> {
738        None
739    }
740
741    fn channels(&self) -> u16 {
742        2
743    }
744
745    fn sample_rate(&self) -> u32 {
746        SAMPLE_RATE
747    }
748
749    fn total_duration(&self) -> Option<core::time::Duration> {
750        None
751    }
752}
753
754#[cfg(not(target_os = "android"))]
755impl Iterator for AudioReader {
756    type Item = f32;
757
758    fn next(&mut self) -> Option<Self::Item> {
759        let s = self.recv.try_recv().unwrap_or_default();
760        if let Some(wav) = self.wav.as_mut() {
761            wav.write_sample(s).unwrap()
762        }
763        let s = f32::from(s) / f32::from(i16::MAX);
764        Some(s)
765    }
766}