tartan_pci/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! Support for Peripheral Component Interconnect (PCI) and PCI Express (`PCIe`) devices.

#![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;

/// Access methods for PCI configuration space.
pub mod access;

/// Data exposed in PCI configuration space.
pub mod config;

/// Highest device number allowed by the PCI specification.
pub const MAX_DEVICE: u8 = (1 << 5) - 1;

/// Highest function number allowed by the PCI specification.
pub const MAX_FUNCTION: u8 = (1 << 3) - 1;

/// Placeholder value that will always be returned in the vendor field when querying PCI
/// configuration space for a device that does not exist.
pub const INVALID_VENDOR: u16 = 0xffff;


/// Iterate over the devices and functions present on the specified bus.
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))
}


/// Iterate over the devices present on the specified bus.
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
        }
    })
}

/// Iterate over the functions available on the specified device.
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
        }
    })
}

/// Return true if a function is available at the specified selector.
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()
}

/// Information to identify and locate capability registers
pub struct CapabilityEntry {
    /// Capability ID defined by PCI-SIG
    pub id: u8,
    /// Register number in config space (offset in 4-byte units) of the start of the
    /// capability data.
    pub register: u16,
}

/// Iterate over all capabilities defined in the configuration space for a PCI function
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 })
        }
    })
}