Skip to content

Commit ff5f9c7

Browse files
committed
Add basic IAD support
1 parent c737d67 commit ff5f9c7

File tree

3 files changed

+54
-0
lines changed

3 files changed

+54
-0
lines changed

src/descriptor.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub mod descriptor_type {
1111
pub const STRING: u8 = 3;
1212
pub const INTERFACE: u8 = 4;
1313
pub const ENDPOINT: u8 = 5;
14+
pub const IAD: u8 = 11;
1415
pub const BOS: u8 = 15;
1516
pub const CAPABILITY: u8 = 16;
1617
}
@@ -39,6 +40,7 @@ pub struct DescriptorWriter<'a> {
3940
position: usize,
4041
num_interfaces_mark: Option<usize>,
4142
num_endpoints_mark: Option<usize>,
43+
write_iads: bool,
4244
}
4345

4446
impl DescriptorWriter<'_> {
@@ -48,6 +50,7 @@ impl DescriptorWriter<'_> {
4850
position: 0,
4951
num_interfaces_mark: None,
5052
num_endpoints_mark: None,
53+
write_iads: false,
5154
}
5255
}
5356

@@ -98,6 +101,8 @@ impl DescriptorWriter<'_> {
98101
pub(crate) fn configuration(&mut self, config: &device::Config) -> Result<()> {
99102
self.num_interfaces_mark = Some(self.position + 4);
100103

104+
self.write_iads = config.composite_with_iads;
105+
101106
self.write(
102107
descriptor_type::CONFIGURATION,
103108
&[
@@ -121,6 +126,42 @@ impl DescriptorWriter<'_> {
121126
self.buf[2..4].copy_from_slice(&position.to_le_bytes());
122127
}
123128

129+
/// Writes a interface association descriptor. Call from `UsbClass::get_configuration_descriptors`
130+
/// before writing the USB class or function's interface descriptors if your class has more than
131+
/// one interface and wants to play nicely with composite devices on Windows. If the USB device
132+
/// hosting the class was not configured as composite with IADs enabled, calling this function
133+
/// does nothing, so it is safe to call from libraries.
134+
///
135+
/// # Arguments
136+
///
137+
/// * `first_interface` - Number of the function's first interface, previously allocated with
138+
/// [`UsbBusAllocator::interface`](crate::bus::UsbBusAllocator::interface).
139+
/// * `interface_count` - Number of interfaces in the function.
140+
/// * `function_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices
141+
/// that do not conform to any class.
142+
/// * `function_sub_class` - Sub-class code. Depends on class.
143+
/// * `function_protocol` - Protocol code. Depends on class and sub-class.
144+
pub fn iad(&mut self, first_interface: InterfaceNumber, interface_count: u8,
145+
function_class: u8, function_sub_class: u8, function_protocol: u8) -> Result<()>
146+
{
147+
if !self.write_iads {
148+
return Ok(());
149+
}
150+
151+
self.write(
152+
descriptor_type::IAD,
153+
&[
154+
first_interface.into(), // bFirstInterface
155+
interface_count, // bInterfaceCount
156+
function_class,
157+
function_sub_class,
158+
function_protocol,
159+
0
160+
])?;
161+
162+
Ok(())
163+
}
164+
124165
/// Writes a interface descriptor.
125166
///
126167
/// # Arguments

src/device.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub(crate) struct Config<'a> {
5353
pub serial_number: Option<&'a str>,
5454
pub self_powered: bool,
5555
pub supports_remote_wakeup: bool,
56+
pub composite_with_iads: bool,
5657
pub max_power: u8,
5758
}
5859

src/device_builder.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ impl<'a, B: UsbBus> UsbDeviceBuilder<'a, B> {
4343
serial_number: None,
4444
self_powered: false,
4545
supports_remote_wakeup: false,
46+
composite_with_iads: false,
4647
max_power: 50,
4748
}
4849
}
@@ -91,6 +92,17 @@ impl<'a, B: UsbBus> UsbDeviceBuilder<'a, B> {
9192
supports_remote_wakeup: bool,
9293
}
9394

95+
/// Configures the device as a composite device with interface association descriptors.
96+
pub fn composite_with_iads(mut self) -> Self {
97+
// Magic values specified in USB-IF ECN on IADs.
98+
self.config.device_class = 0xEF;
99+
self.config.device_sub_class = 0x02;
100+
self.config.device_protocol = 0x01;
101+
102+
self.config.composite_with_iads = true;
103+
self
104+
}
105+
94106
/// Sets the manufacturer name string descriptor.
95107
///
96108
/// Default: (none)

0 commit comments

Comments
 (0)