Compare commits

...

28 Commits

Author SHA1 Message Date
0b015bd727 usb: remove msos-descriptor feature. 2023-11-08 23:21:52 +01:00
79acb560ec Pin noproto git rev. 2023-11-08 23:05:00 +01:00
94586576a0 Merge pull request #2160 from Nigecat/main
Fix typo in embassy-net docs
2023-11-07 23:56:03 +00:00
a3e200d011 Fix typo in embassy-net docs 2023-11-08 09:28:33 +11:00
3bccb67231 Merge pull request #2155 from xoviat/eth
stm32: resolve eth/v2 security bug
2023-11-07 21:42:55 +00:00
553f0158c0 stm32: resolve eth/v2 security bug
fixes #2129
2023-11-07 15:39:06 -06:00
b44a5fcaf6 Merge pull request #2159 from kalkyl/usb-bulk-cleanup
rp: Remove control handler from USB raw bulk example
2023-11-07 19:52:11 +00:00
8effff3383 rp: Remove control handler from USB raw bulk example 2023-11-07 20:45:01 +01:00
05f983c607 Merge pull request #2158 from kalkyl/usb-bulk
rp: Add USB raw bulk example
2023-11-07 19:22:55 +00:00
d44383e9a7 fmt 2023-11-07 20:19:56 +01:00
37a773c037 Use driver reexport 2023-11-07 20:17:19 +01:00
d1adc93614 rp: Add USB raw bulk example 2023-11-07 19:57:05 +01:00
78a7ee7ec4 Merge pull request #2157 from kalkyl/usb-raw
rp: Add USB raw example + msos-descriptor to examples and usb-logger
2023-11-07 10:30:52 +00:00
e3fe13e905 Add docs 2023-11-07 10:58:35 +01:00
38bfa6916f Update pio-uart example 2023-11-07 09:15:21 +01:00
50139752bc Add comments 2023-11-07 09:10:18 +01:00
db4cd73894 rp: Add USB raw example + msos descriptor to examples and usb-logger 2023-11-07 09:05:10 +01:00
8369f7614a Merge pull request #2156 from adamgreig/usb-raw-example
Update stm32 usb_raw example to use MSOS descriptors for WinUSB
2023-11-07 02:35:59 +00:00
326bc98bd2 Update stm32 usb_raw example to use MSOS descriptors for WinUSB 2023-11-07 02:34:27 +00:00
584fc358fd Merge pull request #2154 from bugadani/executor
Prepare embassy-executor 0.3.2
2023-11-06 20:53:19 +00:00
b8f9341edc Prepare embassy-executor 0.3.2 2023-11-06 21:50:40 +01:00
7ca557b917 Merge pull request #2152 from bugadani/atomic
Executor: Yeet core::sync::atomic from risc-v
2023-11-06 18:46:50 +00:00
8d8d50cc7a Yeet core::sync::atomic, remove futures-util dep 2023-11-06 17:35:02 +01:00
5948d934d3 Merge pull request #2150 from bugadani/block
Ensure TcpIo not blocking when reading into empty slice
2023-11-06 12:44:15 +00:00
746936f8cd Merge pull request #2151 from eZioPan/rcc-init-set-vos
check PLL settings before set VOS
2023-11-06 12:43:29 +00:00
8f543062aa check PLL settings before set VOS 2023-11-06 18:30:59 +08:00
15660cfc68 Ensure TcpIo not blocking when reading into empty slice 2023-11-06 09:12:16 +01:00
74f70dc7b4 Merge pull request #2149 from embassy-rs/usb-fixes3
stm32/otg: fix enumeration on non-f4 chips.
2023-11-06 03:19:50 +00:00
37 changed files with 432 additions and 69 deletions

View File

@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.3.2 - 2023-11-06
- Use `atomic-polyfill` for `riscv32`
- Removed unused dependencies (static_cell, futures-util)
## 0.3.1 - 2023-11-01 ## 0.3.1 - 2023-11-01
- Fix spurious "Found waker not created by the Embassy executor" error in recent nightlies. - Fix spurious "Found waker not created by the Embassy executor" error in recent nightlies.

View File

@ -1,6 +1,6 @@
[package] [package]
name = "embassy-executor" name = "embassy-executor"
version = "0.3.1" version = "0.3.2"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
description = "async/await executor designed for embedded usage" description = "async/await executor designed for embedded usage"
@ -57,7 +57,6 @@ defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true } log = { version = "0.4.14", optional = true }
rtos-trace = { version = "0.1.2", optional = true } rtos-trace = { version = "0.1.2", optional = true }
futures-util = { version = "0.3.17", default-features = false }
embassy-macros = { version = "0.2.1", path = "../embassy-macros" } embassy-macros = { version = "0.2.1", path = "../embassy-macros" }
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true} embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true}
atomic-polyfill = "1.0.1" atomic-polyfill = "1.0.1"

View File

