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::{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    _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    /// Called by the GUI to set input from UI and keyboard.
77    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
540/// A collection that holds 4 latest TCP connections.
541///
542/// If there are already 4 TCP connections and a new one comes in,
543/// the oldest one is dropped.
544struct 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    /// The index of the next sample that we'll need to try sending.
604    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        // write as much as we can from the buffer into the channel
617        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        // fill the now empty part of the buffer with audio data
626        &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}