aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2019-02-18 14:06:09 +0000
committerRobin Krahl <robin.krahl@ireas.org>2019-02-18 15:31:05 +0100
commita9e4071913ef1ce32cb4a1581d5683ad56627ec2 (patch)
treee75cc76b6fdaae931a3c6e0ebbfa7ded5212f56b
parentccbbf657743eb656f4e49b0204092b4450ffa7ba (diff)
downloadntw-a9e4071913ef1ce32cb4a1581d5683ad56627ec2.tar.gz
ntw-a9e4071913ef1ce32cb4a1581d5683ad56627ec2.tar.bz2
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).
-rw-r--r--README.md1
-rw-r--r--src/device.rs12
-rw-r--r--src/hid.rs65
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<B: UsbBus>(alloc: &UsbBusAllocator<B>) -> 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<B: UsbBus, D: HidDevice> HidClass<'_, B, D> {
expect_interrupt_in_complete: false,
}
}
+
+ fn get_report_descriptor(&self, index: u8, buf: &mut [u8]) -> Result<usize> {
+ 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<B: UsbBus, D: HidDevice> UsbClass<B> for HidClass<'_, B, D> {
@@ -64,6 +92,27 @@ impl<B: UsbBus, D: HidDevice> UsbClass<B> 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<B: UsbBus, D: HidDevice> UsbClass<B> for HidClass<'_, B, D> {
fn endpoint_out(&mut self, _addr: EndpointAddress) {}
- fn control_in(&mut self, _xfer: ControlIn<B>) {}
+ fn control_in(&mut self, xfer: ControlIn<B>) {
+ 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<B>) {}
}