use std::sync::Arc;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use crate::{
bcd,
channel::HidppChannel,
feature::{CreatableFeature, Feature},
nibble::U4,
protocol::v20::{self, Hidpp20Error},
};
#[derive(Clone)]
pub struct DeviceInformationFeatureV0 {
chan: Arc<HidppChannel>,
device_index: u8,
feature_index: u8,
}
impl CreatableFeature for DeviceInformationFeatureV0 {
const ID: u16 = 0x0003;
const STARTING_VERSION: u8 = 0;
fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
Self {
chan,
device_index,
feature_index,
}
}
}
impl Feature for DeviceInformationFeatureV0 {
}
impl DeviceInformationFeatureV0 {
pub async fn get_device_info(&self) -> Result<DeviceInformation, Hidpp20Error> {
let response = self
.chan
.send_v20(v20::Message::Short(
v20::MessageHeader {
device_index: self.device_index,
feature_index: self.feature_index,
function_id: U4::from_lo(0),
software_id: self.chan.get_sw_id(),
},
[0x00, 0x00, 0x00],
))
.await?;
let payload = response.extend_payload();
Ok(DeviceInformation {
entity_count: payload[0],
unit_id: payload[1..=4].try_into().unwrap(),
transport: DeviceTransport::from(payload[6]),
model_id: [
u16::from_be_bytes(payload[7..=8].try_into().unwrap()),
u16::from_be_bytes(payload[9..=10].try_into().unwrap()),
u16::from_be_bytes(payload[11..=12].try_into().unwrap()),
],
extended_model_id: payload[13],
capabilities: DeviceInformationCapabilities::from(payload[14]),
})
}
pub async fn get_fw_info(
&self,
entity_index: u8,
) -> Result<DeviceEntityFirmwareInfo, Hidpp20Error> {
let response = self
.chan
.send_v20(v20::Message::Short(
v20::MessageHeader {
device_index: self.device_index,
feature_index: self.feature_index,
function_id: U4::from_lo(1),
software_id: self.chan.get_sw_id(),
},
[entity_index, 0x00, 0x00],
))
.await?;
let payload = response.extend_payload();
Ok(DeviceEntityFirmwareInfo {
entity_type: DeviceEntityType::try_from(payload[0])
.map_err(|_| Hidpp20Error::UnsupportedResponse)?,
firmware_prefix: String::from_utf8(payload[1..=3].to_vec())
.map_err(|_| Hidpp20Error::UnsupportedResponse)?,
firmware_number: bcd::convert_packed_u8(payload[4])
.map_err(|_| Hidpp20Error::UnsupportedResponse)?,
revision: bcd::convert_packed_u8(payload[5])
.map_err(|_| Hidpp20Error::UnsupportedResponse)?,
build: bcd::convert_packed_u16(u16::from_be_bytes(payload[6..=7].try_into().unwrap()))
.map_err(|_| Hidpp20Error::UnsupportedResponse)?,
active: payload[8] & 1 != 0,
transport_pid: u16::from_be_bytes(payload[9..=10].try_into().unwrap()),
extra_version: payload[11..=15].try_into().unwrap(),
})
}
pub async fn get_serial_number(&self) -> Result<String, Hidpp20Error> {
let response = self
.chan
.send_v20(v20::Message::Short(
v20::MessageHeader {
device_index: self.device_index,
feature_index: self.feature_index,
function_id: U4::from_lo(2),
software_id: self.chan.get_sw_id(),
},
[0x00, 0x00, 0x00],
))
.await?;
let payload = response.extend_payload();
String::from_utf8(payload[..12].to_vec()).map_err(|_| Hidpp20Error::UnsupportedResponse)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub struct DeviceInformation {
pub entity_count: u8,
pub unit_id: [u8; 4],
pub transport: DeviceTransport,
pub model_id: [u16; 3],
pub extended_model_id: u8,
pub capabilities: DeviceInformationCapabilities,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub struct DeviceTransport {
pub usb: bool,
pub e_quad: bool,
pub btle: bool,
pub bluetooth: bool,
}
impl From<u8> for DeviceTransport {
fn from(value: u8) -> Self {
Self {
usb: value & (1 << 3) != 0,
e_quad: value & (1 << 2) != 0,
btle: value & (1 << 1) != 0,
bluetooth: value & 1 != 0,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub struct DeviceInformationCapabilities {
pub serial_number: bool,
}
impl From<u8> for DeviceInformationCapabilities {
fn from(value: u8) -> Self {
Self {
serial_number: value & 1 != 0,
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub struct DeviceEntityFirmwareInfo {
pub entity_type: DeviceEntityType,
pub firmware_prefix: String,
pub firmware_number: u8,
pub revision: u8,
pub build: u16,
pub active: bool,
pub transport_pid: u16,
pub extra_version: [u8; 5],
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
#[non_exhaustive]
#[repr(u8)]
pub enum DeviceEntityType {
MainApplication = 0,
Bootloader = 1,
Hardware = 2,
Touchpad = 3,
OpticalSensor = 4,
Softdevice = 5,
RfCompanionMcu = 6,
FactoryApplication = 7,
RgbCustomEffect = 8,
MotorDrive = 9,
}