@ -6,8 +6,8 @@ pub use thread::*;
#[cfg(feature = "executor-thread")] #[cfg(feature = "executor-thread")]
mod thread { mod thread {
use core::marker::PhantomData; use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, Ordering};
use atomic_polyfill::{AtomicBool, Ordering};
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
pub use embassy_macros::main_riscv as main; pub use embassy_macros::main_riscv as main;

View File

@ -15,7 +15,7 @@ embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-
embedded-hal = { version = "1.0.0-rc.1" } embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1" } embedded-hal-async = { version = "=1.0.0-rc.1" }
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] } noproto = { git="https://github.com/embassy-rs/noproto", rev = "c90f3a78d7b5642415e0a07af401320b84d8ab6f", default-features = false, features = ["derive"] }
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] } #noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
heapless = "0.7.16" heapless = "0.7.16"

View File

@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
- Avoid never resolving `TcpIo::read` when the output buffer is empty.
## 0.2.1 - 2023-10-31 ## 0.2.1 - 2023-10-31
- Re-add impl_trait_projections - Re-add impl_trait_projections

View File

@ -4,7 +4,7 @@
It builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated It builds on [`smoltcp`](https://github.com/smoltcp-rs/smoltcp). It provides a higher-level and more opinionated
API. It glues together the components provided by `smoltcp`, handling the low-level details with defaults and API. It glues together the components provided by `smoltcp`, handling the low-level details with defaults and
memory management designed to work well for embedded systems, aiiming for a more "Just Works" experience. memory management designed to work well for embedded systems, aiming for a more "Just Works" experience.
## Features ## Features

View File

@ -390,6 +390,13 @@ impl<'d> TcpIo<'d> {
// CAUTION: smoltcp semantics around EOF are different to what you'd expect // CAUTION: smoltcp semantics around EOF are different to what you'd expect
// from posix-like IO, so we have to tweak things here. // from posix-like IO, so we have to tweak things here.
self.with_mut(|s, _| match s.recv_slice(buf) { self.with_mut(|s, _| match s.recv_slice(buf) {
// Reading into empty buffer
Ok(0) if buf.is_empty() => {
// embedded_io_async::Read's contract is to not block if buf is empty. While
// this function is not a direct implementor of the trait method, we still don't
// want our future to never resolve.
Poll::Ready(Ok(0))
}
// No data ready // No data ready
Ok(0) => { Ok(0) => {
s.register_recv_waker(cx.waker()); s.register_recv_waker(cx.waker());

View File

@ -119,13 +119,11 @@ impl<'a> TDesRing<'a> {
// "Preceding reads and writes cannot be moved past subsequent writes." // "Preceding reads and writes cannot be moved past subsequent writes."
fence(Ordering::Release); fence(Ordering::Release);
self.index = self.index + 1;
if self.index == self.descriptors.len() {
self.index = 0;
}
// signal DMA it can try again. // signal DMA it can try again.
ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = 0) // See issue #2129
ETH.ethernet_dma().dmactx_dtpr().write(|w| w.0 = &td as *const _ as u32);
self.index = (self.index + 1) % self.descriptors.len();
} }
} }
@ -237,21 +235,19 @@ impl<'a> RDesRing<'a> {
/// Pop the packet previously returned by `available`. /// Pop the packet previously returned by `available`.
pub(crate) fn pop_packet(&mut self) { pub(crate) fn pop_packet(&mut self) {
let descriptor = &mut self.descriptors[self.index]; let rd = &mut self.descriptors[self.index];
assert!(descriptor.available()); assert!(rd.available());
self.descriptors[self.index].set_ready(self.buffers[self.index].0.as_mut_ptr()); rd.set_ready(self.buffers[self.index].0.as_mut_ptr());
// "Preceding reads and writes cannot be moved past subsequent writes." // "Preceding reads and writes cannot be moved past subsequent writes."
fence(Ordering::Release); fence(Ordering::Release);
// signal DMA it can try again. // signal DMA it can try again.
ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = 0); // See issue #2129
ETH.ethernet_dma().dmacrx_dtpr().write(|w| w.0 = &rd as *const _ as u32);
// Increment index. // Increment index.
self.index += 1; self.index = (self.index + 1) % self.descriptors.len();
if self.index == self.descriptors.len() {
self.index = 0
}
} }
} }

View File

@ -1,8 +1,9 @@
use crate::pac::pwr::vals::Vos;
pub use crate::pac::rcc::vals::{ pub use crate::pac::rcc::vals::{
Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp, Pllq, Pllr, Pllsrc as PllSource, Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp, Pllq, Pllr, Pllsrc as PllSource,
Ppre as APBPrescaler, Sw as Sysclk, Ppre as APBPrescaler, Sw as Sysclk,
}; };
use crate::pac::{FLASH, RCC}; use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks}; use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz; use crate::time::Hertz;
@ -100,12 +101,17 @@ impl Default for Config {
} }
pub(crate) unsafe fn init(config: Config) { pub(crate) unsafe fn init(config: Config) {
// set VOS to SCALE1, if use PLL
// TODO: check real clock speed before set VOS
if config.pll.is_some() {
PWR.cr1().modify(|w| w.set_vos(Vos::SCALE1));
}
// always enable overdrive for now. Make it configurable in the future. // always enable overdrive for now. Make it configurable in the future.
#[cfg(not(any( #[cfg(not(any(
stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f405, stm32f407, stm32f415, stm32f417 stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423, stm32f405, stm32f407, stm32f415, stm32f417
)))] )))]
{ {
use crate::pac::PWR;
PWR.cr1().modify(|w| w.set_oden(true)); PWR.cr1().modify(|w| w.set_oden(true));
while !PWR.csr1().read().odrdy() {} while !PWR.csr1().read().odrdy() {}
@ -113,14 +119,6 @@ pub(crate) unsafe fn init(config: Config) {
while !PWR.csr1().read().odswrdy() {} while !PWR.csr1().read().odswrdy() {}
} }
#[cfg(any(stm32f401, stm32f410, stm32f411, stm32f412, stm32f413, stm32f423))]
{
use crate::pac::pwr::vals::Vos;
use crate::pac::PWR;
PWR.cr1().modify(|w| w.set_vos(Vos::SCALE1));
}
// Configure HSI // Configure HSI
let hsi = match config.hsi { let hsi = match config.hsi {
false => { false => {

View File

@ -19,6 +19,7 @@ pub struct LoggerState<'d> {
device_descriptor: [u8; 32], device_descriptor: [u8; 32],
config_descriptor: [u8; 128], config_descriptor: [u8; 128],
bos_descriptor: [u8; 16], bos_descriptor: [u8; 16],
msos_descriptor: [u8; 256],
control_buf: [u8; 64], control_buf: [u8; 64],
} }
@ -30,6 +31,7 @@ impl<'d> LoggerState<'d> {
device_descriptor: [0; 32], device_descriptor: [0; 32],
config_descriptor: [0; 128], config_descriptor: [0; 128],
bos_descriptor: [0; 16], bos_descriptor: [0; 16],
msos_descriptor: [0; 256],
control_buf: [0; 64], control_buf: [0; 64],
} }
} }
@ -73,6 +75,7 @@ impl<const N: usize> UsbLogger<N> {
&mut state.device_descriptor, &mut state.device_descriptor,
&mut state.config_descriptor, &mut state.config_descriptor,
&mut state.bos_descriptor, &mut state.bos_descriptor,
&mut state.msos_descriptor,
&mut state.control_buf, &mut state.control_buf,
); );

