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
    }
}