Implement HID report reading.
This commit is contained in:
parent
2594d20bcf
commit
ebbfd4b336
10
README.md
Normal file
10
README.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Installation
|
||||||
|
|
||||||
|
## Windows
|
||||||
|
|
||||||
|
## Linux
|
||||||
|
|
||||||
|
Install the following libraries:
|
||||||
|
|
||||||
|
- libusb-dev
|
||||||
|
- libudev-dev
|
BIN
doc/Device Class Definition for HID.pdf
Normal file
BIN
doc/Device Class Definition for HID.pdf
Normal file
Binary file not shown.
97388
doc/HID Usage Tables.pdf
Normal file
97388
doc/HID Usage Tables.pdf
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,52 +2,23 @@
|
|||||||
|
|
||||||
use hidapi::HidApi;
|
use hidapi::HidApi;
|
||||||
|
|
||||||
//use donten::hid;
|
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>,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct Device {
|
struct Device {
|
||||||
product:String,
|
product:String,
|
||||||
manufacturer:String,
|
manufacturer:String,
|
||||||
serial:String,
|
serial:String,
|
||||||
|
|
||||||
interfaces:Vec<DeviceInterface>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main()
|
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() {
|
if let Ok(hid) = HidApi::new() {
|
||||||
|
|
||||||
let mut devices = Vec::<Device>::new();
|
let mut devices = Vec::<Device>::new();
|
||||||
|
|
||||||
println!("[Device List]");
|
|
||||||
for device_info in hid.device_list() {
|
for device_info in hid.device_list() {
|
||||||
|
|
||||||
let product = if let Some(s) = device_info.product_string() { s } else { "" }.to_string();
|
let product = if let Some(s) = device_info.product_string() { s } else { "" }.to_string();
|
||||||
@ -72,61 +43,94 @@ fn main()
|
|||||||
product,
|
product,
|
||||||
manufacturer,
|
manufacturer,
|
||||||
serial,
|
serial,
|
||||||
|
|
||||||
interfaces:Vec::new(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
println!(" - {} ({})",
|
/*println!("{} ({})",
|
||||||
device.product,
|
device.product,
|
||||||
device.manufacturer,
|
device.manufacturer,
|
||||||
);
|
);*/
|
||||||
|
|
||||||
devices.push(device);
|
devices.push(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
// Add usage to device.
|
// Add usage to device.
|
||||||
let mut desc_bytes = [0u8; hidapi::MAX_REPORT_DESCRIPTOR_SIZE];
|
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(hwd) = device_info.open_device(&hid) {
|
||||||
if let Ok(desc_size) = hwd.get_report_descriptor(&mut desc_bytes) {
|
if let Ok(desc_size) = hwd.get_report_descriptor(&mut desc_bytes) {
|
||||||
|
|
||||||
if let Ok(descriptor) = hid::ReportDescriptor::parse(&desc_bytes[0..desc_size]) {
|
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.
|
/*println!("[INPUT]");
|
||||||
let interface_index = devices[index].interfaces.len();
|
for id in &descriptor.inputs {
|
||||||
devices[index].interfaces.push(interface);
|
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.
|
// Listen for input reports from device.
|
||||||
let _ttx = tx.clone();
|
let _ttx = tx.clone();
|
||||||
|
let device = devices[index].clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let _device_index = index;
|
let _device_index = index;
|
||||||
let _interface_index = interface_index;
|
let mut descriptor = descriptor;
|
||||||
let buffer_length = buffer_length;
|
|
||||||
|
|
||||||
let hwd = hwd;
|
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];
|
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.
|
// Process input reports.
|
||||||
/*while let Ok(_msg) = rx.recv() {
|
while let Ok(_msg) = rx.recv() {
|
||||||
|
|
||||||
}*/
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("failed to init hidapi.");
|
println!("failed to init hidapi.");
|
||||||
}
|
}
|
||||||
|
@ -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:"",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
],
|
||||||
|
};
|
513
src/hid/mod.rs
513
src/hid/mod.rs
@ -1,35 +1,4 @@
|
|||||||
/*
|
pub mod consts;
|
||||||
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,
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Main Items
|
// Main Items
|
||||||
const MN_INPUT :u8 = 0b1000_00_00;
|
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_STRING_MAX :u8 = 0b1001_10_00;
|
||||||
const LC_DELIMITER :u8 = 0b1010_10_00;
|
const LC_DELIMITER :u8 = 0b1010_10_00;
|
||||||
|
|
||||||
struct Context {
|
#[derive(Clone, Copy)]
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct GlobalState {
|
struct GlobalState {
|
||||||
usage_page:u32,
|
pub usage_page:u16,
|
||||||
logical_min:i32,
|
pub logical_min:i32,
|
||||||
logical_max:i32,
|
pub logical_max:i32,
|
||||||
physical_min:i32,
|
pub physical_min:i32,
|
||||||
physical_max:i32,
|
pub physical_max:i32,
|
||||||
unit_exponent:i32,
|
pub unit_exponent:i32,
|
||||||
unit:u32,
|
pub unit:u32,
|
||||||
report_id:u32,
|
pub report_id:u32,
|
||||||
report_size:u32,
|
pub report_size:u32,
|
||||||
report_count:u32,
|
pub report_count:u32,
|
||||||
}
|
}
|
||||||
impl GlobalState {
|
impl GlobalState {
|
||||||
pub fn new() -> Self
|
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 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 {
|
impl ReportDescriptor {
|
||||||
pub fn parse(bytes:&[u8]) -> Result<Self,()>
|
pub fn parse(bytes:&[u8]) -> Result<Self,()>
|
||||||
{
|
{
|
||||||
// Output
|
// Output
|
||||||
let mut descriptor = Self {
|
let mut descriptor = Self {
|
||||||
|
interfaces:Vec::new(),
|
||||||
|
inputs:Vec::new(),
|
||||||
|
outputs:Vec::new(),
|
||||||
|
features:Vec::new(),
|
||||||
|
reports:Vec::new(),
|
||||||
|
|
||||||
|
has_id:false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Global State
|
// Global State
|
||||||
@ -123,12 +156,12 @@ impl ReportDescriptor {
|
|||||||
global_state.push(GlobalState::new());
|
global_state.push(GlobalState::new());
|
||||||
|
|
||||||
// Local State
|
// Local State
|
||||||
let usages = Vec::<[u16;2]>::new();
|
let mut local_state = LocalState::new();
|
||||||
|
|
||||||
// Tracking
|
// Tracking
|
||||||
|
let mut collection_stack = vec![Collection { usage:0, index:0 }];
|
||||||
let mut collection_index = 0;
|
let mut collection_index = 0;
|
||||||
let mut offset = 0;
|
//let mut delimiter = false;
|
||||||
let mut report_id = 0;
|
|
||||||
|
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
while index < bytes.len() {
|
while index < bytes.len() {
|
||||||
@ -141,6 +174,8 @@ impl ReportDescriptor {
|
|||||||
index += 1;
|
index += 1;
|
||||||
|
|
||||||
if bytes.len() - index >= sz as usize {
|
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
|
// Handle long tag
|
||||||
if sz == 2 && tg == MN_EXTENDED {
|
if sz == 2 && tg == MN_EXTENDED {
|
||||||
@ -153,8 +188,11 @@ impl ReportDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sz <= 4 {
|
if sz <= 4 {
|
||||||
|
let udata = Self::unpack(&bytes, &index, sz as usize);
|
||||||
|
let idata = Self::unpack_signed(&bytes, &index, sz as usize);
|
||||||
|
|
||||||
match tg {
|
match tg {
|
||||||
MN_INPUT => {
|
MN_INPUT | MN_OUTPUT | MN_FEATURE => {
|
||||||
/*
|
/*
|
||||||
** 0: Data(0), Constant(1)
|
** 0: Data(0), Constant(1)
|
||||||
** 1: Array(0), Variable(1)
|
** 1: Array(0), Variable(1)
|
||||||
@ -168,57 +206,140 @@ impl ReportDescriptor {
|
|||||||
** _: reserved
|
** _: reserved
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let mut is_const = false;
|
let mut field = Field {
|
||||||
let mut is_var = false;
|
container:tg,
|
||||||
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;
|
|
||||||
|
|
||||||
if sz >= 1 {
|
is_const:(udata & 0x01) != 0,
|
||||||
is_const = (bytes[index + 1] & 0x01) != 0;
|
is_var:(udata & 0x02) != 0,
|
||||||
is_var = (bytes[index + 1] & 0x02) != 0;
|
is_rel:(udata & 0x04) != 0,
|
||||||
is_rel = (bytes[index + 1] & 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 => {
|
let bit_offset = if let Some(field) = report.fields.last() {
|
||||||
/*
|
field.bit_offset + field.report_size
|
||||||
** 0: Data(0), Constant(1)
|
} else { 0 };
|
||||||
** 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
field.bit_offset = bit_offset;
|
||||||
|
//field.bit_length = global_state[global_state_index].report_size as usize;
|
||||||
|
|
||||||
MN_FEATURE => {
|
report.length += field.report_size * field.report_count;
|
||||||
/*
|
report.fields.push(field);
|
||||||
** 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
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
local_state = LocalState::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
MN_COLLECTION_BEGIN => {
|
MN_COLLECTION_BEGIN => {
|
||||||
@ -234,70 +355,89 @@ impl ReportDescriptor {
|
|||||||
** 80-FF: vendor-defined
|
** 80-FF: vendor-defined
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
collection_stack.push(Collection {
|
||||||
|
usage:udata,
|
||||||
|
index:collection_index,
|
||||||
|
});
|
||||||
|
collection_index += 1;
|
||||||
|
local_state = LocalState::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
MN_COLLECTION_END => {
|
MN_COLLECTION_END => {
|
||||||
|
collection_stack.pop();
|
||||||
|
local_state = LocalState::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_USAGE_PAGE => {
|
GB_USAGE_PAGE => {
|
||||||
|
global_state[global_state_index].usage_page = udata as u16;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_LOGICAL_MIN => {
|
GB_LOGICAL_MIN => {
|
||||||
|
global_state[global_state_index].logical_min = idata;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_LOGICAL_MAX => {
|
GB_LOGICAL_MAX => {
|
||||||
|
global_state[global_state_index].logical_max = idata;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_PHYSICAL_MIN => {
|
GB_PHYSICAL_MIN => {
|
||||||
|
global_state[global_state_index].physical_min = idata;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_PHYSICAL_MAX => {
|
GB_PHYSICAL_MAX => {
|
||||||
|
global_state[global_state_index].physical_max = idata;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_UNIT_EXPONENT => {
|
GB_UNIT_EXPONENT => {
|
||||||
|
global_state[global_state_index].unit_exponent = idata;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_UNIT => {
|
GB_UNIT => {
|
||||||
|
global_state[global_state_index].unit = udata;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_REPORT_SIZE => {
|
GB_REPORT_SIZE => {
|
||||||
|
global_state[global_state_index].report_size = udata;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_REPORT_ID => {
|
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 => {
|
GB_REPORT_COUNT => {
|
||||||
|
global_state[global_state_index].report_count = udata;
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_PUSH => {
|
GB_PUSH => {
|
||||||
|
global_state.push(global_state[global_state.len() - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_POP => {
|
GB_POP => {
|
||||||
|
if global_state.len() > 1 {
|
||||||
|
global_state.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LC_USAGE => {
|
LC_USAGE => {
|
||||||
|
local_state.usages.push(udata);
|
||||||
}
|
}
|
||||||
|
|
||||||
LC_USAGE_MIN => {
|
LC_USAGE_MIN => {
|
||||||
|
local_state.usage_range[0] = udata;
|
||||||
}
|
}
|
||||||
|
|
||||||
LC_USAGE_MAX => {
|
LC_USAGE_MAX => {
|
||||||
|
local_state.usage_range[1] = udata;
|
||||||
}
|
}
|
||||||
|
|
||||||
LC_DESIGNATOR_INDEX => {
|
LC_DESIGNATOR_INDEX => {
|
||||||
@ -338,4 +478,145 @@ impl ReportDescriptor {
|
|||||||
|
|
||||||
Ok(descriptor)
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user