use embedded_time::duration::Extensions;
use ref_swap::OptionRefSwap;
use trussed_core::InterruptFlag;
use crate::{
constants::{INTERRUPT_POLL_MILLISECONDS, PACKET_SIZE},
pipe::Pipe,
types::Status,
};
use ctaphid_dispatch::Requester;
use usb_device::{
bus::{InterfaceNumber, UsbBus, UsbBusAllocator},
class::{ControlIn, ControlOut, UsbClass},
control,
descriptor::DescriptorWriter,
endpoint::{EndpointAddress, EndpointIn, EndpointOut},
Result as UsbResult,
};
pub struct CtapHid<'alloc, 'pipe, 'interrupt, Bus: UsbBus, const N: usize> {
interface: InterfaceNumber,
pipe: Pipe<'alloc, 'pipe, 'interrupt, Bus, N>,
}
impl<'alloc, 'pipe, Bus, const N: usize> CtapHid<'alloc, 'pipe, '_, Bus, N>
where
Bus: UsbBus,
{
pub fn new(
allocate: &'alloc UsbBusAllocator<Bus>,
interchange: Requester<'pipe, N>,
initial_milliseconds: u32,
) -> Self {
let read_endpoint: EndpointOut<'alloc, Bus> =
allocate.interrupt(PACKET_SIZE as u16, INTERRUPT_POLL_MILLISECONDS);
let write_endpoint: EndpointIn<'alloc, Bus> =
allocate.interrupt(PACKET_SIZE as u16, INTERRUPT_POLL_MILLISECONDS);
let pipe = Pipe::new(
read_endpoint,
write_endpoint,
interchange,
initial_milliseconds,
);
Self {
interface: allocate.interface(),
pipe,
}
}
}
impl<'alloc, 'pipe, 'interrupt, Bus, const N: usize> CtapHid<'alloc, 'pipe, 'interrupt, Bus, N>
where
Bus: UsbBus,
{
pub fn with_interrupt(
allocate: &'alloc UsbBusAllocator<Bus>,
interchange: Requester<'pipe, N>,
interrupt: Option<&'interrupt OptionRefSwap<'interrupt, InterruptFlag>>,
initial_milliseconds: u32,
) -> Self {
let read_endpoint: EndpointOut<'alloc, Bus> =
allocate.interrupt(PACKET_SIZE as u16, INTERRUPT_POLL_MILLISECONDS);
let write_endpoint: EndpointIn<'alloc, Bus> =
allocate.interrupt(PACKET_SIZE as u16, INTERRUPT_POLL_MILLISECONDS);
let pipe = Pipe::with_interrupt(
read_endpoint,
write_endpoint,
interchange,
interrupt,
initial_milliseconds,
);
Self {
interface: allocate.interface(),
pipe,
}
}
pub fn set_version(&mut self, version: crate::Version) {
self.pipe.set_version(version);
}
pub fn implements_wink(mut self) -> Self {
self.pipe.implements |= 0x01;
self
}
pub fn implements_ctap1(mut self) -> Self {
self.pipe.implements &= !0x80;
self
}
pub fn implements_ctap2(mut self) -> Self {
self.pipe.implements |= 0x04;
self
}
pub fn pipe(&mut self) -> &mut Pipe<'alloc, 'pipe, 'interrupt, Bus, N> {
&mut self.pipe
}
pub fn check_timeout(&mut self, milliseconds: u32) {
self.pipe.check_timeout(milliseconds);
}
pub fn check_for_app_response(&mut self) {
self.poll();
}
pub fn did_start_processing(&mut self) -> Status {
if self.pipe.did_start_processing() {
Status::ReceivedData(250.milliseconds())
} else {
Status::Idle
}
}
pub fn send_keepalive(&mut self, is_waiting_for_user_presence: bool) -> Status {
if self.pipe.send_keepalive(is_waiting_for_user_presence) {
Status::ReceivedData(250.milliseconds())
} else {
Status::Idle
}
}
}
const HID_INTERFACE_CLASS: u8 = 0x03;
const INTERFACE_SUBCLASS_NONE: u8 = 0x0;
const INTERFACE_PROTOCOL_NONE: u8 = 0x0;
const HID_DESCRIPTOR: u8 = 0x21;
const HID_REPORT_DESCRIPTOR: u8 = 0x22;
const FIDO_HID_REPORT_DESCRIPTOR_LENGTH: usize = 34;
const FIDO_HID_REPORT_DESCRIPTOR: [u8; FIDO_HID_REPORT_DESCRIPTOR_LENGTH] = [
0x06,
0xD0,
0xF1,
0x09,
0x01,
0xA1,
0x01,
0x09,
0x03, 0x15,
0x00, 0x26,
0xFF,
0x00, 0x75,
0x08, 0x95,
PACKET_SIZE as u8, 0x81,
0x08, 0x09,
0x04, 0x15,
0x00, 0x26,
0xFF,
0x00, 0x75,
0x08, 0x95,
PACKET_SIZE as u8, 0x91,
0x08, 0xC0,
];
#[derive(Copy, Clone, Eq, Debug, PartialEq)]
pub enum ClassRequests {
GetReport = 0x1,
GetIdle = 0x2,
GetProtocol = 0x3,
SetReport = 0x9,
SetIdle = 0xA,
SetProtocol = 0xB,
}
impl<Bus, const N: usize> UsbClass<Bus> for CtapHid<'_, '_, '_, Bus, N>
where
Bus: UsbBus,
{
fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> UsbResult<()> {
writer.interface(
self.interface,
HID_INTERFACE_CLASS,
INTERFACE_SUBCLASS_NONE,
INTERFACE_PROTOCOL_NONE,
)?;
writer.write(
HID_DESCRIPTOR,
&[
0x11,
0x01, 0x00, 0x01, HID_REPORT_DESCRIPTOR, FIDO_HID_REPORT_DESCRIPTOR_LENGTH as u8,
0x00, ],
)?;
writer.endpoint(self.pipe.read_endpoint())?;
writer.endpoint(self.pipe.write_endpoint())?;
Ok(())
}
#[inline(never)]
fn poll(&mut self) {
self.pipe.handle_response();
self.pipe.maybe_write_packet();
}
fn endpoint_out(&mut self, addr: EndpointAddress) {
if addr == self.pipe.read_address() {
self.pipe.read_and_handle_packet();
}
}
fn endpoint_in_complete(&mut self, addr: EndpointAddress) {
if addr == self.pipe.write_address() {
self.pipe.maybe_write_packet();
}
}
fn control_out(&mut self, xfer: ControlOut<Bus>) {
let req = xfer.request();
if req.request_type == control::RequestType::Class
&& req.recipient == control::Recipient::Interface
&& req.index == u8::from(self.interface) as u16
{
match req.request {
r if r == ClassRequests::SetIdle as u8 => {
xfer.accept().ok();
}
_ => (),
};
}
}
fn control_in(&mut self, xfer: ControlIn<Bus>) {
let req = xfer.request();
if req.request_type == control::RequestType::Standard
&& req.recipient == control::Recipient::Interface
&& req.index == u8::from(self.interface) as u16
{
if req.request == control::Request::GET_DESCRIPTOR {
xfer.accept(|data| {
assert!(data.len() >= FIDO_HID_REPORT_DESCRIPTOR_LENGTH);
data[..FIDO_HID_REPORT_DESCRIPTOR_LENGTH]
.copy_from_slice(&FIDO_HID_REPORT_DESCRIPTOR);
Ok(FIDO_HID_REPORT_DESCRIPTOR_LENGTH)
})
.ok();
}
}
}
}