use crate::hid_report::ReportDescriptor;
use crate::timer::Timer;
use crate::usb::control::{
get_configuration_descriptor, get_report_descriptor, set_configuration, set_idle,
};
use crate::usb::descriptor::ConfigurationDescriptor;
use crate::usb::dwc2::{Channel, ControlEndpoint, TransferError, MAX_TRANSFER_LEN};
use crate::usb::hid::HidInterface;
use crate::usb::Device;
const CONFIG_BUFFER_LEN: usize = MAX_TRANSFER_LEN;
const MAX_REPORT_DESCRIPTOR: usize = MAX_TRANSFER_LEN;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum GamepadError {
ConfigurationDescriptor(TransferError),
SetConfiguration(TransferError),
ReportDescriptor(u8, TransferError),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Report<'a> {
pub id: u8,
pub payload: &'a [u8],
}
pub struct Gamepad {
endpoint: ControlEndpoint,
interface: u8,
report_endpoint: u8,
poll_interval_ms: u32,
toggle: bool,
descriptor: ReportDescriptor,
raw_descriptor: [u8; MAX_REPORT_DESCRIPTOR],
raw_descriptor_len: usize,
}
impl Gamepad {
pub fn from_device(
channel: &mut Channel,
timer: &Timer,
device: Device,
) -> Result<Option<Gamepad>, GamepadError> {
let mut config = [0u8; CONFIG_BUFFER_LEN];
let len = get_configuration_descriptor(channel, timer, device.endpoint, 0, &mut config)
.map_err(GamepadError::ConfigurationDescriptor)?;
if HidInterface::find(&config[..len], |_| true).is_none() {
return Ok(None);
}
let Some(config_value) = ConfigurationDescriptor::parse(&config[..len]).map(|c| c.value())
else {
return Ok(None);
};
set_configuration(channel, timer, device.endpoint, config_value)
.map_err(GamepadError::SetConfiguration)?;
let mut from = 0u16;
let mut stalled = None;
while let Some(interface) =
HidInterface::find(&config[..len], |iface| iface.number() as u16 >= from)
{
from = interface.interface as u16 + 1;
if interface.report_descriptor_len == 0 {
continue;
}
let mut raw_descriptor = [0u8; MAX_REPORT_DESCRIPTOR];
let to_read = (interface.report_descriptor_len as usize).min(MAX_REPORT_DESCRIPTOR);
let raw_descriptor_len = match get_report_descriptor(
channel,
timer,
device.endpoint,
interface.interface,
&mut raw_descriptor[..to_read],
) {
Ok(received) => received,
Err(TransferError::Stall) => {
stalled.get_or_insert(interface.interface);
continue;
}
Err(error) => {
return Err(GamepadError::ReportDescriptor(interface.interface, error))
}
};
let descriptor = ReportDescriptor::parse(&raw_descriptor[..raw_descriptor_len]);
if !descriptor.is_gamepad() {
continue;
}
let _ = set_idle(channel, timer, device.endpoint, interface.interface, 0);
return Ok(Some(Gamepad {
endpoint: ControlEndpoint {
max_packet_size: interface.max_packet_size,
..device.endpoint
},
interface: interface.interface,
report_endpoint: interface.endpoint,
poll_interval_ms: poll_interval_ms(&device.endpoint, interface.interval),
toggle: false,
descriptor,
raw_descriptor,
raw_descriptor_len,
}));
}
match stalled {
Some(interface) => Err(GamepadError::ReportDescriptor(
interface,
TransferError::Stall,
)),
None => Ok(None),
}
}
pub fn interface(&self) -> u8 {
self.interface
}
pub fn report_endpoint(&self) -> u8 {
self.report_endpoint
}
pub fn max_packet_size(&self) -> u16 {
self.endpoint.max_packet_size
}
pub fn poll_interval_ms(&self) -> u32 {
self.poll_interval_ms
}
pub fn report_descriptor(&self) -> &ReportDescriptor {
&self.descriptor
}
pub fn report_descriptor_bytes(&self) -> &[u8] {
&self.raw_descriptor[..self.raw_descriptor_len]
}
pub fn poll<'a>(
&mut self,
channel: &mut Channel,
timer: &Timer,
buf: &'a mut [u8],
) -> Result<Option<Report<'a>>, TransferError> {
let to_read = buf
.len()
.min(self.endpoint.max_packet_size as usize)
.min(MAX_TRANSFER_LEN);
let received = match channel.interrupt_in(
self.endpoint,
self.report_endpoint,
&mut self.toggle,
&mut buf[..to_read],
timer,
) {
Ok(received) => received,
Err(TransferError::Nak) => return Ok(None),
Err(error) => return Err(error),
};
if received == 0 {
return Ok(None);
}
Ok(Some(if self.descriptor.uses_report_ids() {
Report {
id: buf[0],
payload: &buf[1..received],
}
} else {
Report {
id: 0,
payload: &buf[..received],
}
}))
}
}
fn poll_interval_ms(endpoint: &ControlEndpoint, interval: u8) -> u32 {
let high_speed = endpoint.split.is_none() && !endpoint.low_speed;
let ms = if high_speed {
(1u32 << interval.clamp(1, 16).saturating_sub(1)) / 8
} else {
interval as u32
};
ms.max(1)
}