use std::convert::TryFrom;
use std::fs::{File, OpenOptions};
use std::io::{self, prelude::*};
use std::os::unix::fs::OpenOptionsExt;
use std::path::Path;
use crate::codec::*;
pub struct UHIDDevice<T: Read + Write> {
handle: T,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CreateParams {
pub name: String,
pub phys: String,
pub uniq: String,
pub bus: Bus,
pub vendor: u32,
pub product: u32,
pub version: u32,
pub country: u32,
pub rd_data: Vec<u8>,
}
impl<T: Read + Write> UHIDDevice<T> {
pub fn write(&mut self, data: &[u8]) -> io::Result<usize> {
let event: [u8; UHID_EVENT_SIZE] = InputEvent::Input { data }.into();
self.handle.write(&event)
}
pub fn write_set_report_reply(&mut self, id: u32, err: u16) -> io::Result<usize> {
let event: [u8; UHID_EVENT_SIZE] = InputEvent::SetReportReply { id, err }.into();
self.handle.write(&event)
}
pub fn write_get_report_reply(
&mut self,
id: u32,
err: u16,
data: Vec<u8>,
) -> io::Result<usize> {
let event: [u8; UHID_EVENT_SIZE] = InputEvent::GetReportReply { id, err, data }.into();
self.handle.write(&event)
}
pub fn read(&mut self) -> Result<OutputEvent, StreamError> {
let mut event = [0u8; UHID_EVENT_SIZE];
self.handle
.read_exact(&mut event)
.map_err(StreamError::Io)?;
OutputEvent::try_from(event)
}
pub fn destroy(&mut self) -> io::Result<usize> {
let event: [u8; UHID_EVENT_SIZE] = InputEvent::Destroy.into();
self.handle.write(&event)
}
}
impl UHIDDevice<File> {
pub fn create(params: CreateParams) -> io::Result<UHIDDevice<File>> {
UHIDDevice::create_with_path(params, Path::new("/dev/uhid"))
}
pub fn create_with_path(params: CreateParams, path: &Path) -> io::Result<UHIDDevice<File>> {
let mut options = OpenOptions::new();
options.read(true);
options.write(true);
if cfg!(unix) {
options.custom_flags(libc::O_RDWR | libc::O_CLOEXEC | libc::O_NONBLOCK);
}
let mut handle = options.open(path)?;
let event: [u8; UHID_EVENT_SIZE] = InputEvent::Create(params).into();
handle.write_all(&event)?;
Ok(UHIDDevice { handle })
}
}