Implement HID report reading.

This commit is contained in:
yukirij 2024-11-15 18:26:30 -08:00
parent 2594d20bcf
commit ebbfd4b336
6 changed files with 98520 additions and 169 deletions

10
README.md Normal file
View File

@ -0,0 +1,10 @@
# Installation
## Windows
## Linux
Install the following libraries:
- libusb-dev
- libudev-dev

Binary file not shown.

97388
doc/HID Usage Tables.pdf Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,52 +2,23 @@
use hidapi::HidApi;
//use donten::hid;
struct Report {
device:usize,
interface:usize,
data:Vec<u8>,
}
struct Usage {
usage_page:u16,
usage:u16,
offset:usize,
bit_length:usize,
minimum:u32,
maximum:u32,
}
struct ReportDescriptor {
id:usize,
usages:Vec<Usage>,
data:Vec<u8>,
}
struct DeviceInterface {
reports:Vec<ReportDescriptor>,
}
use donten::hid;
#[derive(Clone)]
struct Device {
product:String,
manufacturer:String,
serial:String,
interfaces:Vec<DeviceInterface>,
}
fn main()
{
let (_tx, _rx) = crossbeam::channel::bounded::<Vec<u8>>(100);
let (tx, rx) = crossbeam::channel::bounded::<Vec<u8>>(100);
if let Ok(hid) = HidApi::new() {
let mut devices = Vec::<Device>::new();
println!("[Device List]");
for device_info in hid.device_list() {
let product = if let Some(s) = device_info.product_string() { s } else { "" }.to_string();
@ -72,61 +43,94 @@ fn main()
product,
manufacturer,
serial,
interfaces:Vec::new(),
};
println!(" - {} ({})",
/*println!("{} ({})",
device.product,
device.manufacturer,
);
);*/
devices.push(device);
}
/*
// Add usage to device.
let mut desc_bytes = [0u8; hidapi::MAX_REPORT_DESCRIPTOR_SIZE];
//println!("## {} ##", devices[index].product);
if let Ok(hwd) = device_info.open_device(&hid) {
if let Ok(desc_size) = hwd.get_report_descriptor(&mut desc_bytes) {
if let Ok(descriptor) = hid::ReportDescriptor::parse(&desc_bytes[0..desc_size]) {
let interface = DeviceInterface {
reports:Vec::new(),
};
let mut buffer_length = 0;
// Push interface to device.
let interface_index = devices[index].interfaces.len();
devices[index].interfaces.push(interface);
/*println!("[INPUT]");
for id in &descriptor.inputs {
let interface = &descriptor.interfaces[*id];
let (usage_page, usage) = if let Some(page) = hid::consts::USAGES.get(interface.usage_page) {
(page.name.to_string(), if let Some(usage) = page.get(interface.usage) {
usage.name.to_string()
} else { format!("Unknown({})", interface.usage) })
} else {(
format!("Unknown({})", interface.usage_page),
format!("Unknown({})", interface.usage)
)};
println!(" > {} / {} [{:02x}:{:02x}]",
usage_page,
usage,
interface.collection,
interface.index,
);
}*/
// Listen for input reports from device.
let _ttx = tx.clone();
let device = devices[index].clone();
std::thread::spawn(move || {
let _device_index = index;
let _interface_index = interface_index;
let buffer_length = buffer_length;
let mut descriptor = descriptor;
let hwd = hwd;
let mut buffer_length = 0;
for report in &descriptor.reports {
buffer_length = buffer_length.max(1 + (report.length / 8));
}
hwd.set_blocking_mode(true).ok();
let mut buffer = vec![0u8; buffer_length];
while let Ok(_size) = hwd.read(&mut buffer) {
loop {
match hwd.read(&mut buffer) {
Ok(size) => {
println!("report {}", device.product);
if let Ok(changes) = descriptor.read_report(&buffer[..size]) {
for change in changes {
println!(" - change {}", change);
}
} else {
println!("Failed report read.");
}
}
Err(e) => {
println!("error ({}): {}", device.product, e.to_string());
break;
}
}
}
println!("drop {}", device.product);
});
}
}
}
*/
}
// Process input reports.
/*while let Ok(_msg) = rx.recv() {
while let Ok(_msg) = rx.recv() {
}*/
}
} else {
println!("failed to init hidapi.");
}

