Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
}
Expand Down Expand Up @@ -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"];

Expand All @@ -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"];

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,8 +35,10 @@ wdk-macros.workspace = true
[features]
default = []

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

nightly = ["wdk-macros/nightly"]
test-stubs = []
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),
];

Expand Down Expand Up @@ -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);

Expand All @@ -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);

Expand Down Expand Up @@ -286,22 +296,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 @@ -315,8 +362,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 @@ -327,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
Expand All @@ -351,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(())
}
}
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
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,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"]

Expand Down
2 changes: 2 additions & 0 deletions examples/sample-umdf-driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
Loading