View File

@ -13,7 +13,6 @@ target = "thumbv7em-none-eabi"
[features] [features]
defmt = ["dep:defmt", "embassy-usb-driver/defmt"] defmt = ["dep:defmt", "embassy-usb-driver/defmt"]
usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"]
msos-descriptor = []
default = ["usbd-hid"] default = ["usbd-hid"]
# BEGIN AUTOGENERATED CONFIG FEATURES # BEGIN AUTOGENERATED CONFIG FEATURES

View File

@ -3,7 +3,6 @@ use heapless::Vec;
use crate::config::MAX_HANDLER_COUNT; use crate::config::MAX_HANDLER_COUNT;
use crate::descriptor::{BosWriter, DescriptorWriter}; use crate::descriptor::{BosWriter, DescriptorWriter};
use crate::driver::{Driver, Endpoint, EndpointType}; use crate::driver::{Driver, Endpoint, EndpointType};
#[cfg(feature = "msos-descriptor")]
use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};
use crate::types::{InterfaceNumber, StringIndex}; use crate::types::{InterfaceNumber, StringIndex};
use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
@ -133,7 +132,6 @@ pub struct Builder<'d, D: Driver<'d>> {
config_descriptor: DescriptorWriter<'d>, config_descriptor: DescriptorWriter<'d>,
bos_descriptor: BosWriter<'d>, bos_descriptor: BosWriter<'d>,
#[cfg(feature = "msos-descriptor")]
msos_descriptor: MsOsDescriptorWriter<'d>, msos_descriptor: MsOsDescriptorWriter<'d>,
} }
@ -149,7 +147,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
device_descriptor_buf: &'d mut [u8], device_descriptor_buf: &'d mut [u8],
config_descriptor_buf: &'d mut [u8], config_descriptor_buf: &'d mut [u8],
bos_descriptor_buf: &'d mut [u8], bos_descriptor_buf: &'d mut [u8],
#[cfg(feature = "msos-descriptor")] msos_descriptor_buf: &'d mut [u8], msos_descriptor_buf: &'d mut [u8],
control_buf: &'d mut [u8], control_buf: &'d mut [u8],
) -> Self { ) -> Self {
// Magic values specified in USB-IF ECN on IADs. // Magic values specified in USB-IF ECN on IADs.
@ -189,14 +187,12 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
config_descriptor, config_descriptor,
bos_descriptor, bos_descriptor,
#[cfg(feature = "msos-descriptor")]
msos_descriptor: MsOsDescriptorWriter::new(msos_descriptor_buf), msos_descriptor: MsOsDescriptorWriter::new(msos_descriptor_buf),
} }
} }
/// Creates the [`UsbDevice`] instance with the configuration in this builder. /// Creates the [`UsbDevice`] instance with the configuration in this builder.
pub fn build(mut self) -> UsbDevice<'d, D> { pub fn build(mut self) -> UsbDevice<'d, D> {
#[cfg(feature = "msos-descriptor")]
let msos_descriptor = self.msos_descriptor.build(&mut self.bos_descriptor); let msos_descriptor = self.msos_descriptor.build(&mut self.bos_descriptor);
self.config_descriptor.end_configuration(); self.config_descriptor.end_configuration();
@ -206,7 +202,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
info!("USB: device_descriptor used: {}", self.device_descriptor.position()); info!("USB: device_descriptor used: {}", self.device_descriptor.position());
info!("USB: config_descriptor used: {}", self.config_descriptor.position()); info!("USB: config_descriptor used: {}", self.config_descriptor.position());
info!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position()); info!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position());
#[cfg(feature = "msos-descriptor")]
info!("USB: msos_descriptor used: {}", msos_descriptor.len()); info!("USB: msos_descriptor used: {}", msos_descriptor.len());
info!("USB: control_buf size: {}", self.control_buf.len()); info!("USB: control_buf size: {}", self.control_buf.len());
@ -217,10 +212,9 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
self.device_descriptor.into_buf(), self.device_descriptor.into_buf(),
self.config_descriptor.into_buf(), self.config_descriptor.into_buf(),
self.bos_descriptor.writer.into_buf(), self.bos_descriptor.writer.into_buf(),
msos_descriptor,
self.interfaces, self.interfaces,
self.control_buf, self.control_buf,
#[cfg(feature = "msos-descriptor")]
msos_descriptor,
) )
} }
@ -251,7 +245,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
builder: self, builder: self,
iface_count_index, iface_count_index,
#[cfg(feature = "msos-descriptor")]
first_interface, first_interface,
} }
} }
@ -275,7 +268,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
StringIndex::new(index) StringIndex::new(index)
} }
#[cfg(feature = "msos-descriptor")]
/// Add an MS OS 2.0 Descriptor Set. /// Add an MS OS 2.0 Descriptor Set.
/// ///
/// Panics if called more than once. /// Panics if called more than once.
@ -283,13 +275,11 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
self.msos_descriptor.header(windows_version, vendor_code); self.msos_descriptor.header(windows_version, vendor_code);
} }
#[cfg(feature = "msos-descriptor")]
/// Add an MS OS 2.0 Device Level Feature Descriptor. /// Add an MS OS 2.0 Device Level Feature Descriptor.
pub fn msos_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) { pub fn msos_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) {
self.msos_descriptor.device_feature(desc); self.msos_descriptor.device_feature(desc);
} }
#[cfg(feature = "msos-descriptor")]
/// Gets the underlying [`MsOsDescriptorWriter`] to allow adding subsets and features for classes that /// Gets the underlying [`MsOsDescriptorWriter`] to allow adding subsets and features for classes that
/// do not add their own. /// do not add their own.
pub fn msos_writer(&mut self) -> &mut MsOsDescriptorWriter<'d> { pub fn msos_writer(&mut self) -> &mut MsOsDescriptorWriter<'d> {
@ -306,13 +296,11 @@ pub struct FunctionBuilder<'a, 'd, D: Driver<'d>> {
builder: &'a mut Builder<'d, D>, builder: &'a mut Builder<'d, D>,
iface_count_index: Option<usize>, iface_count_index: Option<usize>,
#[cfg(feature = "msos-descriptor")]
first_interface: InterfaceNumber, first_interface: InterfaceNumber,
} }
impl<'a, 'd, D: Driver<'d>> Drop for FunctionBuilder<'a, 'd, D> { impl<'a, 'd, D: Driver<'d>> Drop for FunctionBuilder<'a, 'd, D> {
fn drop(&mut self) { fn drop(&mut self) {
#[cfg(feature = "msos-descriptor")]
self.builder.msos_descriptor.end_function(); self.builder.msos_descriptor.end_function();
} }
} }
@ -344,7 +332,6 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
} }
} }
#[cfg(feature = "msos-descriptor")]
/// Add an MS OS 2.0 Function Level Feature Descriptor. /// Add an MS OS 2.0 Function Level Feature Descriptor.
pub fn msos_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) { pub fn msos_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) {
if !self.builder.msos_descriptor.is_in_config_subset() { if !self.builder.msos_descriptor.is_in_config_subset() {
@ -355,7 +342,6 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
self.builder.msos_descriptor.function(self.first_interface); self.builder.msos_descriptor.function(self.first_interface);
} }
#[cfg(feature = "msos-descriptor")]
self.builder.msos_descriptor.function_feature(desc); self.builder.msos_descriptor.function_feature(desc);
} }
} }

