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