Support hotplugging (maybe)?

This commit is contained in:
beerpsi 2024-03-23 01:40:03 +07:00
parent bfea435bfc
commit 0115888309
4 changed files with 54 additions and 40 deletions

4
Cargo.lock generated
View File

@ -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",

View File

@ -1,6 +1,6 @@
[package]
name = "aimeio-yubideck"
version = "0.1.2"
version = "0.1.3"
edition = "2021"
[lib]

View File

@ -1,6 +1,6 @@
[package]
name = "chuniio-yubideck"
version = "0.1.2"
version = "0.1.3"
edition = "2021"
[lib]

View File

@ -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<Rc<Shmem>> = None;
static mut OUTPUT_SHMEM: Option<Rc<Mutex<Shmem>>> = 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<DeviceHandle<GlobalContext>> = OnceLock::new();
static mut SLIDER_THREAD: OnceLock<JoinHandle<()>> = OnceLock::new();
static DEVICE: RwLock<Option<DeviceHandle<GlobalContext>>> = 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