Skip to content

Commit

Permalink
feat: expand wdk-sys coverage to include gpio and parallel ports rela…
Browse files Browse the repository at this point in the history
…ted headers (#278)

Signed-off-by: Melvin Wang <[email protected]>
Co-authored-by: Copilot <[email protected]>
  • Loading branch information
wmmc88 and Copilot authored Feb 6, 2025
1 parent 8696928 commit a6a4886
Show file tree
Hide file tree
Showing 18 changed files with 247 additions and 36 deletions.
22 changes: 22 additions & 0 deletions crates/wdk-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,12 @@ pub enum ApiSubset {
Base,
/// API subset required for WDF (Windows Driver Framework) drivers: <https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/_wdf/>
Wdf,
/// API subset for GPIO (General Purpose Input/Output) drivers: <https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/_gpio/>
Gpio,
/// API subset for HID (Human Interface Device) drivers: <https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/_hid/>
Hid,
/// API subset for Parallel Ports drivers: <https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/_parports/>
ParallelPorts,
/// API subset for SPB (Serial Peripheral Bus) drivers: <https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/_spb/>
Spb,
/// API subset for Storage drivers: <https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/_storage/>
Expand Down Expand Up @@ -660,6 +664,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"];

Expand All @@ -673,6 +686,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"];

Expand Down
2 changes: 2 additions & 0 deletions crates/wdk-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ wdk-macros.workspace = true
[features]
default = []

gpio = []
hid = []
parallel-ports = ["gpio"]
spb = []
storage = []

Expand Down
121 changes: 104 additions & 17 deletions crates/wdk-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
("storage.rs", generate_storage),
];
Expand Down Expand Up @@ -196,8 +198,12 @@ 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 = "parallel-ports")]
ApiSubset::ParallelPorts,
#[cfg(feature = "spb")]
ApiSubset::Spb,
#[cfg(feature = "storage")]
Expand All @@ -222,8 +228,12 @@ 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 = "parallel-ports")]
ApiSubset::ParallelPorts,
#[cfg(feature = "spb")]
ApiSubset::Spb,
#[cfg(feature = "storage")]
Expand Down Expand Up @@ -291,22 +301,59 @@ 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")] {
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
Expand All @@ -320,8 +367,48 @@ 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");
Ok(())
}
}
}

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 hid.rs generation since hid feature is not enabled");
"Skipping parallel_ports.rs generation since parallel-ports feature is not enabled"
);
Ok(())
}
}
Expand All @@ -332,17 +419,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
Expand All @@ -356,8 +444,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(())
}
}
Expand Down
37 changes: 37 additions & 0 deletions crates/wdk-sys/src/gpio.rs
Original file line number Diff line number Diff line change
@@ -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 GPIO 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 GPIO that can be \
generated by bindgen, so the `bindings` module is empty"
)]
pub use bindings::*;
24 changes: 22 additions & 2 deletions crates/wdk-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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",
Expand Down
37 changes: 37 additions & 0 deletions crates/wdk-sys/src/parallel_ports.rs
Original file line number Diff line number Diff line change
@@ -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::*;
2 changes: 1 addition & 1 deletion crates/wdk-sys/src/spb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;

Expand Down
4 changes: 2 additions & 2 deletions examples/sample-kmdf-driver/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions examples/sample-kmdf-driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ wdk-sys = { path = "../../crates/wdk-sys", version = "0.3.0" }
[features]
default = []

gpio = ["wdk-sys/gpio"]
hid = ["wdk-sys/hid"]
parallel-ports = ["wdk-sys/parallel-ports"]
spb = ["wdk-sys/spb"]
storage = ["wdk-sys/storage"]

Expand Down
Loading

0 comments on commit a6a4886

Please sign in to comment.