View File

@ -175,10 +175,7 @@ pub struct UsbBufferReport {
/// Number of bos descriptor bytes used /// Number of bos descriptor bytes used
pub bos_descriptor_used: usize, pub bos_descriptor_used: usize,
/// Number of msos descriptor bytes used /// Number of msos descriptor bytes used
/// pub msos_descriptor_used: usize,
/// Will be `None` if the "msos-descriptor" feature is not active.
/// Otherwise will return Some(bytes).
pub msos_descriptor_used: Option<usize>,
/// Size of the control buffer /// Size of the control buffer
pub control_buffer_size: usize, pub control_buffer_size: usize,
} }
@ -197,6 +194,7 @@ struct Inner<'d, D: Driver<'d>> {
device_descriptor: &'d [u8], device_descriptor: &'d [u8],
config_descriptor: &'d [u8], config_descriptor: &'d [u8],
bos_descriptor: &'d [u8], bos_descriptor: &'d [u8],
msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
device_state: UsbDeviceState, device_state: UsbDeviceState,
suspended: bool, suspended: bool,
@ -212,9 +210,6 @@ struct Inner<'d, D: Driver<'d>> {
interfaces: Vec<Interface, MAX_INTERFACE_COUNT>, interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,
handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>, handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,
#[cfg(feature = "msos-descriptor")]
msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
} }
impl<'d, D: Driver<'d>> UsbDevice<'d, D> { impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
@ -225,9 +220,9 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
device_descriptor: &'d [u8], device_descriptor: &'d [u8],
config_descriptor: &'d [u8], config_descriptor: &'d [u8],
bos_descriptor: &'d [u8], bos_descriptor: &'d [u8],
msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
interfaces: Vec<Interface, MAX_INTERFACE_COUNT>, interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,
control_buf: &'d mut [u8], control_buf: &'d mut [u8],
#[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
) -> UsbDevice<'d, D> { ) -> UsbDevice<'d, D> {
// Start the USB bus. // Start the USB bus.
// This prevent further allocation by consuming the driver. // This prevent further allocation by consuming the driver.
@ -242,6 +237,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
device_descriptor, device_descriptor,
config_descriptor, config_descriptor,
bos_descriptor, bos_descriptor,
msos_descriptor,
device_state: UsbDeviceState::Unpowered, device_state: UsbDeviceState::Unpowered,
suspended: false, suspended: false,
@ -251,8 +247,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
set_address_pending: false, set_address_pending: false,
interfaces, interfaces,
handlers, handlers,
#[cfg(feature = "msos-descriptor")]
msos_descriptor,
}, },
} }
} }
@ -261,16 +255,11 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
/// ///
/// Useful for tuning buffer sizes for actual usage /// Useful for tuning buffer sizes for actual usage
pub fn buffer_usage(&self) -> UsbBufferReport { pub fn buffer_usage(&self) -> UsbBufferReport {
#[cfg(not(feature = "msos-descriptor"))]
let mdu = None;
#[cfg(feature = "msos-descriptor")]
let mdu = Some(self.inner.msos_descriptor.len());
UsbBufferReport { UsbBufferReport {
device_descriptor_used: self.inner.device_descriptor.len(), device_descriptor_used: self.inner.device_descriptor.len(),
config_descriptor_used: self.inner.config_descriptor.len(), config_descriptor_used: self.inner.config_descriptor.len(),
bos_descriptor_used: self.inner.bos_descriptor.len(), bos_descriptor_used: self.inner.bos_descriptor.len(),
msos_descriptor_used: mdu, msos_descriptor_used: self.inner.msos_descriptor.len(),
control_buffer_size: self.control_buf.len(), control_buffer_size: self.control_buf.len(),
} }
} }
@ -684,7 +673,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
} }
_ => InResponse::Rejected, _ => InResponse::Rejected,
}, },
#[cfg(feature = "msos-descriptor")]
(RequestType::Vendor, Recipient::Device) => { (RequestType::Vendor, Recipient::Device) => {
if !self.msos_descriptor.is_empty() if !self.msos_descriptor.is_empty()
&& req.request == self.msos_descriptor.vendor_code() && req.request == self.msos_descriptor.vendor_code()

View File

@ -1,5 +1,3 @@
#![cfg(feature = "msos-descriptor")]
//! Microsoft OS Descriptors //! Microsoft OS Descriptors
//! //!
//! <https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification> //! <https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification>

View File

@ -34,7 +34,7 @@ embassy-executor = { version = "0.3.1", path = "../../embassy-executor", feature
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "msos-descriptor",], optional = true } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true }
embedded-io = { version = "0.6.0", features = ["defmt-03"] } embedded-io = { version = "0.6.0", features = ["defmt-03"] }
embedded-io-async = { version = "0.6.0", optional = true, features = ["defmt-03"] } embedded-io-async = { version = "0.6.0", optional = true, features = ["defmt-03"] }
embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true } embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["time", "defmt"], optional = true }

