#![no_std]
#![feature(doc_cfg)]
#![warn(missing_docs)]
#![warn(clippy::pedantic)]
#![allow(clippy::must_use_candidate)]
#![allow(clippy::upper_case_acronyms)]
use access::{ConfigAccess, ConfigSelector};
use core::iter;
pub mod access;
pub mod config;
pub const MAX_DEVICE: u8 = (1 << 5) - 1;
pub const MAX_FUNCTION: u8 = (1 << 3) - 1;
pub const INVALID_VENDOR: u16 = 0xffff;
pub fn enumerate_bus<A>(
access: &A,
bus_selector: ConfigSelector,
) -> impl Iterator<Item = ConfigSelector> + '_
where
A: ConfigAccess,
{
enumerate_bus_devices(access, bus_selector)
.flat_map(move |device| enumerate_device_functions(access, device))
}
pub fn enumerate_bus_devices<A>(
access: &A,
bus_selector: ConfigSelector,
) -> impl Iterator<Item = ConfigSelector> + '_
where
A: ConfigAccess,
{
(0..=MAX_DEVICE).filter_map(move |device| {
let selector = ConfigSelector { device, function: 0, ..bus_selector };
if check_valid(access, selector) {
Some(selector)
} else {
None
}
})
}
pub fn enumerate_device_functions<A>(
access: &A,
device_selector: ConfigSelector,
) -> impl Iterator<Item = ConfigSelector> + '_
where
A: ConfigAccess,
{
let fn_0_register: config::HeaderRegister3 =
access.get_fixed_register(device_selector);
let function_range =
if fn_0_register.multi_function() { 0..MAX_FUNCTION } else { 0..1 };
function_range.filter_map(move |function| {
let fn_selector = ConfigSelector { function, ..device_selector };
if check_valid(access, fn_selector) {
Some(fn_selector)
} else {
None
}
})
}
pub fn check_valid<A>(access: &A, selector: ConfigSelector) -> bool
where
A: ConfigAccess,
{
let id_register: config::HeaderRegister0 = access.get_fixed_register(selector);
id_register.valid()
}
pub struct CapabilityEntry {
pub id: u8,
pub register: u16,
}
pub fn iter_capabilities<A>(
access: &A,
selector: ConfigSelector,
) -> impl Iterator<Item = CapabilityEntry> + '_
where
A: ConfigAccess,
{
let capability_header: config::Type0HeaderRegister13 =
access.get_fixed_register(selector);
let mut next_offset = capability_header.capabilities_offset();
iter::from_fn(move || {
if next_offset == 0 {
None
} else {
let register = u16::from(next_offset / 4);
let capability: config::GenericCapabilityRegister =
access.get_register(selector, register).into();
next_offset = capability.next_offset();
Some(CapabilityEntry { id: capability.id(), register })
}
})
}