Add support for hot plugging controller
This commit is contained in:
parent
e1b285782d
commit
2328447f48
@ -108,7 +108,6 @@ pub fn set_led_colors<T: UsbContext>(
|
|||||||
let air_rgb = if board == 0 { 0x96 } else { 0xB4 };
|
let air_rgb = if board == 0 { 0x96 } else { 0xB4 };
|
||||||
let output_start = if board == 0 { 96 } else { 168 };
|
let output_start = if board == 0 { 96 } else { 168 };
|
||||||
|
|
||||||
|
|
||||||
for (buf_chunk, state_chunk) in output[output_start..output_start + 72]
|
for (buf_chunk, state_chunk) in output[output_start..output_start + 72]
|
||||||
.chunks_mut(3)
|
.chunks_mut(3)
|
||||||
.take(24)
|
.take(24)
|
||||||
|
94
src/lib.rs
94
src/lib.rs
@ -45,17 +45,15 @@ cfg_if::cfg_if! {
|
|||||||
if #[cfg(any(chuni, chusanapp))] {
|
if #[cfg(any(chuni, chusanapp))] {
|
||||||
type SliderCallbackFn = unsafe extern "C" fn(data: *const u8);
|
type SliderCallbackFn = unsafe extern "C" fn(data: *const u8);
|
||||||
|
|
||||||
use std::thread::{self, JoinHandle};
|
use std::thread;
|
||||||
|
|
||||||
use ::log::info;
|
use ::log::info;
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use rusb::{DeviceHandle, GlobalContext};
|
use rusb::{DeviceHandle, GlobalContext};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
|
||||||
use crate::backends::ReadType;
|
use crate::backends::ReadType;
|
||||||
|
|
||||||
static DEVICE: OnceCell<DeviceHandle<GlobalContext>> = OnceCell::new();
|
static DEVICE: RwLock<Option<DeviceHandle<GlobalContext>>> = RwLock::new(None);
|
||||||
static mut SLIDER_THREAD: OnceCell<JoinHandle<()>> = OnceCell::new();
|
|
||||||
static SLIDER_ACTIVE: AtomicBool = AtomicBool::new(false);
|
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]);
|
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 TIMEOUT: Duration = Duration::from_millis(20);
|
||||||
|
pub static DEVICE_POLLING_INTERVAL: Duration = Duration::from_millis(100);
|
||||||
static mut INPUT_SHMEM: Option<Rc<Shmem>> = None;
|
static mut INPUT_SHMEM: Option<Rc<Shmem>> = None;
|
||||||
const INPUT_SHMEM_OS_ID: &str = "fcfe5b1100568d65af167d81acbed71d";
|
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 {
|
pub extern "C" fn chuni_io_jvs_init() -> HRESULT {
|
||||||
#[cfg(chuni)]
|
#[cfg(chuni)]
|
||||||
{
|
{
|
||||||
if let Err(e) = device_init() {
|
// We don't care if we can't find the device at this time, it'll
|
||||||
error!("Could not initialize controller: {e:#?}");
|
// probably come eventually.
|
||||||
return E_FAIL;
|
thread::spawn(device_init);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
create_input_shared_memory()
|
create_input_shared_memory()
|
||||||
@ -209,11 +207,9 @@ pub unsafe extern "C" fn chuni_io_slider_init() -> HRESULT {
|
|||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = device_init() {
|
// We don't care if we can't find the device at this time, it'll
|
||||||
error!("Could not initialize controller: {e:#?}");
|
// probably come eventually.
|
||||||
return E_FAIL;
|
thread::spawn(device_init);
|
||||||
}
|
|
||||||
|
|
||||||
create_input_shared_memory()
|
create_input_shared_memory()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,14 +226,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::SeqCst);
|
SLIDER_ACTIVE.store(true, Ordering::SeqCst);
|
||||||
|
|
||||||
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 +248,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 +257,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::SeqCst);
|
SLIDER_ACTIVE.store(false, Ordering::SeqCst);
|
||||||
|
|
||||||
thread.join().expect("Couldn't join slider input thread");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -282,7 +271,9 @@ 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();
|
||||||
|
let Some(device) = device_rg.as_ref().map(|z| z) else { return };
|
||||||
|
|
||||||
let rgb = std::slice::from_raw_parts(rgb, 93);
|
let rgb = std::slice::from_raw_parts(rgb, 93);
|
||||||
let mut output = SLIDER_OUTPUT.write();
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(device) = DEVICE.get() else { return };
|
let device_rg = DEVICE.read();
|
||||||
let rgb = std::slice::from_raw_parts(rgb, if board == 0 { 153 } else { 183 });
|
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();
|
let mut output = SLIDER_OUTPUT.write();
|
||||||
|
|
||||||
if let Err(e) = con_impl::set_led_colors(device, output.as_mut_slice(), board, rgb) {
|
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))]
|
#[cfg(any(chuni, chusanapp))]
|
||||||
fn device_init() -> Result<()> {
|
fn device_init() -> Result<()> {
|
||||||
|
{
|
||||||
|
let Some(mut global_device) = DEVICE.try_write_for(DEVICE_POLLING_INTERVAL) else {
|
||||||
|
return Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
*global_device = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Waiting for device...");
|
||||||
|
|
||||||
|
loop {
|
||||||
let Some(mut device) =
|
let Some(mut device) =
|
||||||
rusb::open_device_with_vid_pid(con_impl::DEVICE_VID, con_impl::DEVICE_PID)
|
rusb::open_device_with_vid_pid(con_impl::DEVICE_VID, con_impl::DEVICE_PID)
|
||||||
else {
|
else {
|
||||||
return Err(anyhow!("Device not found."));
|
thread::sleep(DEVICE_POLLING_INTERVAL);
|
||||||
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
device.set_active_configuration(1)?;
|
device.set_active_configuration(1)?;
|
||||||
device.claim_interface(0)?;
|
device.claim_interface(0)?;
|
||||||
DEVICE
|
|
||||||
.set(device)
|
let mut global_device = DEVICE.write();
|
||||||
.map_err(|_| anyhow!("Could not store device handle"))?;
|
*global_device = Some(device);
|
||||||
|
|
||||||
thread::spawn(input_thread_proc);
|
thread::spawn(input_thread_proc);
|
||||||
|
|
||||||
Ok(())
|
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 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,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -363,7 +368,8 @@ fn input_thread_proc() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let usb_in = unsafe { shmem.as_slice_mut() };
|
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 {
|
loop {
|
||||||
let result = match con_impl::READ_TYPE {
|
let result = match con_impl::READ_TYPE {
|
||||||
@ -372,15 +378,31 @@ fn input_thread_proc() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = result {
|
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 {
|
fn create_input_shared_memory() -> HRESULT {
|
||||||
match create_shared_memory(INPUT_SHMEM_OS_ID, con_impl::INPUT_MEMORY_SIZE, false) {
|
match create_shared_memory(INPUT_SHMEM_OS_ID, con_impl::INPUT_MEMORY_SIZE, false) {
|
||||||
Ok(s) => {
|
Ok(mut s) => {
|
||||||
unsafe { INPUT_SHMEM = Some(Rc::new(s)) };
|
unsafe {
|
||||||
|
s.as_slice_mut().iter_mut().for_each(|m| *m = 0);
|
||||||
|
|
||||||
|
INPUT_SHMEM = Some(Rc::new(s))
|
||||||
|
};
|
||||||
S_OK
|
S_OK
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user