From 97e448c1d9921cd084e1da70f3943bead03474ed Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 18 Feb 2019 14:20:14 +0000 Subject: hid: Add support for {Get,Set}_Report requests This patch adds support for Get_Report and Set_Report requests to HidClass. We parse the request metadata and the delegate the request handling to the HidDevice. Our HidDevice implementation, Nitrokey, stores the data sent with Set_Report requests. The Get_Report handling is not implemented yet. --- src/device.rs | 30 +++++++++++++++-- src/hid.rs | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 120 insertions(+), 16 deletions(-) diff --git a/src/device.rs b/src/device.rs index f71caf0..f7c4fc7 100644 --- a/src/device.rs +++ b/src/device.rs @@ -4,11 +4,12 @@ use usb_device::bus::{UsbBus, UsbBusAllocator}; use usb_device::device::{UsbDevice, UsbDeviceBuilder, UsbVidPid}; -use crate::hid::{HidDevice, Protocol, Subclass}; +use crate::hid::{HidDevice, Protocol, ReportType, Subclass}; const VID_CLAY_LOGIC: u16 = 0x20a0; const PID_NITROKEY_PRO: u16 = 0x4108; +const REPORT_LEN: usize = 64; const REPORT_DESCRIPTOR: &[u8] = &[ 0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x03, 0x95, 0x05, 0x75, 0x01, @@ -17,11 +18,15 @@ const REPORT_DESCRIPTOR: &[u8] = &[ 0x75, 0x08, 0x95, 0x40, 0xB1, 0x02, 0xC0, ]; -pub struct Nitrokey {} +pub struct Nitrokey { + buf: [u8; REPORT_LEN], +} impl Nitrokey { pub fn new() -> Self { - Nitrokey {} + Nitrokey { + buf: [0; REPORT_LEN], + } } } @@ -37,6 +42,25 @@ impl HidDevice for Nitrokey { fn report_descriptor(&self) -> &[u8] { REPORT_DESCRIPTOR } + + fn get_report(&mut self, report_type: ReportType, report_id: u8) -> Result<&[u8], ()> { + let _ = (report_type, report_id); + Err(()) + } + + fn set_report( + &mut self, + report_type: ReportType, + report_id: u8, + data: &[u8], + ) -> Result<(), ()> { + if report_type != ReportType::Feature || report_id != 0 || data.len() != REPORT_LEN { + Err(()) + } else { + self.buf.copy_from_slice(data); + Ok(()) + } + } } pub fn create_usb_device(alloc: &UsbBusAllocator) -> UsbDevice<'_, B> { diff --git a/src/hid.rs b/src/hid.rs index a2adeb3..79ba7f0 100644 --- a/src/hid.rs +++ b/src/hid.rs @@ -3,10 +3,13 @@ use usb_device::bus::{InterfaceNumber, StringIndex, UsbBus, UsbBusAllocator}; use usb_device::class::{ControlIn, ControlOut, UsbClass}; -use usb_device::control::{Recipient, Request, RequestType}; +use usb_device::control; +use usb_device::control::{Recipient, RequestType}; use usb_device::descriptor::DescriptorWriter; use usb_device::endpoint::{EndpointAddress, EndpointIn}; -use usb_device::{Result, UsbError}; +use usb_device::UsbError; + +use crate::util::TryFrom; const SPECIFICATION_RELEASE: u16 = 0x111; const INTERFACE_CLASS_HID: u8 = 0x03; @@ -37,12 +40,48 @@ enum_u8! { } } +enum_u8! { + #[derive(Debug, Clone, Copy, PartialEq)] + pub enum Request { + GetReport = 0x01, + GetIdle = 0x02, + GetProtocol = 0x03, + SetReport = 0x09, + SetIdle = 0x0a, + SetProtocol = 0x0b, + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum ReportType { + Input, + Output, + Feature, + Reserved(u8), +} + +impl From for ReportType { + fn from(val: u8) -> Self { + match val { + 1 => ReportType::Input, + 2 => ReportType::Output, + 3 => ReportType::Feature, + _ => ReportType::Reserved(val), + } + } +} + pub trait HidDevice { fn subclass(&self) -> Subclass; fn protocol(&self) -> Protocol; fn report_descriptor(&self) -> &[u8]; + + fn set_report(&mut self, report_type: ReportType, report_id: u8, data: &[u8]) + -> Result<(), ()>; + + fn get_report(&mut self, report_type: ReportType, report_id: u8) -> Result<&[u8], ()>; } pub struct HidClass<'a, B: UsbBus, D: HidDevice> { @@ -62,7 +101,7 @@ impl HidClass<'_, B, D> { } } - fn get_report_descriptor(&self, index: u8, buf: &mut [u8]) -> Result { + fn get_report_descriptor(&self, index: u8, buf: &mut [u8]) -> usb_device::Result { if index == 0 { let report_descriptor = self.device.report_descriptor(); let len = report_descriptor.len(); @@ -76,6 +115,26 @@ impl HidClass<'_, B, D> { Err(UsbError::InvalidState) } } + + fn get_report(&mut self, xfer: ControlIn) { + let req = xfer.request(); + let [report_type, report_id] = req.value.to_be_bytes(); + let report_type = ReportType::from(report_type); + match self.device.get_report(report_type, report_id) { + Ok(data) => xfer.accept_with(data).ok(), + Err(()) => xfer.reject().ok(), + }; + } + + fn set_report(&mut self, xfer: ControlOut) { + let req = xfer.request(); + let [report_type, report_id] = req.value.to_be_bytes(); + let report_type = ReportType::from(report_type); + match self.device.set_report(report_type, report_id, xfer.data()) { + Ok(()) => xfer.accept().ok(), + Err(()) => xfer.reject().ok(), + }; + } } impl UsbClass for HidClass<'_, B, D> { @@ -85,7 +144,10 @@ impl UsbClass for HidClass<'_, B, D> { self.expect_interrupt_in_complete = false; } - fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> { + fn get_configuration_descriptors( + &self, + writer: &mut DescriptorWriter, + ) -> usb_device::Result<()> { writer.interface( self.interface, INTERFACE_CLASS_HID, @@ -136,17 +198,35 @@ impl UsbClass for HidClass<'_, B, D> { fn control_in(&mut self, xfer: ControlIn) { let req = xfer.request(); - if req.request_type == RequestType::Standard - && req.recipient == Recipient::Interface - && req.request == Request::GET_DESCRIPTOR - { - let (dtype, index) = req.descriptor_type_index(); - if dtype == DescriptorType::Report.into() { - xfer.accept(|mut buf| self.get_report_descriptor(index, &mut buf)) - .ok(); + match (req.request_type, req.recipient) { + (RequestType::Standard, Recipient::Interface) => { + if req.request == control::Request::GET_DESCRIPTOR { + let (dtype, index) = req.descriptor_type_index(); + if dtype == DescriptorType::Report.into() { + xfer.accept(|mut buf| self.get_report_descriptor(index, &mut buf)) + .ok(); + } + } + } + (RequestType::Class, Recipient::Interface) => { + if let Ok(request) = Request::try_from(req.request) { + if request == Request::GetReport { + self.get_report(xfer); + } + } } + _ => {} } } - fn control_out(&mut self, _xfer: ControlOut) {} + fn control_out(&mut self, xfer: ControlOut) { + let req = xfer.request(); + if req.request_type == RequestType::Class && req.recipient == Recipient::Interface { + if let Ok(request) = Request::try_from(req.request) { + if request == Request::SetReport { + self.set_report(xfer); + } + } + } + } } -- cgit v1.2.1