diff --git a/crates/wdk-build/src/bindgen.rs b/crates/wdk-build/src/bindgen.rs
index 071f7e6a9..200cd807b 100644
--- a/crates/wdk-build/src/bindgen.rs
+++ b/crates/wdk-build/src/bindgen.rs
@@ -71,6 +71,23 @@ impl BuilderExt for Builder {
.blocklist_item("ExAllocatePoolWithQuotaTag") // Deprecated
.blocklist_item("ExAllocatePoolWithTagPriority") // Deprecated
.blocklist_item("ExAllocatePool") // Deprecated
+ .blocklist_item("USBD_CalculateUsbBandwidth") // Deprecated
+ .blocklist_item("USBD_CreateConfigurationRequest") // Deprecated
+ .blocklist_item("USBD_Debug_LogEntry") // Deprecated
+ .blocklist_item("USBD_GetUSBDIVersion") // Deprecated
+ .blocklist_item("USBD_ParseConfigurationDescriptor") // Deprecated
+ .blocklist_item("USBD_QueryBusTime") // Deprecated
+ .blocklist_item("USBD_RegisterHcFilter") // Deprecated
+ .blocklist_item("IOCTL_USB_DIAG_IGNORE_HUBS_OFF") // Deprecated/Internal-Use-Only
+ .blocklist_item("IOCTL_USB_DIAG_IGNORE_HUBS_ON") // Deprecated/Internal-Use-Only
+ .blocklist_item("IOCTL_USB_DIAGNOSTIC_MODE_OFF") // Deprecated/Internal-Use-Only
+ .blocklist_item("IOCTL_USB_DIAGNOSTIC_MODE_ON") // Deprecated/Internal-Use-Only
+ .blocklist_item("IOCTL_USB_GET_HUB_CAPABILITIES") // Deprecated/Internal-Use-Only
+ .blocklist_item("IOCTL_USB_HCD_DISABLE_PORT") // Deprecated/Internal-Use-Only
+ .blocklist_item("IOCTL_USB_HCD_ENABLE_PORT") // Deprecated/Internal-Use-Only
+ .blocklist_item("IOCTL_USB_HCD_GET_STATS_1") // Deprecated/Internal-Use-Only
+ .blocklist_item("IOCTL_USB_HCD_GET_STATS_2") // Deprecated/Internal-Use-Only
+ .blocklist_item("IOCTL_USB_RESET_HUB") // Deprecated/Internal-Use-Only
.opaque_type("_KGDTENTRY64") // No definition in WDK
.opaque_type("_KIDTENTRY64") // No definition in WDK
// FIXME: bitfield generated with non-1byte alignment in _MCG_CAP
diff --git a/crates/wdk-build/src/lib.rs b/crates/wdk-build/src/lib.rs
index 56b854368..55c4b74cc 100644
--- a/crates/wdk-build/src/lib.rs
+++ b/crates/wdk-build/src/lib.rs
@@ -207,6 +207,8 @@ pub enum ApiSubset {
Spb,
/// API subset for Storage drivers:
Storage,
+ /// API subset for USB (Universal Serial Bus) drivers:
+ Usb,
}
impl Default for Config {
@@ -407,6 +409,21 @@ impl Config {
.canonicalize()?
.strip_extended_length_path_prefix()?,
);
+
+ // `ufxclient.h` relies on `ufxbase.h` being on the headers search path. The WDK
+ // normally does not automatically include this search path, but it is required
+ // here so that the headers can be processed successfully.
+ let ufx_include_path = km_or_um_include_path.join("ufx/1.1");
+ if !ufx_include_path.is_dir() {
+ return Err(ConfigError::DirectoryNotFound {
+ directory: ufx_include_path.to_string_lossy().into(),
+ });
+ }
+ include_paths.push(
+ ufx_include_path
+ .canonicalize()?
+ .strip_extended_length_path_prefix()?,
+ );
}
DriverConfig::Umdf(umdf_config) => {
let umdf_include_path = include_directory.join(format!(
@@ -627,8 +644,8 @@ impl Config {
"--warn-=no-ignored-attributes",
"--warn-=no-ignored-pragma-intrinsic",
"--warn-=no-visibility",
- "--warn-=no-microsoft-anon-tag",
- "--warn-=no-microsoft-enum-forward-reference",
+ "--warn-=no-switch",
+ "--warn-=no-comment",
// Don't warn for deprecated declarations. Deprecated items should be explicitly
// blocklisted (i.e. by the bindgen invocation). Any non-blocklisted function
// definitions will trigger a -WDeprecated warning
@@ -637,6 +654,8 @@ impl Config {
// `_variable` are separate tokens already, and don't need `##` to concatenate
// them)
"--warn-=no-invalid-token-paste",
+ // Windows SDK & DDK headers rely on Microsoft extensions to C/C++
+ "--warn-=no-microsoft",
]
.into_iter()
.map(std::string::ToString::to_string)
@@ -649,99 +668,200 @@ impl Config {
/// determine which headers to yield
pub fn headers(&self, api_subset: ApiSubset) -> impl Iterator- {
match api_subset {
- ApiSubset::Base => match &self.driver_config {
- DriverConfig::Wdm | DriverConfig::Kmdf(_) => {
- vec!["ntifs.h", "ntddk.h", "ntstrsafe.h"]
- }
- DriverConfig::Umdf(_) => {
- vec!["windows.h"]
- }
- },
- ApiSubset::Wdf => {
- if let DriverConfig::Kmdf(_) | DriverConfig::Umdf(_) = self.driver_config {
- vec!["wdf.h"]
- } else {
- vec![]
- }
+ ApiSubset::Base => self.base_headers(),
+ ApiSubset::Wdf => self.wdf_headers(),
+ ApiSubset::Gpio => self.gpio_headers(),
+ ApiSubset::Hid => self.hid_headers(),
+ ApiSubset::ParallelPorts => self.parallel_ports_headers(),
+ ApiSubset::Spb => self.spb_headers(),
+ ApiSubset::Storage => self.storage_headers(),
+ ApiSubset::Usb => self.usb_headers(),
+ }
+ .into_iter()
+ .map(str::to_string)
+ }
+
+ fn base_headers(&self) -> Vec<&'static str> {
+ match &self.driver_config {
+ DriverConfig::Wdm | DriverConfig::Kmdf(_) => {
+ vec!["ntifs.h", "ntddk.h", "ntstrsafe.h"]
}
- ApiSubset::Gpio => {
- let mut gpio_headers = vec!["gpio.h"];
+ DriverConfig::Umdf(_) => {
+ vec!["windows.h"]
+ }
+ }
+ }
- if let DriverConfig::Kmdf(_) = self.driver_config {
- gpio_headers.extend(["gpioclx.h"]);
- }
+ fn wdf_headers(&self) -> Vec<&'static str> {
+ if matches!(
+ self.driver_config,
+ DriverConfig::Kmdf(_) | DriverConfig::Umdf(_)
+ ) {
+ vec!["wdf.h"]
+ } else {
+ vec![]
+ }
+ }
- gpio_headers
- }
- ApiSubset::Hid => {
- let mut hid_headers = vec!["hidclass.h", "hidsdi.h", "hidpi.h", "vhf.h"];
+ fn gpio_headers(&self) -> Vec<&'static str> {
+ let mut headers = vec!["gpio.h"];
+ if matches!(self.driver_config, DriverConfig::Kmdf(_)) {
+ headers.extend(["gpioclx.h"]);
+ }
+ headers
+ }
- if let DriverConfig::Wdm | DriverConfig::Kmdf(_) = self.driver_config {
- hid_headers.extend(["hidpddi.h", "hidport.h", "kbdmou.h", "ntdd8042.h"]);
- }
+ fn hid_headers(&self) -> Vec<&'static str> {
+ let mut headers = vec!["hidclass.h", "hidsdi.h", "hidpi.h", "vhf.h"];
+ if matches!(
+ self.driver_config,
+ DriverConfig::Wdm | DriverConfig::Kmdf(_)
+ ) {
+ headers.extend(["hidpddi.h", "hidport.h", "kbdmou.h", "ntdd8042.h"]);
+ }
- if let DriverConfig::Kmdf(_) = self.driver_config {
- hid_headers.extend(["HidSpiCx/1.0/hidspicx.h"]);
- }
+ if matches!(self.driver_config, DriverConfig::Kmdf(_)) {
+ headers.extend(["HidSpiCx/1.0/hidspicx.h"]);
+ }
+ headers
+ }
- hid_headers
- }
- ApiSubset::ParallelPorts => {
- let mut parallel_ports_headers = vec!["ntddpar.h", "ntddser.h"];
+ fn parallel_ports_headers(&self) -> Vec<&'static str> {
+ let mut headers = vec!["ntddpar.h", "ntddser.h"];
+ if matches!(
+ self.driver_config,
+ DriverConfig::Wdm | DriverConfig::Kmdf(_)
+ ) {
+ headers.extend(["parallel.h"]);
+ }
+ headers
+ }
- if let DriverConfig::Wdm | DriverConfig::Kmdf(_) = self.driver_config {
- parallel_ports_headers.extend(["parallel.h"]);
- }
+ fn spb_headers(&self) -> Vec<&'static str> {
+ let mut headers = vec!["spb.h", "reshub.h"];
+ if matches!(
+ self.driver_config,
+ DriverConfig::Wdm | DriverConfig::Kmdf(_)
+ ) {
+ headers.extend(["pwmutil.h"]);
+ }
+ if matches!(self.driver_config, DriverConfig::Kmdf(_)) {
+ headers.extend(["spb/1.1/spbcx.h"]);
+ }
+ headers
+ }
- parallel_ports_headers
- }
- ApiSubset::Spb => {
- let mut spb_headers = vec!["spb.h", "reshub.h"];
+ fn storage_headers(&self) -> Vec<&'static str> {
+ let mut headers = vec![
+ "ehstorioctl.h",
+ "ntddcdrm.h",
+ "ntddcdvd.h",
+ "ntdddisk.h",
+ "ntddmmc.h",
+ "ntddscsi.h",
+ "ntddstor.h",
+ "ntddtape.h",
+ "ntddvol.h",
+ "ufs.h",
+ ];
+ if matches!(
+ self.driver_config,
+ DriverConfig::Wdm | DriverConfig::Kmdf(_)
+ ) {
+ headers.extend([
+ "mountdev.h",
+ "mountmgr.h",
+ "ntddchgr.h",
+ "ntdddump.h",
+ "storduid.h",
+ "storport.h",
+ ]);
+ }
+ if matches!(self.driver_config, DriverConfig::Kmdf(_)) {
+ headers.extend(["ehstorbandmgmt.h"]);
+ }
+ headers
+ }
- if let DriverConfig::Wdm | DriverConfig::Kmdf(_) = self.driver_config {
- spb_headers.extend(["pwmutil.h"]);
- }
+ fn usb_headers(&self) -> Vec<&'static str> {
+ let mut headers = vec![
+ "usb.h",
+ "usbfnbase.h",
+ "usbioctl.h",
+ "usbspec.h",
+ "Usbpmapi.h",
+ ];
+
+ if matches!(
+ self.driver_config,
+ DriverConfig::Wdm | DriverConfig::Kmdf(_)
+ ) {
+ headers.extend(["usbbusif.h", "usbdlib.h", "usbfnattach.h", "usbfnioctl.h"]);
+ }
- if let DriverConfig::Kmdf(_) = self.driver_config {
- spb_headers.extend(["spb/1.1/spbcx.h"]);
- }
+ if matches!(
+ self.driver_config,
+ DriverConfig::Kmdf(_) | DriverConfig::Umdf(_)
+ ) {
+ headers.extend(["wdfusb.h"]);
+ }
- spb_headers
+ if matches!(self.driver_config, DriverConfig::Kmdf(_)) {
+ headers.extend([
+ "ucm/1.0/UcmCx.h",
+ "UcmTcpci/1.0/UcmTcpciCx.h",
+ "UcmUcsi/1.0/UcmucsiCx.h",
+ "ucx/1.6/ucxclass.h",
+ "ude/1.1/UdeCx.h",
+ "ufx/1.1/ufxbase.h",
+ "ufxproprietarycharger.h",
+ "urs/1.0/UrsCx.h",
+ ]);
+
+ if Self::should_include_ufxclient() {
+ headers.extend(["ufx/1.1/ufxclient.h"]);
}
- ApiSubset::Storage => {
- let mut storage_headers = vec![
- "ehstorioctl.h",
- "ntddcdrm.h",
- "ntddcdvd.h",
- "ntdddisk.h",
- "ntddmmc.h",
- "ntddscsi.h",
- "ntddstor.h",
- "ntddtape.h",
- "ntddvol.h",
- "ufs.h",
- ];
-
- if let DriverConfig::Wdm | DriverConfig::Kmdf(_) = self.driver_config {
- storage_headers.extend([
- "mountdev.h",
- "mountmgr.h",
- "ntddchgr.h",
- "ntdddump.h",
- "storduid.h",
- "storport.h",
- ]);
- }
-
- if let DriverConfig::Kmdf(_) = self.driver_config {
- storage_headers.extend(["ehstorbandmgmt.h"]);
- }
+ }
+ headers
+ }
- storage_headers
+ /// Determines whether to include the ufxclient.h header based on the Clang
+ /// version used by bindgen.
+ ///
+ /// The ufxclient.h header contains FORCEINLINE annotations that are invalid
+ /// according to the C standard. While MSVC silently ignores these in C
+ /// mode, older versions of Clang (pre-20.0) will error, even with MSVC
+ /// compatibility enabled.
+ ///
+ /// This function checks if the current Clang version is 20.0 or newer,
+ /// where the issue was fixed. See
+ /// for details.
+ fn should_include_ufxclient() -> bool {
+ const MINIMUM_CLANG_MAJOR_VERISON_WITH_INVALID_INLINE_FIX: u32 = 20;
+
+ let clang_version = ::bindgen::clang_version();
+ match clang_version.parsed {
+ Some((major, _minor))
+ if major >= MINIMUM_CLANG_MAJOR_VERISON_WITH_INVALID_INLINE_FIX =>
+ {
+ true
+ }
+ Some(_) => {
+ tracing::info!(
+ "Skipping ufxclient.h due to FORCEINLINE bug in {}",
+ clang_version.full
+ );
+ false
+ }
+ None => {
+ tracing::warn!(
+ "Failed to parse semver Major and Minor components from full Clang version \
+ string: {}",
+ clang_version.full
+ );
+ false
}
}
- .into_iter()
- .map(std::string::ToString::to_string)
}
/// Returns a [`String`] containing the contents of a header file designed
diff --git a/crates/wdk-sys/Cargo.toml b/crates/wdk-sys/Cargo.toml
index 30561d657..7b665ae7b 100644
--- a/crates/wdk-sys/Cargo.toml
+++ b/crates/wdk-sys/Cargo.toml
@@ -40,6 +40,7 @@ hid = []
parallel-ports = ["gpio"]
spb = []
storage = []
+usb = []
nightly = ["wdk-macros/nightly"]
test-stubs = []
diff --git a/crates/wdk-sys/build.rs b/crates/wdk-sys/build.rs
index 9bbc91915..15dd33aa0 100644
--- a/crates/wdk-sys/build.rs
+++ b/crates/wdk-sys/build.rs
@@ -136,6 +136,7 @@ const BINDGEN_FILE_GENERATORS_TUPLES: &[(&str, GenerateFn)] = &[
("parallel_ports.rs", generate_parallel_ports),
("spb.rs", generate_spb),
("storage.rs", generate_storage),
+ ("usb.rs", generate_usb),
];
fn initialize_tracing() -> Result<(), ParseError> {
@@ -208,6 +209,8 @@ fn generate_constants(out_path: &Path, config: &Config) -> Result<(), ConfigErro
ApiSubset::Spb,
#[cfg(feature = "storage")]
ApiSubset::Storage,
+ #[cfg(feature = "usb")]
+ ApiSubset::Usb,
]);
trace!(header_contents = ?header_contents);
@@ -238,6 +241,8 @@ fn generate_types(out_path: &Path, config: &Config) -> Result<(), ConfigError> {
ApiSubset::Spb,
#[cfg(feature = "storage")]
ApiSubset::Storage,
+ #[cfg(feature = "usb")]
+ ApiSubset::Usb,
]);
trace!(header_contents = ?header_contents);
@@ -489,6 +494,42 @@ fn generate_storage(out_path: &Path, config: &Config) -> Result<(), ConfigError>
}
}
+fn generate_usb(out_path: &Path, config: &Config) -> Result<(), ConfigError> {
+ cfg_if::cfg_if! {
+ if #[cfg(feature = "usb")] {
+ info!("Generating bindings to WDK: usb.rs");
+
+ let header_contents =
+ config.bindgen_header_contents([ApiSubset::Base, ApiSubset::Wdf, ApiSubset::Usb]);
+ trace!(header_contents = ?header_contents);
+
+ let bindgen_builder = {
+ let mut builder = bindgen::Builder::wdk_default(config)?
+ .with_codegen_config((CodegenConfig::TYPES | CodegenConfig::VARS).complement())
+ .header_contents("usb-input.h", &header_contents);
+
+ // Only allowlist files in the usb-specific files to avoid
+ // duplicate definitions
+ for header_file in config.headers(ApiSubset::Usb) {
+ builder = builder.allowlist_file(format!("(?i).*{header_file}.*"));
+ }
+ builder
+ };
+ trace!(bindgen_builder = ?bindgen_builder);
+
+ Ok(bindgen_builder
+ .generate()
+ .expect("Bindings should succeed to generate")
+ .write_to_file(out_path.join("usb.rs"))?)
+ } else {
+ let _ = (out_path, config); // Silence unused variable warnings when usb feature is not enabled
+
+ info!("Skipping usb.rs generation since usb feature is not enabled");
+ Ok(())
+ }
+ }
+}
+
/// Generates a `wdf_function_count.rs` file in `OUT_DIR` which contains the
/// definition of the function `get_wdf_function_count()`. This is required to
/// be generated here since the size of the table is derived from either a
diff --git a/crates/wdk-sys/src/lib.rs b/crates/wdk-sys/src/lib.rs
index 96c22abc4..1c84eea02 100644
--- a/crates/wdk-sys/src/lib.rs
+++ b/crates/wdk-sys/src/lib.rs
@@ -79,6 +79,16 @@ pub mod spb;
))]
pub mod storage;
+#[cfg(all(
+ any(
+ driver_model__driver_type = "WDM",
+ driver_model__driver_type = "KMDF",
+ driver_model__driver_type = "UMDF"
+ ),
+ feature = "usb"
+))]
+pub mod usb;
+
#[cfg(feature = "test-stubs")]
pub mod test_stubs;
diff --git a/crates/wdk-sys/src/usb.rs b/crates/wdk-sys/src/usb.rs
new file mode 100644
index 000000000..2b570f2c8
--- /dev/null
+++ b/crates/wdk-sys/src/usb.rs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft Corporation
+// License: MIT OR Apache-2.0
+
+//! Direct FFI bindings to USB APIs from the Windows Driver Kit (WDK)
+//!
+//! This module contains all bindings to functions, constants, methods,
+//! constructors and destructors for USB headers. Types are not included in this
+//! module, but are available in the top-level `wdk_sys` module.
+
+#[allow(
+ missing_docs,
+ reason = "most items in the WDK headers have no inline documentation, so bindgen is unable to \
+ generate documentation for their bindings"
+)]
+mod bindings {
+ #[allow(
+ clippy::wildcard_imports,
+ reason = "the underlying c code relies on all type definitions being in scope, which \
+ results in the bindgen generated code relying on the generated types being in \
+ scope as well"
+ )]
+ use crate::types::*;
+
+ include!(concat!(env!("OUT_DIR"), "/usb.rs"));
+}
+pub use bindings::*;
diff --git a/examples/sample-kmdf-driver/Cargo.toml b/examples/sample-kmdf-driver/Cargo.toml
index 183078e20..524b9d37d 100644
--- a/examples/sample-kmdf-driver/Cargo.toml
+++ b/examples/sample-kmdf-driver/Cargo.toml
@@ -37,6 +37,7 @@ hid = ["wdk-sys/hid"]
parallel-ports = ["wdk-sys/parallel-ports"]
spb = ["wdk-sys/spb"]
storage = ["wdk-sys/storage"]
+usb = ["wdk-sys/usb"]
nightly = ["wdk/nightly", "wdk-sys/nightly"]
diff --git a/examples/sample-umdf-driver/Cargo.toml b/examples/sample-umdf-driver/Cargo.toml
index 4602732e6..8e5678efb 100644
--- a/examples/sample-umdf-driver/Cargo.toml
+++ b/examples/sample-umdf-driver/Cargo.toml
@@ -35,6 +35,7 @@ hid = ["wdk-sys/hid"]
parallel-ports = ["wdk-sys/parallel-ports"]
spb = ["wdk-sys/spb"]
storage = ["wdk-sys/storage"]
+usb = ["wdk-sys/usb"]
nightly = ["wdk/nightly", "wdk-sys/nightly"]
diff --git a/examples/sample-wdm-driver/Cargo.toml b/examples/sample-wdm-driver/Cargo.toml
index c1f6e4bf9..7f2f7f7ec 100644
--- a/examples/sample-wdm-driver/Cargo.toml
+++ b/examples/sample-wdm-driver/Cargo.toml
@@ -35,6 +35,7 @@ hid = ["wdk-sys/hid"]
parallel-ports = ["wdk-sys/parallel-ports"]
spb = ["wdk-sys/spb"]
storage = ["wdk-sys/storage"]
+usb = ["wdk-sys/usb"]
nightly = ["wdk/nightly", "wdk-sys/nightly"]