blob: 4de6ff45d6c0751624347fa96fb506335e6c0d41 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fuchsia_bluetooth::profile::DataElement;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use tracing::warn;
/// ClassDescriptorType from HID v1.1.1 5.3.4.7
#[derive(Debug, FromPrimitive, PartialEq)]
enum ClassDescriptorType {
Report = 0x22,
Physical = 0x23,
}
#[derive(Debug, PartialEq)]
pub struct Descriptor {
class_descriptor_type: ClassDescriptorType,
pub data: Vec<u8>,
}
#[derive(Debug, PartialEq)]
pub struct DescriptorList(pub Vec<Descriptor>);
#[derive(Debug, PartialEq)]
pub enum DescriptorReadError {
DataElementNotSequence(DataElement),
DescriptorNotSequence(DataElement),
BadSequence(Vec<Box<DataElement>>),
TypeNotUint8(DataElement),
DataNotString(DataElement),
BadType(u8),
}
impl TryFrom<DataElement> for DescriptorList {
type Error = DescriptorReadError;
fn try_from(data_element: DataElement) -> Result<DescriptorList, DescriptorReadError> {
let srcs = match data_element {
DataElement::Sequence(srcs) => srcs,
data_element => {
warn!("Top level data element {:?} was not a sequence when reading descptiptor list from SDP data element.", data_element);
Err(DescriptorReadError::DataElementNotSequence(data_element))?
}
};
let mut descs = Vec::new();
for box_data_element in srcs {
let desc = Descriptor::try_from(*box_data_element)?;
descs.push(desc);
}
Ok(DescriptorList(descs))
}
}
impl TryFrom<DataElement> for Descriptor {
type Error = DescriptorReadError;
// HID v1.1.1 5.3.4.7 HIDDescriptors are sequences which consist of a uint8 type and string
// data elements. For future compamtibility, any further elements are not an error but are
// ignored.
fn try_from(data_element: DataElement) -> Result<Descriptor, DescriptorReadError> {
let vec = match data_element {
DataElement::Sequence(vec) => vec,
_ => {
warn!(
"Unexpected data element {:?} when reading descriptor list from SDP data element.",
data_element
);
Err(DescriptorReadError::DescriptorNotSequence(data_element))?
}
};
let (class_descriptor_type, data) = if vec.len() >= 2 {
let class_descriptor_type = vec[0].clone();
let data = vec[1].clone();
(class_descriptor_type, data)
} else {
warn!(
"Unexpected sequence {:?} when reading descriptor list from SDP data elelement",
vec
);
Err(DescriptorReadError::BadSequence(vec))?
};
let class_descriptor_type = match *class_descriptor_type {
DataElement::Uint8(class_descriptor_type) => class_descriptor_type,
_ => {
warn!(
"Unexptected data element {:?} when reading type from data element",
class_descriptor_type
);
Err(DescriptorReadError::TypeNotUint8(*class_descriptor_type))?
}
};
let class_descriptor_type = match ClassDescriptorType::from_u8(class_descriptor_type) {
Some(class_descriptor_type) => class_descriptor_type,
None => {
warn!(
"Unexpected type {:?} when reading descriptor from SDP data elelement",
class_descriptor_type
);
Err(DescriptorReadError::BadType(class_descriptor_type))?
}
};
let data = match *data {
DataElement::Str(data) => data,
_ => {
warn!("Unexpected data element {:?} when reading data from SDP data element", data);
Err(DescriptorReadError::DataNotString(*data))?
}
};
Ok(Descriptor { class_descriptor_type, data })
}
}
#[cfg(test)]
mod test {
use super::*;
use assert_matches::assert_matches;
#[fuchsia::test]
fn success() {
let data_elements = DataElement::Sequence(vec![
Box::new(DataElement::Sequence(vec![
Box::new(DataElement::Uint8(0x22)),
Box::new(DataElement::Str(vec![0x1, 0x2, 0x3])),
])),
Box::new(DataElement::Sequence(vec![
Box::new(DataElement::Uint8(0x23)),
Box::new(DataElement::Str(vec![0x3, 0x2, 0x1])),
])),
]);
let expected_descriptor_list = DescriptorList(vec![
Descriptor {
class_descriptor_type: ClassDescriptorType::Report,
data: vec![0x1, 0x2, 0x3],
},
Descriptor {
class_descriptor_type: ClassDescriptorType::Physical,
data: vec![0x3, 0x2, 0x1],
},
]);
let descriptor_list = DescriptorList::try_from(data_elements).expect("Read data elements.");
assert_eq!(descriptor_list, expected_descriptor_list);
}
#[fuchsia::test]
fn descriptor_not_sequence() {
let data_element = DataElement::Uint8(0x1);
let err = Descriptor::try_from(data_element);
assert_matches!(err, Err(DescriptorReadError::DescriptorNotSequence(_)));
}
#[fuchsia::test]
fn bad_sequence() {
let data_element = DataElement::Sequence(vec![Box::new(DataElement::Uint8(0x22))]);
let err = Descriptor::try_from(data_element);
assert_matches!(err, Err(DescriptorReadError::BadSequence(_)));
}
#[fuchsia::test]
fn extra_sequence_elements_ignored() {
let data_element = DataElement::Sequence(vec![
Box::new(DataElement::Uint8(0x22)),
Box::new(DataElement::Str(vec![0x1, 0x2, 0x3])),
Box::new(DataElement::Str(vec![0x4, 0x5, 0x6])),
]);
let expected_descriptor = Descriptor {
class_descriptor_type: ClassDescriptorType::Report,
data: vec![0x1, 0x2, 0x3],
};
let descriptor = Descriptor::try_from(data_element).expect("Read data element");
assert_eq!(descriptor, expected_descriptor);
}
#[fuchsia::test]
fn type_not_uint8() {
let data_element = DataElement::Sequence(vec![
Box::new(DataElement::Uint16(0x1)),
Box::new(DataElement::Str(vec![0x1, 0x2, 0x3])),
]);
let err = Descriptor::try_from(data_element);
assert_matches!(err, Err(DescriptorReadError::TypeNotUint8(_)));
}
#[fuchsia::test]
fn data_not_string() {
let data_element = DataElement::Sequence(vec![
Box::new(DataElement::Uint8(0x22)),
Box::new(DataElement::Uint16(0x1)),
]);
let err = Descriptor::try_from(data_element);
assert_matches!(err, Err(DescriptorReadError::DataNotString(_)));
}
#[fuchsia::test]
fn bad_type() {
let data_element = DataElement::Sequence(vec![
Box::new(DataElement::Uint8(0x1)),
Box::new(DataElement::Str(vec![0x1, 0x2, 0x3])),
]);
let err = Descriptor::try_from(data_element);
assert_matches!(err, Err(DescriptorReadError::BadType(_)));
}
}