diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 775c0437..94fcf8ee 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -295,6 +295,13 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { pub fn msos_writer(&mut self) -> &mut MsOsDescriptorWriter<'d> { &mut self.msos_descriptor } + + /// Returns a handle to the inner config descriptor writer owned by the [`UsbDevice`] builder. + /// + /// This can be used to overwrite parts of the descriptor buffer or other granular controls over the buffer. + pub fn config_descriptor_writer(&mut self) -> &mut DescriptorWriter<'d> { + &mut self.config_descriptor + } } /// Function builder. @@ -360,6 +367,13 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { #[cfg(feature = "msos-descriptor")] self.builder.msos_descriptor.function_feature(desc); } + + /// Returns a handle to the inner config descriptor writer owned by the [`UsbDevice`] builder. + /// + /// This can be used to overwrite parts of the descriptor buffer or other granular controls over the buffer. + pub fn config_descriptor_writer(&mut self) -> &mut DescriptorWriter<'d> { + self.builder.config_descriptor_writer() + } } /// Interface builder. @@ -411,6 +425,13 @@ impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> { alt_setting_number: number, } } + + /// Returns a handle to the inner config descriptor writer owned by the [`UsbDevice`] builder. + /// + /// This can be used to overwrite parts of the descriptor buffer or other granular controls over the buffer. + pub fn config_descriptor_writer(&mut self) -> &mut DescriptorWriter<'d> { + self.builder.config_descriptor_writer() + } } /// Interface alternate setting builder. @@ -435,50 +456,17 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { /// /// Descriptors are written in the order builder functions are called. Note that some /// classes care about the order. - pub fn descriptor(&mut self, descriptor_type: u8, descriptor: &[u8]) { + /// + /// Returns the byte length of the descriptor which has been written. + pub fn descriptor(&mut self, descriptor_type: u8, descriptor: &[u8]) -> usize { self.builder.config_descriptor.write(descriptor_type, descriptor) } - /// Add a custom descriptor to this alternate setting, - /// assuming that the descriptor is the initial one which will be followed by variable number - /// of descriptors of the same type forming a compound descriptor set. + /// Returns a handle to the inner config descriptor writer owned by the [`UsbDevice`] builder. /// - /// Descriptors are written in the order builder functions are called. Note that some - /// classes care about the order. - /// - /// # Details - /// - /// In the USB specification, when it comes to class-specific descriptors, the presence of a "total length" field is not universally common across all classes. - /// However, it is found in several class specifications, particularly in those where a variable number of descriptors or a variable configuration might follow. - /// - /// For instance: - /// - /// - **Audio Class:** The class-specific header descriptor in the Audio class contains a "Total Length" field, which indicates the entire length of the class-specific AudioControl interface descriptor. This is useful because the AudioControl interface can have a variable configuration depending on the number and type of audio channels, terminal interfaces, etc. - /// - /// - **Video Class:** The Video class's class-specific header descriptor also contains a "Total Length" field. - /// - /// The purpose behind the "Total Length" (or equivalent) field in these class-specific descriptors is to provide a straightforward way to determine the full length of a compound descriptor set. - /// This can be especially helpful for parsing or skipping over the entirety of the descriptor set if needed. - /// - /// While it's common in certain classes like Audio and Video, - /// it's not a universal feature of all USB class-specific descriptors. - pub fn start_writing_compound_set_tracking_total_length(&mut self, descriptor_type: u8, descriptor: &[u8]) { - let descriptor_writer = &mut self.builder.config_descriptor; - // Start tracking the total length of the compound descriptor set. - descriptor_writer.start_tracking_total_length_of_compound_descriptor_set(descriptor_writer.position()); - // Write the initial descriptor of that particular set. - self.builder.config_descriptor.write(descriptor_type, descriptor); - } - - /// End writing a compound descriptor set updating the total length area in the initial descriptor. - /// - /// The offset of the total length bytes should be provided. - /// This number may change depending on the class, please derive it from the specification. - pub fn end_writing_compound_set_updating_total_length(&mut self, offset: usize) { - // End tracking the total length of the compound descriptor set and write it to the initial descriptor of that set on provided offset. - self.builder - .config_descriptor - .end_tracking_total_length_of_compound_descriptor_set_and_update_the_initial_descriptor(offset); + /// This can be used to overwrite parts of the descriptor buffer or other granular controls over the buffer. + pub fn config_descriptor_writer(&mut self) -> &mut DescriptorWriter<'d> { + self.builder.config_descriptor_writer() } fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index a341e10d..4dfe5bbd 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -182,7 +182,6 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { // Control interface let mut iface = func.interface(); let comm_if = iface.interface_number(); - let data_if = u8::from(comm_if) + 1; let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE, None); alt.descriptor( @@ -208,19 +207,29 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { &[ CDC_TYPE_UNION, // bDescriptorSubtype comm_if.into(), // bControlInterface - data_if.into(), // bSubordinateInterface + // Padding, bSubordinateInterface byte will be written later. + 0x0, // bSubordinateInterface ], ); + // Keep the position of the subordinate interface byte so we can fill it in later + let subordinate_interface_position = alt.config_descriptor_writer().position() - 1; + let comm_ep = alt.endpoint_interrupt_in(8, 255); // Data interface let mut iface = func.interface(); let data_if = iface.interface_number(); + let data_if_number_byte = data_if.0; + let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NONE, None); let read_ep = alt.endpoint_bulk_out(max_packet_size); let write_ep = alt.endpoint_bulk_in(max_packet_size); + // Fill in the subordinate interface byte with the right interface number. + alt.config_descriptor_writer() + .overwrite(subordinate_interface_position, &[data_if_number_byte]); + drop(func); let control = state.control.write(Control { diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs index be9f825a..cab19f25 100644 --- a/embassy-usb/src/descriptor.rs +++ b/embassy-usb/src/descriptor.rs @@ -37,7 +37,8 @@ pub mod capability_type { } /// A writer for USB descriptors. -pub(crate) struct DescriptorWriter<'a> { +pub struct DescriptorWriter<'a> { + /// The inner buffer of the descriptor writer. pub buf: &'a mut [u8], position: usize, num_interfaces_mark: Option,