use crate::types::*;
use crate::{ensure, ParserError};
use thiserror::Error;
#[cfg(feature = "hut")]
use hut;
use alloc::{borrow::ToOwned, format, string::String, vec, vec::Vec};
fn bit(bits: u32, bit: u8) -> bool {
assert!(bit < 32);
bits & (1 << bit) != 0
}
struct HidBytes(Vec<u8>);
impl HidBytes {
fn take(self) -> Vec<u8> {
self.0
}
}
impl core::ops::Deref for HidBytes {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<u32> for HidBytes {
fn from(value: u32) -> HidBytes {
let bytes = value.to_le_bytes();
let cutoff = match value {
0..=255 => 1,
256..=0xffff => 2,
_ => 4,
};
HidBytes(bytes[0..cutoff].to_vec())
}
}
impl From<u16> for HidBytes {
fn from(value: u16) -> HidBytes {
let bytes = value.to_le_bytes();
let cutoff = match value {
0..=255 => 1,
_ => 2,
};
HidBytes(bytes[0..cutoff].to_vec())
}
}
impl From<u8> for HidBytes {
fn from(value: u8) -> HidBytes {
HidBytes(vec![value])
}
}
impl From<usize> for HidBytes {
fn from(value: usize) -> HidBytes {
let bytes = value.to_le_bytes();
let cutoff = match value {
0..=255 => 1,
256..=0xffff => 2,
_ => 4,
};
HidBytes(bytes[0..cutoff].to_vec())
}
}
impl From<i32> for HidBytes {
fn from(value: i32) -> HidBytes {
const MIN16: i32 = i16::MIN as i32;
const MAX16: i32 = i16::MAX as i32;
let bytes = match value {
-128..=127 => (value as i8).to_le_bytes().to_vec(),
MIN16..=MAX16 => (value as i16).to_le_bytes().to_vec(),
_ => value.to_le_bytes().to_vec(),
};
HidBytes(bytes)
}
}
impl From<i16> for HidBytes {
fn from(value: i16) -> HidBytes {
let bytes = match value {
-128..=127 => (value as i8).to_le_bytes().to_vec(),
_ => value.to_le_bytes().to_vec(),
};
HidBytes(bytes)
}
}
impl From<i8> for HidBytes {
fn from(value: i8) -> HidBytes {
HidBytes(vec![value as u8])
}
}
pub(crate) struct HidValue {
value: u32,
nbytes: usize,
}
impl HidValue {
pub(crate) fn len(&self) -> usize {
self.nbytes
}
}
impl TryFrom<&[u8]> for HidValue {
type Error = HidError;
fn try_from(bytes: &[u8]) -> Result<HidValue> {
ensure!(!bytes.is_empty(), HidError::InsufficientData);
let value = match bytes.len() {
1 => bytes[0] as u32,
2 => u16::from_le_bytes(bytes[0..2].try_into().unwrap()) as u32,
4 => u32::from_le_bytes(bytes[0..4].try_into().unwrap()),
_ => panic!("Size {} cannot happen", bytes.len()),
};
Ok(HidValue {
value,
nbytes: bytes.len(),
})
}
}
impl From<&HidValue> for usize {
fn from(v: &HidValue) -> usize {
v.value as usize
}
}
impl From<HidValue> for usize {
fn from(v: HidValue) -> usize {
usize::from(&v)
}
}
impl From<&HidValue> for u32 {
fn from(v: &HidValue) -> u32 {
v.value
}
}
impl From<HidValue> for u32 {
fn from(v: HidValue) -> u32 {
u32::from(&v)
}
}
impl From<&HidValue> for u16 {
fn from(v: &HidValue) -> u16 {
(v.value & 0xFFFF) as u16
}
}
impl From<HidValue> for u16 {
fn from(v: HidValue) -> u16 {
u16::from(&v)
}
}
impl From<&HidValue> for u8 {
fn from(v: &HidValue) -> u8 {
(v.value & 0xFF) as u8
}
}
impl From<HidValue> for u8 {
fn from(v: HidValue) -> u8 {
u8::from(&v)
}
}
impl From<&HidValue> for i32 {
fn from(v: &HidValue) -> i32 {
match v.len() {
1 => ((v.value & 0xFF) as i8) as i32,
2 => ((v.value & 0xFFFF) as i16) as i32,
4 => v.value as i32,
_ => panic!("Size {} cannot happen", v.len()),
}
}
}
impl From<HidValue> for i32 {
fn from(v: HidValue) -> i32 {
i32::from(&v)
}
}
#[derive(Error, Debug)]
pub enum HidError {
#[error("Invalid data: {message}")]
InvalidData { message: String },
#[error("Insufficient data")]
InsufficientData,
}
type Result<T> = core::result::Result<T, HidError>;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ItemType {
Main(MainItem),
Global(GlobalItem),
Local(LocalItem),
Long,
Reserved,
}
impl ItemType {
pub fn as_bytes(&self) -> Vec<u8> {
match self {
ItemType::Main(item) => item.as_bytes(),
ItemType::Global(item) => item.as_bytes(),
ItemType::Local(item) => item.as_bytes(),
ItemType::Long | ItemType::Reserved => vec![],
}
}
}
#[cfg(feature = "hut")]
impl From<&hut::UsagePage> for ItemType {
fn from(hut: &hut::UsagePage) -> ItemType {
GlobalItem::UsagePage(UsagePage::from(hut)).into()
}
}
#[cfg(feature = "hut")]
impl From<hut::UsagePage> for ItemType {
fn from(hut: hut::UsagePage) -> ItemType {
ItemType::from(&hut)
}
}
#[cfg(feature = "hut")]
impl From<&hut::Usage> for ItemType {
fn from(hut: &hut::Usage) -> ItemType {
LocalItem::Usage(UsagePage::from(hut), UsageId::from(hut)).into()
}
}
#[cfg(feature = "hut")]
impl From<hut::Usage> for ItemType {
fn from(hut: hut::Usage) -> ItemType {
ItemType::from(&hut)
}
}
impl From<MainItem> for ItemType {
fn from(item: MainItem) -> ItemType {
ItemType::Main(item)
}
}
impl From<GlobalItem> for ItemType {
fn from(item: GlobalItem) -> ItemType {
ItemType::Global(item)
}
}
impl From<LocalItem> for ItemType {
fn from(item: LocalItem) -> ItemType {
ItemType::Local(item)
}
}
impl From<InputItem> for ItemType {
fn from(item: InputItem) -> ItemType {
MainItem::Input(item).into()
}
}
impl From<OutputItem> for ItemType {
fn from(item: OutputItem) -> ItemType {
MainItem::Output(item).into()
}
}
impl From<FeatureItem> for ItemType {
fn from(item: FeatureItem) -> ItemType {
MainItem::Feature(item).into()
}
}
impl From<CollectionItem> for ItemType {
fn from(item: CollectionItem) -> ItemType {
MainItem::Collection(item).into()
}
}
impl From<UsagePage> for ItemType {
fn from(usage_page: UsagePage) -> ItemType {
GlobalItem::UsagePage(usage_page).into()
}
}
impl From<LogicalMinimum> for ItemType {
fn from(minimum: LogicalMinimum) -> ItemType {
GlobalItem::LogicalMinimum(minimum).into()
}
}
impl From<LogicalMaximum> for ItemType {
fn from(maximum: LogicalMaximum) -> ItemType {
GlobalItem::LogicalMaximum(maximum).into()
}
}
impl From<PhysicalMinimum> for ItemType {
fn from(minimum: PhysicalMinimum) -> ItemType {
GlobalItem::PhysicalMinimum(minimum).into()
}
}
impl From<PhysicalMaximum> for ItemType {
fn from(maximum: PhysicalMaximum) -> ItemType {
GlobalItem::PhysicalMaximum(maximum).into()
}
}
impl From<UnitExponent> for ItemType {
fn from(exponent: UnitExponent) -> ItemType {
GlobalItem::UnitExponent(exponent).into()
}
}
impl From<ReportSize> for ItemType {
fn from(size: ReportSize) -> ItemType {
GlobalItem::ReportSize(size).into()
}
}
impl From<ReportId> for ItemType {
fn from(id: ReportId) -> ItemType {
GlobalItem::ReportId(id).into()
}
}
impl From<ReportCount> for ItemType {
fn from(count: ReportCount) -> ItemType {
GlobalItem::ReportCount(count).into()
}
}
impl From<UsageId> for ItemType {
fn from(usage_id: UsageId) -> ItemType {
LocalItem::UsageId(usage_id).into()
}
}
impl From<(UsagePage, UsageId)> for ItemType {
fn from(usage: (UsagePage, UsageId)) -> ItemType {
LocalItem::Usage(usage.0, usage.1).into()
}
}
impl From<UsageMinimum> for ItemType {
fn from(minimum: UsageMinimum) -> ItemType {
LocalItem::UsageMinimum(minimum).into()
}
}
impl From<UsageMaximum> for ItemType {
fn from(maximum: UsageMaximum) -> ItemType {
LocalItem::UsageMaximum(maximum).into()
}
}
impl From<DesignatorMinimum> for ItemType {
fn from(minimum: DesignatorMinimum) -> ItemType {
LocalItem::DesignatorMinimum(minimum).into()
}
}
impl From<DesignatorMaximum> for ItemType {
fn from(maximum: DesignatorMaximum) -> ItemType {
LocalItem::DesignatorMaximum(maximum).into()
}
}
impl From<DesignatorIndex> for ItemType {
fn from(index: DesignatorIndex) -> ItemType {
LocalItem::DesignatorIndex(index).into()
}
}
impl From<StringMinimum> for ItemType {
fn from(minimum: StringMinimum) -> ItemType {
LocalItem::StringMinimum(minimum).into()
}
}
impl From<StringMaximum> for ItemType {
fn from(maximum: StringMaximum) -> ItemType {
LocalItem::StringMaximum(maximum).into()
}
}
impl From<StringIndex> for ItemType {
fn from(index: StringIndex) -> ItemType {
LocalItem::StringIndex(index).into()
}
}
impl From<Delimiter> for ItemType {
fn from(delimiter: Delimiter) -> ItemType {
LocalItem::Delimiter(delimiter).into()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MainItem {
Input(InputItem),
Output(OutputItem),
Feature(FeatureItem),
Collection(CollectionItem),
EndCollection,
}
impl MainItem {
pub fn as_bytes(&self) -> Vec<u8> {
match self {
MainItem::Input(item) => item.as_bytes(),
MainItem::Output(item) => item.as_bytes(),
MainItem::Feature(item) => item.as_bytes(),
MainItem::Collection(item) => item.as_bytes(),
MainItem::EndCollection => vec![0b11000000],
}
}
}
pub trait MainDataItem {
fn is_constant(&self) -> bool;
fn is_data(&self) -> bool {
!self.is_constant()
}
fn is_variable(&self) -> bool;
fn is_array(&self) -> bool {
!self.is_variable()
}
fn is_relative(&self) -> bool;
fn is_absolute(&self) -> bool {
!self.is_relative()
}
fn wraps(&self) -> bool;
fn does_not_wrap(&self) -> bool {
!self.wraps()
}
fn is_nonlinear(&self) -> bool;
fn is_linear(&self) -> bool {
!self.is_nonlinear()
}
fn has_no_preferred_state(&self) -> bool;
fn has_preferred_state(&self) -> bool {
!self.has_no_preferred_state()
}
fn has_null_state(&self) -> bool;
fn has_no_null_state(&self) -> bool {
!self.has_null_state()
}
fn is_buffered_bytes(&self) -> bool;
fn is_bitfield(&self) -> bool {
!self.is_buffered_bytes()
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct InputItem {
is_constant: bool,
is_variable: bool,
is_relative: bool,
wraps: bool,
is_nonlinear: bool,
has_no_preferred_state: bool,
has_null_state: bool,
is_buffered_bytes: bool,
}
impl InputItem {
pub fn as_bytes(&self) -> Vec<u8> {
let len = if self.is_buffered_bytes { 2 } else { 1 };
let prefix = 0b10000000 + len;
let mut flags: u16 = 0;
if self.is_constant {
flags |= 1 << 0;
}
if self.is_variable {
flags |= 1 << 1;
}
if self.is_relative {
flags |= 1 << 2;
}
if self.wraps {
flags |= 1 << 3;
}
if self.is_nonlinear {
flags |= 1 << 4;
}
if self.has_no_preferred_state {
flags |= 1 << 5;
}
if self.has_null_state {
flags |= 1 << 6;
}
if self.is_buffered_bytes {
flags |= 1 << 8;
vec![prefix, (flags & 0xff) as u8, ((flags >> 8) & 0xff) as u8]
} else {
vec![prefix, (flags & 0xff) as u8]
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct OutputItem {
is_constant: bool,
is_variable: bool,
is_relative: bool,
wraps: bool,
is_nonlinear: bool,
has_no_preferred_state: bool,
has_null_state: bool,
is_volatile: bool,
is_buffered_bytes: bool,
}
impl OutputItem {
pub fn is_volatile(&self) -> bool {
self.is_volatile
}
pub fn is_nonvolatile(&self) -> bool {
!self.is_volatile()
}
pub fn as_bytes(&self) -> Vec<u8> {
let len = if self.is_buffered_bytes { 2 } else { 1 };
let prefix = 0b10010000 + len;
let mut flags: u16 = 0;
if self.is_constant {
flags |= 1 << 0;
}
if self.is_variable {
flags |= 1 << 1;
}
if self.is_relative {
flags |= 1 << 2;
}
if self.wraps {
flags |= 1 << 3;
}
if self.is_nonlinear {
flags |= 1 << 4;
}
if self.has_no_preferred_state {
flags |= 1 << 5;
}
if self.has_null_state {
flags |= 1 << 6;
}
if self.is_volatile {
flags |= 1 << 7;
}
if self.is_buffered_bytes {
flags |= 1 << 8;
vec![prefix, (flags & 0xff) as u8, ((flags >> 8) & 0xff) as u8]
} else {
vec![prefix, (flags & 0xff) as u8]
}
}
}
impl MainDataItem for OutputItem {
fn is_constant(&self) -> bool {
self.is_constant
}
fn is_variable(&self) -> bool {
self.is_variable
}
fn is_relative(&self) -> bool {
self.is_relative
}
fn wraps(&self) -> bool {
self.wraps
}
fn is_nonlinear(&self) -> bool {
self.is_nonlinear
}
fn has_no_preferred_state(&self) -> bool {
self.has_no_preferred_state
}
fn has_null_state(&self) -> bool {
self.has_null_state
}
fn is_buffered_bytes(&self) -> bool {
self.is_buffered_bytes
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct FeatureItem {
is_constant: bool,
is_variable: bool,
is_relative: bool,
wraps: bool,
is_nonlinear: bool,
has_no_preferred_state: bool,
has_null_state: bool,
is_volatile: bool,
is_buffered_bytes: bool,
}
impl FeatureItem {
pub fn is_volatile(&self) -> bool {
self.is_volatile
}
pub fn is_nonvolatile(&self) -> bool {
!self.is_volatile()
}
pub fn as_bytes(&self) -> Vec<u8> {
let len = if self.is_buffered_bytes { 2 } else { 1 };
let prefix = 0b10110000 + len;
let mut flags: u16 = 0;
if self.is_constant {
flags |= 1 << 0;
}
if self.is_variable {
flags |= 1 << 1;
}
if self.is_relative {
flags |= 1 << 2;
}
if self.wraps {
flags |= 1 << 3;
}
if self.is_nonlinear {
flags |= 1 << 4;
}
if self.has_no_preferred_state {
flags |= 1 << 5;
}
if self.has_null_state {
flags |= 1 << 6;
}
if self.is_volatile {
flags |= 1 << 7;
}
if self.is_buffered_bytes {
flags |= 1 << 8;
vec![prefix, (flags & 0xff) as u8, ((flags >> 8) & 0xff) as u8]
} else {
vec![prefix, (flags & 0xff) as u8]
}
}
}
impl MainDataItem for FeatureItem {
fn is_constant(&self) -> bool {
self.is_constant
}
fn is_variable(&self) -> bool {
self.is_variable
}
fn is_relative(&self) -> bool {
self.is_relative
}
fn wraps(&self) -> bool {
self.wraps
}
fn is_nonlinear(&self) -> bool {
self.is_nonlinear
}
fn has_no_preferred_state(&self) -> bool {
self.has_no_preferred_state
}
fn has_null_state(&self) -> bool {
self.has_null_state
}
fn is_buffered_bytes(&self) -> bool {
self.is_buffered_bytes
}
}
macro_rules! impl_ibstate {
($t:ident, $a:ident, $b:ident) => {
#[doc(hidden)]
pub trait $t {}
#[doc(hidden)]
pub enum $a {}
#[doc(hidden)]
pub enum $b {}
impl $t for $a {}
impl $t for $b {}
impl $t for IBUndefined {}
};
}
#[doc(hidden)]
pub enum IBUndefined {}
impl_ibstate!(IBSData, IBData, IBConstant);
impl_ibstate!(IBSArr, IBVariable, IBArray);
impl_ibstate!(IBSAbs, IBAbsolute, IBRelative);
impl_ibstate!(IBSWrap, IBWrap, IBNoWrap);
impl_ibstate!(IBSLin, IBLinear, IBNonLinear);
impl_ibstate!(IBSPref, IBPreferred, IBNoPreferred);
impl_ibstate!(IBSNull, IBNull, IBNoNull);
impl_ibstate!(IBSVol, IBVolatile, IBNonVolatile);
impl_ibstate!(IBSBit, IBBitfield, IBBuffered);
#[derive(Default)]
pub struct ItemBuilder<A, B, C, D, E, F, G, H, I>
where
A: IBSData,
B: IBSArr,
C: IBSAbs,
D: IBSWrap,
E: IBSLin,
F: IBSPref,
G: IBSNull,
H: IBSBit,
I: IBSVol,
{
item: OutputItem,
m1: core::marker::PhantomData<A>,
m2: core::marker::PhantomData<B>,
m3: core::marker::PhantomData<C>,
m4: core::marker::PhantomData<D>,
m5: core::marker::PhantomData<E>,
m6: core::marker::PhantomData<F>,
m7: core::marker::PhantomData<G>,
m8: core::marker::PhantomData<H>,
m9: core::marker::PhantomData<I>,
}
impl
ItemBuilder<
IBUndefined,
IBUndefined,
IBUndefined,
IBUndefined,
IBUndefined,
IBUndefined,
IBUndefined,
IBUndefined,
IBUndefined,
>
{
pub fn new() -> ItemBuilder<
IBUndefined,
IBUndefined,
IBUndefined,
IBUndefined,
IBUndefined,
IBUndefined,
IBUndefined,
IBUndefined,
IBUndefined,
> {
ItemBuilder {
item: OutputItem::default(),
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
}
impl<A, B, C, D, E, F, G, H, I> ItemBuilder<A, B, C, D, E, F, G, H, I>
where
A: IBSData,
B: IBSArr,
C: IBSAbs,
D: IBSWrap,
E: IBSLin,
F: IBSPref,
G: IBSNull,
H: IBSBit,
I: IBSVol,
{
pub fn input(self) -> InputItem {
InputItem {
is_constant: self.item.is_constant,
is_variable: self.item.is_variable,
is_relative: self.item.is_relative,
wraps: self.item.wraps,
is_nonlinear: self.item.is_nonlinear,
has_no_preferred_state: self.item.has_no_preferred_state,
has_null_state: self.item.has_null_state,
is_buffered_bytes: self.item.is_buffered_bytes,
}
}
pub fn output(self) -> OutputItem {
self.item
}
pub fn feature(self) -> FeatureItem {
FeatureItem {
is_constant: self.item.is_constant,
is_variable: self.item.is_variable,
is_relative: self.item.is_relative,
wraps: self.item.wraps,
is_nonlinear: self.item.is_nonlinear,
has_no_preferred_state: self.item.has_no_preferred_state,
has_null_state: self.item.has_null_state,
is_volatile: self.item.is_volatile,
is_buffered_bytes: self.item.is_buffered_bytes,
}
}
}
impl<B, C, D, E, F, G, H, I> ItemBuilder<IBUndefined, B, C, D, E, F, G, H, I>
where
B: IBSArr,
C: IBSAbs,
D: IBSWrap,
E: IBSLin,
F: IBSPref,
G: IBSNull,
H: IBSBit,
I: IBSVol,
{
pub fn constant(mut self) -> ItemBuilder<IBConstant, B, C, D, E, F, G, H, I> {
self.item.is_constant = true;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
pub fn data(mut self) -> ItemBuilder<IBData, B, C, D, E, F, G, H, I> {
self.item.is_constant = false;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
}
impl<A, C, D, E, F, G, H, I> ItemBuilder<A, IBUndefined, C, D, E, F, G, H, I>
where
A: IBSData,
C: IBSAbs,
D: IBSWrap,
E: IBSLin,
F: IBSPref,
G: IBSNull,
H: IBSBit,
I: IBSVol,
{
pub fn variable(mut self) -> ItemBuilder<A, IBVariable, C, D, E, F, G, H, I> {
self.item.is_variable = true;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
pub fn array(mut self) -> ItemBuilder<A, IBArray, C, D, E, F, G, H, I> {
self.item.is_variable = false;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
}
impl<A, B, D, E, F, G, H, I> ItemBuilder<A, B, IBUndefined, D, E, F, G, H, I>
where
A: IBSData,
B: IBSArr,
D: IBSWrap,
E: IBSLin,
F: IBSPref,
G: IBSNull,
H: IBSBit,
I: IBSVol,
{
pub fn absolute(mut self) -> ItemBuilder<A, B, IBAbsolute, D, E, F, G, H, I> {
self.item.is_relative = false;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
pub fn relative(mut self) -> ItemBuilder<A, B, IBRelative, D, E, F, G, H, I> {
self.item.is_relative = true;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
}
impl<A, B, C, E, F, G, H, I> ItemBuilder<A, B, C, IBUndefined, E, F, G, H, I>
where
A: IBSData,
B: IBSArr,
C: IBSAbs,
E: IBSLin,
F: IBSPref,
G: IBSNull,
H: IBSBit,
I: IBSVol,
{
pub fn wrap(mut self) -> ItemBuilder<A, B, C, IBWrap, E, F, G, H, I> {
self.item.wraps = true;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
pub fn nowrap(mut self) -> ItemBuilder<A, B, C, IBNoWrap, E, F, G, H, I> {
self.item.wraps = false;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
}
impl<A, B, C, D, F, G, H, I> ItemBuilder<A, B, C, D, IBUndefined, F, G, H, I>
where
A: IBSData,
B: IBSArr,
C: IBSAbs,
D: IBSWrap,
F: IBSPref,
G: IBSNull,
H: IBSBit,
I: IBSVol,
{
pub fn linear(mut self) -> ItemBuilder<A, B, C, D, IBLinear, F, G, H, I> {
self.item.is_nonlinear = false;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
pub fn nonlinear(mut self) -> ItemBuilder<A, B, C, D, IBNonLinear, F, G, H, I> {
self.item.is_nonlinear = true;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
}
impl<A, B, C, D, E, G, H, I> ItemBuilder<A, B, C, D, E, IBUndefined, G, H, I>
where
A: IBSData,
B: IBSArr,
C: IBSAbs,
D: IBSWrap,
E: IBSLin,
G: IBSNull,
H: IBSBit,
I: IBSVol,
{
pub fn preferred_state(mut self) -> ItemBuilder<A, B, C, D, E, IBPreferred, G, H, I> {
self.item.has_no_preferred_state = false;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
pub fn no_preferred_state(mut self) -> ItemBuilder<A, B, C, D, E, IBNoPreferred, G, H, I> {
self.item.has_no_preferred_state = true;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
}
impl<A, B, C, D, E, F, H, I> ItemBuilder<A, B, C, D, E, F, IBUndefined, H, I>
where
A: IBSData,
B: IBSArr,
C: IBSAbs,
D: IBSWrap,
E: IBSLin,
F: IBSPref,
H: IBSBit,
I: IBSVol,
{
pub fn null(mut self) -> ItemBuilder<A, B, C, D, E, F, IBNull, H, I> {
self.item.has_null_state = true;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
pub fn no_null(mut self) -> ItemBuilder<A, B, C, D, E, F, IBNoNull, H, I> {
self.item.has_null_state = false;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
}
impl<A, B, C, D, E, F, G, I> ItemBuilder<A, B, C, D, E, F, G, IBUndefined, I>
where
A: IBSData,
B: IBSArr,
C: IBSAbs,
D: IBSWrap,
E: IBSLin,
F: IBSPref,
G: IBSNull,
I: IBSVol,
{
pub fn bitfield(mut self) -> ItemBuilder<A, B, C, D, E, F, G, IBBitfield, I> {
self.item.is_buffered_bytes = false;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
pub fn buffered_bytes(mut self) -> ItemBuilder<A, B, C, D, E, F, G, IBBuffered, I> {
self.item.is_buffered_bytes = true;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
}
impl<A, B, C, D, E, F, G, H> ItemBuilder<A, B, C, D, E, F, G, H, IBUndefined>
where
A: IBSData,
B: IBSArr,
C: IBSAbs,
D: IBSWrap,
E: IBSLin,
F: IBSPref,
G: IBSNull,
H: IBSBit,
{
pub fn volatile(mut self) -> ItemBuilder<A, B, C, D, E, F, G, H, IBVolatile> {
self.item.is_volatile = true;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
pub fn non_volatile(mut self) -> ItemBuilder<A, B, C, D, E, F, G, H, IBNonVolatile> {
self.item.is_volatile = false;
ItemBuilder {
item: self.item,
m1: core::marker::PhantomData,
m2: core::marker::PhantomData,
m3: core::marker::PhantomData,
m4: core::marker::PhantomData,
m5: core::marker::PhantomData,
m6: core::marker::PhantomData,
m7: core::marker::PhantomData,
m8: core::marker::PhantomData,
m9: core::marker::PhantomData,
}
}
}
impl MainDataItem for InputItem {
fn is_constant(&self) -> bool {
self.is_constant
}
fn is_variable(&self) -> bool {
self.is_variable
}
fn is_relative(&self) -> bool {
self.is_relative
}
fn wraps(&self) -> bool {
self.wraps
}
fn is_nonlinear(&self) -> bool {
self.is_nonlinear
}
fn has_no_preferred_state(&self) -> bool {
self.has_no_preferred_state
}
fn has_null_state(&self) -> bool {
self.has_null_state
}
fn is_buffered_bytes(&self) -> bool {
self.is_buffered_bytes
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CollectionItem {
Physical,
Application,
Logical,
Report,
NamedArray,
UsageSwitch,
UsageModifier,
Reserved { value: u8 },
VendorDefined { value: u8 },
}
impl CollectionItem {
pub fn as_bytes(&self) -> Vec<u8> {
let data: u8 = match self {
CollectionItem::Physical => 0x00,
CollectionItem::Application => 0x01,
CollectionItem::Logical => 0x02,
CollectionItem::Report => 0x03,
CollectionItem::NamedArray => 0x04,
CollectionItem::UsageSwitch => 0x05,
CollectionItem::UsageModifier => 0x06,
CollectionItem::Reserved { value } => *value,
CollectionItem::VendorDefined { value } => *value,
};
vec![0b10100001, data]
}
}
impl From<CollectionItem> for u8 {
fn from(c: CollectionItem) -> u8 {
match c {
CollectionItem::Physical => 0x00,
CollectionItem::Application => 0x01,
CollectionItem::Logical => 0x02,
CollectionItem::Report => 0x03,
CollectionItem::NamedArray => 0x04,
CollectionItem::UsageSwitch => 0x05,
CollectionItem::UsageModifier => 0x06,
CollectionItem::Reserved { value } => value,
CollectionItem::VendorDefined { value } => value,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum GlobalItem {
UsagePage(UsagePage),
LogicalMinimum(LogicalMinimum),
LogicalMaximum(LogicalMaximum),
PhysicalMinimum(PhysicalMinimum),
PhysicalMaximum(PhysicalMaximum),
UnitExponent(UnitExponent),
Unit(Unit),
ReportSize(ReportSize),
ReportId(ReportId),
ReportCount(ReportCount),
Push,
Pop,
Reserved,
}
impl GlobalItem {
pub fn as_bytes(&self) -> Vec<u8> {
let prefix = self.prefix();
let data: Option<HidBytes> = match self {
GlobalItem::UsagePage(usage_page) => Some(HidBytes::from(u16::from(usage_page))),
GlobalItem::LogicalMinimum(min) => Some(HidBytes::from(i32::from(min))),
GlobalItem::LogicalMaximum(max) => Some(HidBytes::from(i32::from(max))),
GlobalItem::PhysicalMinimum(min) => Some(HidBytes::from(i32::from(min))),
GlobalItem::PhysicalMaximum(max) => Some(HidBytes::from(i32::from(max))),
GlobalItem::UnitExponent(exponent) => Some(HidBytes::from(i32::from(exponent))),
GlobalItem::Unit(unit) => Some(HidBytes::from(u32::from(unit))),
GlobalItem::ReportSize(size) => Some(HidBytes::from(usize::from(size))),
GlobalItem::ReportId(id) => Some(HidBytes::from(u8::from(id))),
GlobalItem::ReportCount(count) => Some(HidBytes::from(usize::from(count))),
GlobalItem::Push => None,
GlobalItem::Pop => None,
GlobalItem::Reserved => None,
};
if let Some(data) = data {
let len = match data.len() {
0 => 0b00,
1 => 0b01,
2 => 0b10,
4 => 0b11,
n => panic!("Invalid data length {n}"),
};
[vec![prefix + len], data.take()].concat()
} else {
vec![prefix]
}
}
pub fn prefix(&self) -> u8 {
match self {
GlobalItem::UsagePage(_) => 0b00000100,
GlobalItem::LogicalMinimum(_) => 0b00010100,
GlobalItem::LogicalMaximum(_) => 0b00100100,
GlobalItem::PhysicalMinimum(_) => 0b00110100,
GlobalItem::PhysicalMaximum(_) => 0b01000100,
GlobalItem::UnitExponent(_) => 0b01010100,
GlobalItem::Unit(_) => 0b01100100,
GlobalItem::ReportSize(_) => 0b01110100,
GlobalItem::ReportId(_) => 0b10000100,
GlobalItem::ReportCount(_) => 0b10010100,
GlobalItem::Push => 0b10100100,
GlobalItem::Pop => 0b10110100,
GlobalItem::Reserved => 0b11000100,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LocalItem {
Usage(UsagePage, UsageId),
UsageId(UsageId),
UsageMinimum(UsageMinimum),
UsageMaximum(UsageMaximum),
DesignatorIndex(DesignatorIndex),
DesignatorMinimum(DesignatorMinimum),
DesignatorMaximum(DesignatorMaximum),
StringIndex(StringIndex),
StringMinimum(StringMinimum),
StringMaximum(StringMaximum),
Delimiter(Delimiter),
Reserved {
value: u8,
},
}
impl LocalItem {
pub fn as_bytes(&self) -> Vec<u8> {
let prefix = self.prefix();
let data = match self {
LocalItem::Usage(page, id) => {
let up: u32 = u16::from(page) as u32;
let uid: u32 = u16::from(id) as u32;
let usage = (up << 16) | uid;
HidBytes::from(usage)
}
LocalItem::UsageId(id) => HidBytes::from(u16::from(id)),
LocalItem::UsageMinimum(min) => HidBytes::from(u32::from(min)),
LocalItem::UsageMaximum(max) => HidBytes::from(u32::from(max)),
LocalItem::DesignatorIndex(idx) => HidBytes::from(u32::from(idx)),
LocalItem::DesignatorMinimum(min) => HidBytes::from(u32::from(min)),
LocalItem::DesignatorMaximum(max) => HidBytes::from(u32::from(max)),
LocalItem::StringIndex(idx) => HidBytes::from(u32::from(idx)),
LocalItem::StringMinimum(min) => HidBytes::from(u32::from(min)),
LocalItem::StringMaximum(max) => HidBytes::from(u32::from(max)),
LocalItem::Delimiter(delimiter) => HidBytes::from(u32::from(delimiter)),
LocalItem::Reserved { value } => HidBytes::from(*value),
};
let len = match data.len() {
0 => 0b00,
1 => 0b01,
2 => 0b10,
4 => 0b11,
n => panic!("Invalid data length {n}"),
};
[vec![prefix + len], data.take()].concat()
}
pub fn prefix(&self) -> u8 {
match self {
LocalItem::Usage(_, _) => 0b00001000,
LocalItem::UsageId(_) => 0b00001000,
LocalItem::UsageMinimum(_) => 0b00011000,
LocalItem::UsageMaximum(_) => 0b00101000,
LocalItem::DesignatorIndex(_) => 0b00111000,
LocalItem::DesignatorMinimum(_) => 0b01001000,
LocalItem::DesignatorMaximum(_) => 0b01011000,
LocalItem::StringIndex(_) => 0b01111000,
LocalItem::StringMinimum(_) => 0b10001000,
LocalItem::StringMaximum(_) => 0b10011000,
LocalItem::Delimiter(_) => 0b10101000,
LocalItem::Reserved { value: _ } => 0b11111000,
}
}
}
impl TryFrom<&[u8]> for ItemType {
type Error = HidError;
fn try_from(bytes: &[u8]) -> Result<ItemType> {
ensure!(!bytes.is_empty(), HidError::InsufficientData);
let itype = (bytes[0] & 0b1100) >> 2;
match itype {
0 => Ok(ItemType::Main(MainItem::try_from(bytes)?)),
1 => Ok(ItemType::Global(GlobalItem::try_from(bytes)?)),
2 => Ok(ItemType::Local(LocalItem::try_from(bytes)?)),
3 => Ok(ItemType::Reserved),
_ => panic!("Item type {itype} cannot happen"),
}
}
}
impl TryFrom<&[u8]> for MainItem {
type Error = HidError;
fn try_from(bytes: &[u8]) -> Result<MainItem> {
ensure!(!bytes.is_empty(), HidError::InsufficientData);
let tag = bytes[0] & 0b11111100;
match tag {
0b10000000 => Ok(MainItem::Input(InputItem::try_from(bytes)?)),
0b10010000 => Ok(MainItem::Output(OutputItem::try_from(bytes)?)),
0b10110000 => Ok(MainItem::Feature(FeatureItem::try_from(bytes)?)),
0b10100000 => Ok(MainItem::Collection(CollectionItem::try_from(bytes)?)),
0b11000000 => Ok(MainItem::EndCollection),
_ => Err(HidError::InvalidData {
message: format!("Invalid item tag {tag:#08b}"),
}),
}
}
}
impl TryFrom<&[u8]> for InputItem {
type Error = HidError;
fn try_from(bytes: &[u8]) -> Result<Self> {
ensure!(bytes.len() >= 2, HidError::InsufficientData);
let data: u32 = HidValue::try_from(&bytes[1..]).unwrap().into();
Ok(Self {
is_constant: bit(data, 0),
is_variable: bit(data, 1),
is_relative: bit(data, 2),
wraps: bit(data, 3),
is_nonlinear: bit(data, 4),
has_no_preferred_state: bit(data, 5),
has_null_state: bit(data, 6),
is_buffered_bytes: bit(data, 8),
})
}
}
impl TryFrom<&[u8]> for OutputItem {
type Error = HidError;
fn try_from(bytes: &[u8]) -> Result<Self> {
ensure!(bytes.len() >= 2, HidError::InsufficientData);
let data: u32 = HidValue::try_from(&bytes[1..]).unwrap().into();
Ok(Self {
is_constant: bit(data, 0),
is_variable: bit(data, 1),
is_relative: bit(data, 2),
wraps: bit(data, 3),
is_nonlinear: bit(data, 4),
has_no_preferred_state: bit(data, 5),
has_null_state: bit(data, 6),
is_volatile: bit(data, 7),
is_buffered_bytes: bit(data, 8),
})
}
}
impl TryFrom<&[u8]> for FeatureItem {
type Error = HidError;
fn try_from(bytes: &[u8]) -> Result<Self> {
ensure!(bytes.len() >= 2, HidError::InsufficientData);
let data: u32 = HidValue::try_from(&bytes[1..]).unwrap().into();
Ok(Self {
is_constant: bit(data, 0),
is_variable: bit(data, 1),
is_relative: bit(data, 2),
wraps: bit(data, 3),
is_nonlinear: bit(data, 4),
has_no_preferred_state: bit(data, 5),
has_null_state: bit(data, 6),
is_volatile: bit(data, 7),
is_buffered_bytes: bit(data, 8),
})
}
}
impl From<&CollectionItem> for u8 {
fn from(c: &CollectionItem) -> u8 {
match c {
CollectionItem::Physical => 0u8,
CollectionItem::Application => 1u8,
CollectionItem::Logical => 2u8,
CollectionItem::Report => 3u8,
CollectionItem::NamedArray => 4u8,
CollectionItem::UsageSwitch => 5u8,
CollectionItem::UsageModifier => 6u8,
CollectionItem::Reserved { value } => *value,
CollectionItem::VendorDefined { value } => *value,
}
}
}
impl From<u8> for CollectionItem {
fn from(v: u8) -> CollectionItem {
match v {
0x00 => CollectionItem::Physical,
0x01 => CollectionItem::Application,
0x02 => CollectionItem::Logical,
0x03 => CollectionItem::Report,
0x04 => CollectionItem::NamedArray,
0x05 => CollectionItem::UsageSwitch,
0x06 => CollectionItem::UsageModifier,
value @ 0x07..=0x7f => CollectionItem::Reserved { value },
value @ 0x80..=0xff => CollectionItem::VendorDefined { value },
}
}
}
impl TryFrom<&[u8]> for CollectionItem {
type Error = HidError;
fn try_from(bytes: &[u8]) -> Result<CollectionItem> {
if bytes.len() == 1 {
return Ok(CollectionItem::Physical);
}
match bytes[1] {
0x00 => Ok(CollectionItem::Physical),
0x01 => Ok(CollectionItem::Application),
0x02 => Ok(CollectionItem::Logical),
0x03 => Ok(CollectionItem::Report),
0x04 => Ok(CollectionItem::NamedArray),
0x05 => Ok(CollectionItem::UsageSwitch),
0x06 => Ok(CollectionItem::UsageModifier),
value @ 0x07..=0x7f => Ok(CollectionItem::Reserved { value }),
value @ 0x80..=0xff => Ok(CollectionItem::VendorDefined { value }),
}
}
}
impl TryFrom<&[u8]> for GlobalItem {
type Error = HidError;
fn try_from(bytes: &[u8]) -> Result<GlobalItem> {
let value = if bytes.len() >= 2 {
HidValue::try_from(&bytes[1..]).unwrap()
} else {
let v = vec![0u8];
HidValue::try_from(v.as_slice()).unwrap()
};
let item = match bytes[0] & 0b11111100 {
0b00000100 => GlobalItem::UsagePage(UsagePage(value.into())),
0b00010100 => GlobalItem::LogicalMinimum(LogicalMinimum(value.into())),
0b00100100 => GlobalItem::LogicalMaximum(LogicalMaximum(value.into())),
0b00110100 => GlobalItem::PhysicalMinimum(PhysicalMinimum(value.into())),
0b01000100 => GlobalItem::PhysicalMaximum(PhysicalMaximum(value.into())),
0b01010100 => GlobalItem::UnitExponent(UnitExponent(value.into())),
0b01100100 => GlobalItem::Unit(Unit(value.into())),
0b01110100 => {
ensure!(bytes.len() >= 2, HidError::InsufficientData);
GlobalItem::ReportSize(ReportSize(value.into()))
}
0b10000100 => {
ensure!(bytes.len() >= 2, HidError::InsufficientData);
GlobalItem::ReportId(ReportId(value.into()))
}
0b10010100 => {
ensure!(bytes.len() >= 2, HidError::InsufficientData);
GlobalItem::ReportCount(ReportCount(value.into()))
}
0b10100100 => GlobalItem::Push,
0b10110100 => GlobalItem::Pop,
_ => GlobalItem::Reserved,
};
Ok(item)
}
}
impl TryFrom<&[u8]> for LocalItem {
type Error = HidError;
fn try_from(bytes: &[u8]) -> Result<LocalItem> {
ensure!(bytes.len() >= 2, HidError::InsufficientData);
let value = HidValue::try_from(&bytes[1..]).unwrap();
let item = match bytes[0] & 0b11111100 {
0b00001000 => match bytes[1..].len() {
1 | 2 => LocalItem::UsageId(UsageId(value.into())),
4 => LocalItem::Usage(
UsagePage((u32::from(&value) >> 16) as u16),
UsageId(value.into()),
),
n => panic!("Invalid data length {n}"),
},
0b00011000 => LocalItem::UsageMinimum(UsageMinimum(value.into())),
0b00101000 => LocalItem::UsageMaximum(UsageMaximum(value.into())),
0b00111000 => LocalItem::DesignatorIndex(DesignatorIndex(value.into())),
0b01001000 => LocalItem::DesignatorMinimum(DesignatorMinimum(value.into())),
0b01011000 => LocalItem::DesignatorMaximum(DesignatorMaximum(value.into())),
0b01111000 => LocalItem::StringIndex(StringIndex(value.into())),
0b10001000 => LocalItem::StringMinimum(StringMinimum(value.into())),
0b10011000 => LocalItem::StringMaximum(StringMaximum(value.into())),
0b10101000 => LocalItem::Delimiter(Delimiter(value.into())),
n => LocalItem::Reserved { value: n },
};
Ok(item)
}
}
pub trait Item {
fn size(&self) -> usize;
fn item_type(&self) -> ItemType;
fn tag(&self) -> u8;
fn header(&self) -> u8;
fn is_long_item(&self) -> bool;
fn bytes(&self) -> &[u8];
fn data(&self) -> Option<ItemData<'_>>;
}
#[derive(Debug)]
pub struct ItemData<'a> {
bytes: &'a [u8],
}
impl core::ops::Deref for ItemData<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.bytes
}
}
impl TryFrom<&ItemData<'_>> for u32 {
type Error = HidError;
fn try_from(data: &ItemData) -> Result<u32> {
ensure!(!data.is_empty(), HidError::InsufficientData);
match data.len() {
0 => panic!("Item data with zero bytes must not happen"),
1 => Ok(data.bytes[0] as u32),
2 => Ok(u16::from_le_bytes(data.bytes[0..2].try_into().unwrap()) as u32),
4 => Ok(u32::from_le_bytes(data.bytes[0..4].try_into().unwrap())),
_ => panic!("Size of {} cannot happen", data.bytes.len()),
}
}
}
impl TryFrom<&ItemData<'_>> for u8 {
type Error = HidError;
fn try_from(data: &ItemData) -> Result<u8> {
ensure!(!data.is_empty(), HidError::InsufficientData);
match data.len() {
0 => panic!("Item data with zero bytes must not happen"),
1 => Ok(data.bytes[0]),
n @ (2 | 4) => Err(HidError::InvalidData {
message: format!("Cannot convert {n} bytes to u8"),
}),
_ => panic!("Size of {} cannot happen", data.bytes.len()),
}
}
}
impl TryFrom<&ItemData<'_>> for Vec<u8> {
type Error = HidError;
fn try_from(data: &ItemData) -> Result<Vec<u8>> {
ensure!(!data.is_empty(), HidError::InsufficientData);
match data.len() {
0 => panic!("Item data with zero bytes must not happen"),
3 => panic!("Size of {} cannot happen", data.bytes.len()),
1..=4 => Ok(data.bytes.to_owned()),
_ => panic!("Size of {} cannot happen", data.bytes.len()),
}
}
}
#[derive(Debug)]
pub struct ReportDescriptorItem {
offset: usize,
item: ShortItem,
}
impl ReportDescriptorItem {
pub fn offset(&self) -> usize {
self.offset
}
pub fn item(&self) -> &impl Item {
&self.item
}
}
#[derive(Debug)]
pub struct ReportDescriptorItems {
items: Vec<ReportDescriptorItem>,
}
impl core::ops::Deref for ReportDescriptorItems {
type Target = [ReportDescriptorItem];
fn deref(&self) -> &Self::Target {
&self.items
}
}
impl TryFrom<&[u8]> for ReportDescriptorItems {
type Error = ParserError;
fn try_from(bytes: &[u8]) -> crate::Result<Self> {
itemize(bytes)
}
}
#[derive(Debug)]
struct ShortItem {
item_size: usize,
header: u8,
item_type: ItemType,
bytes: Vec<u8>,
}
impl Item for ShortItem {
fn is_long_item(&self) -> bool {
false
}
fn size(&self) -> usize {
self.item_size
}
fn item_type(&self) -> ItemType {
self.item_type
}
fn tag(&self) -> u8 {
(self.header & 0b11110000) >> 4
}
fn header(&self) -> u8 {
self.header
}
fn bytes(&self) -> &[u8] {
&self.bytes
}
fn data(&self) -> Option<ItemData<'_>> {
match self.item_size {
1 => None,
2 | 3 | 5 => Some(ItemData {
bytes: &self.bytes[1..],
}),
_ => panic!("Invalid item size {}", self.size()),
}
}
}
impl From<&ItemType> for ShortItem {
fn from(item: &ItemType) -> ShortItem {
let bytes = item.as_bytes();
ShortItem::try_from(bytes.as_slice()).unwrap()
}
}
impl TryFrom<&[u8]> for ShortItem {
type Error = HidError;
fn try_from(bytes: &[u8]) -> Result<ShortItem> {
ensure!(!bytes.is_empty(), HidError::InsufficientData);
let size = bytes[0] & 0b0011;
let size = match size {
0 => 0,
1 => 1,
2 => 2,
3 => 4,
_ => panic!("Size {size} cannot happen"),
};
ensure!(bytes.len() > size, HidError::InsufficientData);
let itype = ItemType::try_from(&bytes[0..size + 1])?;
Ok(ShortItem {
item_size: size + 1,
item_type: itype,
header: bytes[0],
bytes: bytes[0..size + 1].to_owned(),
})
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
struct LongItem {
size: usize,
bytes: Vec<u8>,
}
impl Item for LongItem {
fn is_long_item(&self) -> bool {
true
}
fn size(&self) -> usize {
self.size
}
fn item_type(&self) -> ItemType {
ItemType::Long
}
fn tag(&self) -> u8 {
self.bytes[2]
}
fn header(&self) -> u8 {
self.bytes[0]
}
fn data(&self) -> Option<ItemData<'_>> {
Some(ItemData {
bytes: &self.bytes[3..],
})
}
fn bytes(&self) -> &[u8] {
&self.bytes
}
}
impl TryFrom<&[u8]> for LongItem {
type Error = HidError;
fn try_from(bytes: &[u8]) -> Result<LongItem> {
ensure!(bytes.len() >= 3, HidError::InsufficientData);
if bytes[1] != 0b11111110 {
return Err(HidError::InvalidData {
message: "Item is not a long item".into(),
});
}
let size = bytes[1] as usize;
Ok(LongItem {
size,
bytes: bytes[0..size + 3].to_owned(),
})
}
}
fn itemize(bytes: &[u8]) -> crate::Result<ReportDescriptorItems> {
let mut offset = 0;
let mut items: Vec<ReportDescriptorItem> = Vec::new();
loop {
let item = match ShortItem::try_from(&bytes[offset..]) {
Ok(item) => item,
Err(e) => {
return Err(ParserError::InvalidData {
offset,
message: format!("{e}"),
});
}
};
let off = offset;
offset += item.size();
items.push(ReportDescriptorItem { offset: off, item });
if offset >= bytes.len() {
break;
}
}
Ok(ReportDescriptorItems { items })
}
#[doc(hidden)]
pub trait ReportDescriptorBuilderState {}
macro_rules! impl_builder_state {
($s:ident) => {
#[doc(hidden)]
pub enum $s {}
impl ReportDescriptorBuilderState for $s {}
};
}
impl_builder_state!(ReportDescriptorBuilderToplevel);
impl_builder_state!(ReportDescriptorBuilderC1);
impl_builder_state!(ReportDescriptorBuilderC2);
impl_builder_state!(ReportDescriptorBuilderC3);
impl_builder_state!(ReportDescriptorBuilderC4);
impl_builder_state!(ReportDescriptorBuilderC5);
impl_builder_state!(ReportDescriptorBuilderC6);
impl_builder_state!(ReportDescriptorBuilderC7);
impl_builder_state!(ReportDescriptorBuilderC8);
impl_builder_state!(ReportDescriptorBuilderToplevelPush);
impl_builder_state!(ReportDescriptorBuilderC1Push);
impl_builder_state!(ReportDescriptorBuilderC2Push);
impl_builder_state!(ReportDescriptorBuilderC3Push);
impl_builder_state!(ReportDescriptorBuilderC4Push);
impl_builder_state!(ReportDescriptorBuilderC5Push);
impl_builder_state!(ReportDescriptorBuilderC6Push);
impl_builder_state!(ReportDescriptorBuilderC7Push);
impl_builder_state!(ReportDescriptorBuilderC8Push);
pub struct ReportDescriptorBuilder<S: ReportDescriptorBuilderState> {
items: Vec<ItemType>,
marker: core::marker::PhantomData<S>,
}
impl<S: ReportDescriptorBuilderState> ReportDescriptorBuilder<S> {
pub fn append(mut self, item: ItemType) -> Self {
self.items.push(item);
self
}
#[cfg(feature = "hut")]
pub fn usage_page(self, usage_page: impl hut::AsUsagePage) -> Self {
let usage_page = usage_page.usage_page();
self.append(usage_page.into())
}
#[cfg(feature = "hut")]
pub fn usage_id(self, usage: impl hut::AsUsage) -> Self {
let usage_id: UsageId = usage.usage().into();
self.append(usage_id.into())
}
pub fn input(mut self, item: InputItem) -> Self {
self.items.push(item.into());
self
}
pub fn output(mut self, item: OutputItem) -> Self {
self.items.push(item.into());
self
}
pub fn feature(mut self, item: FeatureItem) -> Self {
self.items.push(item.into());
self
}
}
impl ReportDescriptorBuilder<ReportDescriptorBuilderToplevel> {
pub fn new() -> ReportDescriptorBuilder<ReportDescriptorBuilderToplevel> {
ReportDescriptorBuilder {
items: Vec::new(),
marker: core::marker::PhantomData,
}
}
pub fn open_collection(
self,
item: CollectionItem,
) -> ReportDescriptorBuilder<ReportDescriptorBuilderC1> {
let tmp = self.append(item.into());
ReportDescriptorBuilder {
items: tmp.items,
marker: core::marker::PhantomData,
}
}
pub fn push(self) -> ReportDescriptorBuilder<ReportDescriptorBuilderToplevelPush> {
ReportDescriptorBuilder {
items: self.items,
marker: core::marker::PhantomData,
}
}
pub fn build(&self) -> Vec<u8> {
let mut bytes: Vec<u8> = vec![];
self.items.iter().for_each(|item| {
let short = ShortItem::from(item);
bytes.extend(short.bytes());
});
bytes
}
}
impl Default for ReportDescriptorBuilder<ReportDescriptorBuilderToplevel> {
fn default() -> Self {
Self::new()
}
}
impl ReportDescriptorBuilder<ReportDescriptorBuilderToplevelPush> {
pub fn pop(self) -> ReportDescriptorBuilder<ReportDescriptorBuilderToplevel> {
ReportDescriptorBuilder {
items: self.items,
marker: core::marker::PhantomData,
}
}
}
impl ReportDescriptorBuilder<ReportDescriptorBuilderC1> {
pub fn close_collection(self) -> ReportDescriptorBuilder<ReportDescriptorBuilderToplevel> {
let tmp = self.append(MainItem::EndCollection.into());
ReportDescriptorBuilder {
items: tmp.items,
marker: core::marker::PhantomData,
}
}
}
macro_rules! impl_open_collection {
($cb:ty, $cc:ty) => {
impl ReportDescriptorBuilder<$cb> {
#[doc(hidden)]
pub fn open_collection(self, item: CollectionItem) -> ReportDescriptorBuilder<$cc> {
let tmp = self.append(item.into());
ReportDescriptorBuilder {
items: tmp.items,
marker: core::marker::PhantomData,
}
}
}
};
}
macro_rules! impl_close_collection {
($ca:ty, $cb:ty) => {
impl ReportDescriptorBuilder<$cb> {
#[doc(hidden)]
pub fn close_collection(self) -> ReportDescriptorBuilder<$ca> {
let tmp = self.append(MainItem::EndCollection.into());
ReportDescriptorBuilder {
items: tmp.items,
marker: core::marker::PhantomData,
}
}
}
};
}
impl_open_collection!(ReportDescriptorBuilderC1, ReportDescriptorBuilderC2);
impl_open_collection!(ReportDescriptorBuilderC2, ReportDescriptorBuilderC3);
impl_open_collection!(ReportDescriptorBuilderC3, ReportDescriptorBuilderC4);
impl_open_collection!(ReportDescriptorBuilderC4, ReportDescriptorBuilderC5);
impl_open_collection!(ReportDescriptorBuilderC5, ReportDescriptorBuilderC6);
impl_open_collection!(ReportDescriptorBuilderC6, ReportDescriptorBuilderC7);
impl_open_collection!(ReportDescriptorBuilderC7, ReportDescriptorBuilderC8);
impl_close_collection!(ReportDescriptorBuilderC1, ReportDescriptorBuilderC2);
impl_close_collection!(ReportDescriptorBuilderC2, ReportDescriptorBuilderC3);
impl_close_collection!(ReportDescriptorBuilderC3, ReportDescriptorBuilderC4);
impl_close_collection!(ReportDescriptorBuilderC4, ReportDescriptorBuilderC5);
impl_close_collection!(ReportDescriptorBuilderC5, ReportDescriptorBuilderC6);
impl_close_collection!(ReportDescriptorBuilderC6, ReportDescriptorBuilderC7);
macro_rules! impl_builder_for_push {
($ca:ty, $cb:ty) => {
impl ReportDescriptorBuilder<$ca> {
#[doc(hidden)]
pub fn push(self) -> ReportDescriptorBuilder<$cb> {
let tmp = self.append(GlobalItem::Push.into());
ReportDescriptorBuilder {
items: tmp.items,
marker: core::marker::PhantomData,
}
}
}
impl ReportDescriptorBuilder<$cb> {
#[doc(hidden)]
pub fn pop(self) -> ReportDescriptorBuilder<$ca> {
let tmp = self.append(GlobalItem::Pop.into());
ReportDescriptorBuilder {
items: tmp.items,
marker: core::marker::PhantomData,
}
}
}
};
}
impl_builder_for_push!(ReportDescriptorBuilderC1, ReportDescriptorBuilderC1Push);
impl_builder_for_push!(ReportDescriptorBuilderC2, ReportDescriptorBuilderC2Push);
impl_builder_for_push!(ReportDescriptorBuilderC3, ReportDescriptorBuilderC3Push);
impl_builder_for_push!(ReportDescriptorBuilderC4, ReportDescriptorBuilderC4Push);
impl_builder_for_push!(ReportDescriptorBuilderC5, ReportDescriptorBuilderC5Push);
impl_builder_for_push!(ReportDescriptorBuilderC6, ReportDescriptorBuilderC6Push);
#[cfg(test)]
mod tests {
use super::*;
use crate::{Field, Report, ReportDescriptor, Usage, VariableField};
use hut::{self, AsUsage};
#[test]
fn item_size() {
for size in 1..4 {
let itype = 0b100; let tag = 0b00010000; let bytes: [u8; 5] = [tag | itype | size, 1, 2, 3, 4];
let bytes = bytes.as_slice();
let item = ShortItem::try_from(bytes).unwrap();
match size {
0 => assert_eq!(item.size(), 1),
1 => assert_eq!(item.size(), 2),
2 => assert_eq!(item.size(), 3),
3 => assert_eq!(item.size(), 5),
_ => panic!("Size {size} cannot happen"),
}
}
}
#[test]
fn item_type() {
let itype = 0b10010000; let size = 3;
let bytes: [u8; 5] = [itype | size, 0b10101010, 0b1, 0, 0];
let bytes = bytes.as_slice();
let item = ShortItem::try_from(bytes).unwrap();
assert!(matches!(item.item_type(), ItemType::Main { .. }));
match item.item_type() {
ItemType::Main(mi) => match mi {
MainItem::Output(o) => match o {
_ => {
assert!(o.is_constant == false);
assert!(o.is_variable == true);
assert!(o.is_relative == false);
assert!(o.wraps == true);
assert!(o.is_nonlinear == false);
assert!(o.has_no_preferred_state == true);
assert!(o.has_null_state == false);
assert!(o.is_volatile == true);
assert!(o.is_buffered_bytes == true);
}
},
_ => panic!("Failed match against MainItem"),
},
_ => panic!("Wrong item type"),
}
}
#[test]
fn item_data() {
let bytes = [1, 2, 3, 4, 5];
let item_data = ItemData { bytes: &bytes[..1] };
assert_eq!(u32::try_from(&item_data).unwrap(), 1);
let item_data = ItemData { bytes: &bytes[..2] };
assert_eq!(u32::try_from(&item_data).unwrap(), 0x0201);
let item_data = ItemData { bytes: &bytes[..4] };
assert_eq!(u32::try_from(&item_data).unwrap(), 0x04030201);
}
macro_rules! test_hid_value {
($bytes:expr, $unsigned:expr, $signed:expr) => {
let v = HidValue::try_from($bytes.as_slice()).unwrap();
assert_eq!(u32::from(&v), $unsigned);
if ($bytes.len() <= 2) {
assert!($unsigned as u32 <= 0xFFFFu32);
assert_eq!(u16::from(&v), $unsigned as u16);
}
if ($bytes.len() <= 1) {
assert!($unsigned as u32 <= 0xFFu32);
assert_eq!(u8::from(&v), $unsigned as u8);
}
assert_eq!(i32::from(&v), $signed);
};
}
#[test]
fn hid_value() {
test_hid_value!([0x1, 0x2, 0x3, 0x4], 0x04030201u32, 0x04030201);
test_hid_value!([0x7F], 0x7F, 127);
test_hid_value!([0x80], 0x80, -128);
test_hid_value!([0xFF], 0xFF, -1);
test_hid_value!([0x0], 0x0, 0);
test_hid_value!([0x1], 0x1, 1);
test_hid_value!([0xFF, 0x7F], 0x7FFFu32, 32767); test_hid_value!([0x00, 0x80], 0x8000u32, -32768); test_hid_value!([0xFF, 0xFF], 0xFFFFu32, -1); test_hid_value!([0x01, 0x00], 0x0001u32, 1); test_hid_value!([0x34, 0x12], 0x1234u32, 4660); test_hid_value!([0xCC, 0xED], 0xEDCCu32, -4660); test_hid_value!([0x00, 0x00], 0x0000u32, 0);
test_hid_value!([0xFF, 0xFF, 0xFF, 0x7F], 0x7FFFFFFFu32, 2147483647); test_hid_value!([0x00, 0x00, 0x00, 0x80], 0x80000000u32, -2147483648); test_hid_value!([0xFF, 0xFF, 0xFF, 0xFF], 0xFFFFFFFFu32, -1); test_hid_value!([0x01, 0x00, 0x00, 0x00], 0x00000001u32, 1); test_hid_value!([0x78, 0x56, 0x34, 0x12], 0x12345678u32, 305419896); test_hid_value!([0x88, 0xA9, 0xCB, 0xED], 0xEDCBA988u32, -305419896); test_hid_value!([0x00, 0x00, 0x00, 0x00], 0x00000000u32, 0); }
#[test]
fn hidbytes() {
let bytes = HidBytes::from(1u8).take();
assert_eq!(bytes, [0x1]);
let bytes = HidBytes::from(1u16).take();
assert_eq!(bytes, [0x1]);
let bytes = HidBytes::from(1u32).take();
assert_eq!(bytes, [0x1]);
let bytes = HidBytes::from(255u8).take();
assert_eq!(bytes, [0xff]);
let bytes = HidBytes::from(255u16).take();
assert_eq!(bytes, [0xff]);
let bytes = HidBytes::from(255u32).take();
assert_eq!(bytes, [0xff]);
let bytes = HidBytes::from(128i16).take();
assert_eq!(bytes, [0x80, 0x0]);
let bytes = HidBytes::from(128i32).take();
assert_eq!(bytes, [0x80, 0x0]);
let bytes = HidBytes::from(255i16).take();
assert_eq!(bytes, [0xff, 0x0]);
let bytes = HidBytes::from(255i32).take();
assert_eq!(bytes, [0xff, 0x0]);
let bytes = HidBytes::from(256u16).take();
assert_eq!(bytes, [0x0, 0x1]);
let bytes = HidBytes::from(256u32).take();
assert_eq!(bytes, [0x0, 0x1]);
let bytes = HidBytes::from(0x10000u32).take();
assert_eq!(bytes, [0x0, 0x0, 0x1, 0x0]);
let bytes = HidBytes::from(0x1000000u32).take();
assert_eq!(bytes, [0x0, 0x0, 0x0, 0x1]);
let bytes = HidBytes::from(u32::MAX).take();
assert_eq!(bytes, [0xff, 0xff, 0xff, 0xff]);
let bytes = HidBytes::from(-1i8).take();
assert_eq!(bytes, [0xff]);
let bytes = HidBytes::from(-1i16).take();
assert_eq!(bytes, [0xff]);
let bytes = HidBytes::from(-1i32).take();
assert_eq!(bytes, [0xff]);
let bytes = HidBytes::from(i16::MAX - 1).take();
assert_eq!(bytes, [0xfe, 0x7f]);
let bytes = HidBytes::from(i32::MAX - 1).take();
assert_eq!(bytes, [0xfe, 0xff, 0xff, 0x7f]);
}
#[test]
fn builder_example() {
let builder = ReportDescriptorBuilder::new();
let rdesc: Vec<u8> = builder
.usage_page(hut::UsagePage::GenericDesktop)
.usage_id(hut::GenericDesktop::Mouse)
.open_collection(CollectionItem::Application)
.open_collection(CollectionItem::Physical)
.push()
.append(LogicalMinimum::from(0).into())
.append(LogicalMaximum::from(128).into())
.pop()
.append(ReportCount::from(2).into())
.append(ReportSize::from(8).into())
.usage_id(hut::GenericDesktop::X)
.usage_id(hut::GenericDesktop::Y)
.input(ItemBuilder::new().variable().absolute().input())
.close_collection()
.close_collection()
.build();
#[rustfmt::skip]
let expected_bytes = [
0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0xa1, 0x00, 0xa4, 0x15, 0x00, 0x26, 0x80, 0x00, 0xb4, 0x95, 0x02, 0x75, 0x08, 0x09, 0x30, 0x09, 0x31, 0x81, 0x02, 0xc0, 0xc0, ];
assert_eq!(rdesc, expected_bytes);
}
#[test]
fn builder_buggy_usages() {
let rdesc = ReportDescriptorBuilder::new()
.usage_page(hut::GenericDesktop::X) .usage_id(hut::Arcade::CoinDoor) .append(LogicalMinimum::from(0).into())
.append(LogicalMaximum::from(128).into())
.append(ReportCount::from(2).into())
.append(ReportSize::from(8).into())
.input(ItemBuilder::new().variable().absolute().input())
.build();
let rdesc: ReportDescriptor = ReportDescriptor::try_from(&rdesc).unwrap();
let input = rdesc.input_reports().first().unwrap();
let item = input.fields().first().unwrap();
let usage: Usage = match item {
Field::Variable(VariableField { usage, .. }) => *usage,
_ => panic!("Invalid field type"),
};
let expected_buggy_usage: Usage = hut::GenericDesktop::Mouse.usage().into();
assert_eq!(usage, expected_buggy_usage);
}
#[test]
fn builder() {
#[rustfmt::skip]
let expected_bytes = [
0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x03, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x03, 0x91, 0x02, 0x95, 0x05, 0x91, 0x01, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x95, 0x08, 0x81, 0x02, 0x75, 0x08, 0x95, 0x01, 0x81, 0x01, 0x19, 0x00, 0x29, 0x91, 0x26, 0xff, 0x00, 0x95, 0x06, 0x81, 0x00, 0x05, 0x0c, 0x0a, 0xc0, 0x02, 0xa1, 0x02, 0x1a, 0xc1, 0x02, 0x2a, 0xc6, 0x02, 0x95, 0x06, 0xb1, 0x03, 0xc0, 0xc0, ];
let builder = ReportDescriptorBuilder::new();
let rdesc: Vec<u8> = builder
.append(hut::UsagePage::GenericDesktop.into())
.append(UsageId::from(hut::GenericDesktop::Keyboard.usage()).into())
.open_collection(CollectionItem::Application)
.append(hut::UsagePage::LED.into())
.append(UsageMinimum(1).into())
.append(UsageMaximum(3).into())
.append(LogicalMinimum(0).into())
.append(LogicalMaximum(1).into())
.append(ReportSize(1).into())
.append(ReportCount(3).into())
.output(ItemBuilder::new().data().variable().absolute().output())
.append(ReportCount(5).into())
.output(ItemBuilder::new().constant().array().absolute().output())
.append(hut::UsagePage::KeyboardKeypad.into())
.append(UsageMinimum(224).into())
.append(UsageMaximum(231).into())
.append(ReportCount(8).into())
.input(ItemBuilder::new().data().variable().absolute().input())
.append(ReportSize(8).into())
.append(ReportCount(1).into())
.input(ItemBuilder::new().constant().array().absolute().input())
.append(UsageMinimum(0).into())
.append(UsageMaximum(145).into())
.append(LogicalMaximum(255).into())
.append(ReportCount(6).into())
.input(ItemBuilder::new().data().array().absolute().input())
.append(hut::UsagePage::Consumer.into())
.append(
UsageId::from(hut::Consumer::ExtendedKeyboardAttributesCollection.usage()).into(),
)
.open_collection(CollectionItem::Logical)
.append(UsageMinimum(705).into())
.append(UsageMaximum(710).into())
.append(ReportCount(6).into())
.feature(
ItemBuilder::new()
.constant()
.variable()
.absolute()
.feature(),
)
.close_collection()
.close_collection()
.build();
assert_eq!(rdesc, expected_bytes);
}
}