View File

@ -0,0 +1,668 @@
pub struct UsageCollection {
pages:&'static [UsagePage],
}
impl UsageCollection {
pub fn get(&self, id:u16) -> Option<&'static UsagePage>
{
for page in self.pages {
if page.id == id {
return Some(page);
}
}
None
}
}
pub struct UsagePage {
pub id:u16,
pub name:&'static str,
usages:&'static [Usage],
}
impl UsagePage {
pub fn get(&self, id:u16) -> Option<&'static Usage>
{
for usage in self.usages {
if usage.id == id {
return Some(usage);
}
}
None
}
}
pub struct Usage {
pub id:u16,
pub usage_type:UsageType,
pub name:&'static str,
}
pub enum UsageType {
Any,
Physical,
Application,
Value,
Toggle,
Button,
}
pub const USAGES :UsageCollection = UsageCollection {
pages:&[
UsagePage {
id:0x0001,
name:"Generic Desktop",
usages:&[
Usage {
id:0x0001,
usage_type:UsageType::Physical,
name:"Pointer",
},
Usage {
id:0x0002,
usage_type:UsageType::Application,
name:"Mouse",
},
Usage {
id:0x0004,
usage_type:UsageType::Application,
name:"Joystick",
},
Usage {
id:0x0005,
usage_type:UsageType::Application,
name:"Gamepad",
},
Usage {
id:0x0006,
usage_type:UsageType::Application,
name:"Keyboard",
},
Usage {
id:0x0007,
usage_type:UsageType::Application,
name:"Keypad",
},
Usage {
id:0x0008,
usage_type:UsageType::Application,
name:"Multi-axis Controller",
},
Usage {
id:0x0009,
usage_type:UsageType::Application,
name:"Tablet PC",
},
Usage {
id:0x000E,
usage_type:UsageType::Application,
name:"Spatial Controller",
},
Usage {
id:0x0030,
usage_type:UsageType::Value,
name:"X",
},
Usage {
id:0x0031,
usage_type:UsageType::Value,
name:"Y",
},
Usage {
id:0x0032,
usage_type:UsageType::Value,
name:"Z",
},
Usage {
id:0x0033,
usage_type:UsageType::Value,
name:"Rx",
},
Usage {
id:0x0034,
usage_type:UsageType::Value,
name:"Ry",
},
Usage {
id:0x0035,
usage_type:UsageType::Value,
name:"Rz",
},
Usage {
id:0x0036,
usage_type:UsageType::Value,
name:"Slider",
},
Usage {
id:0x0037,
usage_type:UsageType::Value,
name:"Dial",
},
Usage {
id:0x0038,
usage_type:UsageType::Value,
name:"Wheel",
},
Usage {
id:0x0039,
usage_type:UsageType::Value,
name:"Hat Switch",
},
Usage {
id:0x003D,
usage_type:UsageType::Toggle,
name:"Start",
},
Usage {
id:0x003E,
usage_type:UsageType::Toggle,
name:"Select",
},
Usage {
id:0x0040,
usage_type:UsageType::Value,
name:"Vx",
},
Usage {
id:0x0041,
usage_type:UsageType::Value,
name:"Vy",
},
Usage {
id:0x0042,
usage_type:UsageType::Value,
name:"Vz",
},
Usage {
id:0x0043,
usage_type:UsageType::Value,
name:"Vbx",
},
Usage {
id:0x0044,
usage_type:UsageType::Value,
name:"Vby",
},
Usage {
id:0x0045,
usage_type:UsageType::Value,
name:"Vbz",
},
Usage {
id:0x0046,
usage_type:UsageType::Value,
name:"Vno",
},
Usage {
id:0x0048,
usage_type:UsageType::Value,
name:"Resolution Multiplier",
},
Usage {
id:0x0049,
usage_type:UsageType::Value,
name:"Qx",
},
Usage {
id:0x004A,
usage_type:UsageType::Value,
name:"Qy",
},
Usage {
id:0x004B,
usage_type:UsageType::Value,
name:"Qz",
},
Usage {
id:0x004C,
usage_type:UsageType::Value,
name:"Qw",
},
Usage {
id:0x0090,
usage_type:UsageType::Toggle,
name:"D-pad Up",
},
Usage {
id:0x0091,
usage_type:UsageType::Toggle,
name:"D-pad Down",
},
Usage {
id:0x0092,
usage_type:UsageType::Toggle,
name:"D-pad Right",
},
Usage {
id:0x0093,
usage_type:UsageType::Toggle,
name:"D-pad Left",
},
],
},
UsagePage {
id:0x0002,
name:"Simulation Controls",
usages:&[
Usage {
id:0x0001,
usage_type:UsageType::Application,
name:"Flight Simulation Device",
},
Usage {
id:0x0002,
usage_type:UsageType::Application,
name:"Automobile Simulation Device",
},
Usage {
id:0x0003,
usage_type:UsageType::Application,
name:"Tank Simulation Device",
},
Usage {
id:0x0004,
usage_type:UsageType::Application,
name:"Spaceship Simulation Device",
},
Usage {
id:0x0005,
usage_type:UsageType::Application,
name:"Submarine Simulation Device",
},
Usage {
id:0x0006,
usage_type:UsageType::Application,
name:"Sailing Simulation Device",
},
Usage {
id:0x0007,
usage_type:UsageType::Application,
name:"Motorcycle Simulation Device",
},
Usage {
id:0x0008,
usage_type:UsageType::Application,
name:"Sports Simulation Device",
},
Usage {
id:0x0009,
usage_type:UsageType::Application,
name:"Airplane Simulation Device",
},
Usage {
id:0x000A,
usage_type:UsageType::Application,
name:"Helicopter Simulation Device",
},
Usage {
id:0x000B,
usage_type:UsageType::Application,
name:"Magic Carpet Simulation Device",
},
Usage {
id:0x000C,
usage_type:UsageType::Application,
name:"Bicycle Simulation Device",
},
Usage {
id:0x0020,
usage_type:UsageType::Application,
name:"Flight Control Stick",
},
Usage {
id:0x0021,
usage_type:UsageType::Application,
name:"Flight Stick",
},
Usage {
id:0x0022,
usage_type:UsageType::Physical,
name:"Cyclic Control",
},
Usage {
id:0x0023,
usage_type:UsageType::Physical,
name:"Cyclic Trim",
},
Usage {
id:0x0034,
usage_type:UsageType::Application,
name:"Flight Yoke",
},
Usage {
id:0x0025,
usage_type:UsageType::Physical,
name:"Track Control",
},
Usage {
id:0x00B0,
usage_type:UsageType::Value,
name:"Aileron",
},
Usage {
id:0x00B1,
usage_type:UsageType::Value,
name:"Aileron Trim",
},
Usage {
id:0x00B2,
usage_type:UsageType::Value,
name:"Anti-Torque Control",
},
Usage {
id:0x00B3,
usage_type:UsageType::Toggle,
name:"Autopilot",
},
Usage {
id:0x00B4,
usage_type:UsageType::Button,
name:"Chaff Release",
},
Usage {
id:0x00B5,
usage_type:UsageType::Value,
name:"Collective Control",
},
Usage {
id:0x00B6,
usage_type:UsageType::Value,
name:"Drive Brake",
},
Usage {
id:0x00B7,
usage_type:UsageType::Toggle,
name:"Electronic Countermeasures",
},
Usage {
id:0x00B8,
usage_type:UsageType::Value,
name:"Elevator",
},
Usage {
id:0x00B9,
usage_type:UsageType::Value,
name:"Elevator Trim",
},
Usage {
id:0x00BA,
usage_type:UsageType::Value,
name:"Rudder",
},
Usage {
id:0x00BB,
usage_type:UsageType::Value,
name:"Throttle",
},
Usage {
id:0x00BC,
usage_type:UsageType::Toggle,
name:"Flight Communications",
},
Usage {
id:0x00BD,
usage_type:UsageType::Button,
name:"Flare Release",
},
Usage {
id:0x00BE,
usage_type:UsageType::Toggle,
name:"Landing Gear",
},
Usage {
id:0x00BF,
usage_type:UsageType::Value,
name:"Toe Brake",
},
Usage {
id:0x00C0,
usage_type:UsageType::Button,
name:"Trigger",
},
Usage {
id:0x00C1,
usage_type:UsageType::Toggle,
name:"Weapons Arm",
},
Usage {
id:0x00C2,
usage_type:UsageType::Button,
name:"Weapons Select",
},
Usage {
id:0x00C3,
usage_type:UsageType::Value,
name:"Wing Flags",
},
Usage {
id:0x00C4,
usage_type:UsageType::Value,
name:"Accelerator",
},
Usage {
id:0x00C5,
usage_type:UsageType::Value,
name:"Brake",
},
Usage {
id:0x00C6,
usage_type:UsageType::Value,
name:"Clutch",
},
Usage {
id:0x00C7,
usage_type:UsageType::Value,
name:"Steering",
},
Usage {
id:0x00C9,
usage_type:UsageType::Value,
name:"Turret Direction",
},
Usage {
id:0x00CA,
usage_type:UsageType::Value,
name:"Turret Elevation",
},
Usage {
id:0x00CB,
usage_type:UsageType::Value,
name:"Dive Plane",
},
Usage {
id:0x00CC,
usage_type:UsageType::Value,
name:"Ballast",
},
Usage {
id:0x00CD,
usage_type:UsageType::Value,
name:"Bicycle Crank",
},
Usage {
id:0x00CE,
usage_type:UsageType::Value,
name:"Handle Bars",
},
Usage {
id:0x00CF,
usage_type:UsageType::Value,
name:"Front Brake",
},
Usage {
id:0x00D0,
usage_type:UsageType::Value,
name:"Rear Brake",
},
],
},
UsagePage {
id:0x0003,
name:"Virtual Reality Controls",
usages:&[
Usage {
id:0x0005,
usage_type:UsageType::Physical,
name:"Head Tracker",
},
],
},
UsagePage {
id:0x0007,
name:"Keyboard",
usages:&[ ],
},
UsagePage {
id:0x0008,
name:"LED",
usages:&[
Usage {
id:0x0001,
usage_type:UsageType::Toggle,
name:"Num Lock",
},
Usage {
id:0x0002,
usage_type:UsageType::Toggle,
name:"Caps Lock",
},
Usage {
id:0x0003,
usage_type:UsageType::Toggle,
name:"Scroll Lock",
},
Usage {
id:0x0004,
usage_type:UsageType::Toggle,
name:"Compose",
},
Usage {
id:0x0005,
usage_type:UsageType::Toggle,
name:"Kana",
},
Usage {
id:0x0006,
usage_type:UsageType::Toggle,
name:"Power",
},
Usage {
id:0x0007,
usage_type:UsageType::Toggle,
name:"Shift",
},
Usage {
id:0x0008,
usage_type:UsageType::Toggle,
name:"Do Not Disturb",
},
Usage {
id:0x0009,
usage_type:UsageType::Toggle,
name:"Mute",
},
Usage {
id:0x000A,
usage_type:UsageType::Toggle,
name:"Tone Enable",
},
Usage {
id:0x000B,
usage_type:UsageType::Toggle,
name:"High Cut Filter",
},
Usage {
id:0x000C,
usage_type:UsageType::Toggle,
name:"Low Cut Filter",
},
],
},
UsagePage {
id:0x0009,
name:"Button",
usages:&[
Usage {
id:0x0001,
usage_type:UsageType::Any,
name:"Primary",
},
Usage {
id:0x0002,
usage_type:UsageType::Any,
name:"Secondary",
},
Usage {
id:0x0003,
usage_type:UsageType::Any,
name:"Tertiary",
},
],
},
UsagePage {
id:0x000C,
name:"Consumer",
usages:&[
Usage {
id:0x0001,
usage_type:UsageType::Application,
name:"Consumer Control",
},
Usage {
id:0x0002,
usage_type:UsageType::Any,
name:"Numeric Key Pad",
},
Usage {
id:0x0003,
usage_type:UsageType::Any,
name:"Programmable Buttons",
},
Usage {
id:0x0004,
usage_type:UsageType::Application,
name:"Microphone",
},
Usage {
id:0x0005,
usage_type:UsageType::Application,
name:"Headphone",
},
],
},
UsagePage {
id:0x000D,
name:"Digitizer",
usages:&[
Usage {
id:0x0001,
usage_type:UsageType::Application,
name:"Digitizer",
},
],
},
/*
UsagePage {
id:0x00,
name:"",
usages:&[
Usage {
id:0x00,
usage_type:UsageType::,
name:"",
},
],
},
*/
],
};

