aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2019-02-18 14:20:14 +0000
committerRobin Krahl <robin.krahl@ireas.org>2019-02-18 15:53:20 +0100
commit97e448c1d9921cd084e1da70f3943bead03474ed (patch)
treef48471939041af16d1347fee1e877bffaada3dcb
parenta9e4071913ef1ce32cb4a1581d5683ad56627ec2 (diff)
downloadntw-97e448c1d9921cd084e1da70f3943bead03474ed.tar.gz
ntw-97e448c1d9921cd084e1da70f3943bead03474ed.tar.bz2
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.
-rw-r--r--src/device.rs30
-rw-r--r--src/hid.rs106
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<B: UsbBus>(alloc: &UsbBusAllocator<B>) -> 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<u8> 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<B: UsbBus, D: HidDevice> HidClass<'_, B, D> {
}
}
- fn get_report_descriptor(&self, index: u8, buf: &mut [u8]) -> Result<usize> {
+ fn get_report_descriptor(&self, index: u8, buf: &mut [u8]) -> usb_device::Result<usize> {
if index == 0 {
let report_descriptor = self.device.report_descriptor();
let len = report_descriptor.len();
@@ -76,6 +115,26 @@ impl<B: UsbBus, D: HidDevice> HidClass<'_, B, D> {
Err(UsbError::InvalidState)
}
}
+
+ fn get_report(&mut self, xfer: ControlIn<B>) {
+ 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<B>) {
+ 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<B: UsbBus, D: HidDevice> UsbClass<B> for HidClass<'_, B, D> {
@@ -85,7 +144,10 @@ impl<B: UsbBus, D: HidDevice> UsbClass<B> 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<B: UsbBus, D: HidDevice> UsbClass<B> for HidClass<'_, B, D> {
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();
+ 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<B>) {}
+ fn control_out(&mut self, xfer: ControlOut<B>) {
+ 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);
+ }
+ }
+ }
+ }
}