diff --git a/Cargo.lock b/Cargo.lock index 61292fb..238aebb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,7 +13,7 @@ dependencies = [ [[package]] name = "aimeio-yubideck" -version = "0.1.2" +version = "0.1.3" dependencies = [ "cfg_aliases", "faster-hex", @@ -89,7 +89,7 @@ checksum = "77e53693616d3075149f4ead59bdeecd204ac6b8192d8969757601b74bddf00f" [[package]] name = "chuniio-yubideck" -version = "0.1.2" +version = "0.1.3" dependencies = [ "anyhow", "cfg-if 1.0.0", diff --git a/aimeio/Cargo.toml b/aimeio/Cargo.toml index e387a68..4b7e87c 100644 --- a/aimeio/Cargo.toml +++ b/aimeio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aimeio-yubideck" -version = "0.1.2" +version = "0.1.3" edition = "2021" [lib] diff --git a/chuniio/Cargo.toml b/chuniio/Cargo.toml index b1c7b05..bf2ac3d 100644 --- a/chuniio/Cargo.toml +++ b/chuniio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chuniio-yubideck" -version = "0.1.2" +version = "0.1.3" edition = "2021" [lib] diff --git a/chuniio/src/lib.rs b/chuniio/src/lib.rs index 44a7fae..39473de 100644 --- a/chuniio/src/lib.rs +++ b/chuniio/src/lib.rs @@ -9,6 +9,7 @@ use std::{ atomic::Ordering, Mutex, }, + time::Duration, }; use ::log::error; @@ -24,16 +25,13 @@ use yubideck_common::{create_shared_memory, init_logger, INPUT_SHMEM_SIZE, OUTPU static mut INPUT_SHMEM: Option> = None; static mut OUTPUT_SHMEM: Option>> = None; +pub static DEVICE_POLLING_INTERVAL: Duration = Duration::from_millis(100); cfg_if::cfg_if! { if #[cfg(any(chuni, chusanapp))] { - use std::{ - sync::{OnceLock, atomic::AtomicBool}, - thread::{self, JoinHandle}, - time::Duration, - }; + use std::{sync::{atomic::AtomicBool, RwLock}, thread}; - use anyhow::{anyhow, Result}; + use anyhow::Result; use ::log::info; use rusb::{DeviceHandle, GlobalContext}; @@ -41,8 +39,7 @@ cfg_if::cfg_if! { const TIMEOUT: Duration = Duration::from_millis(20); - static DEVICE: OnceLock> = OnceLock::new(); - static mut SLIDER_THREAD: OnceLock> = OnceLock::new(); + static DEVICE: RwLock>> = RwLock::new(None); static SLIDER_ACTIVE: AtomicBool = AtomicBool::new(false); } } @@ -128,10 +125,7 @@ pub extern "C" fn chuni_io_jvs_init() -> HRESULT { #[cfg(not(amdaemon))] { - if let Err(e) = yubideck_init() { - error!("Could not initialize YubiDeck: {e:#?}"); - return E_FAIL; - } + thread::spawn(yubideck_init); } S_OK @@ -228,14 +222,15 @@ pub unsafe extern "C" fn chuni_io_slider_start(callback: *const c_void) { return; } - if SLIDER_THREAD.get().is_some() { + if SLIDER_ACTIVE.load(Ordering::SeqCst) { return; } SLIDER_ACTIVE.store(true, Ordering::Relaxed); let callback = std::mem::transmute::<_, SliderCallbackFn>(callback); - let thread = thread::spawn(move || { + + thread::spawn(move || { let Some(input_shmem) = (unsafe { &INPUT_SHMEM }) else { return; }; @@ -251,8 +246,6 @@ pub unsafe extern "C" fn chuni_io_slider_start(callback: *const c_void) { thread::sleep(Duration::from_nanos(1_000_000)); } }); - - SLIDER_THREAD.set(thread).unwrap(); } #[no_mangle] @@ -262,13 +255,7 @@ pub extern "C" fn chuni_io_slider_start(_callback: *const c_void) {} #[no_mangle] #[cfg(any(chuni, chusanapp))] pub extern "C" fn chuni_io_slider_stop() { - let Some(thread) = (unsafe { SLIDER_THREAD.take() }) else { - return; - }; - SLIDER_ACTIVE.store(false, Ordering::Relaxed); - - thread.join().expect("Couldn't join slider input thread"); } #[no_mangle] @@ -282,7 +269,8 @@ pub unsafe extern "C" fn chuni_io_slider_set_leds(rgb: *const u8) { return; } - let Some(device) = DEVICE.get() else { return }; + let device_rg = DEVICE.read().unwrap(); + let Some(device) = device_rg.as_ref().map(|z| z) else { return }; let Some(out_shmem) = &OUTPUT_SHMEM else { error!("OUTPUT_SHMEM is unset."); return; @@ -333,7 +321,8 @@ pub extern "C" fn chuni_io_led_init() -> HRESULT { #[no_mangle] #[cfg(any(chuni, chusanapp))] pub unsafe extern "C" fn chuni_io_led_set_colors(board: u8, rgb: *const u8) { - let Some(device) = DEVICE.get() else { return }; + let device_rg = DEVICE.read().unwrap(); + let Some(device) = device_rg.as_ref().map(|z| z) else { return }; let Some(out_shmem) = &OUTPUT_SHMEM else { error!("OUTPUT_SHMEM is unset."); return; @@ -372,26 +361,39 @@ pub extern "C" fn chuni_io_led_set_colors(_rgb: *const u8) {} #[cfg(any(chuni, chusanapp))] fn yubideck_init() -> Result<()> { - let Some(mut device) = rusb::open_device_with_vid_pid(0x1973, 0x2001) else { - return Err(anyhow!("YubiDeck not found.")); - }; + { + let mut global_device = DEVICE.write().unwrap(); + *global_device = None; + } - device.set_active_configuration(1)?; - device.claim_interface(0)?; - DEVICE - .set(device) - .map_err(|e| anyhow!("Cannot store device handle: {e:#?}"))?; + info!("Waiting for device..."); - thread::spawn(input_thread_proc); + loop { + let Some(mut device) = + rusb::open_device_with_vid_pid(0x1973, 0x2001) + else { + thread::sleep(DEVICE_POLLING_INTERVAL); + continue; + }; - Ok(()) + device.set_active_configuration(1)?; + device.claim_interface(0)?; + + let mut global_device = DEVICE.write().unwrap(); + *global_device = Some(device); + + thread::spawn(input_thread_proc); + + return Ok(()) + } } #[cfg(any(chuni, chusanapp))] fn input_thread_proc() { info!("Input thread started"); - let device = DEVICE.get().unwrap(); + let device_rg = DEVICE.read().unwrap(); + let Some(device) = device_rg.as_ref().map(|z| z) else { return }; let mut input_shmem = match create_shared_memory("YubideckInput", INPUT_SHMEM_SIZE, true) { Ok(s) => s, @@ -413,7 +415,19 @@ fn input_thread_proc() { loop { if let Err(e) = device.read_interrupt(0x81, usb_in, TIMEOUT) { - error!("Failed to read data from YubiDeck: {e:#}"); + match e { + rusb::Error::NoDevice | rusb::Error::Io => { + error!("Controller disconnected."); + + usb_in.iter_mut().for_each(|m| *m = 0); + + // Spawn a thread polling for a connection again + thread::spawn(yubideck_init); + + return; + } + _ => error!("Failed to read data from YubiDeck: {e:#}"), + } } // Update reader LED data