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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// SPDX-License-Identifier: MPL-2.0

#![allow(dead_code)]

use alloc::vec::Vec;

use self::{msix::CapabilityMsixData, vendor::CapabilityVndrData};
use super::{
    cfg_space::{PciDeviceCommonCfgOffset, Status},
    common_device::PciCommonDevice,
    PciDeviceLocation,
};

pub mod msix;
pub mod vendor;

#[derive(Debug)]
pub struct Capability {
    id: u8,
    /// Pointer to the capability.
    pos: u16,
    /// Next Capability pointer, 0xFC if self is the last one.
    next_ptr: u16,
    /// The length of this Capability
    len: u16,
    cap_data: CapabilityData,
}

#[derive(Debug, Clone)]
pub enum CapabilityData {
    /// Id:0x01, Power Management
    Pm,
    /// Id:0x02, Accelerated Graphics Part
    Agp,
    /// Id:0x03, Vital Product Data
    Vpd,
    /// Id:0x04, Slot Identification
    SlotId,
    /// Id:0x05, Message Signalled Interrupts
    Msi,
    /// Id:0x06, CompactPCI HotSwap
    Chswp,
    /// Id:0x07, PCI-X
    PciX,
    /// Id:0x08, HyperTransport
    Hp,
    /// Id:0x09, Vendor-Specific
    Vndr(CapabilityVndrData),
    /// Id:0x0A, Debug port
    Dbg,
    /// Id:0x0B, CompactPCI Central Resource Control
    Ccrc,
    /// Id:0x0C, PCI Standard Hot-Plug Controller
    Shpc,
    /// Id:0x0D, Bridge subsystem vendor/device ID
    Ssvid,
    /// Id:0x0R, AGP Target PCI-PCI bridge
    Agp3,
    /// Id:0x0F, Secure Device
    Secdev,
    /// Id:0x10, PCI Express
    Exp,
    /// Id:0x11, MSI-X
    Msix(CapabilityMsixData),
    /// Id:0x12, SATA Data/Index Conf
    Sata,
    /// Id:0x13, PCI Advanced Features
    Af,
    /// Id:0x14, Enhanced Allocation
    Ea,
    /// Id:?, Unknown
    Unknown(u8),
}

impl Capability {
    /// 0xFC, the top of the capability position.
    const CAPABILITY_TOP: u16 = 0xFC;

    pub fn capability_data(&self) -> &CapabilityData {
        &self.cap_data
    }

    /// get the capabilities of one device
    pub(super) fn device_capabilities(dev: &mut PciCommonDevice) -> Vec<Self> {
        if !dev.status().contains(Status::CAPABILITIES_LIST) {
            return Vec::new();
        }
        let mut capabilities = Vec::new();
        let mut cap_ptr =
            dev.location()
                .read8(PciDeviceCommonCfgOffset::CapabilitiesPointer as u16) as u16
                & PciDeviceLocation::BIT32_ALIGN_MASK;
        let mut cap_ptr_vec = Vec::new();
        // read all cap_ptr so that it is easy for us to get the length.
        while cap_ptr > 0 {
            cap_ptr_vec.push(cap_ptr);
            cap_ptr =
                dev.location().read8(cap_ptr + 1) as u16 & PciDeviceLocation::BIT32_ALIGN_MASK;
        }
        cap_ptr_vec.sort();
        // Push here so that we can calculate the length of the last capability.
        cap_ptr_vec.push(Self::CAPABILITY_TOP);
        let length = cap_ptr_vec.len();
        for i in 0..length - 1 {
            let cap_ptr = cap_ptr_vec[i];
            let next_ptr = cap_ptr_vec[i + 1];
            let cap_type = dev.location().read8(cap_ptr);
            let data = match cap_type {
                0x01 => CapabilityData::Pm,
                0x02 => CapabilityData::Agp,
                0x03 => CapabilityData::Vpd,
                0x04 => CapabilityData::SlotId,
                0x05 => CapabilityData::Msi,
                0x06 => CapabilityData::Chswp,
                0x07 => CapabilityData::PciX,
                0x08 => CapabilityData::Hp,
                0x09 => {
                    CapabilityData::Vndr(CapabilityVndrData::new(dev, cap_ptr, next_ptr - cap_ptr))
                }
                0x0A => CapabilityData::Dbg,
                0x0B => CapabilityData::Ccrc,
                0x0C => CapabilityData::Shpc,
                0x0D => CapabilityData::Ssvid,
                0x0E => CapabilityData::Agp3,
                0x0F => CapabilityData::Secdev,
                0x10 => CapabilityData::Exp,
                0x11 => CapabilityData::Msix(CapabilityMsixData::new(dev, cap_ptr)),
                0x12 => CapabilityData::Sata,
                0x13 => CapabilityData::Af,
                0x14 => CapabilityData::Ea,
                _ => CapabilityData::Unknown(cap_type),
            };
            capabilities.push(Self {
                id: cap_type,
                pos: cap_ptr,
                next_ptr,
                len: next_ptr - cap_ptr,
                cap_data: data,
            });
        }
        capabilities
    }
}