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
144
145
146
147
148
149
150
151
152
153
154
// SPDX-License-Identifier: MPL-2.0

#![allow(dead_code)]
#![allow(unused_variables)]

use alloc::vec::Vec;

use super::{
    capability::Capability,
    cfg_space::{AddrLen, Bar, Command, PciDeviceCommonCfgOffset, Status},
    device_info::{PciDeviceId, PciDeviceLocation},
};

/// PCI common device, Contains a range of information and functions common to PCI devices.
#[derive(Debug)]
pub struct PciCommonDevice {
    device_id: PciDeviceId,
    location: PciDeviceLocation,
    bar_manager: BarManager,
    capabilities: Vec<Capability>,
}

impl PciCommonDevice {
    pub fn device_id(&self) -> &PciDeviceId {
        &self.device_id
    }

    pub fn location(&self) -> &PciDeviceLocation {
        &self.location
    }

    pub fn bar_manager(&self) -> &BarManager {
        &self.bar_manager
    }

    pub fn capabilities(&self) -> &Vec<Capability> {
        &self.capabilities
    }

    pub fn command(&self) -> Command {
        Command::from_bits_truncate(
            self.location
                .read16(PciDeviceCommonCfgOffset::Command as u16),
        )
    }

    pub fn set_command(&self, command: Command) {
        self.location
            .write16(PciDeviceCommonCfgOffset::Command as u16, command.bits())
    }

    pub fn status(&self) -> Status {
        Status::from_bits_truncate(
            self.location
                .read16(PciDeviceCommonCfgOffset::Status as u16),
        )
    }

    pub(super) fn new(location: PciDeviceLocation) -> Option<Self> {
        if location.read16(0) == 0xFFFF {
            // not exists
            return None;
        }

        let capabilities = Vec::new();
        let device_id = PciDeviceId::new(location);
        let bar_manager = BarManager::new(location);
        let mut device = Self {
            device_id,
            location,
            bar_manager,
            capabilities,
        };
        device.capabilities = Capability::device_capabilities(&mut device);
        Some(device)
    }

    pub(super) fn bar_manager_mut(&mut self) -> &mut BarManager {
        &mut self.bar_manager
    }

    pub(super) fn capabilities_mut(&mut self) -> &mut Vec<Capability> {
        &mut self.capabilities
    }
}

#[derive(Debug)]
pub struct BarManager {
    /// BARs, the bool indicate whether this bar should exposed to unprivileged part.
    bars: [Option<(Bar, bool)>; 6],
}

impl BarManager {
    /// Gain access to the BAR space and return None if that BAR is set to be invisible or absent.
    pub fn bar(&self, idx: u8) -> Option<Bar> {
        let (bar, visible) = self.bars[idx as usize].clone()?;
        if visible {
            Some(bar)
        } else {
            None
        }
    }

    /// Parse the BAR space by PCI device location.
    fn new(location: PciDeviceLocation) -> Self {
        let header_type = location.read8(PciDeviceCommonCfgOffset::HeaderType as u16) & !(1 << 7);
        // Get the max bar amount, header type=0 => end device; header type=1 => PCI bridge.
        let max = match header_type {
            0 => 6,
            1 => 2,
            _ => 0,
        };
        let mut idx = 0;
        let mut bars = [None, None, None, None, None, None];
        while idx < max {
            if let Ok(bar) = Bar::new(location, idx) {
                let mut idx_step = 0;
                match &bar {
                    Bar::Memory(memory_bar) => {
                        if memory_bar.address_length() == AddrLen::Bits64 {
                            idx_step = 1;
                        }
                    }
                    Bar::Io(_) => {}
                }
                bars[idx as usize] = Some((bar, true));
                idx += idx_step;
            }
            idx += 1;
        }
        Self { bars }
    }

    pub(super) fn set_invisible(&mut self, idx: u8) {
        if self.bars[idx as usize].is_some() {
            let Some((bar, _)) = self.bars[idx as usize].clone() else {
                return;
            };
            self.bars[idx as usize] = Some((bar, false));
        }
        let Some((_, visible)) = self.bars[idx as usize] else {
            return;
        };
    }

    /// Gain access to the BAR space and return None if that BAR is absent.
    pub(super) fn bar_space_without_invisible(&self, idx: u8) -> Option<Bar> {
        if let Some((bar, _)) = self.bars[idx as usize].clone() {
            Some(bar)
        } else {
            None
        }
    }
}