#![no_std]
#![feature(associated_type_defaults)]
pub mod buffer;
pub mod commands;
pub mod test;
use core::convert::TryFrom;
use core::fmt;
use heapless::Vec;
use num_enum::{IntoPrimitive, TryFromPrimitive};
#[cfg(feature = "defmt")]
use defmt::{error, trace, warn};
#[cfg(not(feature = "defmt"))]
use log::{error, trace, warn};
#[cfg(not(feature = "server"))]
macro_rules! warn {
(target: $target:expr, $($arg:tt)+) => {};
($($arg:tt)+) => {};
}
#[cfg(not(feature = "server"))]
macro_rules! error {
(target: $target:expr, $($arg:tt)+) => {};
($($arg:tt)+) => {};
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum HidIoPacketType {
Data = 0,
Ack = 1,
Nak = 2,
Sync = 3,
Continued = 4,
NaData = 5,
NaContinued = 6,
}
#[repr(u32)]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum HidIoCommandId {
SupportedIds = 0x00,
GetInfo = 0x01,
TestPacket = 0x02,
ResetHidIo = 0x03,
Reserved = 0x04,
GetProperties = 0x10,
KeyState = 0x11,
KeyboardLayout = 0x12,
KeyLayout = 0x13,
KeyShapes = 0x14,
LedLayout = 0x15,
FlashMode = 0x16,
UnicodeText = 0x17,
UnicodeState = 0x18,
HostMacro = 0x19,
SleepMode = 0x1A,
KllState = 0x20,
PixelSetting = 0x21,
PixelSet1c8b = 0x22,
PixelSet3c8b = 0x23,
PixelSet1c16b = 0x24,
PixelSet3c16b = 0x25,
DirectSet = 0x26,
OpenUrl = 0x30,
TerminalCmd = 0x31,
GetInputLayout = 0x32,
SetInputLayout = 0x33,
TerminalOut = 0x34,
HidKeyboard = 0x40,
HidKeyboardLed = 0x41,
HidMouse = 0x42,
HidJoystick = 0x43,
HidSystemCtrl = 0x44,
HidConsumerCtrl = 0x45,
ManufacturingTest = 0x50,
ManufacturingResult = 0x51,
Unused = 0xFFFF,
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum HidIoParseError {
BufferNotReady,
BufferDataTooSmall(usize),
InvalidContinuedIdByte(u8),
InvalidHidIoCommandId(u32),
InvalidPacketIdWidth(u8),
InvalidPacketType(u8),
MissingContinuedIdByte,
MissingPacketIdWidthByte,
MissingPacketTypeByte,
MissingPayloadLengthByte,
NotEnoughActualBytesPacketId { len: usize, id_width: usize },
NotEnoughPossibleBytesPacketId { len: u32, id_width: usize },
PayloadAddFailed(usize),
SerializationError,
SerializationFailedResultTooSmall(usize),
VecAddFailed,
VecResizeFailed,
}
#[repr(C)]
#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct HidIoPacketBuffer<const H: usize> {
pub ptype: HidIoPacketType,
pub id: HidIoCommandId,
pub max_len: u32,
pub data: Vec<u8, H>,
pub done: bool,
}
pub fn packet_type(packet_data: &[u8]) -> Result<HidIoPacketType, HidIoParseError> {
let packet_data_len = packet_data.len();
if packet_data_len < 1 {
return Err(HidIoParseError::MissingPacketTypeByte);
}
let ptype: u8 = (packet_data[0] & 0xE0) >> 5;
match ptype {
0 => Ok(HidIoPacketType::Data),
1 => Ok(HidIoPacketType::Ack),
2 => Ok(HidIoPacketType::Nak),
3 => Ok(HidIoPacketType::Sync),
4 => Ok(HidIoPacketType::Continued),
5 => Ok(HidIoPacketType::NaData),
6 => Ok(HidIoPacketType::NaContinued),
_ => Err(HidIoParseError::InvalidPacketType(ptype)),
}
}
pub fn payload_len(packet_data: &[u8]) -> Result<u32, HidIoParseError> {
let packet_data_len = packet_data.len();
if packet_data_len < 2 {
return Err(HidIoParseError::MissingPayloadLengthByte);
}
let upper_len = u32::from(packet_data[0] & 0x3);
let len = u32::from(packet_data[1]);
let payload_len: u32 = (upper_len << 8) | len;
Ok(payload_len)
}
pub fn packet_id_width(packet_data: &[u8]) -> Result<usize, HidIoParseError> {
let packet_data_len = packet_data.len();
if packet_data_len < 2 {
return Err(HidIoParseError::MissingPacketIdWidthByte);
}
match packet_data[0] & 0x08 {
0x00 => Ok(2), 0x08 => Ok(4), _ => Err(HidIoParseError::InvalidPacketIdWidth(packet_data[0])),
}
}
pub fn packet_id(packet_data: &[u8]) -> Result<u32, HidIoParseError> {
let packet_data_len = packet_data.len();
let id_width = packet_id_width(packet_data)?;
if payload_len(packet_data)? < id_width as u32 {
return Err(HidIoParseError::NotEnoughPossibleBytesPacketId {
len: payload_len(packet_data)?,
id_width,
});
}
if packet_data_len < id_width + 2 {
return Err(HidIoParseError::NotEnoughActualBytesPacketId {
len: packet_data_len,
id_width,
});
}
let mut id: u32 = 0;
let offset = 2;
for idx in 0..id_width {
id |= u32::from(packet_data[offset + idx]) << (idx * 8);
}
Ok(id)
}
pub fn continued_packet(packet_data: &[u8]) -> Result<bool, HidIoParseError> {
let packet_data_len = packet_data.len() as u32;
if packet_data_len < 1 {
return Err(HidIoParseError::MissingContinuedIdByte);
}
match packet_data[0] & 0x10 {
0x10 => Ok(true),
0x00 => Ok(false),
_ => Err(HidIoParseError::InvalidContinuedIdByte(packet_data[0])),
}
}
pub fn payload_start(packet_data: &[u8]) -> Result<usize, HidIoParseError> {
let id_width = packet_id_width(packet_data)?;
if payload_len(packet_data)? == 0 {
return Ok(2);
}
Ok(2 + id_width)
}
pub fn hid_bitmask2vec(bitmask: &[u8]) -> Result<Vec<u8, 32>, HidIoParseError> {
let mut data: Vec<u8, 32> = Vec::new();
for (byte_pos, byte) in bitmask.iter().enumerate() {
for b in 0..=7 {
let active = ((byte >> b) & 0x01) == 0x01;
if active {
let code = b + byte_pos * 8;
if data.push(code as u8).is_err() {
return Err(HidIoParseError::VecAddFailed);
}
}
}
}
Ok(data)
}
pub fn hid_vec2bitmask(codes: &[u8]) -> Result<Vec<u8, 32>, HidIoParseError> {
let mut data: Vec<u8, 32> = Vec::new(); if data.resize_default(32).is_err() {
return Err(HidIoParseError::VecResizeFailed);
}
for code in codes {
let byte_pos = code / 8; let bit_mask = 1 << (code - 8 * byte_pos); data[byte_pos as usize] |= bit_mask;
}
Ok(data)
}
impl<const H: usize> Default for HidIoPacketBuffer<H> {
fn default() -> Self {
HidIoPacketBuffer {
ptype: HidIoPacketType::Data,
id: HidIoCommandId::try_from(0).unwrap(),
max_len: 64, data: Vec::new(),
done: false,
}
}
}
impl<const H: usize> HidIoPacketBuffer<H> {
pub fn new() -> HidIoPacketBuffer<H> {
HidIoPacketBuffer {
..Default::default()
}
}
pub fn clear(&mut self) {
self.done = false;
self.data.resize_default(0).unwrap();
}
pub fn set(&mut self, buf: HidIoPacketBuffer<H>) {
self.ptype = buf.ptype;
self.id = buf.id;
self.max_len = buf.max_len;
self.data = buf.data;
self.done = buf.done;
}
fn id_width(&self) -> u8 {
match self.id as u32 {
0x00..=0xFFFF => 0, 0x01_0000..=0xFFFF_FFFF => 1, }
}
fn id_width_len(&self) -> u8 {
match self.id_width() {
0 => 2, 1 => 4, _ => 0,
}
}
fn hdr_len(&self) -> u8 {
2 + self.id_width_len()
}
fn payload_len(&self) -> u32 {
self.max_len - u32::from(self.hdr_len())
}
pub fn serialized_len(&self) -> u32 {
if self.ptype == HidIoPacketType::Sync {
return 1;
}
let hdr_len = self.hdr_len();
let data_len = self.data.len() as u32;
let payload_len = self.payload_len();
let fullpackets = (data_len / payload_len) * (payload_len + u32::from(hdr_len));
let partialpacket = if data_len % payload_len > 0 || data_len == 0 {
data_len % payload_len + u32::from(hdr_len)
} else {
0
};
fullpackets + partialpacket
}
pub fn append_payload(&mut self, new_data: &[u8]) -> bool {
if self.done {
warn!("HidIoPacketBuffer is already 'done'");
return false;
}
self.data.extend_from_slice(new_data).is_ok()
}
pub fn decode_packet(&mut self, packet_data: &[u8]) -> Result<u32, HidIoParseError> {
if self.done {
warn!("HidIoPacketBuffer is already 'done'");
return Ok(0);
}
let packet_data_len = packet_data.len() as u32;
let ptype = packet_type(packet_data)?;
if ptype == HidIoPacketType::Sync {
self.ptype = ptype;
self.done = true;
return Ok(1);
}
let payload_len = payload_len(packet_data)?;
let packet_len = payload_len + 2;
trace!(
"decode_packet: {:?} - ptype({:?}) payload_len({:?}) packet_len({:?})",
packet_data,
ptype,
payload_len,
packet_len
);
if packet_data_len - 2 < payload_len {
warn!(
"Dropping. Not enough bytes available in packet stream. got:{}, expected:{}",
packet_data_len - 2,
payload_len
);
return Ok(packet_data_len);
}
let id_num = packet_id(packet_data)?;
let id = match HidIoCommandId::try_from(id_num) {
Ok(id) => id,
Err(_) => {
error!("Failed to convert {} to HidIoCommandId", id_num);
return Err(HidIoParseError::InvalidHidIoCommandId(id_num));
}
};
if self.data.is_empty()
&& (ptype != HidIoPacketType::Continued && ptype != HidIoPacketType::NaContinued)
{
self.ptype = ptype;
self.id = id;
} else {
if self.data.is_empty() && ptype == HidIoPacketType::Continued {
warn!("Dropping. Invalid packet type when initializing buffer, HidIoPacketType::Continued");
return Ok(packet_len);
}
if self.data.is_empty() && ptype == HidIoPacketType::NaContinued {
warn!("Dropping. Invalid packet type when initializing buffer, HidIoPacketType::NaContinued");
return Ok(packet_len);
}
if !self.data.is_empty() {
match ptype {
HidIoPacketType::Continued | HidIoPacketType::NaContinued => {}
_ => {
warn!("Dropping buffer. Invalid packet type (non-HidIoPacketType::Continued) on a already initialized buffer: {} {}", ptype, self.data.is_empty());
self.clear();
return self.decode_packet(packet_data);
}
}
}
if self.id != id {
warn!(
"Dropping. Invalid incoming id:{:?}, expected:{:?}",
id, self.id
);
return Ok(packet_len);
}
}
let payload_start = payload_start(packet_data)?;
let id_width_len = packet_id_width(packet_data)?;
self.done = !continued_packet(packet_data)?;
let slice =
&packet_data[payload_start..payload_start + payload_len as usize - id_width_len];
match self.data.extend_from_slice(slice) {
Ok(_) => {}
Err(_) => {
return Err(HidIoParseError::PayloadAddFailed(slice.len()));
}
}
Ok(packet_len)
}
pub fn serialize_buffer<'a>(&self, data: &'a mut [u8]) -> Result<&'a [u8], HidIoParseError> {
if !self.done {
return Err(HidIoParseError::BufferNotReady);
}
let mut pos = 0;
let id_width = self.id_width();
let id_width_len = self.id_width_len();
let payload_len = self.payload_len();
let data_len = self.data.len() as u32;
let mut cont: bool = data_len > payload_len;
let packet_len: u16 = if cont {
payload_len as u16 + u16::from(id_width_len)
} else {
data_len as u16 + u16::from(id_width_len)
};
let upper_len: u8 = (packet_len >> 8) as u8;
let len: u8 = packet_len as u8;
let ptype: u8 = match self.ptype {
HidIoPacketType::Data => 0,
HidIoPacketType::Ack => 1,
HidIoPacketType::Nak => 2,
HidIoPacketType::Sync => 3,
HidIoPacketType::Continued => 4,
HidIoPacketType::NaData => 5,
HidIoPacketType::NaContinued => 6,
};
let mut id_vec: Vec<u8, 4> = Vec::new();
for idx in 0..id_width_len {
let id = (self.id as u32 >> (idx * 8)) as u8;
if id_vec.push(id).is_err() {
return Err(HidIoParseError::VecAddFailed);
}
}
let hdr_byte: u8 =
(ptype << 5) |
(u8::from(cont) << 4) |
(id_width << 3) |
(upper_len & 0x3);
pos = serialize_byte(data, hdr_byte, pos)?;
if self.ptype == HidIoPacketType::Sync {
return Ok(data);
}
pos = serialize_byte(data, len, pos)?;
pos = serialize_bytes(data, &id_vec[..id_width_len as usize], pos)?;
let slice = if cont {
&self.data[0..payload_len as usize]
} else {
&self.data[0..data_len as usize]
};
pos = serialize_bytes(data, slice, pos)?;
if !cont {
return Ok(data);
}
let mut payload_left = self.data.len() as u32 - payload_len;
let mut last_slice_index = payload_len as usize;
while cont {
cont = payload_left > payload_len;
let ptype = match self.ptype {
HidIoPacketType::Ack | HidIoPacketType::Nak | HidIoPacketType::Data => {
HidIoPacketType::Continued as u8
}
HidIoPacketType::NaData => HidIoPacketType::NaContinued as u8,
_ => {
warn!("Dropping. Invalid continued packet type: {:?}", self.ptype);
break;
}
};
let packet_len: u16 = if cont {
payload_len as u16 + u16::from(id_width_len)
} else {
payload_left as u16 + u16::from(id_width_len)
};
let upper_len: u8 = (packet_len >> 8) as u8;
let len: u8 = packet_len as u8;
let hdr_byte: u8 =
(ptype << 5) |
(u8::from(cont) << 4) |
(id_width << 3) |
(upper_len & 0x3);
pos = serialize_byte(data, hdr_byte, pos)?;
pos = serialize_byte(data, len, pos)?;
pos = serialize_bytes(data, &id_vec[..id_width_len as usize], pos)?;
let slice_end = if cont {
last_slice_index + payload_len as usize
} else {
data_len as usize
};
let slice = &self.data[last_slice_index..slice_end];
pos = serialize_bytes(data, slice, pos)?;
payload_left -= (slice_end - last_slice_index) as u32;
last_slice_index += payload_len as usize;
}
Ok(data)
}
}
fn serialize_byte(data: &mut [u8], byte: u8, pos: usize) -> Result<usize, HidIoParseError> {
if pos >= data.len() {
return Err(HidIoParseError::BufferDataTooSmall(pos - 1));
}
data[pos] = byte;
Ok(pos + 1)
}
fn serialize_bytes(data: &mut [u8], bytes: &[u8], pos: usize) -> Result<usize, HidIoParseError> {
if pos + bytes.len() > data.len() && !bytes.is_empty() {
return Err(HidIoParseError::BufferDataTooSmall(pos - 1));
}
for (i, byte) in bytes.iter().enumerate() {
data[pos + i] = *byte;
}
Ok(pos + bytes.len())
}
impl fmt::Display for HidIoPacketType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ptype_name = match *self {
HidIoPacketType::Data => "HidIoPacketBuffer::Data",
HidIoPacketType::Ack => "HidIoPacketBuffer::Ack",
HidIoPacketType::Nak => "HidIoPacketBuffer::Nak",
HidIoPacketType::Sync => "HidIoPacketBuffer::Sync",
HidIoPacketType::Continued => "HidIoPacketBuffer::Continued",
HidIoPacketType::NaData => "HidIoPacketBuffer::NaData",
HidIoPacketType::NaContinued => "HidIoPacketBuffer::NaContinued",
};
write!(f, "{ptype_name}")
}
}
impl<const H: usize> fmt::Display for HidIoPacketBuffer<H> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"\n{{\n ptype: {}\n id: {:?}\n max_len: {}\n done: {}\n data: {:#?}\n}}",
self.ptype, self.id, self.max_len, self.done, self.data,
)
}
}