View File

@ -75,6 +75,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -71,6 +71,7 @@ async fn main(spawner: Spawner) {
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut [], // no msos descriptors
&mut make_static!([0; 128])[..], &mut make_static!([0; 128])[..],
); );

View File

@ -41,7 +41,7 @@ async fn main(_spawner: Spawner) {
let mut config_descriptor = [0; 256]; let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256]; let mut bos_descriptor = [0; 256];
// You can also add a Microsoft OS descriptor. // You can also add a Microsoft OS descriptor.
// let mut msos_descriptor = [0; 256]; let mut msos_descriptor = [0; 256];
let mut control_buf = [0; 64]; let mut control_buf = [0; 64];
let request_handler = MyRequestHandler {}; let request_handler = MyRequestHandler {};
let mut device_handler = MyDeviceHandler::new(); let mut device_handler = MyDeviceHandler::new();
@ -54,7 +54,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
// &mut msos_descriptor, &mut msos_descriptor,
&mut control_buf, &mut control_buf,
); );

View File

@ -58,6 +58,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -0,0 +1,199 @@
//! Example of using USB without a pre-defined class, but instead responding to
//! raw USB control requests.
//!
//! The host computer can either:
//! * send a command, with a 16-bit request ID, a 16-bit value, and an optional data buffer
//! * request some data, with a 16-bit request ID, a 16-bit value, and a length of data to receive
//!
//! For higher throughput data, you can add some bulk endpoints after creating the alternate,
//! but for low rate command/response, plain control transfers can be very simple and effective.
//!
//! Example code to send/receive data using `nusb`:
//!
//! ```ignore
//! use futures_lite::future::block_on;
//! use nusb::transfer::{ControlIn, ControlOut, ControlType, Recipient};
//!
//! fn main() {
//! let di = nusb::list_devices()
//! .unwrap()
//! .find(|d| d.vendor_id() == 0xc0de && d.product_id() == 0xcafe)
//! .expect("no device found");
//! let device = di.open().expect("error opening device");
//! let interface = device.claim_interface(0).expect("error claiming interface");
//!
//! // Send "hello world" to device
//! let result = block_on(interface.control_out(ControlOut {
//! control_type: ControlType::Vendor,
//! recipient: Recipient::Interface,
//! request: 100,
//! value: 200,
//! index: 0,
//! data: b"hello world",
//! }));
//! println!("{result:?}");
//!
//! // Receive "hello" from device
//! let result = block_on(interface.control_in(ControlIn {
//! control_type: ControlType::Vendor,
//! recipient: Recipient::Interface,
//! request: 101,
//! value: 201,
//! index: 0,
//! length: 5,
//! }));
//! println!("{result:?}");
//! }
//! ```
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::info;
use embassy_executor::Spawner;
use embassy_rp::bind_interrupts;
use embassy_rp::peripherals::USB;
use embassy_rp::usb::{Driver, InterruptHandler};
use embassy_usb::control::{InResponse, OutResponse, Recipient, Request, RequestType};
use embassy_usb::msos::{self, windows_version};
use embassy_usb::types::InterfaceNumber;
use embassy_usb::{Builder, Config, Handler};
use {defmt_rtt as _, panic_probe as _};
// This is a randomly generated GUID to allow clients on Windows to find our device
const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"];
bind_interrupts!(struct Irqs {
USBCTRL_IRQ => InterruptHandler<USB>;
});
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
info!("Hello there!");
let p = embassy_rp::init(Default::default());
// Create the driver, from the HAL.
let driver = Driver::new(p.USB, Irqs);
// Create embassy-usb Config
let mut config = Config::new(0xc0de, 0xcafe);
config.manufacturer = Some("Embassy");
config.product = Some("USB raw example");
config.serial_number = Some("12345678");
config.max_power = 100;
config.max_packet_size_0 = 64;
// // Required for windows compatibility.
// // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
config.device_class = 0xEF;
config.device_sub_class = 0x02;
config.device_protocol = 0x01;
config.composite_with_iads = true;
// Create embassy-usb DeviceBuilder using the driver and config.
// It needs some buffers for building the descriptors.
let mut device_descriptor = [0; 256];
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256];
let mut msos_descriptor = [0; 256];
let mut control_buf = [0; 64];
let mut handler = ControlHandler {
if_num: InterfaceNumber(0),
};
let mut builder = Builder::new(
driver,
config,
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut msos_descriptor,
&mut control_buf,
);
// Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.
// We tell Windows that this entire device is compatible with the "WINUSB" feature,
// which causes it to use the built-in WinUSB driver automatically, which in turn
// can be used by libusb/rusb software without needing a custom driver or INF file.
// In principle you might want to call msos_feature() just on a specific function,
// if your device also has other functions that still use standard class drivers.
builder.msos_descriptor(windows_version::WIN8_1, 0);
builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
"DeviceInterfaceGUIDs",
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
));
// Add a vendor-specific function (class 0xFF), and corresponding interface,
// that uses our custom handler.
let mut function = builder.function(0xFF, 0, 0);
let mut interface = function.interface();
let _alt = interface.alt_setting(0xFF, 0, 0, None);
handler.if_num = interface.interface_number();
drop(function);
builder.handler(&mut handler);
// Build the builder.
let mut usb = builder.build();
// Run the USB device.
usb.run().await;
}
/// Handle CONTROL endpoint requests and responses. For many simple requests and responses
/// you can get away with only using the control endpoint.
struct ControlHandler {
if_num: InterfaceNumber,
}
impl Handler for ControlHandler {
/// Respond to HostToDevice control messages, where the host sends us a command and
/// optionally some data, and we can only acknowledge or reject it.
fn control_out<'a>(&'a mut self, req: Request, buf: &'a [u8]) -> Option<OutResponse> {
// Log the request before filtering to help with debugging.
info!("Got control_out, request={}, buf={:a}", req, buf);
// Only handle Vendor request types to an Interface.
if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface {
return None;
}
// Ignore requests to other interfaces.
if req.index != self.if_num.0 as u16 {
return None;
}
// Accept request 100, value 200, reject others.
if req.request == 100 && req.value == 200 {
Some(OutResponse::Accepted)
} else {
Some(OutResponse::Rejected)
}
}
/// Respond to DeviceToHost control messages, where the host requests some data from us.
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option<InResponse<'a>> {
info!("Got control_in, request={}", req);
// Only handle Vendor request types to an Interface.
if req.request_type != RequestType::Vendor || req.recipient != Recipient::Interface {
return None;
}
// Ignore requests to other interfaces.
if req.index != self.if_num.0 as u16 {
return None;
}
// Respond "hello" to request 101, value 201, when asked for 5 bytes, otherwise reject.
if req.request == 101 && req.value == 201 && req.length == 5 {
buf[..5].copy_from_slice(b"hello");
Some(InResponse::Accepted(&buf[..5]))
} else {
Some(InResponse::Rejected)
}
}
}