View File

@ -1,35 +1,4 @@
/*
enum ByteCode {
Report = 0x00,
Collection = 0xA1,
UsagePage = 0x05,
Usage = 0x09,
UsageMin = 0x19,
UsageMax = 0x29,
LogicalMin = 0x15,
LogicalMax = 0x25,
PhysicalMin = 0x100,
PhysicalMax = 0x101,
ReportSize = 0x75,
ReportCount = 0x95,
}
const F_TYPE :u16 = 0x03;
const TYPE_INPUT :u16 = 0x01;
const TYPE_OUTPUT :u16 = 0x02;
const TYPE_FEATURE :u16 = 0x03;
const F_DC :u16 = 0x40; // Data / Const
const F_RA :u16 = 0x80; // Relative / Absolute
struct _Usage {
usage_page:u16,
usage:u16,
offset:u16,
size:u16,
fields:u16,
}
*/
pub mod consts;
// Main Items
const MN_INPUT :u8 = 0b1000_00_00;
@ -65,28 +34,18 @@ const LC_STRING_MIN :u8 = 0b1000_10_00;
const LC_STRING_MAX :u8 = 0b1001_10_00;
const LC_DELIMITER :u8 = 0b1010_10_00;
struct Context {
usage_page:u16,
usages:Vec<u16>,
logical_min:i32,
logical_max:i32,
physical_min:Option<i32>,
physical_max:Option<i32>,
report_count:u16,
report_size:u16,
}
#[derive(Clone, Copy)]
struct GlobalState {
usage_page:u32,
logical_min:i32,
logical_max:i32,
physical_min:i32,
physical_max:i32,
unit_exponent:i32,
unit:u32,
report_id:u32,
report_size:u32,
report_count:u32,
pub usage_page:u16,
pub logical_min:i32,
pub logical_max:i32,
pub physical_min:i32,
pub physical_max:i32,
pub unit_exponent:i32,
pub unit:u32,
pub report_id:u32,
pub report_size:u32,
pub report_count:u32,
}
impl GlobalState {
pub fn new() -> Self
@ -106,16 +65,90 @@ impl GlobalState {
}
}
struct LocalState {
pub usages:Vec<u32>,
pub usage_range:[u32;2],
}
impl LocalState {
pub fn new() -> Self
{
Self {
usages:Vec::new(),
usage_range:[0, 0],
}
}
}
struct Collection {
pub usage:u32,
pub index:u32,
}
pub struct Interface {
pub usage_page:u16,
pub usage:u16,
pub collection:u16,
pub index:u16,
working_value:i32,
pub value:i32,
}
pub struct Field {
container:u8,
is_const:bool,
is_var:bool,
is_rel:bool,
is_wrap:bool,
is_linear:bool,
has_pref:bool,
has_null:bool,
is_volatile:bool,
is_buffered:bool,
report_size:usize,
report_count:usize,
logical_min:i32,
logical_max:i32,
physical_min:i32,
physical_max:i32,
unit_exponent:i32,
unit:u32,
interfaces:Vec<usize>,
bit_offset:usize,
}
pub struct Report {
pub id:u32,
pub length:usize,
pub fields:Vec<Field>,
}
pub struct ReportDescriptor {
pub interfaces:Vec<Interface>,
pub inputs:Vec<usize>,
pub outputs:Vec<usize>,
pub features:Vec<usize>,
pub reports:Vec<Report>,
has_id:bool,
}
impl ReportDescriptor {
pub fn parse(bytes:&[u8]) -> Result<Self,()>
{
// Output
let mut descriptor = Self {
interfaces:Vec::new(),
inputs:Vec::new(),
outputs:Vec::new(),
features:Vec::new(),
reports:Vec::new(),
has_id:false,
};
// Global State
@ -123,12 +156,12 @@ impl ReportDescriptor {
global_state.push(GlobalState::new());
// Local State
let usages = Vec::<[u16;2]>::new();
let mut local_state = LocalState::new();
// Tracking
let mut collection_stack = vec![Collection { usage:0, index:0 }];
let mut collection_index = 0;
let mut offset = 0;
let mut report_id = 0;
//let mut delimiter = false;
let mut index = 0;
while index < bytes.len() {
@ -141,6 +174,8 @@ impl ReportDescriptor {
index += 1;
if bytes.len() - index >= sz as usize {
let global_state_index = global_state.len() - 1;
let collection_stack_index = collection_stack.len() - 1;
// Handle long tag
if sz == 2 && tg == MN_EXTENDED {
@ -153,8 +188,11 @@ impl ReportDescriptor {
}
if sz <= 4 {
let udata = Self::unpack(&bytes, &index, sz as usize);
let idata = Self::unpack_signed(&bytes, &index, sz as usize);
match tg {
MN_INPUT => {
MN_INPUT | MN_OUTPUT | MN_FEATURE => {
/*
** 0: Data(0), Constant(1)
** 1: Array(0), Variable(1)
@ -168,57 +206,140 @@ impl ReportDescriptor {
** _: reserved
*/
let mut is_const = false;
let mut is_var = false;
let mut is_rel = false;
let mut is_wrap = false;
let mut is_linear = false;
let mut is_pref = false;
let mut has_null = false;
let mut is_volatile = false;
let mut is_buffered = false;
let mut field = Field {
container:tg,
if sz >= 1 {
is_const = (bytes[index + 1] & 0x01) != 0;
is_var = (bytes[index + 1] & 0x02) != 0;
is_rel = (bytes[index + 1] & 0x04) != 0;
is_const:(udata & 0x01) != 0,
is_var:(udata & 0x02) != 0,
is_rel:(udata & 0x04) != 0,
is_wrap:(udata & 0x04) != 0,
is_linear:(udata & 0x04) != 0,
has_pref:(udata & 0x04) != 0,
has_null:(udata & 0x04) != 0,
is_volatile:(udata & 0x04) != 0,
is_buffered:(udata & 0x04) != 0,
report_size:global_state[global_state_index].report_size as usize,
report_count:global_state[global_state_index].report_count as usize,
logical_min:global_state[global_state_index].logical_min,
logical_max:global_state[global_state_index].logical_max,
physical_min:global_state[global_state_index].physical_min,
physical_max:global_state[global_state_index].physical_max,
unit_exponent:global_state[global_state_index].unit_exponent,
unit:global_state[global_state_index].unit,
interfaces:Vec::new(),
bit_offset:0,
};
// Add data interfaces to descriptor.
if !field.is_const {
let interface_count = if field.is_var {
global_state[global_state_index].report_count as usize
} else {
if local_state.usage_range != [0; 2] {
(local_state.usage_range[1] - local_state.usage_range[0]) as usize
} else {
local_state.usages.len() as usize
}
};
for i in 0..interface_count {
let mut usage_index = 0;
let usage = if local_state.usage_range != [0; 2] {
if (i as u32) <= local_state.usage_range[1] - local_state.usage_range[0] {
local_state.usage_range[0] + i as u32
} else {
usage_index += 1;
local_state.usage_range[1]
}
} else if i < local_state.usages.len() {
local_state.usages[i]
} else if local_state.usages.len() > 0 {
let index = local_state.usages.len() - 1;
usage_index += 1;
local_state.usages[index]
} else {
0
};
let usage_page = if usage <= 0xFFFF {
global_state[global_state_index].usage_page
} else {
(usage >> 16) as u16
};
let usage = usage as u16;
let collection = collection_stack[collection_stack_index].index as u16;
// Check if interface already exists
let mut existing_index = None;
for i in 0..descriptor.interfaces.len() {
if descriptor.interfaces[i].usage_page == usage_page
&& descriptor.interfaces[i].usage == usage
&& descriptor.interfaces[i].collection == collection
&& descriptor.interfaces[i].index == usage_index
{
existing_index = Some(i);
break;
}
}
if let Some(existing_index) = existing_index {
field.interfaces.push(existing_index);
} else {
let interface = Interface {
usage_page,
usage:usage,
collection,
index:usage_index,
working_value:0,
value:0,
};
descriptor.interfaces.push(interface);
let id = descriptor.interfaces.len() - 1;
field.interfaces.push(id);
match tg {
MN_OUTPUT => { descriptor.outputs.push(id); }
MN_FEATURE => { descriptor.features.push(id); }
_ => { descriptor.inputs.push(id); }
}
}
}
}
if sz >= 2 {
let report_index = if let Some(report_index) = descriptor.find_report(global_state[global_state_index].report_id) {
report_index
} else {
descriptor.reports.push(Report {
id:global_state[global_state_index].report_id,
length:0,
fields:Vec::new(),
});
descriptor.reports.len() - 1
};
}
}
// Add interfaces to report.
let report = &mut descriptor.reports[report_index];
MN_OUTPUT => {
/*
** 0: Data(0), Constant(1)
** 1: Array(0), Variable(1)
** 2: Absolute(0), Relative(1)
** 3: NoWrap(0), Wrap(1)
** 4: Linear(0), NonLinear(1)
** 5: PreferredState(0), NoPreferred(1)
** 6: NoNull(0), NullState(1)
** 7: NonVolatile(0), Volatile(1)
** 8: BitField(0), BufferedBytes(1)
** _: reserved
*/
let bit_offset = if let Some(field) = report.fields.last() {
field.bit_offset + field.report_size
} else { 0 };
}
field.bit_offset = bit_offset;
//field.bit_length = global_state[global_state_index].report_size as usize;
MN_FEATURE => {
/*
** 0: Data(0), Constant(1)
** 1: Array(0), Variable(1)
** 2: Absolute(0), Relative(1)
** 3: NoWrap(0), Wrap(1)
** 4: Linear(0), NonLinear(1)
** 5: PreferredState(0), NoPreferred(1)
** 6: NoNull(0), NullState(1)
** 7: NonVolatile(0), Volatile(1)
** 8: BitField(0), BufferedBytes(1)
** _: reserved
*/
report.length += field.report_size * field.report_count;
report.fields.push(field);
local_state = LocalState::new();
}
MN_COLLECTION_BEGIN => {
@ -234,70 +355,89 @@ impl ReportDescriptor {
** 80-FF: vendor-defined
*/
collection_stack.push(Collection {
usage:udata,
index:collection_index,
});
collection_index += 1;
local_state = LocalState::new();
}
MN_COLLECTION_END => {
collection_stack.pop();
local_state = LocalState::new();
}
GB_USAGE_PAGE => {
global_state[global_state_index].usage_page = udata as u16;
}
GB_LOGICAL_MIN => {
global_state[global_state_index].logical_min = idata;
}
GB_LOGICAL_MAX => {
global_state[global_state_index].logical_max = idata;
}
GB_PHYSICAL_MIN => {
global_state[global_state_index].physical_min = idata;
}
GB_PHYSICAL_MAX => {
global_state[global_state_index].physical_max = idata;
}
GB_UNIT_EXPONENT => {
global_state[global_state_index].unit_exponent = idata;
}
GB_UNIT => {
global_state[global_state_index].unit = udata;
}
GB_REPORT_SIZE => {
global_state[global_state_index].report_size = udata;
}
GB_REPORT_ID => {
descriptor.has_id = true;
global_state[global_state_index].report_id = udata;
// Create new report if id not found.
if descriptor.find_report(udata).is_none() {
descriptor.reports.push(Report {
id:udata,
length:0,
fields:Vec::new(),
});
}
}
GB_REPORT_COUNT => {
global_state[global_state_index].report_count = udata;
}
GB_PUSH => {
global_state.push(global_state[global_state.len() - 1]);
}
GB_POP => {
if global_state.len() > 1 {
global_state.pop();
}
}
LC_USAGE => {
local_state.usages.push(udata);
}
LC_USAGE_MIN => {
local_state.usage_range[0] = udata;
}
LC_USAGE_MAX => {
local_state.usage_range[1] = udata;
}
LC_DESIGNATOR_INDEX => {
@ -338,4 +478,145 @@ impl ReportDescriptor {
Ok(descriptor)
}
pub fn read_report(&mut self, bytes:&[u8]) -> Result<Vec<usize>,()>
{
let mut start = 0;
if bytes.len() == 0 { return Err(()); }
// Read report id
let report_id = if self.has_id {
start += 1;
bytes[0]
} else {
0
} as u32;
if let Some(report_index) = self.find_report(report_id) {
let report = &self.reports[report_index];
let mut changes = Vec::new();
for field in &report.fields {
// Set interface working values to zero.
for inf in &field.interfaces {
self.interfaces[*inf].working_value = 0;
}
// Get report values.
for ri in 0..field.report_count {
let bit_offset = field.bit_offset + (ri * field.report_size);
let byte = bit_offset / 8;
let bit = bit_offset % 8;
let byte_count = ((bit_offset + field.report_size) / 8) - byte;
let mut value = (bytes[start + byte] as i64) >> bit;
for i in 1..byte_count {
value |= (bytes[start + byte + i] as i64) << ((i * 8) - bit);
}
if value >= field.logical_min as i64 && value <= field.logical_max as i64 {
if field.is_var {
let interface = &mut self.interfaces[field.interfaces[ri]];
// Normalize value
if field.physical_min != 0 || field.physical_max != 0 {
let domain = (field.logical_max - field.logical_min) as i64;
let range = (field.physical_max - field.physical_min) as i64;
let basis = 1 << 24;
interface.working_value = field.physical_min + (((value - field.logical_min as i64) * (range * basis)) / domain) as i32;
}
} else {
let interface = &mut self.interfaces[field.interfaces[value as usize]];
interface.working_value = 1 << 24;
}
}
}
// Set interface working values to zero.
for inf in &field.interfaces {
if self.interfaces[*inf].working_value != self.interfaces[*inf].value {
self.interfaces[*inf].value = self.interfaces[*inf].working_value;
changes.push(*inf);
}
}
}
Ok(changes)
} else {
Err(())
}
}
pub fn write_report(&self) -> Vec<u8>
{
Vec::new()
}
fn find_report(&self, id:u32) -> Option<usize>
{
let mut ri = 0;
while ri < self.reports.len() {
if self.reports[ri].id == id { return Some(ri); }
ri += 1;
}
None
}
fn unpack(data:&[u8], index:&usize, size:usize) -> u32
{
let mut result = 0u32;
if data.len() - *index >= size {
match size {
1 => {
result = data[*index] as u32;
}
2 => {
result = (data[*index] as u32)
| ((data[*index + 1] as u32) << 8);
}
4 => {
result = (data[*index] as u32)
| ((data[*index + 1] as u32) << 8)
| ((data[*index + 2] as u32) << 16)
| ((data[*index + 3] as u32) << 24);
}
_ => { }
}
}
result
}
fn unpack_signed(data:&[u8], index:&usize, size:usize) -> i32
{
let mut result = 0i32;
if data.len() - *index >= size {
match size {
1 => {
result = data[*index] as i32;
}
2 => {
result = (data[*index] as i32)
| ((data[*index + 1] as i32) << 8);
}
4 => {
result = (data[*index] as i32)
| ((data[*index + 1] as i32) << 8)
| ((data[*index + 2] as i32) << 16)
| ((data[*index + 3] as i32) << 24);
}
_ => { }
}
}
result
}
}