From ff5f9c7def93c5975ec5001f7b383fd427b6d0d6 Mon Sep 17 00:00:00 2001 From: Anton Tykhyy Date: Mon, 28 Sep 2020 18:45:06 +0300 Subject: [PATCH] Add basic IAD support --- src/descriptor.rs | 41 +++++++++++++++++++++++++++++++++++++++++ src/device.rs | 1 + src/device_builder.rs | 12 ++++++++++++ 3 files changed, 54 insertions(+) diff --git a/src/descriptor.rs b/src/descriptor.rs index 00f659e..cb40fda 100644 --- a/src/descriptor.rs +++ b/src/descriptor.rs @@ -11,6 +11,7 @@ pub mod descriptor_type { pub const STRING: u8 = 3; pub const INTERFACE: u8 = 4; pub const ENDPOINT: u8 = 5; + pub const IAD: u8 = 11; pub const BOS: u8 = 15; pub const CAPABILITY: u8 = 16; } @@ -39,6 +40,7 @@ pub struct DescriptorWriter<'a> { position: usize, num_interfaces_mark: Option, num_endpoints_mark: Option, + write_iads: bool, } impl DescriptorWriter<'_> { @@ -48,6 +50,7 @@ impl DescriptorWriter<'_> { position: 0, num_interfaces_mark: None, num_endpoints_mark: None, + write_iads: false, } } @@ -98,6 +101,8 @@ impl DescriptorWriter<'_> { pub(crate) fn configuration(&mut self, config: &device::Config) -> Result<()> { self.num_interfaces_mark = Some(self.position + 4); + self.write_iads = config.composite_with_iads; + self.write( descriptor_type::CONFIGURATION, &[ @@ -121,6 +126,42 @@ impl DescriptorWriter<'_> { self.buf[2..4].copy_from_slice(&position.to_le_bytes()); } + /// Writes a interface association descriptor. Call from `UsbClass::get_configuration_descriptors` + /// before writing the USB class or function's interface descriptors if your class has more than + /// one interface and wants to play nicely with composite devices on Windows. If the USB device + /// hosting the class was not configured as composite with IADs enabled, calling this function + /// does nothing, so it is safe to call from libraries. + /// + /// # Arguments + /// + /// * `first_interface` - Number of the function's first interface, previously allocated with + /// [`UsbBusAllocator::interface`](crate::bus::UsbBusAllocator::interface). + /// * `interface_count` - Number of interfaces in the function. + /// * `function_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices + /// that do not conform to any class. + /// * `function_sub_class` - Sub-class code. Depends on class. + /// * `function_protocol` - Protocol code. Depends on class and sub-class. + pub fn iad(&mut self, first_interface: InterfaceNumber, interface_count: u8, + function_class: u8, function_sub_class: u8, function_protocol: u8) -> Result<()> + { + if !self.write_iads { + return Ok(()); + } + + self.write( + descriptor_type::IAD, + &[ + first_interface.into(), // bFirstInterface + interface_count, // bInterfaceCount + function_class, + function_sub_class, + function_protocol, + 0 + ])?; + + Ok(()) + } + /// Writes a interface descriptor. /// /// # Arguments diff --git a/src/device.rs b/src/device.rs index 9620cbf..e597807 100644 --- a/src/device.rs +++ b/src/device.rs @@ -53,6 +53,7 @@ pub(crate) struct Config<'a> { pub serial_number: Option<&'a str>, pub self_powered: bool, pub supports_remote_wakeup: bool, + pub composite_with_iads: bool, pub max_power: u8, } diff --git a/src/device_builder.rs b/src/device_builder.rs index 9fca0c1..4424d2b 100644 --- a/src/device_builder.rs +++ b/src/device_builder.rs @@ -43,6 +43,7 @@ impl<'a, B: UsbBus> UsbDeviceBuilder<'a, B> { serial_number: None, self_powered: false, supports_remote_wakeup: false, + composite_with_iads: false, max_power: 50, } } @@ -91,6 +92,17 @@ impl<'a, B: UsbBus> UsbDeviceBuilder<'a, B> { supports_remote_wakeup: bool, } + /// Configures the device as a composite device with interface association descriptors. + pub fn composite_with_iads(mut self) -> Self { + // Magic values specified in USB-IF ECN on IADs. + self.config.device_class = 0xEF; + self.config.device_sub_class = 0x02; + self.config.device_protocol = 0x01; + + self.config.composite_with_iads = true; + self + } + /// Sets the manufacturer name string descriptor. /// /// Default: (none)