pci_types/capability/
msix.rs

1use super::PciCapabilityAddress;
2use crate::ConfigRegionAccess;
3use bit_field::BitField;
4
5#[derive(Clone, Copy, Debug)]
6pub struct MsixCapability {
7    pub(super) address: PciCapabilityAddress,
8    table_size: u16,
9    /// Table BAR in bits 0..3 and offset into that BAR in bits 3..31
10    table: u32,
11    /// Pending Bit Array BAR in bits 0..3 and offset into that BAR in bits 3..31
12    pba: u32,
13}
14
15impl MsixCapability {
16    pub(crate) fn new(
17        address: PciCapabilityAddress,
18        control: u16,
19        access: impl ConfigRegionAccess,
20    ) -> MsixCapability {
21        let table_size = control.get_bits(0..11) + 1;
22        let table = unsafe { access.read(address.address, address.offset + 0x04) };
23        let pba = unsafe { access.read(address.address, address.offset + 0x08) };
24        MsixCapability { address, table_size, table, pba }
25    }
26
27    /// Enable MSI-X on the specified device feature.
28    ///
29    /// Unlike with MSI, the MSI message data and delivery address is not contained within the
30    /// capability, but instead in system memory, and pointed to by the BAR specified by
31    /// [`MsixCapability::table_bar`] and [`MsixCapability::table_offset`]. The caller is therefore
32    /// responsible for configuring this separately, as this crate does not have access to
33    /// arbitrary physical memory.
34    pub fn set_enabled(&mut self, enabled: bool, access: impl ConfigRegionAccess) {
35        let mut control = unsafe { access.read(self.address.address, self.address.offset) };
36        control.set_bit(31, enabled);
37        unsafe {
38            access.write(self.address.address, self.address.offset, control);
39        }
40    }
41
42    pub fn enabled(&self, access: impl ConfigRegionAccess) -> bool {
43        let control = unsafe { access.read(self.address.address, self.address.offset) };
44        control.get_bit(31)
45    }
46
47    /// Enable/disable masking of all interrupts for this PCI function.
48    ///
49    /// Individual interrupt sources can be masked using mask field of the corresponding entry in
50    /// the MSI-X table.
51    pub fn set_function_mask(&mut self, mask: bool, access: impl ConfigRegionAccess) {
52        let mut control = unsafe { access.read(self.address.address, self.address.offset) };
53        control.set_bit(30, mask);
54        unsafe {
55            access.write(self.address.address, self.address.offset, control);
56        }
57    }
58
59    pub fn function_mask(&self, access: impl ConfigRegionAccess) -> bool {
60        let control = unsafe { access.read(self.address.address, self.address.offset) };
61        control.get_bit(30)
62    }
63
64    /// The index of the BAR that contains the MSI-X table.
65    pub fn table_bar(&self) -> u8 {
66        self.table.get_bits(0..3) as u8
67    }
68
69    /// The offset, in bytes, of the MSI-X table within its BAR.
70    pub fn table_offset(&self) -> u32 {
71        /*
72         * To form the offset into the BAR, we mask off (set to zero) the lowest 3 bits, but
73         * they're retained as part of the offset.
74         */
75        self.table & !0b111
76    }
77
78    pub fn table_size(&self) -> u16 {
79        self.table_size
80    }
81
82    pub fn pba_bar(&self) -> u8 {
83        self.pba.get_bits(0..3) as u8
84    }
85
86    pub fn pba_offset(&self) -> u32 {
87        /*
88         * To form the offset into the BAR, we mask off (set to zero) the lowest 3 bits, but
89         * they're retained as part of the offset.
90         */
91        self.pba & !0b111
92    }
93}