async_std_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, open};
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    os::unix::io::{AsRawFd, RawFd},
23    pin::Pin,
24    task::{Context, Poll},
25};
26
27use async_io::Async;
28use async_std::{
29    io::{Read, ReadExt, Write, WriteExt},
30    path::Path,
31    task::spawn_blocking as asyncify,
32};
33
34#[doc(hidden)]
35pub struct File {
36    // use file to call close when drop
37    inner: Async<std::fs::File>,
38}
39
40impl File {
41    pub fn from_file(file: std::fs::File) -> Result<Self> {
42        Ok(Self {
43            inner: Async::new(file)?,
44        })
45    }
46}
47
48impl AsRawFd for File {
49    fn as_raw_fd(&self) -> RawFd {
50        self.inner.as_raw_fd()
51    }
52}
53
54impl Read for File {
55    fn poll_read(
56        self: Pin<&mut Self>,
57        cx: &mut Context<'_>,
58        buf: &mut [u8],
59    ) -> Poll<Result<usize>> {
60        use std::io::Read;
61
62        match self.inner.poll_readable(cx) {
63            Poll::Ready(x) => x,
64            Poll::Pending => return Poll::Pending,
65        }?;
66
67        Poll::Ready(self.inner.get_ref().read(buf))
68    }
69}
70
71impl Write for File {
72    fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
73        use std::io::Write;
74
75        match self.inner.poll_writable(cx) {
76            Poll::Ready(x) => x,
77            Poll::Pending => return Poll::Pending,
78        }?;
79
80        Poll::Ready(self.inner.get_ref().write(buf))
81    }
82
83    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>> {
84        Poll::Ready(Ok(()))
85    }
86
87    fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>> {
88        Poll::Ready(Ok(()))
89    }
90}
91
92/// HID Gadget Device
93pub struct Device<C: Class> {
94    file: File,
95    _class: PhantomData<C>,
96}
97
98impl<C: Class> Device<C> {
99    /// Open device by path or name
100    pub async fn open(path: impl AsRef<Path>) -> Result<Self> {
101        let path = path.as_ref().to_owned();
102        let file = asyncify(move || open(path, false)).await?;
103        let file = File::from_file(file)?;
104        Ok(Self {
105            file,
106            _class: PhantomData,
107        })
108    }
109
110    /// Send input report
111    pub async fn input(&mut self, input: &C::Input) -> Result<()>
112    where
113        C::Input: AsRef<[u8]>,
114    {
115        let raw = input.as_ref();
116        let len = self.file.write(raw).await?;
117
118        check_write(len, raw.len())
119    }
120
121    /// Receive output report
122    pub async fn output(&mut self, output: &mut C::Output) -> Result<()>
123    where
124        C::Output: AsMut<[u8]>,
125    {
126        let raw = output.as_mut();
127        let len = self.file.read(raw).await?;
128
129        check_read(len, raw.len())?;
130
131        Ok(())
132    }
133}