use super::{PciIoAddress, PciIoUnit, encode_io_mode_and_unit};
use crate::StatusExt;
#[cfg(feature = "alloc")]
use crate::proto::pci::configuration::QwordAddressSpaceDescriptor;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use core::ffi::c_void;
use core::ptr;
use uefi_macros::unsafe_protocol;
use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol};
#[cfg(doc)]
use crate::Status;
#[derive(Debug)]
#[repr(transparent)]
#[unsafe_protocol(PciRootBridgeIoProtocol::GUID)]
pub struct PciRootBridgeIo(PciRootBridgeIoProtocol);
impl PciRootBridgeIo {
#[must_use]
pub const fn segment_nr(&self) -> u32 {
self.0.segment_number
}
pub const fn pci(&mut self) -> PciIoAccessPci<'_> {
PciIoAccessPci {
proto: &mut self.0,
io_access: &mut self.0.pci,
}
}
pub fn flush(&mut self) -> crate::Result<()> {
unsafe { (self.0.flush)(&mut self.0).to_result() }
}
#[cfg(feature = "alloc")]
pub fn configuration(&self) -> crate::Result<Vec<QwordAddressSpaceDescriptor>> {
use crate::proto::pci::configuration;
let mut resources: *const c_void = ptr::null();
unsafe {
((self.0.configuration)(&self.0, &mut resources))
.to_result_with_val(|| configuration::parse(resources))
}
}
#[cfg(feature = "alloc")]
pub fn enumerate(&mut self) -> crate::Result<super::enumeration::PciTree> {
use super::enumeration::{self, PciTree};
use crate::proto::pci::configuration::ResourceRangeType;
let mut tree = PciTree::new(self.segment_nr());
for descriptor in self.configuration()? {
if descriptor.resource_range_type == ResourceRangeType::Bus {
for bus in (descriptor.address_min as u8)..=(descriptor.address_max as u8) {
if tree.should_visit_bus(bus) {
let addr = PciIoAddress::new(bus, 0, 0);
enumeration::visit_bus(self, addr, &mut tree)?;
}
}
}
}
Ok(tree)
}
}
#[derive(Debug)]
pub struct PciIoAccessPci<'a> {
proto: *mut PciRootBridgeIoProtocol,
io_access: &'a mut PciRootBridgeIoAccess,
}
impl PciIoAccessPci<'_> {
pub fn read_one<U: PciIoUnit>(&self, addr: PciIoAddress) -> crate::Result<U> {
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Normal);
let mut result = U::default();
unsafe {
(self.io_access.read)(
self.proto,
width_mode,
addr.into(),
1,
ptr::from_mut(&mut result).cast(),
)
.to_result_with_val(|| result)
}
}
pub fn write_one<U: PciIoUnit>(&self, addr: PciIoAddress, data: U) -> crate::Result<()> {
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Normal);
unsafe {
(self.io_access.write)(
self.proto,
width_mode,
addr.into(),
1,
ptr::from_ref(&data).cast(),
)
.to_result()
}
}
pub fn read<U: PciIoUnit>(&self, addr: PciIoAddress, data: &mut [U]) -> crate::Result<()> {
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Normal);
unsafe {
(self.io_access.read)(
self.proto,
width_mode,
addr.into(),
data.len(),
data.as_mut_ptr().cast(),
)
.to_result()
}
}
pub fn write<U: PciIoUnit>(&self, addr: PciIoAddress, data: &[U]) -> crate::Result<()> {
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Normal);
unsafe {
(self.io_access.write)(
self.proto,
width_mode,
addr.into(),
data.len(),
data.as_ptr().cast(),
)
.to_result()
}
}
pub fn fill_write<U: PciIoUnit>(
&self,
addr: PciIoAddress,
count: usize,
data: U,
) -> crate::Result<()> {
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Fill);
unsafe {
(self.io_access.write)(
self.proto,
width_mode,
addr.into(),
count,
ptr::from_ref(&data).cast(),
)
.to_result()
}
}
pub fn fifo_read<U: PciIoUnit>(&self, addr: PciIoAddress, data: &mut [U]) -> crate::Result<()> {
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Fifo);
unsafe {
(self.io_access.read)(
self.proto,
width_mode,
addr.into(),
data.len(),
data.as_mut_ptr().cast(),
)
.to_result()
}
}
pub fn fifo_write<U: PciIoUnit>(&self, addr: PciIoAddress, data: &[U]) -> crate::Result<()> {
let width_mode = encode_io_mode_and_unit::<U>(super::PciIoMode::Fifo);
unsafe {
(self.io_access.write)(
self.proto,
width_mode,
addr.into(),
data.len(),
data.as_ptr().cast(),
)
.to_result()
}
}
}