use std::sync::Arc;
use num_enum::TryFromPrimitive;
use crate::{
channel::HidppChannel,
feature::{CreatableFeature, Feature, FeatureEndpoint},
protocol::v20::Hidpp20Error,
};
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct HostsInfoCapabilities: u8 {
const GET_NAME = 1 << 0;
const SET_NAME = 1 << 1;
const MOVE_HOST = 1 << 2;
const DELETE_HOST = 1 << 3;
const SET_OS_VERSION = 1 << 4;
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct HostDescriptorCapabilities: u8 {
const EQUAD = 1 << 0;
const USB = 1 << 1;
const BT = 1 << 2;
const BLE = 1 << 3;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum HostIndex {
Current,
Slot(u8),
}
impl From<HostIndex> for u8 {
fn from(value: HostIndex) -> Self {
match value {
HostIndex::Current => 0xff,
HostIndex::Slot(index) => index,
}
}
}
impl From<u8> for HostIndex {
fn from(value: u8) -> Self {
if value == 0xff {
Self::Current
} else {
Self::Slot(value)
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
#[repr(u8)]
pub enum HostSlotStatus {
Empty = 0,
Paired = 1,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
#[repr(u8)]
pub enum HostBusType {
Undefined = 0,
Equad = 1,
Usb = 2,
Bt = 3,
Ble = 4,
BlePro = 5,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct HostsInfoFeatureInfo {
pub capabilities: HostsInfoCapabilities,
pub descriptor_capabilities: HostDescriptorCapabilities,
pub host_count: u8,
pub current_host: HostIndex,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct HostInfo {
pub host_index: HostIndex,
pub status: HostSlotStatus,
pub bus_type: HostBusType,
pub page_count: u8,
pub name_len: u8,
pub name_max_len: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct HostDescriptorPage {
pub host_index: HostIndex,
pub bus_type: HostBusType,
pub page_index: u8,
pub body: [u8; 14],
}
#[derive(Clone)]
pub struct HostsInfoFeature {
endpoint: FeatureEndpoint,
}
impl CreatableFeature for HostsInfoFeature {
const ID: u16 = 0x1815;
const STARTING_VERSION: u8 = 2;
fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
Self {
endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
}
}
}
impl Feature for HostsInfoFeature {}
impl HostsInfoFeature {
pub async fn get_feature_info(&self) -> Result<HostsInfoFeatureInfo, Hidpp20Error> {
let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
Ok(HostsInfoFeatureInfo {
capabilities: HostsInfoCapabilities::from_bits_retain(payload[0]),
descriptor_capabilities: HostDescriptorCapabilities::from_bits_retain(payload[1]),
host_count: payload[2],
current_host: HostIndex::from(payload[3]),
})
}
pub async fn get_host_info(&self, host: HostIndex) -> Result<HostInfo, Hidpp20Error> {
let payload = self
.endpoint
.call(1, [u8::from(host), 0, 0])
.await?
.extend_payload();
Ok(HostInfo {
host_index: HostIndex::from(payload[0]),
status: HostSlotStatus::try_from(payload[1])
.map_err(|_| Hidpp20Error::UnsupportedResponse)?,
bus_type: HostBusType::try_from(payload[2])
.map_err(|_| Hidpp20Error::UnsupportedResponse)?,
page_count: payload[3],
name_len: payload[4],
name_max_len: payload[5],
})
}
pub async fn get_host_descriptor(
&self,
host: HostIndex,
page: u8,
) -> Result<HostDescriptorPage, Hidpp20Error> {
let payload = self
.endpoint
.call(2, [u8::from(host), page, 0])
.await?
.extend_payload();
let mut body = [0; 14];
body.copy_from_slice(&payload[2..16]);
Ok(HostDescriptorPage {
host_index: HostIndex::from(payload[0]),
bus_type: HostBusType::try_from(payload[1] >> 4)
.map_err(|_| Hidpp20Error::UnsupportedResponse)?,
page_index: payload[1] & 0x0f,
body,
})
}
}