diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml
index 1487d1f62..48ea74d30 100644
--- a/.github/workflows/build-deploy-docs.yml
+++ b/.github/workflows/build-deploy-docs.yml
@@ -48,7 +48,7 @@ jobs:
- name: Build rustdoc docs
run: |
- cargo doc -p riot-rs --features no-boards,bench,threading,random,csprng,hwrng
+ cargo doc -p riot-rs --features no-boards,bench,threading,random,csprng,hwrng,usb,_dev-doc
echo "" > target/doc/index.html
mkdir -p ./_site/dev/docs/api && mv target/doc/* ./_site/dev/docs/api
diff --git a/book/src/coding-conventions.md b/book/src/coding-conventions.md
index 1835d315d..5cd8123b7 100644
--- a/book/src/coding-conventions.md
+++ b/book/src/coding-conventions.md
@@ -32,7 +32,13 @@ Imports from the same crate with the same visibility MUST be [merged into a sing
### Doc Comments
-All public items listed in the documentation—i.e., not marked with [`#[doc(hidden)]`](https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#hidden)—SHOULD be documented.
+Items that need to be exported by a crate but that should not be used by users SHOULD be marked using
+
+```rust
+#[cfg_attr(not(feature = "_dev-doc"), doc(hidden))]
+```
+
+All public items visible to users SHOULD be documented.
Doc comments MUST use the [line comment style](https://doc.rust-lang.org/reference/comments.html#doc-comments), not the block style.
diff --git a/src/riot-rs-embassy/Cargo.toml b/src/riot-rs-embassy/Cargo.toml
index 1a7c15e2b..f1609c302 100644
--- a/src/riot-rs-embassy/Cargo.toml
+++ b/src/riot-rs-embassy/Cargo.toml
@@ -115,3 +115,6 @@ override-usb-config = []
executor-single-thread = []
executor-interrupt = []
+
+# Enables developer docs, shows items that are otherwise hidden from users.
+_dev-doc = []
diff --git a/src/riot-rs-embassy/src/lib.rs b/src/riot-rs-embassy/src/lib.rs
index b0bc9b54c..3ba3eb003 100644
--- a/src/riot-rs-embassy/src/lib.rs
+++ b/src/riot-rs-embassy/src/lib.rs
@@ -63,14 +63,20 @@ pub use network::NetworkStack;
#[cfg(feature = "threading")]
pub mod blocker;
+
+#[cfg_attr(not(feature = "_dev-doc"), doc(hidden))]
pub mod delegate;
+#[cfg_attr(not(feature = "_dev-doc"), doc(hidden))]
pub mod sendcell;
+#[cfg_attr(not(feature = "_dev-doc"), doc(hidden))]
pub type Task = fn(Spawner, &mut arch::OptionalPeripherals);
+#[cfg_attr(not(feature = "_dev-doc"), doc(hidden))]
#[distributed_slice]
pub static EMBASSY_TASKS: [Task] = [..];
+#[cfg_attr(not(feature = "_dev-doc"), doc(hidden))]
#[cfg(feature = "executor-interrupt")]
pub static EXECUTOR: arch::Executor = arch::Executor::new();
diff --git a/src/riot-rs-embassy/src/usb.rs b/src/riot-rs-embassy/src/usb.rs
index 17050eb1b..0497ef418 100644
--- a/src/riot-rs-embassy/src/usb.rs
+++ b/src/riot-rs-embassy/src/usb.rs
@@ -6,6 +6,7 @@ pub type UsbBuilder = embassy_usb::Builder<'static, UsbDriver>;
pub type UsbBuilderHook = &'static crate::delegate::Delegate;
+#[cfg_attr(not(feature = "_dev-doc"), doc(hidden))]
#[linkme::distributed_slice]
pub static USB_BUILDER_HOOKS: [UsbBuilderHook] = [..];
diff --git a/src/riot-rs-rt/Cargo.toml b/src/riot-rs-rt/Cargo.toml
index 556f6eaeb..3e14a17b9 100644
--- a/src/riot-rs-rt/Cargo.toml
+++ b/src/riot-rs-rt/Cargo.toml
@@ -41,5 +41,8 @@ _panic-handler = []
_esp32c3 = []
_esp32c6 = []
+# Enables developer docs, shows items that are otherwise hidden from users.
+_dev-doc = []
+
[dev-dependencies]
riot-rs-boards = { path = "../riot-rs-boards" }
diff --git a/src/riot-rs-rt/src/lib.rs b/src/riot-rs-rt/src/lib.rs
index 39d120340..da5cedf90 100644
--- a/src/riot-rs-rt/src/lib.rs
+++ b/src/riot-rs-rt/src/lib.rs
@@ -60,6 +60,7 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
use linkme::distributed_slice;
+#[cfg_attr(not(feature = "_dev-doc"), doc(hidden))]
#[distributed_slice]
pub static INIT_FUNCS: [fn()] = [..];
diff --git a/src/riot-rs-threads/Cargo.toml b/src/riot-rs-threads/Cargo.toml
index 3988bd974..45115cede 100644
--- a/src/riot-rs-threads/Cargo.toml
+++ b/src/riot-rs-threads/Cargo.toml
@@ -30,3 +30,7 @@ cortex-m.workspace = true
cortex-m-rt.workspace = true
cortex-m-semihosting.workspace = true
panic-semihosting = { version = "0.6.0", features = ["exit"] }
+
+[features]
+# Enables developer docs, shows items that are otherwise hidden from users.
+_dev-doc = []
diff --git a/src/riot-rs-threads/src/lib.rs b/src/riot-rs-threads/src/lib.rs
index ad3775a11..483dd7fcb 100644
--- a/src/riot-rs-threads/src/lib.rs
+++ b/src/riot-rs-threads/src/lib.rs
@@ -15,7 +15,7 @@ pub mod channel;
pub mod lock;
pub mod thread_flags;
-#[doc(hidden)]
+#[cfg_attr(not(feature = "_dev-doc"), doc(hidden))]
pub mod macro_reexports {
// Used by `autostart_thread`
pub use linkme;
@@ -39,8 +39,10 @@ pub const THREADS_NUMOF: usize = 16;
static THREADS: EnsureOnce = EnsureOnce::new(Threads::new());
+#[cfg_attr(not(feature = "_dev-doc"), doc(hidden))]
pub type ThreadFn = fn();
+#[cfg_attr(not(feature = "_dev-doc"), doc(hidden))]
#[linkme::distributed_slice]
pub static THREAD_FNS: [ThreadFn] = [..];
@@ -184,6 +186,7 @@ impl Threads {
///
/// Currently it expects at least:
/// - Cortex-M: to be called from the reset handler while MSP is active
+#[cfg_attr(not(feature = "_dev-doc"), doc(hidden))]
pub unsafe fn start_threading() {
Cpu::start_threading();
}
@@ -239,6 +242,7 @@ pub fn thread_create_noarg(func: fn(), stack: &'static mut [u8], prio: u8) -> Th
///
/// # Safety
/// only use when you know what you are doing.
+#[cfg_attr(not(feature = "_dev-doc"), doc(hidden))]
pub unsafe fn thread_create_raw(
func: usize,
arg: usize,
diff --git a/src/riot-rs/Cargo.toml b/src/riot-rs/Cargo.toml
index d9ddd9b45..24fa077bf 100644
--- a/src/riot-rs/Cargo.toml
+++ b/src/riot-rs/Cargo.toml
@@ -89,4 +89,11 @@ silent-panic = ["riot-rs-rt/silent-panic"]
## Allows to have no boards selected, useful to run target-independent tooling.
no-boards = ["riot-rs-boards/no-boards"]
+# Enables developer docs, shows items that are otherwise hidden from users.
+_dev-doc = [
+ "riot-rs-embassy/_dev-doc",
+ "riot-rs-rt/_dev-doc",
+ "riot-rs-threads/_dev-doc",
+]
+
net = ["riot-rs-embassy/net"]