diff --git a/src/backends/tasoller_v2.rs b/src/backends/tasoller_v2.rs index 2d37ff1..8d6de43 100644 --- a/src/backends/tasoller_v2.rs +++ b/src/backends/tasoller_v2.rs @@ -107,7 +107,6 @@ pub fn set_led_colors( let air_rgb = if board == 0 { 0x96 } else { 0xB4 }; let output_start = if board == 0 { 96 } else { 168 }; - for (buf_chunk, state_chunk) in output[output_start..output_start + 72] .chunks_mut(3) diff --git a/src/lib.rs b/src/lib.rs index a437116..825e8eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,17 +45,15 @@ cfg_if::cfg_if! { if #[cfg(any(chuni, chusanapp))] { type SliderCallbackFn = unsafe extern "C" fn(data: *const u8); - use std::thread::{self, JoinHandle}; + use std::thread; use ::log::info; - use once_cell::sync::OnceCell; use rusb::{DeviceHandle, GlobalContext}; use parking_lot::RwLock; use crate::backends::ReadType; - static DEVICE: OnceCell> = OnceCell::new(); - static mut SLIDER_THREAD: OnceCell> = OnceCell::new(); + static DEVICE: RwLock>> = RwLock::new(None); static SLIDER_ACTIVE: AtomicBool = AtomicBool::new(false); static SLIDER_OUTPUT: RwLock<[u8; con_impl::OUTPUT_MEMORY_SIZE]> = RwLock::new([0u8; con_impl::OUTPUT_MEMORY_SIZE]); } @@ -109,6 +107,7 @@ cfg_if::cfg_if! { } pub static TIMEOUT: Duration = Duration::from_millis(20); +pub static DEVICE_POLLING_INTERVAL: Duration = Duration::from_millis(100); static mut INPUT_SHMEM: Option> = None; const INPUT_SHMEM_OS_ID: &str = "fcfe5b1100568d65af167d81acbed71d"; @@ -133,10 +132,9 @@ pub extern "C" fn chuni_io_get_api_version() -> u16 { pub extern "C" fn chuni_io_jvs_init() -> HRESULT { #[cfg(chuni)] { - if let Err(e) = device_init() { - error!("Could not initialize controller: {e:#?}"); - return E_FAIL; - } + // We don't care if we can't find the device at this time, it'll + // probably come eventually. + thread::spawn(device_init); } create_input_shared_memory() @@ -209,11 +207,9 @@ pub unsafe extern "C" fn chuni_io_slider_init() -> HRESULT { return S_OK; } - if let Err(e) = device_init() { - error!("Could not initialize controller: {e:#?}"); - return E_FAIL; - } - + // We don't care if we can't find the device at this time, it'll + // probably come eventually. + thread::spawn(device_init); create_input_shared_memory() } @@ -230,14 +226,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::SeqCst); 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 +248,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 +257,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::SeqCst); - - thread.join().expect("Couldn't join slider input thread"); } #[no_mangle] @@ -282,7 +271,9 @@ 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(); + let Some(device) = device_rg.as_ref().map(|z| z) else { return }; + let rgb = std::slice::from_raw_parts(rgb, 93); let mut output = SLIDER_OUTPUT.write(); @@ -318,8 +309,9 @@ pub unsafe extern "C" fn chuni_io_led_set_colors(board: u8, rgb: *const u8) { return; } - let Some(device) = DEVICE.get() else { return }; - let rgb = std::slice::from_raw_parts(rgb, if board == 0 { 153 } else { 183 }); + let device_rg = DEVICE.read(); + let Some(device) = device_rg.as_ref().map(|z| z) else { return }; + let rgb = std::slice::from_raw_parts(rgb, 256); let mut output = SLIDER_OUTPUT.write(); if let Err(e) = con_impl::set_led_colors(device, output.as_mut_slice(), board, rgb) { @@ -333,28 +325,41 @@ pub extern "C" fn chuni_io_led_set_colors(_rgb: *const u8) {} #[cfg(any(chuni, chusanapp))] fn device_init() -> Result<()> { - let Some(mut device) = - rusb::open_device_with_vid_pid(con_impl::DEVICE_VID, con_impl::DEVICE_PID) - else { - return Err(anyhow!("Device not found.")); - }; + { + let Some(mut global_device) = DEVICE.try_write_for(DEVICE_POLLING_INTERVAL) else { + return Ok(()) + }; - device.set_active_configuration(1)?; - device.claim_interface(0)?; - DEVICE - .set(device) - .map_err(|_| anyhow!("Could not store device handle"))?; + *global_device = None; + } - thread::spawn(input_thread_proc); + info!("Waiting for device..."); - Ok(()) + loop { + let Some(mut device) = + rusb::open_device_with_vid_pid(con_impl::DEVICE_VID, con_impl::DEVICE_PID) + else { + thread::sleep(DEVICE_POLLING_INTERVAL); + continue; + }; + + device.set_active_configuration(1)?; + device.claim_interface(0)?; + + let mut global_device = DEVICE.write(); + *global_device = Some(device); + + thread::spawn(input_thread_proc); + + return Ok(()) + } } #[cfg(any(chuni, chusanapp))] fn input_thread_proc() { info!("Input thread started"); - let mut shmem = match create_shared_memory(INPUT_SHMEM_OS_ID, con_impl::INPUT_MEMORY_SIZE, true) + let mut shmem = match create_shared_memory(INPUT_SHMEM_OS_ID, con_impl::INPUT_MEMORY_SIZE, false) { Ok(s) => s, Err(e) => { @@ -363,7 +368,8 @@ fn input_thread_proc() { } }; let usb_in = unsafe { shmem.as_slice_mut() }; - let device = DEVICE.get().unwrap(); + let device_rg = DEVICE.read(); + let device = device_rg.as_ref().unwrap(); loop { let result = match con_impl::READ_TYPE { @@ -372,15 +378,31 @@ fn input_thread_proc() { }; if let Err(e) = result { - error!("Failed to read data from controller: {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(device_init); + + return; + } + _ => error!("Could not read data from controller: {e:#?}"), + } } } } fn create_input_shared_memory() -> HRESULT { match create_shared_memory(INPUT_SHMEM_OS_ID, con_impl::INPUT_MEMORY_SIZE, false) { - Ok(s) => { - unsafe { INPUT_SHMEM = Some(Rc::new(s)) }; + Ok(mut s) => { + unsafe { + s.as_slice_mut().iter_mut().for_each(|m| *m = 0); + + INPUT_SHMEM = Some(Rc::new(s)) + }; S_OK } Err(e) => {