1use std::{
5 path::Path,
6 sync::{Arc, Mutex},
7 time::Duration,
8};
9
10use dashmap::DashMap;
11use deku::DekuContainerRead;
12use flume::{Receiver, RecvTimeoutError, Sender};
13use hidapi::{HidApi, HidDevice, HidError};
14use thiserror::Error;
15use tracing::{error, info_span, trace};
16
17use crate::{
18 ParseError,
19 device_ids::{BEYOND_PID, BEYOND_PID_FIRMWARE, BIGSCREEN_VID},
20 helpers::{Brightness, DisplayMode, RgbColor},
21 protocol::{
22 CommandIds, HID_REPORT_SIZE, TIMESTAT_STEP,
23 memory::{BANK_CAPACITY, BANK_COUNT, ConfigurationMemory, MEMORY_SIZE},
24 status::DeviceStatus,
25 },
26};
27
28#[derive(Debug, Clone, Error)]
29pub enum DeviceError {
30 #[error("HID error: {0}")]
31 HidError(String),
32 #[error("the returned message was an incorrect size ({0} != {HID_REPORT_SIZE})")]
33 ReturnMessageBadSize(usize),
34 #[error("HMD took too long to respond (over {CMD_TIMEOUT}ms)")]
35 Timeout,
36 #[error("Device is closed")]
37 Closed,
38 #[error(transparent)]
39 Parse(#[from] ParseError),
40}
41impl From<HidError> for DeviceError {
42 fn from(e: HidError) -> Self {
43 Self::HidError(e.to_string())
44 }
45}
46impl From<RecvTimeoutError> for DeviceError {
47 fn from(e: RecvTimeoutError) -> Self {
48 match e {
49 RecvTimeoutError::Timeout => Self::Timeout,
50 RecvTimeoutError::Disconnected => todo!(),
51 }
52 }
53}
54
55type DeviceResult<T> = Result<T, DeviceError>;
56
57pub const CMD_TIMEOUT: u64 = 10_000;
58pub const HID_TIMEOUT: i32 = 10_000;
59
60type Data = [u8; HID_REPORT_SIZE];
61
62struct EventDispatcher {
63 listeners: Arc<DashMap<u8, Sender<DeviceResult<Data>>>>,
64
65 dispatch_tx: Sender<Option<HidDevice>>,
67 state: Arc<Mutex<Result<(), DeviceError>>>,
69}
70
71impl EventDispatcher {
72 fn dispatcher(
73 device: HidDevice,
74 listeners: Arc<DashMap<u8, Sender<DeviceResult<Data>>>>,
75 state: Arc<Mutex<DeviceResult<()>>>,
76 ) -> Sender<Option<HidDevice>> {
77 let (tx, rx) = flume::bounded::<Option<HidDevice>>(1);
78
79 let _handle = std::thread::spawn(move || {
80 let span = info_span!("receiver_thread");
81 let _handle = span.enter();
82
83 let mut data: Data = [0u8; HID_REPORT_SIZE];
84 let mut device: Option<HidDevice> = Some(device);
85
86 loop {
87 match rx.try_recv() {
88 Err(flume::TryRecvError::Empty) => {}
90 Err(flume::TryRecvError::Disconnected) => {
91 panic!("Socket disconnected")
92 }
93 Ok(None) => {
94 device = None;
95 *state.try_lock().unwrap() = Err(DeviceError::Closed);
96 }
97 Ok(Some(dev)) => {
98 device = Some(dev);
99 *state.try_lock().unwrap() = Ok(());
100 }
101 }
102
103 if device.is_none() {
106 loop {
107 if let Some(dev) = rx.recv().expect("Socket disconnected") {
108 device = Some(dev);
109 *state.try_lock().unwrap() = Ok(());
110 break;
111 }
112 }
113 }
114
115 assert_eq!(
118 state.lock().unwrap().is_ok(),
119 device.is_some(),
120 "Struct state & local device state should always match"
121 );
122
123 let dev: &HidDevice = device.as_ref().unwrap_or_else(|| unreachable!());
124
125 match dev.read_timeout(&mut data, HID_TIMEOUT) {
126 Ok(HID_REPORT_SIZE) => {
129 trace!(?data, "Received HID interrupt");
130 }
131
132 Ok(len) => {
133 trace!(%len, ?data, "Returned data has incorrect size (must be {HID_REPORT_SIZE})");
134 let err = DeviceError::ReturnMessageBadSize(len);
135 Self::dispatch_error(&listeners, &err);
136 *state.try_lock().unwrap() = Err(err);
137
138 device = None;
139 continue;
140 }
141 Err(e) => {
142 error!(error = ?e, "There was an HID error");
143 let err = e.into();
144 Self::dispatch_error(&listeners, &err);
145 *state.try_lock().unwrap() = Err(err);
146
147 device = None;
148 continue;
149 }
150 }
151
152 let id = data[0];
153
154 let Some(listener) = listeners.get(&id) else {
155 trace!(%id, "No listeners found (dropping packet)");
156 continue;
157 };
158
159 if listener.try_send(Ok(data)).is_err() {
160 listeners.remove(&id);
161 }
162 }
163 });
164
165 tx
166 }
167
168 fn dispatch_error(listeners: &DashMap<u8, Sender<DeviceResult<Data>>>, error: &DeviceError) {
169 listeners.iter().for_each(|l| {
170 let _ = l.try_send(Err(error.clone()));
172 });
173 listeners.clear();
174 }
175
176 pub fn new(device: HidDevice) -> Self {
177 let listeners = Arc::new(DashMap::with_capacity(256));
178
179 let state = Arc::new(Mutex::new(Ok(())));
180 let dispatch_tx = Self::dispatcher(device, Arc::clone(&listeners), Arc::clone(&state));
181
182 Self {
183 listeners,
184 dispatch_tx,
185 state,
186 }
187 }
188
189 pub fn state(&self) -> Result<(), DeviceError> {
190 self.state.lock().unwrap().clone()
191 }
192 pub fn stop(&self) {
193 Self::dispatch_error(&self.listeners, &DeviceError::Closed);
194 self.dispatch_tx.send(None).unwrap();
195 }
196 pub fn change_device(&self, dev: HidDevice) {
197 self.dispatch_tx.send(Some(dev)).unwrap();
198 }
199
200 pub fn subscribe(&self, id: u8) -> DeviceResult<Receiver<DeviceResult<Data>>> {
207 self.state()?;
208
209 let (tx, rx) = flume::bounded(1);
210
211 if let Some(prev) = self.listeners.insert(id, tx) {
212 assert!(
214 prev.is_disconnected(),
215 "Previously opened socket still open"
216 );
217 }
218
219 Ok(rx)
220 }
221}
222
223struct DeviceWrapper {
226 dev: Mutex<HidDevice>,
228 dispatcher: EventDispatcher,
229 locks: Box<[Mutex<()>; 256]>,
230}
231
232impl DeviceWrapper {
233 pub fn new(api: &mut HidApi) -> Result<Self, HidError> {
234 api.refresh_devices()?;
237 let out_dev = Self::open_main_device(api)?;
238 let in_dev = Self::open_main_device(api)?;
239
240 Ok(Self {
241 dev: out_dev.into(),
242 dispatcher: EventDispatcher::new(in_dev),
243 locks: Box::new(std::array::from_fn(|_| Mutex::new(()))),
244 })
245 }
246
247 fn open_main_device(api: &HidApi) -> Result<HidDevice, HidError> {
248 let dev = api.open(BIGSCREEN_VID, BEYOND_PID)?;
249 dev.set_blocking_mode(true)?;
250
251 Ok(dev)
252 }
253
254 pub fn refresh_device(&self, api: &HidApi) -> Result<(), HidError> {
255 let out_dev = Self::open_main_device(api)?;
256 let in_dev = Self::open_main_device(api)?;
257
258 *self.dev.try_lock().unwrap() = out_dev;
259 self.dispatcher.change_device(in_dev);
260 Ok(())
261 }
262
263 #[inline]
264 fn dev(&self) -> std::sync::MutexGuard<'_, HidDevice> {
265 self.dev.lock().unwrap()
266 }
267
268 fn get_hid_manufacturer(&self) -> Result<Option<String>, HidError> {
269 self.dev().get_manufacturer_string()
270 }
271 fn get_hid_product(&self) -> Result<Option<String>, HidError> {
272 self.dev().get_product_string()
273 }
274 fn get_hid_serial(&self) -> Result<Option<String>, HidError> {
275 self.dev().get_serial_number_string()
276 }
277
278 pub fn send(&self, id: u8, data: &[u8]) -> Result<(), HidError> {
287 assert!(
288 data.len() < HID_REPORT_SIZE,
289 "HID event must be under {HID_REPORT_SIZE} bytes long."
290 );
291 let mut buf = [0u8; HID_REPORT_SIZE + 1];
292 buf[1] = id;
293 buf[2..(data.len() + 2)].copy_from_slice(data);
294
295 trace!(%id, ?data, "Sending data");
296 self.dev().send_feature_report(&buf)
297 }
298
299 pub fn wait_for(&self, response_id: u8) -> DeviceResult<Data> {
301 let _lock = self.locks[response_id as usize].lock().unwrap();
302 let rx = self.dispatcher.subscribe(response_id).unwrap();
303
304 rx.recv_timeout(Duration::from_millis(CMD_TIMEOUT))?
305 }
306
307 pub fn send_and_wait(&self, send_id: u8, data: &[u8], response_id: u8) -> DeviceResult<Data> {
318 let _lock = self.locks[response_id as usize].lock().unwrap();
319 let rx = self.dispatcher.subscribe(response_id).unwrap();
320
321 self.send(send_id, data).unwrap();
322
323 rx.recv_timeout(Duration::from_millis(CMD_TIMEOUT))?
324 }
325
326 pub fn send_and_wait_ack(&self, id: u8, data: &[u8]) -> DeviceResult<()> {
332 let _ = self.send_and_wait(id, data, CommandIds::Ack.id())?;
333 Ok(())
334 }
335}
336
337#[derive(Clone)]
369pub struct BSBDevice {
370 hidapi: Arc<Mutex<HidApi>>,
373 device: Arc<DeviceWrapper>,
374}
375impl BSBDevice {
376 pub fn new() -> Result<Self, DeviceError> {
377 let mut hidapi = HidApi::new()?;
378 let dev = DeviceWrapper::new(&mut hidapi)?;
379
380 Ok(Self {
381 hidapi: Arc::new(Mutex::new(hidapi)),
382 device: Arc::new(dev),
383 })
384 }
385
386 pub fn get_hid_manufacturer(&self) -> DeviceResult<Option<String>> {
388 Ok(self.device.get_hid_manufacturer()?)
389 }
390 pub fn get_hid_product(&self) -> DeviceResult<Option<String>> {
392 Ok(self.device.get_hid_product()?)
393 }
394 pub fn get_hid_serial(&self) -> DeviceResult<Option<String>> {
402 Ok(self.device.get_hid_serial()?)
403 }
404
405 #[inline]
406 fn str_stat(&self, id: u8) -> DeviceResult<String> {
407 let buf = self.device.send_and_wait(id, &[], id)?;
408
409 let size = buf
410 .iter()
411 .skip(1)
413 .position(|&x| x == 0)
415 .unwrap_or(HID_REPORT_SIZE - 1);
416 Ok(String::from_utf8(buf[1..=size].to_vec()).unwrap())
417 }
418 pub fn get_secondary_serial(&self) -> DeviceResult<String> {
422 self.str_stat(CommandIds::SecondarySerial.id())
423 }
424 pub fn get_firmware_version(&self) -> DeviceResult<String> {
426 self.str_stat(CommandIds::FirmwareVersion.id())
427 }
428
429 pub fn set_fan_speed(&self, v: u8) -> DeviceResult<()> {
431 self.device.send_and_wait_ack(CommandIds::SetFan.id(), &[v])
432 }
433 pub fn set_brightness(&self, v: &Brightness) -> DeviceResult<()> {
435 self.device
436 .send_and_wait_ack(CommandIds::SetBrightness.id(), &v.0.to_be_bytes())
437 }
438 pub fn set_led_color(&self, v: &RgbColor) -> DeviceResult<()> {
440 self.device
441 .send_and_wait_ack(CommandIds::SetLed.id(), &v.as_bytes())
442 }
443 pub fn set_display_mode(&self, v: &DisplayMode) -> DeviceResult<()> {
445 let mut memory = self.read_memory()?;
446 if &memory.display_mode()? == v {
447 return Ok(());
448 }
449
450 self.device
451 .send_and_wait_ack(CommandIds::SetMode.id(), &[v.id()])?;
452 unsafe { memory.set_display_mode(*v) };
454
455 self.write_memory(&memory)?;
456 let _ = self.device.wait_for(CommandIds::Ack.id())?;
457 Ok(())
458 }
459
460 #[inline]
462 fn time_stat(&self, v: u8) -> DeviceResult<u32> {
463 const C: u8 = CommandIds::TimeStat.id();
464
465 let data = self.device.send_and_wait(C, &[v], C)?;
466 Ok(
467 u32::from_le_bytes(data[1..=4].try_into().unwrap_or_else(|_| {
468 unreachable!("Data is always 64 bytes long. We will always have enough to parse")
469 })) * TIMESTAT_STEP,
470 )
471 }
472 pub fn get_time_idle(&self) -> DeviceResult<u32> {
476 self.time_stat(0)
477 }
478 pub fn get_time_usage(&self) -> DeviceResult<u32> {
482 self.time_stat(1)
483 }
484 pub fn get_time_longest(&self) -> DeviceResult<u32> {
489 self.time_stat(2)
490 }
491
492 pub fn read_memory(&self) -> DeviceResult<ConfigurationMemory> {
494 const C: u8 = CommandIds::ReadMemory.id();
495 let mut memory = [0u8; MEMORY_SIZE];
496
497 for i in 0..BANK_COUNT {
498 let buf = self.device.send_and_wait(C, &[i], C)?;
499 assert_eq!(buf[1], BANK_CAPACITY, "Bank capacity must be known");
501
502 memory[(i as usize * BANK_CAPACITY as usize)
503 ..((i as usize + 1) * BANK_CAPACITY as usize)]
504 .copy_from_slice(&buf[2..34]);
505 }
506
507 Ok(ConfigurationMemory::try_from_bytes(&memory)?)
508 }
509 pub fn write_memory(&self, data: &ConfigurationMemory) -> DeviceResult<()> {
513 let memory = ConfigurationMemory::to_blocks(data);
514
515 for (i, dat) in memory.iter().enumerate() {
516 let mut buf = [0u8; BANK_CAPACITY as usize + 1];
517 buf[0] = u8::try_from(i).unwrap_or_else(|_| {
518 unreachable!("There will never be more blocks than the size of a u8")
519 });
520 buf[1..=(BANK_CAPACITY as usize)].copy_from_slice(dat);
521
522 self.device
523 .send_and_wait_ack(CommandIds::WriteMemory.id(), &buf)?;
524 }
525
526 self.device
527 .send_and_wait_ack(CommandIds::CommitMemory.id(), &[])?;
528 Ok(())
529 }
530
531 pub fn get_status(&self) -> DeviceResult<DeviceStatus> {
532 let buf = self.device.wait_for(CommandIds::DeviceStat.id())?;
533
534 Ok(DeviceStatus::from_bytes((&buf, 0)).unwrap().1)
535 }
536
537 #[allow(unused)]
539 fn update_firmware(&self, path: &Path) {
540 self.device.dispatcher.stop();
543
544 self.device
546 .send(CommandIds::FirmwareFlash.id(), &[])
547 .unwrap();
548
549 let api = self.hidapi.lock().unwrap();
550 let dev = api.open(BIGSCREEN_VID, BEYOND_PID_FIRMWARE).unwrap();
551
552 let mut buf = [0u8; 65];
555 buf[1] = CommandIds::FirmwareFlash.id();
556 dev.send_feature_report(&buf).unwrap();
557
558 self.device.refresh_device(&api).unwrap();
560 }
561
562 #[cfg(feature = "et-dfu")]
564 #[allow(unused)]
565 fn update_bigeye_firmware(&self, path: &Path) {
566 use dfu_libusb::{Dfu, DfuLibusb};
567 use tracing::info;
568
569 use crate::device_ids::BIGEYE_PID_DFU;
570
571 self.device
572 .send_and_wait_ack(
573 CommandIds::BigeyeCommand.id(),
574 &[CommandIds::FirmwareFlash.id()],
575 )
576 .unwrap();
577
578 let ctx = rusb::Context::new().unwrap();
580 let device: Dfu<rusb::Context> =
581 DfuLibusb::open(&ctx, BIGSCREEN_VID, BIGEYE_PID_DFU, 0, 0).unwrap();
582
583 let file = std::fs::File::open(path).unwrap();
584 #[allow(clippy::single_match_else)]
585 match device.download_all(file) {
586 Ok(Some(_)) => info!("DFU firmware succsessful"),
587 _ => error!("DFU firmware download failed"),
588 }
589
590 self.device.send_and_wait_ack(
591 CommandIds::BigeyeCommand.id(),
592 &[CommandIds::FirmwareFlash.id()],
593 );
594 }
595}