View File

@ -0,0 +1,142 @@
//! Example of using USB without a pre-defined class, but instead using raw USB bulk transfers.
//!
//! Example code to send/receive data using `nusb`:
//!
//! ```ignore
//! use futures_lite::future::block_on;
//! use nusb::transfer::RequestBuffer;
//!
//! const BULK_OUT_EP: u8 = 0x01;
//! const BULK_IN_EP: u8 = 0x81;
//!
//! fn main() {
//! let di = nusb::list_devices()
//! .unwrap()
//! .find(|d| d.vendor_id() == 0xc0de && d.product_id() == 0xcafe)
//! .expect("no device found");
//! let device = di.open().expect("error opening device");
//! let interface = device.claim_interface(0).expect("error claiming interface");
//!
//! let result = block_on(interface.bulk_out(BULK_OUT_EP, b"hello world".into()));
//! println!("{result:?}");
//! let result = block_on(interface.bulk_in(BULK_IN_EP, RequestBuffer::new(64)));
//! println!("{result:?}");
//! }
//! ```
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::info;
use embassy_executor::Spawner;
use embassy_futures::join::join;
use embassy_rp::bind_interrupts;
use embassy_rp::peripherals::USB;
use embassy_rp::usb::{Driver, InterruptHandler};
use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut};
use embassy_usb::msos::{self, windows_version};
use embassy_usb::{Builder, Config};
use {defmt_rtt as _, panic_probe as _};
// This is a randomly generated GUID to allow clients on Windows to find our device
const DEVICE_INTERFACE_GUIDS: &[&str] = &["{AFB9A6FB-30BA-44BC-9232-806CFC875321}"];
bind_interrupts!(struct Irqs {
USBCTRL_IRQ => InterruptHandler<USB>;
});
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
info!("Hello there!");
let p = embassy_rp::init(Default::default());
// Create the driver, from the HAL.
let driver = Driver::new(p.USB, Irqs);
// Create embassy-usb Config
let mut config = Config::new(0xc0de, 0xcafe);
config.manufacturer = Some("Embassy");
config.product = Some("USB raw example");
config.serial_number = Some("12345678");
config.max_power = 100;
config.max_packet_size_0 = 64;
// // Required for windows compatibility.
// // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
config.device_class = 0xEF;
config.device_sub_class = 0x02;
config.device_protocol = 0x01;
config.composite_with_iads = true;
// Create embassy-usb DeviceBuilder using the driver and config.
// It needs some buffers for building the descriptors.
let mut device_descriptor = [0; 256];
let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256];
let mut msos_descriptor = [0; 256];
let mut control_buf = [0; 64];
let mut builder = Builder::new(
driver,
config,
&mut device_descriptor,
&mut config_descriptor,
&mut bos_descriptor,
&mut msos_descriptor,
&mut control_buf,
);
// Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.
// We tell Windows that this entire device is compatible with the "WINUSB" feature,
// which causes it to use the built-in WinUSB driver automatically, which in turn
// can be used by libusb/rusb software without needing a custom driver or INF file.
// In principle you might want to call msos_feature() just on a specific function,
// if your device also has other functions that still use standard class drivers.
builder.msos_descriptor(windows_version::WIN8_1, 0);
builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
"DeviceInterfaceGUIDs",
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
));
// Add a vendor-specific function (class 0xFF), and corresponding interface,
// that uses our custom handler.
let mut function = builder.function(0xFF, 0, 0);
let mut interface = function.interface();
let mut alt = interface.alt_setting(0xFF, 0, 0, None);
let mut read_ep = alt.endpoint_bulk_out(64);
let mut write_ep = alt.endpoint_bulk_in(64);
drop(function);
// Build the builder.
let mut usb = builder.build();
// Run the USB device.
let usb_fut = usb.run();
// Do stuff with the class!
let echo_fut = async {
loop {
read_ep.wait_enabled().await;
info!("Connected");
loop {
let mut data = [0; 64];
match read_ep.read(&mut data).await {
Ok(n) => {
info!("Got bulk: {:a}", data[..n]);
// Echo back to the host:
write_ep.write(&data[..n]).await.ok();
}
Err(_) => break,
}
}
info!("Disconnected");
}
};
// Run everything concurrently.
// If we had made everything `'static` above instead, we could do this using separate tasks instead.
join(usb_fut, echo_fut).await;
}

