use num_enum::{IntoPrimitive, TryFromPrimitive};
use thiserror::Error;
use crate::channel::{
ChannelError,
HidppChannel,
HidppMessage,
LONG_REPORT_LENGTH,
SHORT_REPORT_LENGTH,
};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct MessageHeader {
pub device_index: u8,
pub sub_id: u8,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum Message {
Short(MessageHeader, [u8; SHORT_REPORT_LENGTH - 3]),
Long(MessageHeader, [u8; LONG_REPORT_LENGTH - 3]),
}
impl Message {
pub fn header(&self) -> MessageHeader {
match *self {
Message::Short(header, _) => header,
Message::Long(header, _) => header,
}
}
pub fn extend_payload(&self) -> [u8; LONG_REPORT_LENGTH - 3] {
match *self {
Message::Short(_, payload) => {
let mut data = [0; LONG_REPORT_LENGTH - 3];
data[..SHORT_REPORT_LENGTH - 3].copy_from_slice(&payload);
data
},
Message::Long(_, payload) => payload,
}
}
}
impl From<HidppMessage> for Message {
fn from(msg: HidppMessage) -> Self {
match msg {
HidppMessage::Short(payload) => Message::Short(
MessageHeader {
device_index: payload[0],
sub_id: payload[1],
},
payload[2..].try_into().unwrap(),
),
HidppMessage::Long(payload) => Message::Long(
MessageHeader {
device_index: payload[0],
sub_id: payload[1],
},
payload[2..].try_into().unwrap(),
),
}
}
}
impl From<Message> for HidppMessage {
fn from(msg: Message) -> Self {
match msg {
Message::Short(header, payload) => {
let mut data = [0u8; SHORT_REPORT_LENGTH - 1];
data[0] = header.device_index;
data[1] = header.sub_id;
data[2..].copy_from_slice(&payload);
HidppMessage::Short(data)
},
Message::Long(header, payload) => {
let mut data = [0u8; LONG_REPORT_LENGTH - 1];
data[0] = header.device_index;
data[1] = header.sub_id;
data[2..].copy_from_slice(&payload);
HidppMessage::Long(data)
},
}
}
}
fn is_rap_response(device: u8, msg_type: MessageType, address: u8, msg: &HidppMessage) -> bool {
let raw: [u8; 4] = match msg {
HidppMessage::Short(d) => d[..4].try_into().unwrap(),
HidppMessage::Long(d) => d[..4].try_into().unwrap(),
};
raw[0] == device
&& ((raw[1] == msg_type.into() && raw[2] == address)
|| (raw[1] == MessageType::Error.into()
&& raw[2] == msg_type.into()
&& raw[3] == address))
}
impl HidppChannel {
pub async fn read_register(
&self,
device: u8,
address: u8,
parameters: [u8; 3],
) -> Result<[u8; 3], Hidpp10Error> {
let mut data = [address, 0x00, 0x00, 0x00];
data[1..].copy_from_slice(¶meters);
let response = Message::from(
self.send(
Message::Short(
MessageHeader {
device_index: device,
sub_id: MessageType::GetRegister.into(),
},
data,
)
.into(),
move |raw| is_rap_response(device, MessageType::GetRegister, address, raw),
)
.await?,
);
let payload = response.extend_payload();
if response.header().sub_id == MessageType::Error.into() {
let err =
ErrorType::try_from(payload[2]).map_err(|_| Hidpp10Error::UnsupportedResponse)?;
return Err(Hidpp10Error::RegisterAccess(err));
}
Ok(payload[1..=3].try_into().unwrap())
}
pub async fn write_register(
&self,
device: u8,
address: u8,
payload: [u8; 3],
) -> Result<(), Hidpp10Error> {
let mut data = [address, 0x00, 0x00, 0x00];
data[1..].copy_from_slice(&payload);
let response = Message::from(
self.send(
Message::Short(
MessageHeader {
device_index: device,
sub_id: MessageType::SetRegister.into(),
},
data,
)
.into(),
move |raw| is_rap_response(device, MessageType::SetRegister, address, raw),
)
.await?,
);
if response.header().sub_id == MessageType::Error.into() {
let err = ErrorType::try_from(response.extend_payload()[2])
.map_err(|_| Hidpp10Error::UnsupportedResponse)?;
return Err(Hidpp10Error::RegisterAccess(err));
}
Ok(())
}
pub async fn read_long_register(
&self,
device: u8,
address: u8,
parameters: [u8; 3],
) -> Result<[u8; 16], Hidpp10Error> {
let mut data = [address, 0x00, 0x00, 0x00];
data[1..].copy_from_slice(¶meters);
let response = Message::from(
self.send(
Message::Short(
MessageHeader {
device_index: device,
sub_id: MessageType::GetLongRegister.into(),
},
data,
)
.into(),
move |raw| is_rap_response(device, MessageType::GetLongRegister, address, raw),
)
.await?,
);
let payload = response.extend_payload();
if response.header().sub_id == MessageType::Error.into() {
let err =
ErrorType::try_from(payload[2]).map_err(|_| Hidpp10Error::UnsupportedResponse)?;
return Err(Hidpp10Error::RegisterAccess(err));
}
Ok(payload[1..=16].try_into().unwrap())
}
pub async fn write_long_register(
&self,
device: u8,
address: u8,
payload: [u8; 16],
) -> Result<(), Hidpp10Error> {
let mut data = [0u8; 17];
data[0] = address;
data[1..].copy_from_slice(&payload);
let response = Message::from(
self.send(
Message::Long(
MessageHeader {
device_index: device,
sub_id: MessageType::SetLongRegister.into(),
},
data,
)
.into(),
move |raw| is_rap_response(device, MessageType::SetLongRegister, address, raw),
)
.await?,
);
if response.header().sub_id == MessageType::Error.into() {
let err = ErrorType::try_from(response.extend_payload()[2])
.map_err(|_| Hidpp10Error::UnsupportedResponse)?;
return Err(Hidpp10Error::RegisterAccess(err));
}
Ok(())
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
#[non_exhaustive]
#[repr(u8)]
pub enum MessageType {
SetRegister = 0x80,
GetRegister = 0x81,
SetLongRegister = 0x82,
GetLongRegister = 0x83,
Error = 0x8f,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
#[non_exhaustive]
#[repr(u8)]
pub enum ErrorType {
Success = 0x00,
InvalidSubId = 0x01,
InvalidAddress = 0x02,
InvalidValue = 0x03,
ConnectFail = 0x04,
TooManyDevices = 0x05,
AlreadyExists = 0x06,
Busy = 0x07,
UnknownDevice = 0x08,
ResourceError = 0x09,
RequestUnavailable = 0x0a,
InvalidParamValue = 0x0b,
WrongPinCode = 0x0c,
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum Hidpp10Error {
#[error("the HID++ channel returned an error")]
Channel(#[from] ChannelError),
#[error("a HID++1.0 register access resulted in an error")]
RegisterAccess(ErrorType),
#[error("the received response from the device is (partly) unsupported")]
UnsupportedResponse,
}