use std::sync::{Arc, Mutex};
use std::os::raw::c_void;
use crate::utils::ffi;
pub const MAX_GROUPS: usize = 32;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HotConnectStatus {
Unknown = -1,
Absent = 0,
Present = 1,
}
impl HotConnectStatus {
pub fn from_raw(v: i32) -> Self {
match v {
1 => Self::Present,
0 => Self::Absent,
_ => Self::Unknown,
}
}
}
#[derive(Clone, Debug)]
pub struct HotConnectGroup {
pub group_id: u16,
pub alias_address: u16,
pub vendor_id: u32,
pub product_code: u32,
pub is_present: bool,
pub detected_slave_index: u16,
}
impl HotConnectGroup {
pub fn status(&self) -> HotConnectStatus {
if self.is_present {
HotConnectStatus::Present
} else {
HotConnectStatus::Absent
}
}
}
#[repr(C, packed)]
#[derive(Clone, Copy)]
#[allow(dead_code)]
struct HotConnectGroupNative {
group_id: u16,
alias_address: u16,
vendor_id: u32,
product_code: u32,
is_present: i32,
detected_slave_idx: u16,
reserved: u16,
}
pub struct HotConnect {
master_index: u16,
}
impl HotConnect {
pub fn new(master_index: u16) -> Self {
Self { master_index }
}
pub fn add_group(&self, group_id: u16, alias: u16,
vendor_id: u32, product_code: u32) -> bool {
unsafe {
ffi::HotConnectAddGroup(self.master_index, group_id, alias, vendor_id, product_code) != 0
}
}
pub fn remove_group(&self, group_id: u16) -> bool {
unsafe { ffi::HotConnectRemoveGroup(self.master_index, group_id) != 0 }
}
pub fn group_status(&self, group_id: u16) -> HotConnectStatus {
HotConnectStatus::from_raw(unsafe {
ffi::HotConnectGetGroupStatus(self.master_index, group_id)
})
}
pub fn clear_all(&self) {
unsafe { ffi::HotConnectClearAll(self.master_index) };
}
pub fn group_count(&self) -> i32 {
unsafe { ffi::HotConnectGetGroupCount(self.master_index) }
}
pub fn enumerate(&self) -> Vec<HotConnectGroup> {
let mut buf = [HotConnectGroupNative {
group_id: 0, alias_address: 0, vendor_id: 0, product_code: 0,
is_present: 0, detected_slave_idx: 0, reserved: 0,
}; MAX_GROUPS];
let n = unsafe {
ffi::HotConnectEnumerate(
self.master_index, buf.as_mut_ptr() as *mut c_void, MAX_GROUPS as i32,
)
};
if n <= 0 { return Vec::new(); }
let cnt = (n as usize).min(MAX_GROUPS);
let mut out = Vec::with_capacity(cnt);
for i in 0..cnt {
let entry = &buf[i];
let gid = unsafe { std::ptr::addr_of!(entry.group_id).read_unaligned() };
let alias = unsafe { std::ptr::addr_of!(entry.alias_address).read_unaligned() };
let vid = unsafe { std::ptr::addr_of!(entry.vendor_id).read_unaligned() };
let pid = unsafe { std::ptr::addr_of!(entry.product_code).read_unaligned() };
let present = unsafe { std::ptr::addr_of!(entry.is_present).read_unaligned() };
let det = unsafe { std::ptr::addr_of!(entry.detected_slave_idx).read_unaligned() };
out.push(HotConnectGroup {
group_id: gid,
alias_address: alias,
vendor_id: vid,
product_code: pid,
is_present: present != 0,
detected_slave_index: det,
});
}
out
}
}
#[derive(Clone, Copy, Debug)]
pub struct HotPlugIdentityMismatch {
pub master_index: u16,
pub slave_index: u16,
pub expected_vendor: u32,
pub expected_product: u32,
pub expected_revision: u32,
pub actual_vendor: u32,
pub actual_product: u32,
pub actual_revision: u32,
}
type HotPlugList = Arc<Mutex<Vec<Box<dyn Fn(HotPlugIdentityMismatch) + Send + 'static>>>>;
fn hotplug_list() -> &'static HotPlugList {
use std::sync::OnceLock;
static INSTANCE: OnceLock<HotPlugList> = OnceLock::new();
INSTANCE.get_or_init(|| Arc::new(Mutex::new(Vec::new())))
}
pub fn on_hotplug<F>(callback: F)
where
F: Fn(HotPlugIdentityMismatch) + Send + 'static,
{
if let Ok(mut v) = hotplug_list().lock() {
v.push(Box::new(callback));
}
}
pub fn clear_hotplug_listeners() {
if let Ok(mut v) = hotplug_list().lock() {
v.clear();
}
}
pub fn dispatch_hotplug(ev: HotPlugIdentityMismatch) {
let listeners = match hotplug_list().lock() {
Ok(g) => g,
Err(p) => p.into_inner(),
};
for cb in listeners.iter() {
cb(ev);
}
}