View File

@ -60,6 +60,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -60,6 +60,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -57,6 +57,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["
embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] } embassy-sync = { version = "0.4.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-executor = { version = "0.3.1", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } embassy-time = { version = "0.1.5", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt" ] }
embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] } embassy-net = { version = "0.2.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "nightly"] }
defmt = "0.3" defmt = "0.3"

View File

@ -94,6 +94,7 @@ async fn main(spawner: Spawner) {
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut [], // no msos descriptors
&mut make_static!([0; 128])[..], &mut make_static!([0; 128])[..],
); );

View File

@ -56,10 +56,16 @@ use embassy_stm32::time::Hertz;
use embassy_stm32::usb_otg::Driver; use embassy_stm32::usb_otg::Driver;
use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config};
use embassy_usb::control::{InResponse, OutResponse, Recipient, Request, RequestType}; use embassy_usb::control::{InResponse, OutResponse, Recipient, Request, RequestType};
use embassy_usb::msos::{self, windows_version};
use embassy_usb::types::InterfaceNumber; use embassy_usb::types::InterfaceNumber;
use embassy_usb::{Builder, Handler}; use embassy_usb::{Builder, Handler};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
// Randomly generated UUID because Windows requires you provide one to use WinUSB.
// In principle WinUSB-using software could find this device (or a specific interface
// on it) by its GUID instead of using the VID/PID, but in practice that seems unhelpful.
const DEVICE_INTERFACE_GUIDS: &[&str] = &["{DAC2087C-63FA-458D-A55D-827C0762DEC7}"];
bind_interrupts!(struct Irqs { bind_interrupts!(struct Irqs {
OTG_FS => usb_otg::InterruptHandler<peripherals::USB_OTG_FS>; OTG_FS => usb_otg::InterruptHandler<peripherals::USB_OTG_FS>;
}); });
@ -114,6 +120,7 @@ async fn main(_spawner: Spawner) {
let mut device_descriptor = [0; 256]; let mut device_descriptor = [0; 256];
let mut config_descriptor = [0; 256]; let mut config_descriptor = [0; 256];
let mut bos_descriptor = [0; 256]; let mut bos_descriptor = [0; 256];
let mut msos_descriptor = [0; 256];
let mut control_buf = [0; 64]; let mut control_buf = [0; 64];
let mut handler = ControlHandler { let mut handler = ControlHandler {
@ -126,9 +133,23 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut msos_descriptor,
&mut control_buf, &mut control_buf,
); );
// Add the Microsoft OS Descriptor (MSOS/MOD) descriptor.
// We tell Windows that this entire device is compatible with the "WINUSB" feature,
// which causes it to use the built-in WinUSB driver automatically, which in turn
// can be used by libusb/rusb software without needing a custom driver or INF file.
// In principle you might want to call msos_feature() just on a specific function,
// if your device also has other functions that still use standard class drivers.
builder.msos_descriptor(windows_version::WIN8_1, 0);
builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new(
"DeviceInterfaceGUIDs",
msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS),
));
// Add a vendor-specific function (class 0xFF), and corresponding interface, // Add a vendor-specific function (class 0xFF), and corresponding interface,
// that uses our custom handler. // that uses our custom handler.
let mut function = builder.function(0xFF, 0, 0); let mut function = builder.function(0xFF, 0, 0);

View File

@ -77,6 +77,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -77,6 +77,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -75,6 +75,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -82,6 +82,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -78,6 +78,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -72,6 +72,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -82,6 +82,7 @@ async fn main(spawner: Spawner) {
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut make_static!([0; 256])[..], &mut make_static!([0; 256])[..],
&mut [], // no msos descriptors
&mut make_static!([0; 128])[..], &mut make_static!([0; 128])[..],
); );

View File

@ -62,6 +62,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -57,6 +57,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );

View File

@ -67,6 +67,7 @@ async fn main(_spawner: Spawner) {
&mut device_descriptor, &mut device_descriptor,
&mut config_descriptor, &mut config_descriptor,
&mut bos_descriptor, &mut bos_descriptor,
&mut [], // no msos descriptors
&mut control_buf, &mut control_buf,
); );