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        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        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
520/// A collection that holds 4 latest TCP connections.
521///
522/// If there are already 4 TCP connections and a new one comes in,
523/// the oldest one is dropped.
524struct 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    /// The index of the next sample that we'll need to try sending.
583    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        // write as much as we can from the buffer into the channel
595        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        // fill the now empty part of the buffer with audio data
604        &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}