forked from beerpsi/chuniio-yubideck
Support hotplugging (maybe)?
This commit is contained in:
parent
bfea435bfc
commit
0115888309
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -13,7 +13,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aimeio-yubideck"
|
name = "aimeio-yubideck"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"faster-hex",
|
"faster-hex",
|
||||||
@ -89,7 +89,7 @@ checksum = "77e53693616d3075149f4ead59bdeecd204ac6b8192d8969757601b74bddf00f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chuniio-yubideck"
|
name = "chuniio-yubideck"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "aimeio-yubideck"
|
name = "aimeio-yubideck"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "chuniio-yubideck"
|
name = "chuniio-yubideck"
|
||||||
version = "0.1.2"
|
version = "0.1.3"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
@ -9,6 +9,7 @@ use std::{
|
|||||||
atomic::Ordering,
|
atomic::Ordering,
|
||||||
Mutex,
|
Mutex,
|
||||||
},
|
},
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ::log::error;
|
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 INPUT_SHMEM: Option<Rc<Shmem>> = None;
|
||||||
static mut OUTPUT_SHMEM: Option<Rc<Mutex<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! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(any(chuni, chusanapp))] {
|
if #[cfg(any(chuni, chusanapp))] {
|
||||||
use std::{
|
use std::{sync::{atomic::AtomicBool, RwLock}, thread};
|
||||||
sync::{OnceLock, atomic::AtomicBool},
|
|
||||||
thread::{self, JoinHandle},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::Result;
|
||||||
use ::log::info;
|
use ::log::info;
|
||||||
use rusb::{DeviceHandle, GlobalContext};
|
use rusb::{DeviceHandle, GlobalContext};
|
||||||
|
|
||||||
@ -41,8 +39,7 @@ cfg_if::cfg_if! {
|
|||||||
|
|
||||||
const TIMEOUT: Duration = Duration::from_millis(20);
|
const TIMEOUT: Duration = Duration::from_millis(20);
|
||||||
|
|
||||||
static DEVICE: OnceLock<DeviceHandle<GlobalContext>> = OnceLock::new();
|
static DEVICE: RwLock<Option<DeviceHandle<GlobalContext>>> = RwLock::new(None);
|
||||||
static mut SLIDER_THREAD: OnceLock<JoinHandle<()>> = OnceLock::new();
|
|
||||||
static SLIDER_ACTIVE: AtomicBool = AtomicBool::new(false);
|
static SLIDER_ACTIVE: AtomicBool = AtomicBool::new(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,10 +125,7 @@ pub extern "C" fn chuni_io_jvs_init() -> HRESULT {
|
|||||||
|
|
||||||
#[cfg(not(amdaemon))]
|
#[cfg(not(amdaemon))]
|
||||||
{
|
{
|
||||||
if let Err(e) = yubideck_init() {
|
thread::spawn(yubideck_init);
|
||||||
error!("Could not initialize YubiDeck: {e:#?}");
|
|
||||||
return E_FAIL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
S_OK
|
S_OK
|
||||||
@ -228,14 +222,15 @@ pub unsafe extern "C" fn chuni_io_slider_start(callback: *const c_void) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if SLIDER_THREAD.get().is_some() {
|
if SLIDER_ACTIVE.load(Ordering::SeqCst) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SLIDER_ACTIVE.store(true, Ordering::Relaxed);
|
SLIDER_ACTIVE.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
let callback = std::mem::transmute::<_, SliderCallbackFn>(callback);
|
let callback = std::mem::transmute::<_, SliderCallbackFn>(callback);
|
||||||
let thread = thread::spawn(move || {
|
|
||||||
|
thread::spawn(move || {
|
||||||
let Some(input_shmem) = (unsafe { &INPUT_SHMEM }) else {
|
let Some(input_shmem) = (unsafe { &INPUT_SHMEM }) else {
|
||||||
return;
|
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));
|
thread::sleep(Duration::from_nanos(1_000_000));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
SLIDER_THREAD.set(thread).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -262,13 +255,7 @@ pub extern "C" fn chuni_io_slider_start(_callback: *const c_void) {}
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[cfg(any(chuni, chusanapp))]
|
#[cfg(any(chuni, chusanapp))]
|
||||||
pub extern "C" fn chuni_io_slider_stop() {
|
pub extern "C" fn chuni_io_slider_stop() {
|
||||||
let Some(thread) = (unsafe { SLIDER_THREAD.take() }) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
SLIDER_ACTIVE.store(false, Ordering::Relaxed);
|
SLIDER_ACTIVE.store(false, Ordering::Relaxed);
|
||||||
|
|
||||||
thread.join().expect("Couldn't join slider input thread");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -282,7 +269,8 @@ pub unsafe extern "C" fn chuni_io_slider_set_leds(rgb: *const u8) {
|
|||||||
return;
|
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 {
|
let Some(out_shmem) = &OUTPUT_SHMEM else {
|
||||||
error!("OUTPUT_SHMEM is unset.");
|
error!("OUTPUT_SHMEM is unset.");
|
||||||
return;
|
return;
|
||||||
@ -333,7 +321,8 @@ pub extern "C" fn chuni_io_led_init() -> HRESULT {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[cfg(any(chuni, chusanapp))]
|
#[cfg(any(chuni, chusanapp))]
|
||||||
pub unsafe extern "C" fn chuni_io_led_set_colors(board: u8, rgb: *const u8) {
|
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 {
|
let Some(out_shmem) = &OUTPUT_SHMEM else {
|
||||||
error!("OUTPUT_SHMEM is unset.");
|
error!("OUTPUT_SHMEM is unset.");
|
||||||
return;
|
return;
|
||||||
@ -372,26 +361,39 @@ pub extern "C" fn chuni_io_led_set_colors(_rgb: *const u8) {}
|
|||||||
|
|
||||||
#[cfg(any(chuni, chusanapp))]
|
#[cfg(any(chuni, chusanapp))]
|
||||||
fn yubideck_init() -> Result<()> {
|
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)?;
|
info!("Waiting for device...");
|
||||||
device.claim_interface(0)?;
|
|
||||||
DEVICE
|
|
||||||
.set(device)
|
|
||||||
.map_err(|e| anyhow!("Cannot store device handle: {e:#?}"))?;
|
|
||||||
|
|
||||||
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))]
|
#[cfg(any(chuni, chusanapp))]
|
||||||
fn input_thread_proc() {
|
fn input_thread_proc() {
|
||||||
info!("Input thread started");
|
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) {
|
let mut input_shmem = match create_shared_memory("YubideckInput", INPUT_SHMEM_SIZE, true) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
@ -413,7 +415,19 @@ fn input_thread_proc() {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Err(e) = device.read_interrupt(0x81, usb_in, TIMEOUT) {
|
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
|
// Update reader LED data
|
||||||
|
Loading…
Reference in New Issue
Block a user