196 lines
5.8 KiB
Rust
196 lines
5.8 KiB
Rust
use std::fs::OpenOptions;
|
|
use std::io::{Read, Write};
|
|
|
|
use anyhow::{bail, Context, Result};
|
|
use i2c_interface::{I2cTransferRequest, I2cTransferResponse, I2cTransferSegment};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::acpi::I2cBinding;
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
pub struct HidDescriptor {
|
|
pub hid_desc_length: u16,
|
|
pub bcd_version: u16,
|
|
pub report_desc_length: u16,
|
|
pub report_desc_register: u16,
|
|
pub input_register: u16,
|
|
pub max_input_length: u16,
|
|
pub output_register: u16,
|
|
pub max_output_length: u16,
|
|
pub command_register: u16,
|
|
pub data_register: u16,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default)]
|
|
pub struct ReportDescriptorSummary {
|
|
pub has_keyboard_page: bool,
|
|
pub has_pointer_page: bool,
|
|
pub report_ids: bool,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct I2cAdapterClient {
|
|
binding: I2cBinding,
|
|
}
|
|
|
|
impl I2cAdapterClient {
|
|
pub fn new(binding: I2cBinding) -> Self {
|
|
Self { binding }
|
|
}
|
|
pub fn transfer(&self, segments: Vec<I2cTransferSegment>) -> Result<I2cTransferResponse> {
|
|
let request = I2cTransferRequest {
|
|
adapter: self.binding.adapter.clone(),
|
|
segments,
|
|
stop: true,
|
|
};
|
|
|
|
let serialized = ron::to_string(&request).context("failed to serialize I2C request")?;
|
|
let mut handle = OpenOptions::new()
|
|
.read(true)
|
|
.write(true)
|
|
.open("/scheme/i2c/transfer")
|
|
.context("failed to open /scheme/i2c/transfer")?;
|
|
handle
|
|
.write_all(serialized.as_bytes())
|
|
.context("failed to write I2C transfer request")?;
|
|
|
|
let mut response = String::new();
|
|
handle
|
|
.read_to_string(&mut response)
|
|
.context("failed to read I2C transfer response")?;
|
|
|
|
let transfer: I2cTransferResponse =
|
|
ron::from_str(&response).context("failed to decode I2C transfer response")?;
|
|
if !transfer.ok {
|
|
bail!(
|
|
"I2C transfer failed: {}",
|
|
transfer
|
|
.error
|
|
.unwrap_or_else(|| "unspecified transfer error".to_string())
|
|
);
|
|
}
|
|
Ok(transfer)
|
|
}
|
|
|
|
pub fn write_read(&self, address: u16, write_data: &[u8], read_len: usize) -> Result<Vec<u8>> {
|
|
let response = self.transfer(vec![
|
|
I2cTransferSegment::write(address, write_data.to_vec()),
|
|
I2cTransferSegment::read(address, read_len),
|
|
])?;
|
|
|
|
response
|
|
.read_data
|
|
.last()
|
|
.cloned()
|
|
.ok_or_else(|| anyhow::anyhow!("I2C transfer returned no readable segment payload"))
|
|
}
|
|
}
|
|
|
|
pub fn fetch_hid_descriptor(
|
|
adapter: &I2cAdapterClient,
|
|
address: u16,
|
|
hid_desc_addr: u16,
|
|
) -> Result<HidDescriptor> {
|
|
let prefix = adapter
|
|
.write_read(address, &hid_desc_addr.to_le_bytes(), 2)
|
|
.context("failed to read HID descriptor length prefix")?;
|
|
if prefix.len() < 2 {
|
|
bail!("short HID descriptor prefix: {} bytes", prefix.len());
|
|
}
|
|
|
|
let hid_desc_length = u16::from_le_bytes([prefix[0], prefix[1]]);
|
|
if hid_desc_length < 18 {
|
|
bail!("invalid HID descriptor length {hid_desc_length}");
|
|
}
|
|
|
|
let raw = adapter
|
|
.write_read(
|
|
address,
|
|
&hid_desc_addr.to_le_bytes(),
|
|
usize::from(hid_desc_length),
|
|
)
|
|
.context("failed to read full HID descriptor")?;
|
|
parse_hid_descriptor(&raw)
|
|
}
|
|
|
|
pub fn fetch_report_descriptor(
|
|
adapter: &I2cAdapterClient,
|
|
address: u16,
|
|
desc: &HidDescriptor,
|
|
) -> Result<Vec<u8>> {
|
|
adapter
|
|
.write_read(
|
|
address,
|
|
&desc.report_desc_register.to_le_bytes(),
|
|
usize::from(desc.report_desc_length),
|
|
)
|
|
.context("failed to read HID report descriptor")
|
|
}
|
|
|
|
pub fn stream_input_reports(
|
|
adapter: &I2cAdapterClient,
|
|
address: u16,
|
|
desc: &HidDescriptor,
|
|
report_desc: &[u8],
|
|
sink: &mut crate::input::InputForwarder,
|
|
) -> Result<()> {
|
|
let summary = summarize_report_descriptor(report_desc);
|
|
let input_len = usize::from(desc.max_input_length.max(4));
|
|
|
|
loop {
|
|
let report = adapter
|
|
.write_read(address, &desc.input_register.to_le_bytes(), input_len)
|
|
.context("failed to fetch I2C HID input report")?;
|
|
sink.forward_report(&summary, &report)?;
|
|
}
|
|
}
|
|
|
|
fn parse_hid_descriptor(bytes: &[u8]) -> Result<HidDescriptor> {
|
|
if bytes.len() < 18 {
|
|
bail!("short HID descriptor: {} bytes", bytes.len());
|
|
}
|
|
|
|
Ok(HidDescriptor {
|
|
hid_desc_length: le16(bytes, 0)?,
|
|
bcd_version: le16(bytes, 2)?,
|
|
report_desc_length: le16(bytes, 4)?,
|
|
report_desc_register: le16(bytes, 6)?,
|
|
input_register: le16(bytes, 8)?,
|
|
max_input_length: le16(bytes, 10)?,
|
|
output_register: le16(bytes, 12)?,
|
|
max_output_length: le16(bytes, 14)?,
|
|
command_register: le16(bytes, 16)?,
|
|
data_register: if bytes.len() >= 20 {
|
|
le16(bytes, 18)?
|
|
} else {
|
|
0
|
|
},
|
|
})
|
|
}
|
|
|
|
fn summarize_report_descriptor(report_desc: &[u8]) -> ReportDescriptorSummary {
|
|
let mut summary = ReportDescriptorSummary::default();
|
|
|
|
for window in report_desc.windows(2) {
|
|
match window {
|
|
[0x05, 0x01] => summary.has_pointer_page = true,
|
|
[0x05, 0x07] => summary.has_keyboard_page = true,
|
|
[0x85, _] => summary.report_ids = true,
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
if !summary.has_keyboard_page && !summary.has_pointer_page {
|
|
summary.has_pointer_page = true;
|
|
}
|
|
|
|
summary
|
|
}
|
|
|
|
fn le16(bytes: &[u8], offset: usize) -> Result<u16> {
|
|
let slice = bytes
|
|
.get(offset..offset + 2)
|
|
.ok_or_else(|| anyhow::anyhow!("short LE16 field at offset {offset}"))?;
|
|
Ok(u16::from_le_bytes([slice[0], slice[1]]))
|
|
}
|