Skip to main content

uhid_virt/
uhid_device.rs

1use std::convert::TryFrom;
2use std::fs::{File, OpenOptions};
3use std::io::{self, prelude::*};
4use std::os::unix::fs::OpenOptionsExt;
5use std::path::Path;
6
7use crate::codec::*;
8
9pub struct UHIDDevice<T: Read + Write> {
10    handle: T,
11}
12
13/// Contains information about your HID device, sent when UHIDDevice is created
14#[derive(Debug, Clone, PartialEq)]
15pub struct CreateParams {
16    pub name: String,
17    pub phys: String,
18    pub uniq: String,
19    pub bus: Bus,
20    pub vendor: u32,
21    pub product: u32,
22    pub version: u32,
23    pub country: u32,
24    pub rd_data: Vec<u8>,
25}
26
27/// Character misc-device handle for a specific HID device
28impl<T: Read + Write> UHIDDevice<T> {
29    /// The data parameter should contain a data-payload. This is the raw data that you read from your device. The kernel will parse the HID reports.
30    pub fn write(&mut self, data: &[u8]) -> io::Result<usize> {
31        let event: [u8; UHID_EVENT_SIZE] = InputEvent::Input { data }.into();
32        self.handle.write(&event)
33    }
34
35    /// Write a SetReportReply, only use in reponse to a read SetReport event
36    pub fn write_set_report_reply(&mut self, id: u32, err: u16) -> io::Result<usize> {
37        let event: [u8; UHID_EVENT_SIZE] = InputEvent::SetReportReply { id, err }.into();
38        self.handle.write(&event)
39    }
40
41    /// Write a GetReportReply, only use in reponse to a read GetReport event
42    pub fn write_get_report_reply(
43        &mut self,
44        id: u32,
45        err: u16,
46        data: Vec<u8>,
47    ) -> io::Result<usize> {
48        let event: [u8; UHID_EVENT_SIZE] = InputEvent::GetReportReply { id, err, data }.into();
49        self.handle.write(&event)
50    }
51
52    /// Reads a queued output event. No reaction is required to an output event, but you should handle them according to your needs.
53    pub fn read(&mut self) -> Result<OutputEvent, StreamError> {
54        let mut event = [0u8; UHID_EVENT_SIZE];
55        self.handle
56            .read_exact(&mut event)
57            .map_err(StreamError::Io)?;
58        OutputEvent::try_from(event)
59    }
60
61    /// This destroys the internal HID device. No further I/O will be accepted. There may still be pending output events that you can receive but no further input events can be sent to the kernel.
62    pub fn destroy(&mut self) -> io::Result<usize> {
63        let event: [u8; UHID_EVENT_SIZE] = InputEvent::Destroy.into();
64        self.handle.write(&event)
65    }
66}
67
68impl UHIDDevice<File> {
69    /// Opens the character misc-device at /dev/uhid
70    pub fn create(params: CreateParams) -> io::Result<UHIDDevice<File>> {
71        UHIDDevice::create_with_path(params, Path::new("/dev/uhid"))
72    }
73    pub fn create_with_path(params: CreateParams, path: &Path) -> io::Result<UHIDDevice<File>> {
74        let mut options = OpenOptions::new();
75        options.read(true);
76        options.write(true);
77        if cfg!(unix) {
78            options.custom_flags(libc::O_RDWR | libc::O_CLOEXEC | libc::O_NONBLOCK);
79        }
80        let mut handle = options.open(path)?;
81        let event: [u8; UHID_EVENT_SIZE] = InputEvent::Create(params).into();
82        handle.write_all(&event)?;
83        Ok(UHIDDevice { handle })
84    }
85}