From a9e4071913ef1ce32cb4a1581d5683ad56627ec2 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Mon, 18 Feb 2019 14:06:09 +0000 Subject: hid: Add HID and Report descriptors HID implementations must provide a HID and a Report descriptor. This patch adds these descriptors. The Report descriptor is copied from the Nitrokey Pro. As the control_in implementation in usb-device only handles GET_DESCRIPTOR requests on the device level, we have to change the control_in method in HidClass to handle GET_DESCRIPTOR on interface level (required for the Report descriptor). --- README.md | 1 + src/device.rs | 12 +++++++++++ src/hid.rs | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7570d2e..2b0a285 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ SPDX-License-Identifier: CC0-1.0 development and supports: - connecting as a USB device with the Nitrokey Pro’s product and vendor ID +- connecting as a HID class device ## Resources diff --git a/src/device.rs b/src/device.rs index 31f4293..f71caf0 100644 --- a/src/device.rs +++ b/src/device.rs @@ -9,6 +9,14 @@ use crate::hid::{HidDevice, Protocol, Subclass}; const VID_CLAY_LOGIC: u16 = 0x20a0; const PID_NITROKEY_PRO: u16 = 0x4108; +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, + 0x05, 0x08, 0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03, 0x91, 0x03, 0x95, 0x06, + 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05, 0x07, 0x19, 0x00, 0x29, 0x65, 0x81, 0x00, 0x09, 0x03, + 0x75, 0x08, 0x95, 0x40, 0xB1, 0x02, 0xC0, +]; + pub struct Nitrokey {} impl Nitrokey { @@ -25,6 +33,10 @@ impl HidDevice for Nitrokey { fn protocol(&self) -> Protocol { Protocol::Keyboard } + + fn report_descriptor(&self) -> &[u8] { + REPORT_DESCRIPTOR + } } pub fn create_usb_device(alloc: &UsbBusAllocator) -> UsbDevice<'_, B> { diff --git a/src/hid.rs b/src/hid.rs index 0bb8a11..a2adeb3 100644 --- a/src/hid.rs +++ b/src/hid.rs @@ -3,10 +3,12 @@ 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::descriptor::DescriptorWriter; use usb_device::endpoint::{EndpointAddress, EndpointIn}; -use usb_device::Result; +use usb_device::{Result, UsbError}; +const SPECIFICATION_RELEASE: u16 = 0x111; const INTERFACE_CLASS_HID: u8 = 0x03; enum_u8! { @@ -26,10 +28,21 @@ enum_u8! { } } +enum_u8! { + #[derive(Debug, Clone, Copy)] + pub enum DescriptorType { + Hid = 0x21, + Report = 0x22, + Physical = 0x23, + } +} + pub trait HidDevice { fn subclass(&self) -> Subclass; fn protocol(&self) -> Protocol; + + fn report_descriptor(&self) -> &[u8]; } pub struct HidClass<'a, B: UsbBus, D: HidDevice> { @@ -48,6 +61,21 @@ impl HidClass<'_, B, D> { expect_interrupt_in_complete: false, } } + + fn get_report_descriptor(&self, index: u8, buf: &mut [u8]) -> Result { + if index == 0 { + let report_descriptor = self.device.report_descriptor(); + let len = report_descriptor.len(); + if len > buf.len() { + Err(UsbError::BufferOverflow) + } else { + buf[0..len].copy_from_slice(report_descriptor); + Ok(len) + } + } else { + Err(UsbError::InvalidState) + } + } } impl UsbClass for HidClass<'_, B, D> { @@ -64,6 +92,27 @@ impl UsbClass for HidClass<'_, B, D> { self.device.subclass().into(), self.device.protocol().into(), )?; + + let report_descriptor = self.device.report_descriptor(); + let descriptor_len = report_descriptor.len(); + if descriptor_len > u16::max_value() as usize { + return Err(UsbError::InvalidState); + } + let descriptor_len = (descriptor_len as u16).to_le_bytes(); + let specification_release = SPECIFICATION_RELEASE.to_le_bytes(); + writer.write( + DescriptorType::Hid.into(), + &[ + specification_release[0], // bcdHID.lower + specification_release[1], // bcdHID.upper + 0, // bCountryCode: 0 = not supported + 1, // bNumDescriptors + DescriptorType::Report.into(), // bDescriptorType + descriptor_len[0], // bDescriptorLength.lower + descriptor_len[1], // bDescriptorLength.upper + ], + )?; + writer.endpoint(&self.endpoint_interrupt_in)?; Ok(()) @@ -85,7 +134,19 @@ impl UsbClass for HidClass<'_, B, D> { fn endpoint_out(&mut self, _addr: EndpointAddress) {} - fn control_in(&mut self, _xfer: ControlIn) {} + 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(); + } + } + } fn control_out(&mut self, _xfer: ControlOut) {} } -- cgit v1.2.3