From 16e8cc9a5b1014c90c23890b63f4efbb67223d11 Mon Sep 17 00:00:00 2001 From: Melvin Wang Date: Sat, 25 Jan 2025 19:11:33 -0800 Subject: [PATCH 1/3] feat: expand `wdk-sys` coverage to include gpio and parallel ports related headers --- crates/wdk-build/src/lib.rs | 22 +++++++ crates/wdk-sys/Cargo.toml | 2 + crates/wdk-sys/build.rs | 82 ++++++++++++++++++++++++++ crates/wdk-sys/src/gpio.rs | 37 ++++++++++++ crates/wdk-sys/src/lib.rs | 24 +++++++- crates/wdk-sys/src/parallel_ports.rs | 37 ++++++++++++ crates/wdk-sys/src/spb.rs | 2 +- examples/sample-kmdf-driver/Cargo.toml | 2 + examples/sample-umdf-driver/Cargo.toml | 2 + examples/sample-wdm-driver/Cargo.toml | 2 + 10 files changed, 209 insertions(+), 3 deletions(-) create mode 100644 crates/wdk-sys/src/gpio.rs create mode 100644 crates/wdk-sys/src/parallel_ports.rs diff --git a/crates/wdk-build/src/lib.rs b/crates/wdk-build/src/lib.rs index 9494d9d39..3380eeee8 100644 --- a/crates/wdk-build/src/lib.rs +++ b/crates/wdk-build/src/lib.rs @@ -197,8 +197,12 @@ pub enum ApiSubset { Base, /// API subset required for WDF (Windows Driver Framework) drivers: Wdf, + /// API subset for GPIO (General Purpose Input/Output) drivers: + Gpio, /// API subset for HID (Human Interface Device) drivers: Hid, + /// API subset for Parallel Ports drivers: + ParallelPorts, /// API subset for SPB (Serial Peripheral Bus) drivers: Spb, } @@ -658,6 +662,15 @@ impl Config { vec![] } } + ApiSubset::Gpio => { + let mut gpio_headers = vec!["gpio.h"]; + + if let DriverConfig::Kmdf(_) = self.driver_config { + gpio_headers.extend(["gpioclx.h"]); + } + + gpio_headers + } ApiSubset::Hid => { let mut hid_headers = vec!["hidclass.h", "hidsdi.h", "hidpi.h", "vhf.h"]; @@ -671,6 +684,15 @@ impl Config { hid_headers } + ApiSubset::ParallelPorts => { + let mut parallel_ports_headers = vec!["ntddpar.h", "ntddser.h"]; + + if let DriverConfig::Wdm | DriverConfig::Kmdf(_) = self.driver_config { + parallel_ports_headers.extend(["parallel.h"]); + } + + parallel_ports_headers + } ApiSubset::Spb => { let mut spb_headers = vec!["spb.h", "reshub.h"]; diff --git a/crates/wdk-sys/Cargo.toml b/crates/wdk-sys/Cargo.toml index f7f030b8c..a5ed1a69a 100644 --- a/crates/wdk-sys/Cargo.toml +++ b/crates/wdk-sys/Cargo.toml @@ -35,8 +35,10 @@ wdk-macros.workspace = true [features] default = [] +gpio = [] hid = [] spb = [] +parallel-ports = ["gpio"] nightly = ["wdk-macros/nightly"] test-stubs = [] diff --git a/crates/wdk-sys/build.rs b/crates/wdk-sys/build.rs index 10b66cb47..15041edb4 100644 --- a/crates/wdk-sys/build.rs +++ b/crates/wdk-sys/build.rs @@ -131,7 +131,9 @@ const BINDGEN_FILE_GENERATORS_TUPLES: &[(&str, GenerateFn)] = &[ ("types.rs", generate_types), ("base.rs", generate_base), ("wdf.rs", generate_wdf), + ("gpio.rs", generate_gpio), ("hid.rs", generate_hid), + ("parallel_ports.rs", generate_parallel_ports), ("spb.rs", generate_spb), ]; @@ -195,10 +197,14 @@ fn generate_constants(out_path: &Path, config: &Config) -> Result<(), ConfigErro let header_contents = config.bindgen_header_contents([ ApiSubset::Base, ApiSubset::Wdf, + #[cfg(feature = "gpio")] + ApiSubset::Gpio, #[cfg(feature = "hid")] ApiSubset::Hid, #[cfg(feature = "spb")] ApiSubset::Spb, + #[cfg(feature = "parallel-ports")] + ApiSubset::ParallelPorts, ]); trace!(header_contents = ?header_contents); @@ -219,10 +225,14 @@ fn generate_types(out_path: &Path, config: &Config) -> Result<(), ConfigError> { let header_contents = config.bindgen_header_contents([ ApiSubset::Base, ApiSubset::Wdf, + #[cfg(feature = "gpio")] + ApiSubset::Gpio, #[cfg(feature = "hid")] ApiSubset::Hid, #[cfg(feature = "spb")] ApiSubset::Spb, + #[cfg(feature = "parallel-ports")] + ApiSubset::ParallelPorts, ]); trace!(header_contents = ?header_contents); @@ -286,6 +296,42 @@ fn generate_wdf(out_path: &Path, config: &Config) -> Result<(), ConfigError> { } } +fn generate_gpio(out_path: &Path, config: &Config) -> Result<(), ConfigError> { + cfg_if::cfg_if! { + if #[cfg(feature = "gpio")] { + info!("Generating bindings to WDK: gpio.rs"); + + let header_contents = config.bindgen_header_contents([ApiSubset::Base, ApiSubset::Wdf, ApiSubset::Gpio]); + 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("gpio-input.h", &header_contents); + + // Only allowlist files in the gpio-specific files to avoid duplicate definitions + for header_file in config.headers(ApiSubset::Gpio) + { + 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("gpio.rs"))?) + } else { + let _ = (out_path, config); // Silence unused variable warnings when gpio feature is not enabled + + info!( + "Skipping gpio.rs generation since gpio feature is not enabled"); + Ok(()) + } + } +} + fn generate_hid(out_path: &Path, config: &Config) -> Result<(), ConfigError> { cfg_if::cfg_if! { if #[cfg(feature = "hid")] { @@ -322,6 +368,42 @@ fn generate_hid(out_path: &Path, config: &Config) -> Result<(), ConfigError> { } } +fn generate_parallel_ports(out_path: &Path, config: &Config) -> Result<(), ConfigError> { + cfg_if::cfg_if! { + if #[cfg(feature = "parallel-ports")] { + info!("Generating bindings to WDK: parallel_ports.rs"); + + let header_contents = config.bindgen_header_contents([ApiSubset::Base, ApiSubset::Wdf, ApiSubset::ParallelPorts]); + 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("parallel-ports-input.h", &header_contents); + + // Only allowlist files in the parallel-ports-specific files to avoid duplicate definitions + for header_file in config.headers(ApiSubset::ParallelPorts) + { + 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("parallel_ports.rs"))?) + } else { + let _ = (out_path, config); // Silence unused variable warnings when parallel-ports feature is not enabled + + info!( + "Skipping parallel_ports.rs generation since parallel-ports feature is not enabled"); + Ok(()) + } + } +} + fn generate_spb(out_path: &Path, config: &Config) -> Result<(), ConfigError> { cfg_if::cfg_if! { if #[cfg(feature = "spb")] { diff --git a/crates/wdk-sys/src/gpio.rs b/crates/wdk-sys/src/gpio.rs new file mode 100644 index 000000000..bc925be70 --- /dev/null +++ b/crates/wdk-sys/src/gpio.rs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation +// License: MIT OR Apache-2.0 + +//! Direct FFI bindings to GPIO APIs from the Windows Driver Kit (WDK) +//! +//! This module contains all bindings to functions, constants, methods, +//! constructors and destructors in the following headers: `gpio.h`, +//! `gpioclx.h`. 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" + )] + #[allow( + unused_imports, + reason = "in certain versions of the WDK, there are no functions related to SPB that can \ + be generated by bindgen, so these types are unused" + )] + use crate::types::*; + + include!(concat!(env!("OUT_DIR"), "/gpio.rs")); +} +#[allow( + unused_imports, + reason = "in certain versions of the WDK, there are no functions related to SPB that can be \ + generated by bindgen, so the `bindings` module is empty" +)] +pub use bindings::*; diff --git a/crates/wdk-sys/src/lib.rs b/crates/wdk-sys/src/lib.rs index 130201cd8..6fc47692e 100644 --- a/crates/wdk-sys/src/lib.rs +++ b/crates/wdk-sys/src/lib.rs @@ -23,11 +23,21 @@ pub use crate::{constants::*, types::*}; #[cfg(any(driver_model__driver_type = "WDM", driver_model__driver_type = "KMDF"))] pub mod ntddk; +#[cfg(driver_model__driver_type = "UMDF")] +pub mod windows; + #[cfg(any(driver_model__driver_type = "KMDF", driver_model__driver_type = "UMDF"))] pub mod wdf; -#[cfg(driver_model__driver_type = "UMDF")] -pub mod windows; +#[cfg(all( + any( + driver_model__driver_type = "WDM", + driver_model__driver_type = "KMDF", + driver_model__driver_type = "UMDF" + ), + feature = "gpio" +))] +pub mod gpio; #[cfg(all( any( @@ -39,6 +49,16 @@ pub mod windows; ))] pub mod hid; +#[cfg(all( + any( + driver_model__driver_type = "WDM", + driver_model__driver_type = "KMDF", + driver_model__driver_type = "UMDF" + ), + feature = "parallel-ports" +))] +pub mod parallel_ports; + #[cfg(all( any( driver_model__driver_type = "WDM", diff --git a/crates/wdk-sys/src/parallel_ports.rs b/crates/wdk-sys/src/parallel_ports.rs new file mode 100644 index 000000000..903f19b8f --- /dev/null +++ b/crates/wdk-sys/src/parallel_ports.rs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation +// License: MIT OR Apache-2.0 + +//! Direct FFI bindings to Parallel Ports APIs from the Windows Driver Kit (WDK) +//! +//! This module contains all bindings to functions, constants, methods, +//! constructors and destructors in the following headers: `ntddpar.h`, +//! `ntddser.h`, `parallel.h`. 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" + )] + #[allow( + unused_imports, + reason = "in certain versions of the WDK, there are no functions related to SPB that can \ + be generated by bindgen, so these types are unused" + )] + use crate::types::*; + + include!(concat!(env!("OUT_DIR"), "/parallel_ports.rs")); +} +#[allow( + unused_imports, + reason = "in certain versions of the WDK, there are no functions related to SPB that can be \ + generated by bindgen, so the `bindings` module is empty" +)] +pub use bindings::*; diff --git a/crates/wdk-sys/src/spb.rs b/crates/wdk-sys/src/spb.rs index 467e209f9..a8c862a42 100644 --- a/crates/wdk-sys/src/spb.rs +++ b/crates/wdk-sys/src/spb.rs @@ -23,7 +23,7 @@ mod bindings { #[allow( unused_imports, reason = "in certain versions of the WDK, there are no functions related to SPB that can \ - be generated by bindgen, so these types are unused " + be generated by bindgen, so these types are unused" )] use crate::types::*; diff --git a/examples/sample-kmdf-driver/Cargo.toml b/examples/sample-kmdf-driver/Cargo.toml index cff1287eb..44a3293ed 100644 --- a/examples/sample-kmdf-driver/Cargo.toml +++ b/examples/sample-kmdf-driver/Cargo.toml @@ -32,8 +32,10 @@ wdk-sys = { path = "../../crates/wdk-sys", version = "0.3.0" } [features] default = [] +gpio = ["wdk-sys/gpio"] hid = ["wdk-sys/hid"] spb = ["wdk-sys/spb"] +parallel-ports = ["wdk-sys/parallel-ports"] nightly = ["wdk/nightly", "wdk-sys/nightly"] diff --git a/examples/sample-umdf-driver/Cargo.toml b/examples/sample-umdf-driver/Cargo.toml index 3ae09d099..2176efaba 100644 --- a/examples/sample-umdf-driver/Cargo.toml +++ b/examples/sample-umdf-driver/Cargo.toml @@ -30,8 +30,10 @@ wdk-sys = { path = "../../crates/wdk-sys", version = "0.3.0" } [features] default = [] +gpio = ["wdk-sys/gpio"] hid = ["wdk-sys/hid"] spb = ["wdk-sys/spb"] +parallel-ports = ["wdk-sys/parallel-ports"] nightly = ["wdk/nightly", "wdk-sys/nightly"] diff --git a/examples/sample-wdm-driver/Cargo.toml b/examples/sample-wdm-driver/Cargo.toml index 5fd3a5169..ae9963956 100644 --- a/examples/sample-wdm-driver/Cargo.toml +++ b/examples/sample-wdm-driver/Cargo.toml @@ -31,8 +31,10 @@ wdk-sys = { path = "../../crates/wdk-sys", version = "0.3.0" } [features] default = [] +gpio = ["wdk-sys/gpio"] hid = ["wdk-sys/hid"] spb = ["wdk-sys/spb"] +parallel-ports = ["wdk-sys/parallel-ports"] nightly = ["wdk/nightly", "wdk-sys/nightly"] From 17a430ecb62e4cfa7d6d9a1ed4d505b83ac77640 Mon Sep 17 00:00:00 2001 From: Melvin Wang Date: Sat, 1 Feb 2025 11:33:15 -0800 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Melvin Wang --- crates/wdk-sys/src/gpio.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/wdk-sys/src/gpio.rs b/crates/wdk-sys/src/gpio.rs index bc925be70..0d0532523 100644 --- a/crates/wdk-sys/src/gpio.rs +++ b/crates/wdk-sys/src/gpio.rs @@ -22,7 +22,7 @@ mod bindings { )] #[allow( unused_imports, - reason = "in certain versions of the WDK, there are no functions related to SPB that can \ + reason = "in certain versions of the WDK, there are no functions related to GPIO that can \ be generated by bindgen, so these types are unused" )] use crate::types::*; @@ -31,7 +31,7 @@ mod bindings { } #[allow( unused_imports, - reason = "in certain versions of the WDK, there are no functions related to SPB that can be \ + reason = "in certain versions of the WDK, there are no functions related to GPIO that can be \ generated by bindgen, so the `bindings` module is empty" )] pub use bindings::*; From 6c984fe37e9ece467dbca63b1d028408e3e086a8 Mon Sep 17 00:00:00 2001 From: Melvin Wang Date: Tue, 4 Feb 2025 15:15:04 -0800 Subject: [PATCH 3/3] fix formatting in macros --- crates/wdk-sys/build.rs | 75 ++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/crates/wdk-sys/build.rs b/crates/wdk-sys/build.rs index 15041edb4..ab876e177 100644 --- a/crates/wdk-sys/build.rs +++ b/crates/wdk-sys/build.rs @@ -301,17 +301,18 @@ fn generate_gpio(out_path: &Path, config: &Config) -> Result<(), ConfigError> { if #[cfg(feature = "gpio")] { info!("Generating bindings to WDK: gpio.rs"); - let header_contents = config.bindgen_header_contents([ApiSubset::Base, ApiSubset::Wdf, ApiSubset::Gpio]); + let header_contents = + config.bindgen_header_contents([ApiSubset::Base, ApiSubset::Wdf, ApiSubset::Gpio]); 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("gpio-input.h", &header_contents); + let mut builder = bindgen::Builder::wdk_default(config)? + .with_codegen_config((CodegenConfig::TYPES | CodegenConfig::VARS).complement()) + .header_contents("gpio-input.h", &header_contents); - // Only allowlist files in the gpio-specific files to avoid duplicate definitions - for header_file in config.headers(ApiSubset::Gpio) - { + // Only allowlist files in the gpio-specific files to avoid + // duplicate definitions + for header_file in config.headers(ApiSubset::Gpio) { builder = builder.allowlist_file(format!("(?i).*{header_file}.*")); } builder @@ -325,8 +326,7 @@ fn generate_gpio(out_path: &Path, config: &Config) -> Result<(), ConfigError> { } else { let _ = (out_path, config); // Silence unused variable warnings when gpio feature is not enabled - info!( - "Skipping gpio.rs generation since gpio feature is not enabled"); + info!("Skipping gpio.rs generation since gpio feature is not enabled"); Ok(()) } } @@ -337,17 +337,18 @@ fn generate_hid(out_path: &Path, config: &Config) -> Result<(), ConfigError> { if #[cfg(feature = "hid")] { info!("Generating bindings to WDK: hid.rs"); - let header_contents = config.bindgen_header_contents([ApiSubset::Base, ApiSubset::Wdf, ApiSubset::Hid]); + let header_contents = + config.bindgen_header_contents([ApiSubset::Base, ApiSubset::Wdf, ApiSubset::Hid]); 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("hid-input.h", &header_contents); + let mut builder = bindgen::Builder::wdk_default(config)? + .with_codegen_config((CodegenConfig::TYPES | CodegenConfig::VARS).complement()) + .header_contents("hid-input.h", &header_contents); - // Only allowlist files in the hid-specific files to avoid duplicate definitions - for header_file in config.headers(ApiSubset::Hid) - { + // Only allowlist files in the hid-specific files to avoid + // duplicate definitions + for header_file in config.headers(ApiSubset::Hid) { builder = builder.allowlist_file(format!("(?i).*{header_file}.*")); } builder @@ -361,8 +362,7 @@ fn generate_hid(out_path: &Path, config: &Config) -> Result<(), ConfigError> { } else { let _ = (out_path, config); // Silence unused variable warnings when hid feature is not enabled - info!( - "Skipping hid.rs generation since hid feature is not enabled"); + info!("Skipping hid.rs generation since hid feature is not enabled"); Ok(()) } } @@ -373,17 +373,21 @@ fn generate_parallel_ports(out_path: &Path, config: &Config) -> Result<(), Confi if #[cfg(feature = "parallel-ports")] { info!("Generating bindings to WDK: parallel_ports.rs"); - let header_contents = config.bindgen_header_contents([ApiSubset::Base, ApiSubset::Wdf, ApiSubset::ParallelPorts]); + let header_contents = config.bindgen_header_contents([ + ApiSubset::Base, + ApiSubset::Wdf, + ApiSubset::ParallelPorts, + ]); 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("parallel-ports-input.h", &header_contents); + let mut builder = bindgen::Builder::wdk_default(config)? + .with_codegen_config((CodegenConfig::TYPES | CodegenConfig::VARS).complement()) + .header_contents("parallel-ports-input.h", &header_contents); - // Only allowlist files in the parallel-ports-specific files to avoid duplicate definitions - for header_file in config.headers(ApiSubset::ParallelPorts) - { + // Only allowlist files in the parallel-ports-specific files to + // avoid duplicate definitions + for header_file in config.headers(ApiSubset::ParallelPorts) { builder = builder.allowlist_file(format!("(?i).*{header_file}.*")); } builder @@ -398,7 +402,8 @@ fn generate_parallel_ports(out_path: &Path, config: &Config) -> Result<(), Confi let _ = (out_path, config); // Silence unused variable warnings when parallel-ports feature is not enabled info!( - "Skipping parallel_ports.rs generation since parallel-ports feature is not enabled"); + "Skipping parallel_ports.rs generation since parallel-ports feature is not enabled" + ); Ok(()) } } @@ -409,17 +414,18 @@ fn generate_spb(out_path: &Path, config: &Config) -> Result<(), ConfigError> { if #[cfg(feature = "spb")] { info!("Generating bindings to WDK: spb.rs"); - let header_contents = config.bindgen_header_contents([ApiSubset::Base, ApiSubset::Wdf, ApiSubset::Spb]); + let header_contents = + config.bindgen_header_contents([ApiSubset::Base, ApiSubset::Wdf, ApiSubset::Spb]); 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("spb-input.h", &header_contents); + let mut builder = bindgen::Builder::wdk_default(config)? + .with_codegen_config((CodegenConfig::TYPES | CodegenConfig::VARS).complement()) + .header_contents("spb-input.h", &header_contents); - // Only allowlist files in the spb-specific files to avoid duplicate definitions - for header_file in config.headers(ApiSubset::Spb) - { + // Only allowlist files in the spb-specific files to avoid + // duplicate definitions + for header_file in config.headers(ApiSubset::Spb) { builder = builder.allowlist_file(format!("(?i).*{header_file}.*")); } builder @@ -433,8 +439,7 @@ fn generate_spb(out_path: &Path, config: &Config) -> Result<(), ConfigError> { } else { let _ = (out_path, config); // Silence unused variable warnings when spb feature is not enabled - info!( - "Skipping spb.rs generation since spb feature is not enabled"); + info!("Skipping spb.rs generation since spb feature is not enabled"); Ok(()) } }