pci_x86/
cap.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2025.
4
5//! PCI capabilities list
6//!
7//! This module provides functionality for traversing and interacting with
8//! the capability list of a PCI device, as described in section 6.7 of the
9//! PCI Local Bus specification.
10
11use core::ops::Deref;
12
13use crate::device::Device;
14
15/// A single capability within a specific device's configuration space.
16///
17/// This is a generic representation of a capability. It provides only
18/// low-level methods for reading and writing the capability fields. Other
19/// structs in this module will wrap this struct and provide higher level
20/// methods for accessing specific fields.
21///
22/// The `'a` lifetime references the [`Device`] this capability belongs to.
23#[derive(Copy, Clone, Debug)]
24pub struct BaseCap<'a> {
25    dev: &'a Device,
26    ptr: u16,
27}
28
29impl BaseCap<'_> {
30    /// Returns the ID of this capability.
31    pub fn id(&self) -> u8 {
32        self.dev.read8(self.ptr)
33    }
34
35    /// Returns the next capability pointer.
36    pub fn next(&self) -> u8 {
37        self.dev.read8(self.ptr + 1)
38    }
39
40    /// Reads an 8-bit value relative to the base of this capability.
41    #[inline]
42    pub fn read8(&self, offset: u16) -> u8 {
43        self.dev.read8(self.ptr + offset)
44    }
45
46    /// Writes an 8-bit value relative to the base of this capability.
47    #[inline]
48    pub fn write8(&self, offset: u16, val: u8) {
49        self.dev.write8(self.ptr + offset, val)
50    }
51
52    /// Reads a 16-bit value relative to the base of this capability.
53    #[inline]
54    pub fn read16(&self, offset: u16) -> u16 {
55        self.dev.read16(self.ptr + offset)
56    }
57
58    /// Writes a 16-bit value relative to the base of this capability.
59    #[inline]
60    pub fn write16(&self, offset: u16, val: u16) {
61        self.dev.write16(self.ptr + offset, val)
62    }
63
64    /// Reads a 32-bit value relative to the base of this capability.
65    #[inline]
66    pub fn read32(&self, offset: u16) -> u32 {
67        self.dev.read32(self.ptr + offset)
68    }
69
70    /// Writes a 32-bit value relative to the base of this capability.
71    #[inline]
72    pub fn write32(&self, offset: u16, val: u32) {
73        self.dev.write32(self.ptr + offset, val)
74    }
75}
76
77/// Message signaled interrupt (MSI) capability
78pub struct MsiCap<'a>(BaseCap<'a>);
79
80impl MsiCap<'_> {
81    /// Capability ID for MSI
82    pub const ID: u8 = 0x05;
83
84    /// Returns the MSI control register
85    pub fn control(&self) -> u16 {
86        self.0.dev.read16(self.0.ptr + 2)
87    }
88
89    /// Returns true if MSI is enabled
90    pub fn enabled(&self) -> bool {
91        self.control() & 0x0001 != 0
92    }
93
94    /// Disables MSI by clearing the enable bit
95    pub fn disable(&self) {
96        let mut ctrl = self.control();
97        ctrl &= !0x0001;
98        self.0.dev.write16(self.0.ptr + 2, ctrl);
99    }
100}
101
102/// Vendor specific capability
103///
104/// Implements `Deref` with `Target=BaseCap`, allowing the use of
105/// `BaseCap` methods to read/write raw capability data.
106pub struct VendorCap<'a>(BaseCap<'a>);
107
108impl VendorCap<'_> {
109    /// Capability ID for Vendor-Specific (0x09)
110    pub const ID: u8 = 0x09;
111
112    /// Returns the length field (at cap+2)
113    pub fn length(&self) -> u8 {
114        self.0.dev.read8(self.0.ptr + 2)
115    }
116}
117
118impl<'a> Deref for VendorCap<'a> {
119    type Target = BaseCap<'a>;
120    fn deref(&self) -> &Self::Target {
121        &self.0
122    }
123}
124
125/// MSI-X Capability
126pub struct MsixCap<'a>(BaseCap<'a>);
127
128impl MsixCap<'_> {
129    /// Capability ID for MSI-X
130    pub const ID: u8 = 0x11;
131
132    /// Returns the MSI-X control register
133    pub fn control(&self) -> u16 {
134        self.0.dev.read16(self.0.ptr + 2)
135    }
136
137    /// Returns true if MSI-X is enabled
138    pub fn enabled(&self) -> bool {
139        self.control() & 0x8000 != 0
140    }
141
142    /// Disables MSI-X by clearing the enable bit
143    pub fn disable(&self) {
144        let mut ctrl = self.control();
145        ctrl &= !0x8000;
146        self.0.dev.write16(self.0.ptr + 2, ctrl);
147    }
148}
149
150/// Enum representing supported capability types
151pub enum Cap<'a> {
152    Msi(MsiCap<'a>),
153    Msix(MsixCap<'a>),
154    Vendor(VendorCap<'a>),
155    Other(BaseCap<'a>),
156}
157
158impl<'a> From<BaseCap<'a>> for Cap<'a> {
159    fn from(base: BaseCap<'a>) -> Self {
160        match base.id() {
161            MsiCap::ID => Cap::Msi(MsiCap(base)),
162            MsixCap::ID => Cap::Msix(MsixCap(base)),
163            VendorCap::ID => Cap::Vendor(VendorCap(base)),
164            _ => Cap::Other(base),
165        }
166    }
167}
168
169/// Iterator over supported capabilities for a given device.
170pub struct CapIter<'a> {
171    dev: &'a Device,
172    cur: u16,
173    visited: u8,
174}
175
176impl<'a> CapIter<'a> {
177    /// Creates a new capability iterator for the given device.
178    pub(crate) fn new(dev: &'a Device) -> Self {
179        let cur = dev.cap_ptr().unwrap_or(0) as u16;
180        Self {
181            dev,
182            cur,
183            visited: 0,
184        }
185    }
186}
187
188impl<'a> Iterator for CapIter<'a> {
189    type Item = Cap<'a>;
190
191    fn next(&mut self) -> Option<Self::Item> {
192        // Pointer value of 0 marks end of capabilities list
193        if self.cur == 0 {
194            return None;
195        }
196
197        // Protect against malformed capabilities list
198        if self.cur >= 256 {
199            return None;
200        }
201        if self.visited > 64 {
202            return None;
203        }
204
205        let base = BaseCap {
206            dev: self.dev,
207            ptr: self.cur,
208        };
209
210        self.cur = base.next() as u16;
211        self.visited += 1;
212
213        Some(base.into())
214    }
215}