diff options
| -rw-r--r-- | README.md | 1 | ||||
| -rw-r--r-- | src/device.rs | 12 | ||||
| -rw-r--r-- | src/hid.rs | 65 | 
3 files changed, 76 insertions, 2 deletions
| @@ -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> { @@ -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>) {}  } | 
