diff options
| -rw-r--r-- | embassy-usb/Cargo.toml | 3 | ||||
| -rw-r--r-- | embassy-usb/src/builder.rs | 78 | ||||
| -rw-r--r-- | embassy-usb/src/lib.rs | 14 | ||||
| -rw-r--r-- | embassy-usb/src/msos.rs | 477 | ||||
| -rw-r--r-- | examples/nrf52840/Cargo.toml | 7 | ||||
| -rw-r--r-- | examples/nrf52840/src/bin/usb_serial_winusb.rs | 139 |
6 files changed, 453 insertions, 265 deletions
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 31d1f4cae..54a8f27c7 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml | |||
| @@ -13,6 +13,7 @@ target = "thumbv7em-none-eabi" | |||
| 13 | [features] | 13 | [features] |
| 14 | defmt = ["dep:defmt", "embassy-usb-driver/defmt"] | 14 | defmt = ["dep:defmt", "embassy-usb-driver/defmt"] |
| 15 | usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] | 15 | usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] |
| 16 | msos-descriptor = ["dep:widestring"] | ||
| 16 | default = ["usbd-hid"] | 17 | default = ["usbd-hid"] |
| 17 | 18 | ||
| 18 | [dependencies] | 19 | [dependencies] |
| @@ -24,7 +25,7 @@ embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver- | |||
| 24 | defmt = { version = "0.3", optional = true } | 25 | defmt = { version = "0.3", optional = true } |
| 25 | log = { version = "0.4.14", optional = true } | 26 | log = { version = "0.4.14", optional = true } |
| 26 | heapless = "0.7.10" | 27 | heapless = "0.7.10" |
| 27 | widestring = { version = "1.0.2", default-features = false } | 28 | widestring = { version = "1.0.2", default-features = false, optional = true } |
| 28 | 29 | ||
| 29 | # for HID | 30 | # for HID |
| 30 | usbd-hid = { version = "0.6.0", optional = true } | 31 | usbd-hid = { version = "0.6.0", optional = true } |
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 2c42fd64e..d1cbf674b 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs | |||
| @@ -3,6 +3,8 @@ use heapless::Vec; | |||
| 3 | use crate::control::ControlHandler; | 3 | use crate::control::ControlHandler; |
| 4 | use crate::descriptor::{BosWriter, DescriptorWriter}; | 4 | use crate::descriptor::{BosWriter, DescriptorWriter}; |
| 5 | use crate::driver::{Driver, Endpoint, EndpointType}; | 5 | use crate::driver::{Driver, Endpoint, EndpointType}; |
| 6 | #[cfg(feature = "msos-descriptor")] | ||
| 7 | use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; | ||
| 6 | use crate::types::*; | 8 | use crate::types::*; |
| 7 | use crate::{DeviceStateHandler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; | 9 | use crate::{DeviceStateHandler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; |
| 8 | 10 | ||
| @@ -130,7 +132,9 @@ pub struct Builder<'d, D: Driver<'d>> { | |||
| 130 | device_descriptor: DescriptorWriter<'d>, | 132 | device_descriptor: DescriptorWriter<'d>, |
| 131 | config_descriptor: DescriptorWriter<'d>, | 133 | config_descriptor: DescriptorWriter<'d>, |
| 132 | bos_descriptor: BosWriter<'d>, | 134 | bos_descriptor: BosWriter<'d>, |
| 133 | msos_descriptor: Option<crate::msos::MsOsDescriptorSet<'d>>, | 135 | |
| 136 | #[cfg(feature = "msos-descriptor")] | ||
| 137 | msos_descriptor: MsOsDescriptorWriter<'d>, | ||
| 134 | } | 138 | } |
| 135 | 139 | ||
| 136 | impl<'d, D: Driver<'d>> Builder<'d, D> { | 140 | impl<'d, D: Driver<'d>> Builder<'d, D> { |
| @@ -145,6 +149,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { | |||
| 145 | device_descriptor_buf: &'d mut [u8], | 149 | device_descriptor_buf: &'d mut [u8], |
| 146 | config_descriptor_buf: &'d mut [u8], | 150 | config_descriptor_buf: &'d mut [u8], |
| 147 | bos_descriptor_buf: &'d mut [u8], | 151 | bos_descriptor_buf: &'d mut [u8], |
| 152 | #[cfg(feature = "msos-descriptor")] msos_descriptor_buf: &'d mut [u8], | ||
| 148 | control_buf: &'d mut [u8], | 153 | control_buf: &'d mut [u8], |
| 149 | handler: Option<&'d dyn DeviceStateHandler>, | 154 | handler: Option<&'d dyn DeviceStateHandler>, |
| 150 | ) -> Self { | 155 | ) -> Self { |
| @@ -183,12 +188,17 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { | |||
| 183 | device_descriptor, | 188 | device_descriptor, |
| 184 | config_descriptor, | 189 | config_descriptor, |
| 185 | bos_descriptor, | 190 | bos_descriptor, |
| 186 | msos_descriptor: None, | 191 | |
| 192 | #[cfg(feature = "msos-descriptor")] | ||
| 193 | msos_descriptor: MsOsDescriptorWriter::new(msos_descriptor_buf), | ||
| 187 | } | 194 | } |
| 188 | } | 195 | } |
| 189 | 196 | ||
| 190 | /// Creates the [`UsbDevice`] instance with the configuration in this builder. | 197 | /// Creates the [`UsbDevice`] instance with the configuration in this builder. |
| 191 | pub fn build(mut self) -> UsbDevice<'d, D> { | 198 | pub fn build(mut self) -> UsbDevice<'d, D> { |
| 199 | #[cfg(feature = "msos-descriptor")] | ||
| 200 | let msos_descriptor = self.msos_descriptor.build(&mut self.bos_descriptor); | ||
| 201 | |||
| 192 | self.config_descriptor.end_configuration(); | 202 | self.config_descriptor.end_configuration(); |
| 193 | self.bos_descriptor.end_bos(); | 203 | self.bos_descriptor.end_bos(); |
| 194 | 204 | ||
| @@ -201,7 +211,8 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { | |||
| 201 | self.bos_descriptor.writer.into_buf(), | 211 | self.bos_descriptor.writer.into_buf(), |
| 202 | self.interfaces, | 212 | self.interfaces, |
| 203 | self.control_buf, | 213 | self.control_buf, |
| 204 | self.msos_descriptor, | 214 | #[cfg(feature = "msos-descriptor")] |
| 215 | msos_descriptor, | ||
| 205 | ) | 216 | ) |
| 206 | } | 217 | } |
| 207 | 218 | ||
| @@ -218,14 +229,10 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { | |||
| 218 | /// | 229 | /// |
| 219 | /// If it's not set, no IAD descriptor is added. | 230 | /// If it's not set, no IAD descriptor is added. |
| 220 | pub fn function(&mut self, class: u8, subclass: u8, protocol: u8) -> FunctionBuilder<'_, 'd, D> { | 231 | pub fn function(&mut self, class: u8, subclass: u8, protocol: u8) -> FunctionBuilder<'_, 'd, D> { |
| 232 | let first_interface = InterfaceNumber::new(self.interfaces.len() as u8); | ||
| 221 | let iface_count_index = if self.config.composite_with_iads { | 233 | let iface_count_index = if self.config.composite_with_iads { |
| 222 | self.config_descriptor.iad( | 234 | self.config_descriptor |
| 223 | InterfaceNumber::new(self.interfaces.len() as _), | 235 | .iad(first_interface, 0, class, subclass, protocol); |
| 224 | 0, | ||
| 225 | class, | ||
| 226 | subclass, | ||
| 227 | protocol, | ||
| 228 | ); | ||
| 229 | 236 | ||
| 230 | Some(self.config_descriptor.position() - 5) | 237 | Some(self.config_descriptor.position() - 5) |
| 231 | } else { | 238 | } else { |
| @@ -235,19 +242,31 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { | |||
| 235 | FunctionBuilder { | 242 | FunctionBuilder { |
| 236 | builder: self, | 243 | builder: self, |
| 237 | iface_count_index, | 244 | iface_count_index, |
| 245 | |||
| 246 | #[cfg(feature = "msos-descriptor")] | ||
| 247 | first_interface, | ||
| 238 | } | 248 | } |
| 239 | } | 249 | } |
| 240 | 250 | ||
| 251 | #[cfg(feature = "msos-descriptor")] | ||
| 241 | /// Add an MS OS 2.0 Descriptor Set. | 252 | /// Add an MS OS 2.0 Descriptor Set. |
| 242 | /// | 253 | /// |
| 243 | /// Panics if called more than once. | 254 | /// Panics if called more than once. |
| 244 | pub fn msos_descriptor(&mut self, msos_descriptor: crate::msos::MsOsDescriptorSet<'d>) { | 255 | pub fn msos_descriptor(&mut self, windows_version: u32, vendor_code: u8) { |
| 245 | if self.msos_descriptor.is_some() { | 256 | self.msos_descriptor.header(windows_version, vendor_code); |
| 246 | panic!("msos_descriptor already set"); | 257 | } |
| 247 | } | 258 | |
| 248 | self.msos_descriptor | 259 | #[cfg(feature = "msos-descriptor")] |
| 249 | .insert(msos_descriptor) | 260 | /// Add an MS OS 2.0 Device Level Feature Descriptor. |
| 250 | .write_bos_capability(&mut self.bos_descriptor); | 261 | pub fn msos_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) { |
| 262 | self.msos_descriptor.device_feature(desc); | ||
| 263 | } | ||
| 264 | |||
| 265 | #[cfg(feature = "msos-descriptor")] | ||
| 266 | /// Gets the underlying [`MsOsDescriptorWriter`] to allow adding subsets and features for classes that | ||
| 267 | /// do not add their own. | ||
| 268 | pub fn msos_writer(&mut self) -> &mut MsOsDescriptorWriter<'d> { | ||
| 269 | &mut self.msos_descriptor | ||
| 251 | } | 270 | } |
| 252 | } | 271 | } |
| 253 | 272 | ||
| @@ -259,6 +278,16 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { | |||
| 259 | pub struct FunctionBuilder<'a, 'd, D: Driver<'d>> { | 278 | pub struct FunctionBuilder<'a, 'd, D: Driver<'d>> { |
| 260 | builder: &'a mut Builder<'d, D>, | 279 | builder: &'a mut Builder<'d, D>, |
| 261 | iface_count_index: Option<usize>, | 280 | iface_count_index: Option<usize>, |
| 281 | |||
| 282 | #[cfg(feature = "msos-descriptor")] | ||
| 283 | first_interface: InterfaceNumber, | ||
| 284 | } | ||
| 285 | |||
| 286 | impl<'a, 'd, D: Driver<'d>> Drop for FunctionBuilder<'a, 'd, D> { | ||
| 287 | fn drop(&mut self) { | ||
| 288 | #[cfg(feature = "msos-descriptor")] | ||
| 289 | self.builder.msos_descriptor.end_function(); | ||
| 290 | } | ||
| 262 | } | 291 | } |
| 263 | 292 | ||
| 264 | impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { | 293 | impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { |
| @@ -288,6 +317,21 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { | |||
| 288 | next_alt_setting_number: 0, | 317 | next_alt_setting_number: 0, |
| 289 | } | 318 | } |
| 290 | } | 319 | } |
| 320 | |||
| 321 | #[cfg(feature = "msos-descriptor")] | ||
| 322 | /// Add an MS OS 2.0 Function Level Feature Descriptor. | ||
| 323 | pub fn msos_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) { | ||
| 324 | if !self.builder.msos_descriptor.is_in_config_subset() { | ||
| 325 | self.builder.msos_descriptor.configuration(0); | ||
| 326 | } | ||
| 327 | |||
| 328 | if !self.builder.msos_descriptor.is_in_function_subset() { | ||
| 329 | self.builder.msos_descriptor.function(self.first_interface.0); | ||
| 330 | } | ||
| 331 | |||
| 332 | #[cfg(feature = "msos-descriptor")] | ||
| 333 | self.builder.msos_descriptor.function_feature(desc); | ||
| 334 | } | ||
| 291 | } | 335 | } |
| 292 | 336 | ||
| 293 | /// Interface builder. | 337 | /// Interface builder. |
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 948b8d523..aec18524b 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs | |||
| @@ -136,8 +136,8 @@ struct Inner<'d, D: Driver<'d>> { | |||
| 136 | set_address_pending: bool, | 136 | set_address_pending: bool, |
| 137 | 137 | ||
| 138 | interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>, | 138 | interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>, |
| 139 | 139 | #[cfg(feature = "msos-descriptor")] | |
| 140 | msos_descriptor: Option<crate::msos::MsOsDescriptorSet<'d>>, | 140 | msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, |
| 141 | } | 141 | } |
| 142 | 142 | ||
| 143 | impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | 143 | impl<'d, D: Driver<'d>> UsbDevice<'d, D> { |
| @@ -150,7 +150,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 150 | bos_descriptor: &'d [u8], | 150 | bos_descriptor: &'d [u8], |
| 151 | interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>, | 151 | interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>, |
| 152 | control_buf: &'d mut [u8], | 152 | control_buf: &'d mut [u8], |
| 153 | msos_descriptor: Option<crate::msos::MsOsDescriptorSet<'d>>, | 153 | #[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, |
| 154 | ) -> UsbDevice<'d, D> { | 154 | ) -> UsbDevice<'d, D> { |
| 155 | // Start the USB bus. | 155 | // Start the USB bus. |
| 156 | // This prevent further allocation by consuming the driver. | 156 | // This prevent further allocation by consuming the driver. |
| @@ -174,6 +174,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 174 | address: 0, | 174 | address: 0, |
| 175 | set_address_pending: false, | 175 | set_address_pending: false, |
| 176 | interfaces, | 176 | interfaces, |
| 177 | #[cfg(feature = "msos-descriptor")] | ||
| 177 | msos_descriptor, | 178 | msos_descriptor, |
| 178 | }, | 179 | }, |
| 179 | } | 180 | } |
| @@ -608,11 +609,12 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { | |||
| 608 | None => InResponse::Rejected, | 609 | None => InResponse::Rejected, |
| 609 | } | 610 | } |
| 610 | } | 611 | } |
| 612 | #[cfg(feature = "msos-descriptor")] | ||
| 611 | (RequestType::Vendor, Recipient::Device) => { | 613 | (RequestType::Vendor, Recipient::Device) => { |
| 612 | if let Some(msos) = &self.msos_descriptor { | 614 | if !self.msos_descriptor.is_empty() { |
| 613 | if req.request == msos.vendor_code() && req.index == 7 { | 615 | if req.request == self.msos_descriptor.vendor_code() && req.index == 7 { |
| 614 | // Index 7 retrieves the MS OS Descriptor Set | 616 | // Index 7 retrieves the MS OS Descriptor Set |
| 615 | InResponse::Accepted(msos.descriptor()) | 617 | InResponse::Accepted(self.msos_descriptor.descriptor()) |
| 616 | } else { | 618 | } else { |
| 617 | InResponse::Rejected | 619 | InResponse::Rejected |
| 618 | } | 620 | } |
diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index df8461258..360f80d91 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | #![cfg(feature = "msos-descriptor")] | ||
| 2 | |||
| 1 | //! Microsoft OS Descriptors | 3 | //! Microsoft OS Descriptors |
| 2 | //! | 4 | //! |
| 3 | //! <https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification> | 5 | //! <https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification> |
| @@ -5,10 +7,9 @@ | |||
| 5 | use core::mem::size_of; | 7 | use core::mem::size_of; |
| 6 | use core::ops::Range; | 8 | use core::ops::Range; |
| 7 | 9 | ||
| 8 | pub use widestring::{u16cstr, U16CStr}; | 10 | pub use widestring::{u16cstr, u16str, U16CStr, U16Str}; |
| 9 | 11 | ||
| 10 | use crate::descriptor::{capability_type, BosWriter}; | 12 | use super::{capability_type, BosWriter}; |
| 11 | use crate::types::InterfaceNumber; | ||
| 12 | 13 | ||
| 13 | fn write_u16<T: Into<u16>>(buf: &mut [u8], range: Range<usize>, data: T) { | 14 | fn write_u16<T: Into<u16>>(buf: &mut [u8], range: Range<usize>, data: T) { |
| 14 | (&mut buf[range]).copy_from_slice(data.into().to_le_bytes().as_slice()) | 15 | (&mut buf[range]).copy_from_slice(data.into().to_le_bytes().as_slice()) |
| @@ -17,13 +18,12 @@ fn write_u16<T: Into<u16>>(buf: &mut [u8], range: Range<usize>, data: T) { | |||
| 17 | /// A serialized Microsoft OS 2.0 Descriptor set. | 18 | /// A serialized Microsoft OS 2.0 Descriptor set. |
| 18 | /// | 19 | /// |
| 19 | /// Create with [`DeviceDescriptorSetBuilder`]. | 20 | /// Create with [`DeviceDescriptorSetBuilder`]. |
| 20 | pub struct MsOsDescriptorSet<'a> { | 21 | pub struct MsOsDescriptorSet<'d> { |
| 21 | descriptor: &'a [u8], | 22 | descriptor: &'d [u8], |
| 22 | windows_version: u32, | ||
| 23 | vendor_code: u8, | 23 | vendor_code: u8, |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | impl<'a> MsOsDescriptorSet<'a> { | 26 | impl<'d> MsOsDescriptorSet<'d> { |
| 27 | pub fn descriptor(&self) -> &[u8] { | 27 | pub fn descriptor(&self) -> &[u8] { |
| 28 | self.descriptor | 28 | self.descriptor |
| 29 | } | 29 | } |
| @@ -32,9 +32,150 @@ impl<'a> MsOsDescriptorSet<'a> { | |||
| 32 | self.vendor_code | 32 | self.vendor_code |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | pub fn write_bos_capability(&self, bos: &mut BosWriter) { | 35 | pub fn is_empty(&self) -> bool { |
| 36 | let windows_version = self.windows_version.to_le_bytes(); | 36 | self.descriptor.is_empty() |
| 37 | let len = self.descriptor.len().to_le_bytes(); | 37 | } |
| 38 | } | ||
| 39 | |||
| 40 | /// Writes a Microsoft OS 2.0 Descriptor set into a buffer. | ||
| 41 | pub struct MsOsDescriptorWriter<'d> { | ||
| 42 | pub buf: &'d mut [u8], | ||
| 43 | |||
| 44 | position: usize, | ||
| 45 | config_mark: Option<usize>, | ||
| 46 | function_mark: Option<usize>, | ||
| 47 | vendor_code: u8, | ||
| 48 | } | ||
| 49 | |||
| 50 | impl<'d> MsOsDescriptorWriter<'d> { | ||
| 51 | pub(crate) fn new(buf: &'d mut [u8]) -> Self { | ||
| 52 | MsOsDescriptorWriter { | ||
| 53 | buf, | ||
| 54 | position: 0, | ||
| 55 | config_mark: None, | ||
| 56 | function_mark: None, | ||
| 57 | vendor_code: 0, | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | pub(crate) fn build(mut self, bos: &mut BosWriter) -> MsOsDescriptorSet<'d> { | ||
| 62 | self.end(); | ||
| 63 | |||
| 64 | if self.is_empty() { | ||
| 65 | MsOsDescriptorSet { | ||
| 66 | descriptor: &[], | ||
| 67 | vendor_code: 0, | ||
| 68 | } | ||
| 69 | } else { | ||
| 70 | self.write_bos(bos); | ||
| 71 | MsOsDescriptorSet { | ||
| 72 | descriptor: &self.buf[..self.position], | ||
| 73 | vendor_code: self.vendor_code, | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | pub fn is_empty(&self) -> bool { | ||
| 79 | self.position == 0 | ||
| 80 | } | ||
| 81 | |||
| 82 | pub fn is_in_config_subset(&self) -> bool { | ||
| 83 | self.config_mark.is_some() | ||
| 84 | } | ||
| 85 | |||
| 86 | pub fn is_in_function_subset(&self) -> bool { | ||
| 87 | self.function_mark.is_some() | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Write the MS OS descriptor set header. | ||
| 91 | /// | ||
| 92 | /// - `windows_version` is an NTDDI version constant that describes a windows version. See the [`windows_version`] | ||
| 93 | /// module. | ||
| 94 | /// - `vendor_code` is the vendor request code used to read the MS OS descriptor set. | ||
| 95 | pub fn header(&mut self, windows_version: u32, vendor_code: u8) { | ||
| 96 | assert!(self.is_empty(), "You can only call MsOsDescriptorWriter::header once"); | ||
| 97 | self.write(DescriptorSetHeader::new(windows_version)); | ||
| 98 | self.vendor_code = vendor_code; | ||
| 99 | } | ||
| 100 | |||
| 101 | /// Add a device level feature descriptor. | ||
| 102 | /// | ||
| 103 | /// Note that some feature descriptors may only be used at the device level in non-composite devices. | ||
| 104 | /// Those features must be written before the first call to [`Self::configuration`]. | ||
| 105 | pub fn device_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) { | ||
| 106 | assert!( | ||
| 107 | !self.is_empty(), | ||
| 108 | "device features may only be added after the header is written" | ||
| 109 | ); | ||
| 110 | assert!( | ||
| 111 | self.config_mark.is_none(), | ||
| 112 | "device features must be added before the first configuration subset" | ||
| 113 | ); | ||
| 114 | self.write(desc); | ||
| 115 | } | ||
| 116 | |||
| 117 | /// Add a configuration subset. | ||
| 118 | pub fn configuration(&mut self, config: u8) { | ||
| 119 | assert!( | ||
| 120 | !self.is_empty(), | ||
| 121 | "MsOsDescriptorWriter: configuration must be called after header" | ||
| 122 | ); | ||
| 123 | Self::end_subset::<ConfigurationSubsetHeader>(self.buf, self.position, &mut self.config_mark); | ||
| 124 | self.config_mark = Some(self.position); | ||
| 125 | self.write(ConfigurationSubsetHeader::new(config)); | ||
| 126 | } | ||
| 127 | |||
| 128 | /// Add a function subset. | ||
| 129 | pub fn function(&mut self, first_interface: u8) { | ||
| 130 | assert!( | ||
| 131 | self.config_mark.is_some(), | ||
| 132 | "MsOsDescriptorWriter: function subset requires a configuration subset" | ||
| 133 | ); | ||
| 134 | self.end_function(); | ||
| 135 | self.function_mark = Some(self.position); | ||
| 136 | self.write(FunctionSubsetHeader::new(first_interface)); | ||
| 137 | } | ||
| 138 | |||
| 139 | /// Add a function level feature descriptor. | ||
| 140 | /// | ||
| 141 | /// Note that some features may only be used at the function level. Those features must be written after a call | ||
| 142 | /// to [`Self::function`]. | ||
| 143 | pub fn function_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) { | ||
| 144 | assert!( | ||
| 145 | self.function_mark.is_some(), | ||
| 146 | "function features may only be added to a function subset" | ||
| 147 | ); | ||
| 148 | self.write(desc); | ||
| 149 | } | ||
| 150 | |||
| 151 | pub fn end_function(&mut self) { | ||
| 152 | Self::end_subset::<FunctionSubsetHeader>(self.buf, self.position, &mut self.function_mark); | ||
| 153 | } | ||
| 154 | |||
| 155 | fn write<T: Descriptor>(&mut self, desc: T) { | ||
| 156 | desc.write_to(&mut self.buf[self.position..]); | ||
| 157 | self.position += desc.size(); | ||
| 158 | } | ||
| 159 | |||
| 160 | fn end_subset<T: DescriptorSet>(buf: &mut [u8], position: usize, mark: &mut Option<usize>) { | ||
| 161 | if let Some(mark) = mark.take() { | ||
| 162 | let len = position - mark; | ||
| 163 | let p = mark + T::LENGTH_OFFSET; | ||
| 164 | buf[p..(p + 2)].copy_from_slice(&(len as u16).to_le_bytes()); | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | fn end(&mut self) { | ||
| 169 | if self.position > 0 { | ||
| 170 | Self::end_subset::<FunctionSubsetHeader>(self.buf, self.position, &mut self.function_mark); | ||
| 171 | Self::end_subset::<ConfigurationSubsetHeader>(self.buf, self.position, &mut self.config_mark); | ||
| 172 | Self::end_subset::<DescriptorSetHeader>(self.buf, self.position, &mut Some(0)); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | fn write_bos(&mut self, bos: &mut BosWriter) { | ||
| 177 | let windows_version = &self.buf[4..8]; | ||
| 178 | let len = (self.position as u16).to_le_bytes(); | ||
| 38 | bos.capability( | 179 | bos.capability( |
| 39 | capability_type::PLATFORM, | 180 | capability_type::PLATFORM, |
| 40 | &[ | 181 | &[ |
| @@ -67,30 +208,7 @@ impl<'a> MsOsDescriptorSet<'a> { | |||
| 67 | self.vendor_code, | 208 | self.vendor_code, |
| 68 | 0x0, // Device does not support alternate enumeration | 209 | 0x0, // Device does not support alternate enumeration |
| 69 | ], | 210 | ], |
| 70 | ) | 211 | ); |
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | /// A helper struct to implement the different descriptor set builders. | ||
| 75 | struct DescriptorSetBuilder<'a> { | ||
| 76 | used: usize, | ||
| 77 | buf: &'a mut [u8], | ||
| 78 | } | ||
| 79 | |||
| 80 | impl<'a> DescriptorSetBuilder<'a> { | ||
| 81 | pub fn descriptor<T>(&mut self, desc: T) | ||
| 82 | where | ||
| 83 | T: Descriptor + 'a, | ||
| 84 | { | ||
| 85 | let size = desc.size(); | ||
| 86 | let start = self.used; | ||
| 87 | let end = start + size; | ||
| 88 | desc.write_to(&mut self.buf[start..end]); | ||
| 89 | self.used += size; | ||
| 90 | } | ||
| 91 | |||
| 92 | pub fn remaining(&mut self) -> &mut [u8] { | ||
| 93 | &mut self.buf[self.used..] | ||
| 94 | } | 212 | } |
| 95 | } | 213 | } |
| 96 | 214 | ||
| @@ -120,182 +238,27 @@ pub mod windows_version { | |||
| 120 | pub const WIN10: u32 = 0x0A000000; | 238 | pub const WIN10: u32 = 0x0A000000; |
| 121 | } | 239 | } |
| 122 | 240 | ||
| 123 | /// Helps build a Microsoft OS 2.0 Descriptor set. | 241 | mod sealed { |
| 124 | /// | 242 | use core::mem::size_of; |
| 125 | /// # Example | ||
| 126 | /// ```rust | ||
| 127 | /// # use embassy_usb::types::InterfaceNumber; | ||
| 128 | /// # use embassy_usb::msos::*; | ||
| 129 | /// # let cdc_interface = unsafe { core::mem::transmute::<u8, InterfaceNumber>(0) }; | ||
| 130 | /// # let dfu_interface = unsafe { core::mem::transmute::<u8, InterfaceNumber>(1) }; | ||
| 131 | /// let mut buf = [0u8; 256]; | ||
| 132 | /// let mut builder = DeviceDescriptorSetBuilder::new(&mut buf[..], windows_version::WIN8_1); | ||
| 133 | /// builder.feature(MinimumRecoveryTimeDescriptor::new(5, 10)); | ||
| 134 | /// builder.feature(ModelIdDescriptor::new(0xdeadbeef1234u128)); | ||
| 135 | /// builder.configuration(1, |conf| { | ||
| 136 | /// conf.function(cdc_interface, |func| { | ||
| 137 | /// func.winusb_device(); | ||
| 138 | /// func.feature(VendorRevisionDescriptor::new(1)); | ||
| 139 | /// }); | ||
| 140 | /// conf.function(dfu_interface, |func| { | ||
| 141 | /// func.winusb_device(); | ||
| 142 | /// func.feature(VendorRevisionDescriptor::new(1)); | ||
| 143 | /// }); | ||
| 144 | /// }); | ||
| 145 | /// ``` | ||
| 146 | pub struct DeviceDescriptorSetBuilder<'a> { | ||
| 147 | builder: DescriptorSetBuilder<'a>, | ||
| 148 | windows_version: u32, | ||
| 149 | vendor_code: u8, | ||
| 150 | } | ||
| 151 | 243 | ||
| 152 | impl<'a> DeviceDescriptorSetBuilder<'a> { | 244 | /// A trait for descriptors |
| 153 | /// Create a device descriptor set builder. | 245 | pub trait Descriptor: Sized { |
| 154 | /// | 246 | const TYPE: super::DescriptorType; |
| 155 | /// - `windows_version` is an NTDDI version constant that describes a windows version. See the [`windows_version`] | ||
| 156 | /// module. | ||
| 157 | /// - `vendor_code` is the vendor request code used to read the MS OS descriptor set. | ||
| 158 | pub fn new<'b: 'a>(buf: &'b mut [u8], windows_version: u32, vendor_code: u8) -> Self { | ||
| 159 | let mut builder = DescriptorSetBuilder { used: 0, buf }; | ||
| 160 | builder.descriptor(DescriptorSetHeader { | ||
| 161 | wLength: (size_of::<DescriptorSetHeader>() as u16).to_le(), | ||
| 162 | wDescriptorType: (DescriptorSetHeader::TYPE as u16).to_le(), | ||
| 163 | dwWindowsVersion: windows_version.to_le(), | ||
| 164 | wTotalLength: 0, | ||
| 165 | }); | ||
| 166 | Self { | ||
| 167 | builder, | ||
| 168 | windows_version, | ||
| 169 | vendor_code, | ||
| 170 | } | ||
| 171 | } | ||
| 172 | 247 | ||
| 173 | /// Add a device-level feature descriptor. | 248 | /// The size of the descriptor's header. |
| 174 | /// | 249 | fn size(&self) -> usize { |
| 175 | /// Note that some feature descriptors may only be used at the device level in non-composite devices. | 250 | size_of::<Self>() |
| 176 | pub fn feature<T>(&mut self, desc: T) | ||
| 177 | where | ||
| 178 | T: Descriptor + DeviceLevelDescriptor + 'a, | ||
| 179 | { | ||
| 180 | self.builder.descriptor(desc) | ||
| 181 | } | ||
| 182 | |||
| 183 | /// Add a configuration subset. | ||
| 184 | pub fn configuration(&mut self, configuration: u8, build_conf: impl FnOnce(&mut ConfigurationSubsetBuilder<'_>)) { | ||
| 185 | let mut cb = ConfigurationSubsetBuilder::new(self.builder.remaining(), configuration); | ||
| 186 | build_conf(&mut cb); | ||
| 187 | self.builder.used += cb.finalize(); | ||
| 188 | } | ||
| 189 | |||
| 190 | /// Finishes writing the data. | ||
| 191 | pub fn finalize(self) -> MsOsDescriptorSet<'a> { | ||
| 192 | let used = self.builder.used; | ||
| 193 | let buf = self.builder.buf; | ||
| 194 | // Update length in header with final length | ||
| 195 | let total_len = &mut buf[8..10]; | ||
| 196 | total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); | ||
| 197 | |||
| 198 | MsOsDescriptorSet { | ||
| 199 | descriptor: &buf[..used], | ||
| 200 | windows_version: self.windows_version, | ||
| 201 | vendor_code: self.vendor_code, | ||
| 202 | } | 251 | } |
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | pub struct ConfigurationSubsetBuilder<'a> { | ||
| 207 | builder: DescriptorSetBuilder<'a>, | ||
| 208 | } | ||
| 209 | |||
| 210 | impl<'a> ConfigurationSubsetBuilder<'a> { | ||
| 211 | pub fn new<'b: 'a>(buf: &'b mut [u8], configuration: u8) -> Self { | ||
| 212 | let mut builder = DescriptorSetBuilder { used: 0, buf }; | ||
| 213 | builder.descriptor(ConfigurationSubsetHeader { | ||
| 214 | wLength: (size_of::<ConfigurationSubsetHeader>() as u16).to_le(), | ||
| 215 | wDescriptorType: (ConfigurationSubsetHeader::TYPE as u16).to_le(), | ||
| 216 | bConfigurationValue: configuration, | ||
| 217 | bReserved: 0, | ||
| 218 | wTotalLength: 0, | ||
| 219 | }); | ||
| 220 | Self { builder } | ||
| 221 | } | ||
| 222 | |||
| 223 | /// Add a function subset. | ||
| 224 | pub fn function(&mut self, interface: InterfaceNumber, build_func: impl FnOnce(&mut FunctionSubsetBuilder<'_>)) { | ||
| 225 | let mut fb = FunctionSubsetBuilder::new(self.builder.remaining(), interface); | ||
| 226 | build_func(&mut fb); | ||
| 227 | self.builder.used += fb.finalize(); | ||
| 228 | } | ||
| 229 | |||
| 230 | /// Finishes writing the data. Returns the total number of bytes used by the descriptor set. | ||
| 231 | pub fn finalize(self) -> usize { | ||
| 232 | let used = self.builder.used; | ||
| 233 | let buf = self.builder.buf; | ||
| 234 | // Update length in header with final length | ||
| 235 | let total_len = &mut buf[6..8]; | ||
| 236 | total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); | ||
| 237 | used | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | pub struct FunctionSubsetBuilder<'a> { | ||
| 242 | builder: DescriptorSetBuilder<'a>, | ||
| 243 | } | ||
| 244 | |||
| 245 | impl<'a> FunctionSubsetBuilder<'a> { | ||
| 246 | pub fn new<'b: 'a>(buf: &'b mut [u8], interface: InterfaceNumber) -> Self { | ||
| 247 | let mut builder = DescriptorSetBuilder { used: 0, buf }; | ||
| 248 | builder.descriptor(FunctionSubsetHeader { | ||
| 249 | wLength: (size_of::<FunctionSubsetHeader>() as u16).to_le(), | ||
| 250 | wDescriptorType: (FunctionSubsetHeader::TYPE as u16).to_le(), | ||
| 251 | bFirstInterface: interface.0, | ||
| 252 | bReserved: 0, | ||
| 253 | wSubsetLength: 0, | ||
| 254 | }); | ||
| 255 | Self { builder } | ||
| 256 | } | ||
| 257 | 252 | ||
| 258 | /// Add a function-level descriptor. | 253 | fn write_to(&self, buf: &mut [u8]); |
| 259 | /// | ||
| 260 | /// Note that many descriptors can only be used at function-level in a composite device. | ||
| 261 | pub fn feature<T>(&mut self, desc: T) | ||
| 262 | where | ||
| 263 | T: Descriptor + FunctionLevelDescriptor + 'a, | ||
| 264 | { | ||
| 265 | self.builder.descriptor(desc) | ||
| 266 | } | 254 | } |
| 267 | 255 | ||
| 268 | /// Adds the feature descriptors to configure this function to use the WinUSB driver. | 256 | pub trait DescriptorSet: Descriptor { |
| 269 | /// | 257 | const LENGTH_OFFSET: usize; |
| 270 | /// Adds a compatible id descriptor "WINUSB" and a registry descriptor that sets the DeviceInterfaceGUID to the | ||
| 271 | /// USB_DEVICE GUID. | ||
| 272 | pub fn winusb_device(&mut self) { | ||
| 273 | self.feature(CompatibleIdFeatureDescriptor::new_winusb()); | ||
| 274 | self.feature(RegistryPropertyFeatureDescriptor::new_usb_deviceinterfaceguid()); | ||
| 275 | } | ||
| 276 | |||
| 277 | /// Finishes writing the data. Returns the total number of bytes used by the descriptor set. | ||
| 278 | pub fn finalize(self) -> usize { | ||
| 279 | let used = self.builder.used; | ||
| 280 | let buf = self.builder.buf; | ||
| 281 | // Update length in header with final length | ||
| 282 | let total_len = &mut buf[6..8]; | ||
| 283 | total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); | ||
| 284 | used | ||
| 285 | } | 258 | } |
| 286 | } | 259 | } |
| 287 | 260 | ||
| 288 | /// A trait for descriptors | 261 | use sealed::*; |
| 289 | pub trait Descriptor: Sized { | ||
| 290 | const TYPE: DescriptorType; | ||
| 291 | |||
| 292 | /// The size of the descriptor's header. | ||
| 293 | fn size(&self) -> usize { | ||
| 294 | size_of::<Self>() | ||
| 295 | } | ||
| 296 | |||
| 297 | fn write_to(&self, buf: &mut [u8]); | ||
| 298 | } | ||
| 299 | 262 | ||
| 300 | /// Copies the data of `t` into `buf`. | 263 | /// Copies the data of `t` into `buf`. |
| 301 | /// | 264 | /// |
| @@ -303,7 +266,7 @@ pub trait Descriptor: Sized { | |||
| 303 | /// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct) | 266 | /// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct) |
| 304 | unsafe fn transmute_write_to<T: Sized>(t: &T, buf: &mut [u8]) { | 267 | unsafe fn transmute_write_to<T: Sized>(t: &T, buf: &mut [u8]) { |
| 305 | let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::<T>()); | 268 | let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::<T>()); |
| 306 | assert!(buf.len() >= bytes.len()); | 269 | assert!(buf.len() >= bytes.len(), "MSOS descriptor buffer full"); |
| 307 | (&mut buf[..bytes.len()]).copy_from_slice(bytes); | 270 | (&mut buf[..bytes.len()]).copy_from_slice(bytes); |
| 308 | } | 271 | } |
| 309 | 272 | ||
| @@ -354,6 +317,17 @@ pub struct DescriptorSetHeader { | |||
| 354 | wTotalLength: u16, | 317 | wTotalLength: u16, |
| 355 | } | 318 | } |
| 356 | 319 | ||
| 320 | impl DescriptorSetHeader { | ||
| 321 | pub fn new(windows_version: u32) -> Self { | ||
| 322 | DescriptorSetHeader { | ||
| 323 | wLength: (size_of::<Self>() as u16).to_le(), | ||
| 324 | wDescriptorType: (Self::TYPE as u16).to_le(), | ||
| 325 | dwWindowsVersion: windows_version.to_le(), | ||
| 326 | wTotalLength: 0, | ||
| 327 | } | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 357 | impl Descriptor for DescriptorSetHeader { | 331 | impl Descriptor for DescriptorSetHeader { |
| 358 | const TYPE: DescriptorType = DescriptorType::SetHeaderDescriptor; | 332 | const TYPE: DescriptorType = DescriptorType::SetHeaderDescriptor; |
| 359 | fn write_to(&self, buf: &mut [u8]) { | 333 | fn write_to(&self, buf: &mut [u8]) { |
| @@ -361,6 +335,10 @@ impl Descriptor for DescriptorSetHeader { | |||
| 361 | } | 335 | } |
| 362 | } | 336 | } |
| 363 | 337 | ||
| 338 | impl DescriptorSet for DescriptorSetHeader { | ||
| 339 | const LENGTH_OFFSET: usize = 8; | ||
| 340 | } | ||
| 341 | |||
| 364 | /// Table 11. Configuration subset header. | 342 | /// Table 11. Configuration subset header. |
| 365 | #[allow(non_snake_case)] | 343 | #[allow(non_snake_case)] |
| 366 | #[repr(C, packed(1))] | 344 | #[repr(C, packed(1))] |
| @@ -372,6 +350,18 @@ pub struct ConfigurationSubsetHeader { | |||
| 372 | wTotalLength: u16, | 350 | wTotalLength: u16, |
| 373 | } | 351 | } |
| 374 | 352 | ||
| 353 | impl ConfigurationSubsetHeader { | ||
| 354 | pub fn new(config: u8) -> Self { | ||
| 355 | ConfigurationSubsetHeader { | ||
| 356 | wLength: (size_of::<Self>() as u16).to_le(), | ||
| 357 | wDescriptorType: (Self::TYPE as u16).to_le(), | ||
| 358 | bConfigurationValue: config, | ||
| 359 | bReserved: 0, | ||
| 360 | wTotalLength: 0, | ||
| 361 | } | ||
| 362 | } | ||
| 363 | } | ||
| 364 | |||
| 375 | impl Descriptor for ConfigurationSubsetHeader { | 365 | impl Descriptor for ConfigurationSubsetHeader { |
| 376 | const TYPE: DescriptorType = DescriptorType::SubsetHeaderConfiguration; | 366 | const TYPE: DescriptorType = DescriptorType::SubsetHeaderConfiguration; |
| 377 | fn write_to(&self, buf: &mut [u8]) { | 367 | fn write_to(&self, buf: &mut [u8]) { |
| @@ -379,6 +369,10 @@ impl Descriptor for ConfigurationSubsetHeader { | |||
| 379 | } | 369 | } |
| 380 | } | 370 | } |
| 381 | 371 | ||
| 372 | impl DescriptorSet for ConfigurationSubsetHeader { | ||
| 373 | const LENGTH_OFFSET: usize = 6; | ||
| 374 | } | ||
| 375 | |||
| 382 | /// Table 12. Function subset header. | 376 | /// Table 12. Function subset header. |
| 383 | #[allow(non_snake_case)] | 377 | #[allow(non_snake_case)] |
| 384 | #[repr(C, packed(1))] | 378 | #[repr(C, packed(1))] |
| @@ -390,6 +384,18 @@ pub struct FunctionSubsetHeader { | |||
| 390 | wSubsetLength: u16, | 384 | wSubsetLength: u16, |
| 391 | } | 385 | } |
| 392 | 386 | ||
| 387 | impl FunctionSubsetHeader { | ||
| 388 | pub fn new(first_interface: u8) -> Self { | ||
| 389 | FunctionSubsetHeader { | ||
| 390 | wLength: (size_of::<Self>() as u16).to_le(), | ||
| 391 | wDescriptorType: (Self::TYPE as u16).to_le(), | ||
| 392 | bFirstInterface: first_interface, | ||
| 393 | bReserved: 0, | ||
| 394 | wSubsetLength: 0, | ||
| 395 | } | ||
| 396 | } | ||
| 397 | } | ||
| 398 | |||
| 393 | impl Descriptor for FunctionSubsetHeader { | 399 | impl Descriptor for FunctionSubsetHeader { |
| 394 | const TYPE: DescriptorType = DescriptorType::SubsetHeaderFunction; | 400 | const TYPE: DescriptorType = DescriptorType::SubsetHeaderFunction; |
| 395 | fn write_to(&self, buf: &mut [u8]) { | 401 | fn write_to(&self, buf: &mut [u8]) { |
| @@ -397,16 +403,17 @@ impl Descriptor for FunctionSubsetHeader { | |||
| 397 | } | 403 | } |
| 398 | } | 404 | } |
| 399 | 405 | ||
| 406 | impl DescriptorSet for FunctionSubsetHeader { | ||
| 407 | const LENGTH_OFFSET: usize = 6; | ||
| 408 | } | ||
| 409 | |||
| 400 | // Feature Descriptors | 410 | // Feature Descriptors |
| 401 | 411 | ||
| 402 | /// A marker trait for feature descriptors that are valid at the device level. | 412 | /// A marker trait for feature descriptors that are valid at the device level. |
| 403 | pub trait DeviceLevelDescriptor {} | 413 | pub trait DeviceLevelDescriptor: Descriptor {} |
| 404 | 414 | ||
| 405 | /// A marker trait for feature descriptors that are valid at the function level. | 415 | /// A marker trait for feature descriptors that are valid at the function level. |
| 406 | pub trait FunctionLevelDescriptor { | 416 | pub trait FunctionLevelDescriptor: Descriptor {} |
| 407 | /// `true` when the feature descriptor may only be used at the function level in composite devices. | ||
| 408 | const COMPOSITE_ONLY: bool = false; | ||
| 409 | } | ||
| 410 | 417 | ||
| 411 | /// Table 13. Microsoft OS 2.0 compatible ID descriptor. | 418 | /// Table 13. Microsoft OS 2.0 compatible ID descriptor. |
| 412 | #[allow(non_snake_case)] | 419 | #[allow(non_snake_case)] |
| @@ -419,9 +426,7 @@ pub struct CompatibleIdFeatureDescriptor { | |||
| 419 | } | 426 | } |
| 420 | 427 | ||
| 421 | impl DeviceLevelDescriptor for CompatibleIdFeatureDescriptor {} | 428 | impl DeviceLevelDescriptor for CompatibleIdFeatureDescriptor {} |
| 422 | impl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor { | 429 | impl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor {} |
| 423 | const COMPOSITE_ONLY: bool = true; | ||
| 424 | } | ||
| 425 | 430 | ||
| 426 | impl Descriptor for CompatibleIdFeatureDescriptor { | 431 | impl Descriptor for CompatibleIdFeatureDescriptor { |
| 427 | const TYPE: DescriptorType = DescriptorType::FeatureCompatibleId; | 432 | const TYPE: DescriptorType = DescriptorType::FeatureCompatibleId; |
| @@ -462,16 +467,12 @@ pub struct RegistryPropertyFeatureDescriptor<'a> { | |||
| 462 | wLength: u16, | 467 | wLength: u16, |
| 463 | wDescriptorType: u16, | 468 | wDescriptorType: u16, |
| 464 | wPropertyDataType: u16, | 469 | wPropertyDataType: u16, |
| 465 | wPropertyNameLength: u16, | ||
| 466 | PropertyName: &'a [u8], | 470 | PropertyName: &'a [u8], |
| 467 | wPropertyDataLength: u16, | ||
| 468 | PropertyData: &'a [u8], | 471 | PropertyData: &'a [u8], |
| 469 | } | 472 | } |
| 470 | 473 | ||
| 471 | impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} | 474 | impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} |
| 472 | impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> { | 475 | impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} |
| 473 | const COMPOSITE_ONLY: bool = true; | ||
| 474 | } | ||
| 475 | 476 | ||
| 476 | impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { | 477 | impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { |
| 477 | const TYPE: DescriptorType = DescriptorType::FeatureRegProperty; | 478 | const TYPE: DescriptorType = DescriptorType::FeatureRegProperty; |
| @@ -479,34 +480,44 @@ impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { | |||
| 479 | 10 + self.PropertyName.len() + self.PropertyData.len() | 480 | 10 + self.PropertyName.len() + self.PropertyData.len() |
| 480 | } | 481 | } |
| 481 | fn write_to(&self, buf: &mut [u8]) { | 482 | fn write_to(&self, buf: &mut [u8]) { |
| 482 | assert!(buf.len() >= self.size()); | 483 | assert!(buf.len() >= self.size(), "MSOS descriptor buffer full"); |
| 483 | assert!(self.wPropertyNameLength as usize == self.PropertyName.len()); | ||
| 484 | assert!(self.wPropertyDataLength as usize == self.PropertyData.len()); | ||
| 485 | write_u16(buf, 0..2, self.wLength); | 484 | write_u16(buf, 0..2, self.wLength); |
| 486 | write_u16(buf, 2..4, self.wDescriptorType); | 485 | write_u16(buf, 2..4, self.wDescriptorType); |
| 487 | write_u16(buf, 4..6, self.wPropertyDataType); | 486 | write_u16(buf, 4..6, self.wPropertyDataType); |
| 488 | write_u16(buf, 6..8, self.wPropertyNameLength); | 487 | write_u16(buf, 6..8, (self.PropertyName.len() as u16).to_le()); |
| 489 | let pne = 8 + self.PropertyName.len(); | 488 | let pne = 8 + self.PropertyName.len(); |
| 490 | (&mut buf[8..pne]).copy_from_slice(self.PropertyName); | 489 | (&mut buf[8..pne]).copy_from_slice(self.PropertyName); |
| 491 | let pds = pne + 2; | 490 | let pds = pne + 2; |
| 492 | let pde = pds + self.PropertyData.len(); | 491 | let pde = pds + self.PropertyData.len(); |
| 493 | write_u16(buf, pne..pds, self.wPropertyDataLength); | 492 | write_u16(buf, pne..pds, (self.PropertyData.len() as u16).to_le()); |
| 494 | (&mut buf[pds..pde]).copy_from_slice(self.PropertyData); | 493 | (&mut buf[pds..pde]).copy_from_slice(self.PropertyData); |
| 495 | } | 494 | } |
| 496 | } | 495 | } |
| 497 | 496 | ||
| 498 | impl<'a> RegistryPropertyFeatureDescriptor<'a> { | 497 | impl<'a> RegistryPropertyFeatureDescriptor<'a> { |
| 498 | pub const DEVICE_INTERFACE_GUIDS_NAME: &U16CStr = { | ||
| 499 | // Can't use defmt::panic in constant expressions (inside u16cstr!) | ||
| 500 | macro_rules! panic { | ||
| 501 | ($($x:tt)*) => { | ||
| 502 | { | ||
| 503 | ::core::panic!($($x)*); | ||
| 504 | } | ||
| 505 | }; | ||
| 506 | } | ||
| 507 | |||
| 508 | u16cstr!("DeviceInterfaceGUIDs") | ||
| 509 | }; | ||
| 510 | |||
| 499 | /// A registry property. | 511 | /// A registry property. |
| 500 | /// | 512 | /// |
| 501 | /// `name` should be a NUL-terminated 16-bit Unicode string. | 513 | /// `name` should be a NUL-terminated 16-bit Unicode string. |
| 502 | pub fn new_raw<'n: 'a, 'd: 'a>(name: &'a [u8], data: &'d [u8], data_type: PropertyDataType) -> Self { | 514 | pub fn new_raw<'n: 'a, 'd: 'a>(name: &'a [u8], data: &'d [u8], data_type: PropertyDataType) -> Self { |
| 515 | assert!(name.len() < usize::from(u16::MAX) && data.len() < usize::from(u16::MAX)); | ||
| 503 | Self { | 516 | Self { |
| 504 | wLength: ((10 + name.len() + data.len()) as u16).to_le(), | 517 | wLength: ((10 + name.len() + data.len()) as u16).to_le(), |
| 505 | wDescriptorType: (Self::TYPE as u16).to_le(), | 518 | wDescriptorType: (Self::TYPE as u16).to_le(), |
| 506 | wPropertyDataType: (data_type as u16).to_le(), | 519 | wPropertyDataType: (data_type as u16).to_le(), |
| 507 | wPropertyNameLength: (name.len() as u16).to_le(), | ||
| 508 | PropertyName: name, | 520 | PropertyName: name, |
| 509 | wPropertyDataLength: (data.len() as u16).to_le(), | ||
| 510 | PropertyData: data, | 521 | PropertyData: data, |
| 511 | } | 522 | } |
| 512 | } | 523 | } |
| @@ -515,24 +526,6 @@ impl<'a> RegistryPropertyFeatureDescriptor<'a> { | |||
| 515 | unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) } | 526 | unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) } |
| 516 | } | 527 | } |
| 517 | 528 | ||
| 518 | /// A registry property that sets the DeviceInterfaceGUIDs to the device interface class for USB devices which are | ||
| 519 | /// attached to a USB hub. | ||
| 520 | pub fn new_usb_deviceinterfaceguid() -> Self { | ||
| 521 | // Can't use defmt::panic in constant expressions (inside u16cstr!) | ||
| 522 | macro_rules! panic { | ||
| 523 | ($($x:tt)*) => { | ||
| 524 | { | ||
| 525 | ::core::panic!($($x)*); | ||
| 526 | } | ||
| 527 | }; | ||
| 528 | } | ||
| 529 | |||
| 530 | Self::new_multi_string( | ||
| 531 | u16cstr!("DeviceInterfaceGUIDs"), | ||
| 532 | u16cstr!("{A5DCBF10-6530-11D2-901F-00C04FB951ED}").as_slice_with_nul(), | ||
| 533 | ) | ||
| 534 | } | ||
| 535 | |||
| 536 | /// A registry property containing a NUL-terminated 16-bit Unicode string. | 529 | /// A registry property containing a NUL-terminated 16-bit Unicode string. |
| 537 | pub fn new_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { | 530 | pub fn new_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { |
| 538 | Self::new_raw(Self::u16str_bytes(name), Self::u16str_bytes(data), PropertyDataType::Sz) | 531 | Self::new_raw(Self::u16str_bytes(name), Self::u16str_bytes(data), PropertyDataType::Sz) |
| @@ -558,6 +551,10 @@ impl<'a> RegistryPropertyFeatureDescriptor<'a> { | |||
| 558 | 551 | ||
| 559 | /// A registry property containing multiple NUL-terminated 16-bit Unicode strings. | 552 | /// A registry property containing multiple NUL-terminated 16-bit Unicode strings. |
| 560 | pub fn new_multi_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u16]) -> Self { | 553 | pub fn new_multi_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u16]) -> Self { |
| 554 | assert!( | ||
| 555 | data.len() >= 2 && data[data.len() - 1] == 0 && data[data.len() - 2] == 0, | ||
| 556 | "multi-strings must end in double nul terminators" | ||
| 557 | ); | ||
| 561 | Self::new_raw( | 558 | Self::new_raw( |
| 562 | Self::u16str_bytes(name), | 559 | Self::u16str_bytes(name), |
| 563 | unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 2) }, | 560 | unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 2) }, |
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 95d939873..cfdda076e 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml | |||
| @@ -6,6 +6,7 @@ license = "MIT OR Apache-2.0" | |||
| 6 | 6 | ||
| 7 | [features] | 7 | [features] |
| 8 | default = ["nightly"] | 8 | default = ["nightly"] |
| 9 | msos-descriptor = ["embassy-usb/msos-descriptor"] | ||
| 9 | nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net", | 10 | nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net", |
| 10 | "embassy-lora", "lorawan-device", "lorawan"] | 11 | "embassy-lora", "lorawan-device", "lorawan"] |
| 11 | 12 | ||
| @@ -34,4 +35,8 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa | |||
| 34 | rand = { version = "0.8.4", default-features = false } | 35 | rand = { version = "0.8.4", default-features = false } |
| 35 | embedded-storage = "0.3.0" | 36 | embedded-storage = "0.3.0" |
| 36 | usbd-hid = "0.6.0" | 37 | usbd-hid = "0.6.0" |
| 37 | serde = { version = "1.0.136", default-features = false } \ No newline at end of file | 38 | serde = { version = "1.0.136", default-features = false } |
| 39 | |||
| 40 | [[bin]] | ||
| 41 | name = "usb_serial_winusb" | ||
| 42 | required-features = ["msos-descriptor"] | ||
diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs new file mode 100644 index 000000000..443379a07 --- /dev/null +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::mem; | ||
| 6 | |||
| 7 | use defmt::{info, panic}; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_futures::join::join; | ||
| 10 | use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; | ||
| 11 | use embassy_nrf::{interrupt, pac}; | ||
| 12 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 13 | use embassy_usb::driver::EndpointError; | ||
| 14 | use embassy_usb::msos::{self, windows_version}; | ||
| 15 | use embassy_usb::{Builder, Config}; | ||
| 16 | use {defmt_rtt as _, panic_probe as _}; | ||
| 17 | |||
| 18 | const DEVICE_INTERFACE_GUIDS: &[u16] = { | ||
| 19 | // Can't use defmt::panic in constant expressions (inside u16str!) | ||
| 20 | macro_rules! panic { | ||
| 21 | ($($x:tt)*) => { | ||
| 22 | { | ||
| 23 | ::core::panic!($($x)*); | ||
| 24 | } | ||
| 25 | }; | ||
| 26 | } | ||
| 27 | msos::u16str!("{EAA9A5DC-30BA-44BC-9232-606CDC875321}\0\0").as_slice() | ||
| 28 | }; | ||
| 29 | |||
| 30 | #[embassy_executor::main] | ||
| 31 | async fn main(_spawner: Spawner) { | ||
| 32 | let p = embassy_nrf::init(Default::default()); | ||
| 33 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 34 | |||
| 35 | info!("Enabling ext hfosc..."); | ||
| 36 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 37 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 38 | |||
| 39 | // Create the driver, from the HAL. | ||
| 40 | let irq = interrupt::take!(USBD); | ||
| 41 | let power_irq = interrupt::take!(POWER_CLOCK); | ||
| 42 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | ||
| 43 | |||
| 44 | // Create embassy-usb Config | ||
| 45 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 46 | config.manufacturer = Some("Embassy"); | ||
| 47 | config.product = Some("USB-serial example"); | ||
| 48 | config.serial_number = Some("12345678"); | ||
| 49 | config.max_power = 100; | ||
| 50 | config.max_packet_size_0 = 64; | ||
| 51 | |||
| 52 | // Required for windows compatiblity. | ||
| 53 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 54 | config.device_class = 0xEF; | ||
| 55 | config.device_sub_class = 0x02; | ||
| 56 | config.device_protocol = 0x01; | ||
| 57 | config.composite_with_iads = true; | ||
| 58 | |||
| 59 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 60 | // It needs some buffers for building the descriptors. | ||
| 61 | let mut device_descriptor = [0; 256]; | ||
| 62 | let mut config_descriptor = [0; 256]; | ||
| 63 | let mut bos_descriptor = [0; 256]; | ||
| 64 | let mut msos_descriptor = [0; 256]; | ||
| 65 | let mut control_buf = [0; 64]; | ||
| 66 | |||
| 67 | let mut state = State::new(); | ||
| 68 | |||
| 69 | let mut builder = Builder::new( | ||
| 70 | driver, | ||
| 71 | config, | ||
| 72 | &mut device_descriptor, | ||
| 73 | &mut config_descriptor, | ||
| 74 | &mut bos_descriptor, | ||
| 75 | &mut msos_descriptor, | ||
| 76 | &mut control_buf, | ||
| 77 | None, | ||
| 78 | ); | ||
| 79 | |||
| 80 | builder.msos_descriptor(windows_version::WIN8_1, 2); | ||
| 81 | |||
| 82 | // Create classes on the builder. | ||
| 83 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 84 | |||
| 85 | // Since we want to create MS OS feature descriptors that apply to a function that has already been added to the | ||
| 86 | // builder, need to get the MsOsDescriptorWriter from the builder and manually add those descriptors. | ||
| 87 | // Inside a class constructor, you would just need to call `FunctionBuilder::msos_feature` instead. | ||
| 88 | let msos_writer = builder.msos_writer(); | ||
| 89 | msos_writer.configuration(0); | ||
| 90 | msos_writer.function(0); | ||
| 91 | msos_writer.function_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); | ||
| 92 | msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new_multi_string( | ||
| 93 | msos::RegistryPropertyFeatureDescriptor::DEVICE_INTERFACE_GUIDS_NAME, | ||
| 94 | DEVICE_INTERFACE_GUIDS, | ||
| 95 | )); | ||
| 96 | |||
| 97 | // Build the builder. | ||
| 98 | let mut usb = builder.build(); | ||
| 99 | |||
| 100 | // Run the USB device. | ||
| 101 | let usb_fut = usb.run(); | ||
| 102 | |||
| 103 | // Do stuff with the class! | ||
| 104 | let echo_fut = async { | ||
| 105 | loop { | ||
| 106 | class.wait_connection().await; | ||
| 107 | info!("Connected"); | ||
| 108 | let _ = echo(&mut class).await; | ||
| 109 | info!("Disconnected"); | ||
| 110 | } | ||
| 111 | }; | ||
| 112 | |||
| 113 | // Run everything concurrently. | ||
| 114 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 115 | join(usb_fut, echo_fut).await; | ||
| 116 | } | ||
| 117 | |||
| 118 | struct Disconnected {} | ||
| 119 | |||
| 120 | impl From<EndpointError> for Disconnected { | ||
| 121 | fn from(val: EndpointError) -> Self { | ||
| 122 | match val { | ||
| 123 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 124 | EndpointError::Disabled => Disconnected {}, | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | async fn echo<'d, T: Instance + 'd, P: UsbSupply + 'd>( | ||
| 130 | class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, | ||
| 131 | ) -> Result<(), Disconnected> { | ||
| 132 | let mut buf = [0; 64]; | ||
| 133 | loop { | ||
| 134 | let n = class.read_packet(&mut buf).await?; | ||
| 135 | let data = &buf[..n]; | ||
| 136 | info!("data: {:x}", data); | ||
| 137 | class.write_packet(data).await?; | ||
| 138 | } | ||
| 139 | } | ||
