diff options
| author | Robin Krahl <robin.krahl@ireas.org> | 2019-02-18 14:06:09 +0000 | 
|---|---|---|
| committer | Robin Krahl <robin.krahl@ireas.org> | 2019-02-18 15:31:05 +0100 | 
| commit | a9e4071913ef1ce32cb4a1581d5683ad56627ec2 (patch) | |
| tree | e75cc76b6fdaae931a3c6e0ebbfa7ded5212f56b /src | |
| parent | ccbbf657743eb656f4e49b0204092b4450ffa7ba (diff) | |
| download | ntw-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).
Diffstat (limited to 'src')
| -rw-r--r-- | src/device.rs | 12 | ||||
| -rw-r--r-- | src/hid.rs | 65 | 
2 files changed, 75 insertions, 2 deletions
| 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> { @@ -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>) {}  } | 
