Files
RedBear-OS/drivers/input/i2c-hidd/src/hid.rs
T

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]]))
}