use core::mem::MaybeUninit;
use core::ops::Range;
use core::sync::atomic::{AtomicUsize, Ordering};
#[cfg(feature = "usbd-hid")]
use usbd_hid::descriptor::AsInputReport;
use crate::control::{InResponse, OutResponse, Recipient, Request, RequestType};
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
use crate::types::InterfaceNumber;
use crate::{Builder, Handler};
const USB_CLASS_HID: u8 = 0x03;
const HID_DESC_DESCTYPE_HID: u8 = 0x21;
const HID_DESC_DESCTYPE_HID_REPORT: u8 = 0x22;
const HID_DESC_SPEC_1_10: [u8; 2] = [0x10, 0x01];
const HID_DESC_COUNTRY_UNSPEC: u8 = 0x00;
const HID_REQ_SET_IDLE: u8 = 0x0a;
const HID_REQ_GET_IDLE: u8 = 0x02;
const HID_REQ_GET_REPORT: u8 = 0x01;
const HID_REQ_SET_REPORT: u8 = 0x09;
const HID_REQ_GET_PROTOCOL: u8 = 0x03;
const HID_REQ_SET_PROTOCOL: u8 = 0x0b;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum HidProtocolMode {
Boot = 0,
Report = 1,
}
impl From<u8> for HidProtocolMode {
fn from(mode: u8) -> HidProtocolMode {
if mode == HidProtocolMode::Boot as u8 {
HidProtocolMode::Boot
} else {
HidProtocolMode::Report
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum HidSubclass {
No = 0,
Boot = 1,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum HidBootProtocol {
None = 0,
Keyboard = 1,
Mouse = 2,
}
pub struct Config<'d> {
pub report_descriptor: &'d [u8],
pub request_handler: Option<&'d mut dyn RequestHandler>,
pub poll_ms: u8,
pub max_packet_size: u16,
pub hid_subclass: HidSubclass,
pub hid_boot_protocol: HidBootProtocol,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ReportId {
In(u8),
Out(u8),
Feature(u8),
}
impl ReportId {
const fn try_from(value: u16) -> Result<Self, ()> {
match value >> 8 {
1 => Ok(ReportId::In(value as u8)),
2 => Ok(ReportId::Out(value as u8)),
3 => Ok(ReportId::Feature(value as u8)),
_ => Err(()),
}
}
}
pub struct State<'d> {
control: MaybeUninit<Control<'d>>,
out_report_offset: AtomicUsize,
}
impl<'d> Default for State<'d> {
fn default() -> Self {
Self::new()
}
}
impl<'d> State<'d> {
pub const fn new() -> Self {
State {
control: MaybeUninit::uninit(),
out_report_offset: AtomicUsize::new(0),
}
}
}
pub struct HidReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> {
reader: HidReader<'d, D, READ_N>,
writer: HidWriter<'d, D, WRITE_N>,
}
fn build<'d, D: Driver<'d>>(
builder: &mut Builder<'d, D>,
state: &'d mut State<'d>,
config: Config<'d>,
with_out_endpoint: bool,
) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) {
let len = config.report_descriptor.len();
let mut func = builder.function(USB_CLASS_HID, config.hid_subclass as u8, config.hid_boot_protocol as u8);
let mut iface = func.interface();
let if_num = iface.interface_number();
let mut alt = iface.alt_setting(
USB_CLASS_HID,
config.hid_subclass as u8,
config.hid_boot_protocol as u8,
None,
);
alt.descriptor(
HID_DESC_DESCTYPE_HID,
&[
HID_DESC_SPEC_1_10[0],
HID_DESC_SPEC_1_10[1],
HID_DESC_COUNTRY_UNSPEC,
1,
HID_DESC_DESCTYPE_HID_REPORT,
(len & 0xFF) as u8,
(len >> 8 & 0xFF) as u8,
],
);
let ep_in = alt.endpoint_interrupt_in(None, config.max_packet_size, config.poll_ms);
let ep_out = if with_out_endpoint {
Some(alt.endpoint_interrupt_out(None, config.max_packet_size, config.poll_ms))
} else {
None
};
drop(func);
let control = state.control.write(Control::new(
if_num,
config.report_descriptor,
config.request_handler,
&state.out_report_offset,
));
builder.handler(control);
(ep_out, ep_in, &state.out_report_offset)
}
impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWriter<'d, D, READ_N, WRITE_N> {
pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self {
let (ep_out, ep_in, offset) = build(builder, state, config, true);
Self {
reader: HidReader {
ep_out: ep_out.unwrap(),
offset,
},
writer: HidWriter { ep_in },
}
}
pub fn split(self) -> (HidReader<'d, D, READ_N>, HidWriter<'d, D, WRITE_N>) {
(self.reader, self.writer)
}
pub async fn ready(&mut self) {
self.reader.ready().await;
self.writer.ready().await;
}
#[cfg(feature = "usbd-hid")]
pub async fn write_serialize<IR: AsInputReport>(&mut self, r: &IR) -> Result<(), EndpointError> {
self.writer.write_serialize(r).await
}
pub async fn write(&mut self, report: &[u8]) -> Result<(), EndpointError> {
self.writer.write(report).await
}
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, ReadError> {
self.reader.read(buf).await
}
}
pub struct HidWriter<'d, D: Driver<'d>, const N: usize> {
ep_in: D::EndpointIn,
}
pub struct HidReader<'d, D: Driver<'d>, const N: usize> {
ep_out: D::EndpointOut,
offset: &'d AtomicUsize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ReadError {
BufferOverflow,
Disabled,
Sync(Range<usize>),
}
impl From<EndpointError> for ReadError {
fn from(val: EndpointError) -> Self {
use EndpointError::{BufferOverflow, Disabled};
match val {
BufferOverflow => ReadError::BufferOverflow,
Disabled => ReadError::Disabled,
}
}
}
impl<'d, D: Driver<'d>, const N: usize> HidWriter<'d, D, N> {
pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self {
let (ep_out, ep_in, _offset) = build(builder, state, config, false);
assert!(ep_out.is_none());
Self { ep_in }
}
pub async fn ready(&mut self) {
self.ep_in.wait_enabled().await;
}
#[cfg(feature = "usbd-hid")]
pub async fn write_serialize<IR: AsInputReport>(&mut self, r: &IR) -> Result<(), EndpointError> {
let mut buf: [u8; N] = [0; N];
let Ok(size) = r.serialize(&mut buf) else {
return Err(EndpointError::BufferOverflow);
};
self.write(&buf[0..size]).await
}
pub async fn write(&mut self, report: &[u8]) -> Result<(), EndpointError> {
assert!(report.len() <= N);
let max_packet_size = usize::from(self.ep_in.info().max_packet_size);
let zlp_needed = report.len() < N && (report.len() % max_packet_size == 0);
for chunk in report.chunks(max_packet_size) {
self.ep_in.write(chunk).await?;
}
if zlp_needed {
self.ep_in.write(&[]).await?;
}
Ok(())
}
}
impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> {
pub async fn ready(&mut self) {
self.ep_out.wait_enabled().await;
}
pub async fn run<T: RequestHandler>(mut self, use_report_ids: bool, handler: &mut T) -> ! {
let offset = self.offset.load(Ordering::Acquire);
assert!(offset == 0);
let mut buf = [0; N];
loop {
match self.read(&mut buf).await {
Ok(len) => {
let id = if use_report_ids { buf[0] } else { 0 };
handler.set_report(ReportId::Out(id), &buf[..len]);
}
Err(ReadError::BufferOverflow) => warn!(
"Host sent output report larger than the configured maximum output report length ({})",
N
),
Err(ReadError::Disabled) => self.ep_out.wait_enabled().await,
Err(ReadError::Sync(_)) => unreachable!(),
}
}
}
pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, ReadError> {
assert!(N != 0);
assert!(buf.len() >= N);
let max_packet_size = usize::from(self.ep_out.info().max_packet_size);
let starting_offset = self.offset.load(Ordering::Acquire);
let mut total = starting_offset;
loop {
for chunk in buf[starting_offset..N].chunks_mut(max_packet_size) {
match self.ep_out.read(chunk).await {
Ok(size) => {
total += size;
if size < max_packet_size || total == N {
self.offset.store(0, Ordering::Release);
break;
}
self.offset.store(total, Ordering::Release);
}
Err(err) => {
self.offset.store(0, Ordering::Release);
return Err(err.into());
}
}
}
if total > 0 {
break;
}
}
if starting_offset > 0 {
Err(ReadError::Sync(starting_offset..total))
} else {
Ok(total)
}
}
}
pub trait RequestHandler {
fn get_report(&mut self, id: ReportId, buf: &mut [u8]) -> Option<usize> {
let _ = (id, buf);
None
}
fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse {
let _ = (id, data);
OutResponse::Rejected
}
fn get_protocol(&self) -> HidProtocolMode {
HidProtocolMode::Report
}
fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse {
match protocol {
HidProtocolMode::Report => OutResponse::Accepted,
HidProtocolMode::Boot => OutResponse::Rejected,
}
}
fn get_idle_ms(&mut self, id: Option<ReportId>) -> Option<u32> {
let _ = id;
None
}
fn set_idle_ms(&mut self, id: Option<ReportId>, duration_ms: u32) {
let _ = (id, duration_ms);
}
}
struct Control<'d> {
if_num: InterfaceNumber,
report_descriptor: &'d [u8],
request_handler: Option<&'d mut dyn RequestHandler>,
out_report_offset: &'d AtomicUsize,
hid_descriptor: [u8; 9],
}
impl<'d> Control<'d> {
fn new(
if_num: InterfaceNumber,
report_descriptor: &'d [u8],
request_handler: Option<&'d mut dyn RequestHandler>,
out_report_offset: &'d AtomicUsize,
) -> Self {
Control {
if_num,
report_descriptor,
request_handler,
out_report_offset,
hid_descriptor: [
9,
HID_DESC_DESCTYPE_HID,
HID_DESC_SPEC_1_10[0],
HID_DESC_SPEC_1_10[1],
HID_DESC_COUNTRY_UNSPEC,
1,
HID_DESC_DESCTYPE_HID_REPORT,
(report_descriptor.len() & 0xFF) as u8,
(report_descriptor.len() >> 8 & 0xFF) as u8,
],
}
}
}
impl<'d> Handler for Control<'d> {
fn reset(&mut self) {
self.out_report_offset.store(0, Ordering::Release);
}
fn control_out(&mut self, req: Request, data: &[u8]) -> Option<OutResponse> {
if (req.request_type, req.recipient, req.index)
!= (RequestType::Class, Recipient::Interface, self.if_num.0 as u16)
{
return None;
}
#[cfg(feature = "defmt")]
trace!("HID control_out {:?} {=[u8]:x}", req, data);
match req.request {
HID_REQ_SET_IDLE => {
if let Some(handler) = self.request_handler.as_mut() {
let id = req.value as u8;
let id = (id != 0).then_some(ReportId::In(id));
let dur = u32::from(req.value >> 8);
let dur = if dur == 0 { u32::MAX } else { 4 * dur };
handler.set_idle_ms(id, dur);
}
Some(OutResponse::Accepted)
}
HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler.as_mut()) {
(Ok(id), Some(handler)) => Some(handler.set_report(id, data)),
_ => Some(OutResponse::Rejected),
},
HID_REQ_SET_PROTOCOL => {
let hid_protocol = HidProtocolMode::from(req.value as u8);
match (self.request_handler.as_mut(), hid_protocol) {
(Some(request_handler), hid_protocol) => Some(request_handler.set_protocol(hid_protocol)),
(None, HidProtocolMode::Report) => Some(OutResponse::Accepted),
(None, HidProtocolMode::Boot) => {
info!("Received request to switch to Boot protocol mode, but it is disabled by default.");
Some(OutResponse::Rejected)
}
}
}
_ => Some(OutResponse::Rejected),
}
}
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
if req.index != self.if_num.0 as u16 {
return None;
}
match (req.request_type, req.recipient) {
(RequestType::Standard, Recipient::Interface) => match req.request {
Request::GET_DESCRIPTOR => match (req.value >> 8) as u8 {
HID_DESC_DESCTYPE_HID_REPORT => Some(InResponse::Accepted(self.report_descriptor)),
HID_DESC_DESCTYPE_HID => Some(InResponse::Accepted(&self.hid_descriptor)),
_ => Some(InResponse::Rejected),
},
_ => Some(InResponse::Rejected),
},
(RequestType::Class, Recipient::Interface) => {
trace!("HID control_in {:?}", req);
match req.request {
HID_REQ_GET_REPORT => {
let size = match ReportId::try_from(req.value) {
Ok(id) => self.request_handler.as_mut().and_then(|x| x.get_report(id, buf)),
Err(_) => None,
};
if let Some(size) = size {
Some(InResponse::Accepted(&buf[0..size]))
} else {
Some(InResponse::Rejected)
}
}
HID_REQ_GET_IDLE => {
if let Some(handler) = self.request_handler.as_mut() {
let id = req.value as u8;
let id = (id != 0).then_some(ReportId::In(id));
if let Some(dur) = handler.get_idle_ms(id) {
let dur = u8::try_from(dur / 4).unwrap_or(0);
buf[0] = dur;
Some(InResponse::Accepted(&buf[0..1]))
} else {
Some(InResponse::Rejected)
}
} else {
Some(InResponse::Rejected)
}
}
HID_REQ_GET_PROTOCOL => {
if let Some(request_handler) = self.request_handler.as_mut() {
buf[0] = request_handler.get_protocol() as u8;
} else {
buf[0] = HidProtocolMode::Report as u8;
}
Some(InResponse::Accepted(&buf[0..1]))
}
_ => Some(InResponse::Rejected),
}
}
_ => None,
}
}
}