tokio_hidg/
lib.rs

1#![forbid(future_incompatible)]
2#![deny(bad_style, missing_docs)]
3#![doc = include_str!("../README.md")]
4
5use hidg_core::{check_read, check_write, AsDevicePath};
6
7pub use hidg_core::{Class, Error, Result, StateChange, ValueChange};
8
9#[cfg(feature = "keyboard")]
10pub use hidg_core::{
11    Key, KeyStateChanges, Keyboard, KeyboardInput, KeyboardOutput, Led, LedStateChanges, Leds,
12    Modifiers,
13};
14
15#[cfg(feature = "mouse")]
16pub use hidg_core::{
17    Button, Buttons, Mouse, MouseInput, MouseInputChange, MouseInputChanges, MouseOutput,
18};
19
20use core::marker::PhantomData;
21use std::{
22    fs::File,
23    io::{Read, Write},
24};
25
26use tokio::{fs::OpenOptions, io::unix::AsyncFd};
27
28/// HID Gadget Device
29pub struct Device<C: Class> {
30    file: AsyncFd<File>,
31    _class: PhantomData<C>,
32}
33
34impl<C: Class> Device<C> {
35    /// Open device by path or name or number
36    pub async fn open(device: impl AsDevicePath) -> Result<Self> {
37        let path = device.as_device_path();
38        let file = OpenOptions::new()
39            .read(true)
40            .write(true)
41            .custom_flags(libc::O_NONBLOCK)
42            .open(path)
43            .await?
44            .into_std()
45            .await;
46        let file = AsyncFd::new(file)?;
47        Ok(Self {
48            file,
49            _class: PhantomData,
50        })
51    }
52
53    /// Send input report
54    pub async fn input(&mut self, input: &C::Input) -> Result<()>
55    where
56        C::Input: AsRef<[u8]>,
57    {
58        let _ = self.file.writable().await?;
59        let raw = input.as_ref();
60        let len = self.file.get_ref().write(raw)?;
61
62        check_write(len, raw.len())
63    }
64
65    /// Receive output report
66    pub async fn output(&mut self, output: &mut C::Output) -> Result<()>
67    where
68        C::Output: AsMut<[u8]>,
69    {
70        let _ = self.file.readable().await?;
71        let raw = output.as_mut();
72        let len = self.file.get_ref().read(raw)?;
73
74        check_read(len, raw.len())?;
75
76        Ok(())
77    }
78}