use embassy_usb_driver::host::{PipeError, UsbHostAllocator, UsbPipe, pipe};
use embassy_usb_driver::{Direction as UsbDirection, EndpointAddress, EndpointInfo, EndpointType};
pub use super::hid_report::{ReportDescriptor, ReportField};
use crate::control::SetupPacket;
use crate::descriptor::ConfigurationDescriptor;
use crate::handler::EnumerationInfo;
const USB_CLASS_HID: u8 = 0x03;
const TRANSFER_INTERRUPT: u8 = 0x03;
const GET_REPORT: u8 = 0x01;
const SET_IDLE: u8 = 0x0A;
const SET_PROTOCOL: u8 = 0x0B;
pub const PROTOCOL_BOOT: u8 = 0;
pub const PROTOCOL_REPORT: u8 = 1;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct KeyboardReport {
pub modifiers: u8,
pub keycodes: [u8; 6],
}
impl KeyboardReport {
pub fn parse(buf: &[u8]) -> Option<Self> {
if buf.len() < 8 {
return None;
}
Some(Self {
modifiers: buf[0],
keycodes: [buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]],
})
}
pub fn is_pressed(&self, keycode: u8) -> bool {
keycode != 0 && self.keycodes.contains(&keycode)
}
pub fn ctrl(&self) -> bool {
self.modifiers & 0x11 != 0
}
pub fn shift(&self) -> bool {
self.modifiers & 0x22 != 0
}
pub fn alt(&self) -> bool {
self.modifiers & 0x44 != 0
}
pub fn gui(&self) -> bool {
self.modifiers & 0x88 != 0
}
}
pub type MouseButtons = u8;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MouseReport {
pub buttons: MouseButtons,
pub x: i8,
pub y: i8,
pub wheel: i8,
}
impl MouseReport {
pub const BUTTON_LEFT: MouseButtons = 1 << 0;
pub const BUTTON_RIGHT: MouseButtons = 1 << 1;
pub const BUTTON_MIDDLE: MouseButtons = 1 << 2;
pub fn parse(buf: &[u8]) -> Option<Self> {
if buf.len() < 3 {
return None;
}
Some(Self {
buttons: buf[0],
x: buf[1] as i8,
y: buf[2] as i8,
wheel: if buf.len() >= 4 { buf[3] as i8 } else { 0 },
})
}
pub fn left(&self) -> bool {
self.buttons & Self::BUTTON_LEFT != 0
}
pub fn right(&self) -> bool {
self.buttons & Self::BUTTON_RIGHT != 0
}
pub fn middle(&self) -> bool {
self.buttons & Self::BUTTON_MIDDLE != 0
}
}
const DESC_HID: u8 = 0x21;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct HidInfo {
pub interface_number: u8,
pub interrupt_in_ep: u8,
pub interrupt_in_mps: u16,
pub report_descriptor_len: u16,
}
pub fn find_hid(config_desc: &[u8]) -> Option<HidInfo> {
let cfg = ConfigurationDescriptor::try_from_slice(config_desc).ok()?;
for iface in cfg.iter_interface() {
if iface.interface_class != USB_CLASS_HID {
continue;
}
let report_desc_len = iface
.iter_descriptors()
.find_map(|(_, data)| {
if data.len() >= 7 && data[1] == DESC_HID {
Some(u16::from_le_bytes([data[5], data[6]]))
} else {
None
}
})
.unwrap_or(0);
let ep = iface
.iter_endpoints()
.find(|ep| ep.transfer_type() == TRANSFER_INTERRUPT && ep.is_in())?;
return Some(HidInfo {
interface_number: iface.interface_number,
interrupt_in_ep: ep.endpoint_address,
interrupt_in_mps: ep.max_packet_size,
report_descriptor_len: report_desc_len,
});
}
None
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum HidError {
Transfer(PipeError),
NoInterface,
NoPipe,
}
impl From<PipeError> for HidError {
fn from(e: PipeError) -> Self {
Self::Transfer(e)
}
}
impl core::fmt::Display for HidError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Transfer(_e) => write!(f, "Transfer error"),
Self::NoInterface => write!(f, "No HID interface found"),
Self::NoPipe => write!(f, "No free pipe"),
}
}
}
impl core::error::Error for HidError {}
pub struct HidHost<'d, A: UsbHostAllocator<'d>> {
ctrl_ch: A::Pipe<pipe::Control, pipe::InOut>,
in_ch: A::Pipe<pipe::Interrupt, pipe::In>,
interface: u8,
report_descriptor_len: u16,
_phantom: core::marker::PhantomData<&'d ()>,
}
impl<'d, A: UsbHostAllocator<'d>> HidHost<'d, A> {
pub fn new(alloc: &A, config_desc: &[u8], enum_info: &EnumerationInfo) -> Result<Self, HidError> {
let info = find_hid(config_desc).ok_or(HidError::NoInterface)?;
let ctrl_ep_info = EndpointInfo {
addr: EndpointAddress::from_parts(0, UsbDirection::In),
ep_type: EndpointType::Control,
max_packet_size: enum_info.device_desc.max_packet_size0 as u16,
interval_ms: 0,
};
let in_ep_info = EndpointInfo {
addr: EndpointAddress::from_parts((info.interrupt_in_ep & 0x0F) as usize, UsbDirection::In),
ep_type: EndpointType::Interrupt,
max_packet_size: info.interrupt_in_mps,
interval_ms: 0,
};
let device_address = enum_info.device_address;
let split = enum_info.split();
let ctrl_ch = alloc
.alloc_pipe::<pipe::Control, pipe::InOut>(device_address, &ctrl_ep_info, split)
.map_err(|_| HidError::NoPipe)?;
let in_ch = alloc
.alloc_pipe::<pipe::Interrupt, pipe::In>(device_address, &in_ep_info, split)
.map_err(|_| HidError::NoPipe)?;
Ok(Self {
ctrl_ch,
in_ch,
interface: info.interface_number,
report_descriptor_len: info.report_descriptor_len,
_phantom: core::marker::PhantomData,
})
}
pub async fn fetch_report_descriptor<'a>(&mut self, buf: &'a mut [u8]) -> Result<&'a [u8], HidError> {
let len = (self.report_descriptor_len as usize).min(buf.len()) as u16;
let setup = SetupPacket::get_hid_report_descriptor(self.interface, len);
let n = self
.ctrl_ch
.control_in(&setup.to_bytes(), &mut buf[..len as usize])
.await?;
Ok(&buf[..n])
}
pub async fn set_idle(&mut self, report_id: u8, idle_duration: u8) -> Result<(), HidError> {
let value = (idle_duration as u16) << 8 | report_id as u16;
let setup = SetupPacket::class_interface_out(SET_IDLE, value, self.interface as u16, 0);
match self.ctrl_ch.control_out(&setup.to_bytes(), &[]).await {
Ok(_) => Ok(()),
Err(PipeError::Stall) => Ok(()),
Err(e) => Err(HidError::Transfer(e)),
}
}
pub async fn set_protocol(&mut self, protocol: u8) -> Result<(), HidError> {
let setup = SetupPacket::class_interface_out(SET_PROTOCOL, protocol as u16, self.interface as u16, 0);
self.ctrl_ch.control_out(&setup.to_bytes(), &[]).await?;
Ok(())
}
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, HidError> {
let n = self.in_ch.request_in(buf).await?;
Ok(n)
}
pub async fn read_keyboard(&mut self) -> Result<Option<KeyboardReport>, HidError> {
let mut buf = [0u8; 8];
self.in_ch.request_in(&mut buf).await?;
Ok(KeyboardReport::parse(&buf))
}
pub async fn read_mouse(&mut self) -> Result<Option<MouseReport>, HidError> {
let mut buf = [0u8; 4];
let n = self.in_ch.request_in(&mut buf).await?;
Ok(MouseReport::parse(&buf[..n]))
}
pub async fn get_report(&mut self, report_type: u8, report_id: u8, buf: &mut [u8]) -> Result<usize, HidError> {
let value = (report_type as u16) << 8 | report_id as u16;
let setup = SetupPacket::class_interface_in(GET_REPORT, value, self.interface as u16, buf.len() as u16);
let n = self.ctrl_ch.control_in(&setup.to_bytes(), buf).await?;
Ok(n)
}
}