#![no_std]
#[cfg(feature = "std")]
extern crate std;
pub use libpci_c_sys;
#[cfg(feature = "std")]
use std::os::raw::c_char;
#[cfg(not(feature = "std"))]
use libc::c_char;
#[cfg(not(feature = "std"))]
#[macro_use]
extern crate alloc;
use alloc::string::String;
use core::marker::PhantomData;
use core::{mem, ptr, str};
pub fn version_number() -> u32 {
libpci_c_sys::PCI_LIB_VERSION as u32
}
#[repr(u32)]
#[derive(Clone, PartialEq)]
pub enum Fill {
IDENT = libpci_c_sys::PCI_FILL_IDENT as u32,
IRQ = libpci_c_sys::PCI_FILL_IRQ as u32,
BASES = libpci_c_sys::PCI_FILL_BASES as u32,
RomBase = libpci_c_sys::PCI_FILL_ROM_BASE as u32,
SIZES = libpci_c_sys::PCI_FILL_SIZES as u32,
CLASS = libpci_c_sys::PCI_FILL_CLASS as u32,
CAPS = libpci_c_sys::PCI_FILL_CAPS as u32,
ExtCaps = libpci_c_sys::PCI_FILL_EXT_CAPS as u32,
PhysSlot = libpci_c_sys::PCI_FILL_PHYS_SLOT as u32,
ModuleAlias = libpci_c_sys::PCI_FILL_MODULE_ALIAS as u32,
LABEL = libpci_c_sys::PCI_FILL_LABEL as u32,
NumaNode = libpci_c_sys::PCI_FILL_NUMA_NODE as u32,
IoFlags = libpci_c_sys::PCI_FILL_IO_FLAGS as u32,
DtNode = libpci_c_sys::PCI_FILL_DT_NODE as u32,
IommuGroup = libpci_c_sys::PCI_FILL_IOMMU_GROUP as u32,
BridgeBases = libpci_c_sys::PCI_FILL_BRIDGE_BASES as u32,
RESCAN = libpci_c_sys::PCI_FILL_RESCAN as u32,
ClassExt = libpci_c_sys::PCI_FILL_CLASS_EXT as u32,
SUBSYS = libpci_c_sys::PCI_FILL_SUBSYS as u32,
PARENT = libpci_c_sys::PCI_FILL_PARENT as u32,
DRIVER = libpci_c_sys::PCI_FILL_DRIVER as u32,
}
#[repr(u32)]
#[derive(Clone, PartialEq)]
pub enum AccessType {
Auto = libpci_c_sys::pci_access_type_PCI_ACCESS_AUTO,
SysBusPci = libpci_c_sys::pci_access_type_PCI_ACCESS_SYS_BUS_PCI,
ProcBusPci = libpci_c_sys::pci_access_type_PCI_ACCESS_PROC_BUS_PCI,
I386Type1 = libpci_c_sys::pci_access_type_PCI_ACCESS_I386_TYPE1,
I386Type2 = libpci_c_sys::pci_access_type_PCI_ACCESS_I386_TYPE2,
FbsdDevice = libpci_c_sys::pci_access_type_PCI_ACCESS_FBSD_DEVICE,
AixDevice = libpci_c_sys::pci_access_type_PCI_ACCESS_AIX_DEVICE,
NbsdLibpci = libpci_c_sys::pci_access_type_PCI_ACCESS_NBSD_LIBPCI,
ObsdDevice = libpci_c_sys::pci_access_type_PCI_ACCESS_OBSD_DEVICE,
Dump = libpci_c_sys::pci_access_type_PCI_ACCESS_DUMP,
Darwin = libpci_c_sys::pci_access_type_PCI_ACCESS_DARWIN,
SylixosDevice = libpci_c_sys::pci_access_type_PCI_ACCESS_SYLIXOS_DEVICE,
HURD = libpci_c_sys::pci_access_type_PCI_ACCESS_HURD,
Win32Cfgmgr32 = libpci_c_sys::pci_access_type_PCI_ACCESS_WIN32_CFGMGR32,
Win32Kldbg = libpci_c_sys::pci_access_type_PCI_ACCESS_WIN32_KLDBG,
Win32Sysdbg = libpci_c_sys::pci_access_type_PCI_ACCESS_WIN32_SYSDBG,
MmioType1 = libpci_c_sys::pci_access_type_PCI_ACCESS_MMIO_TYPE1,
Type1Ext = libpci_c_sys::pci_access_type_PCI_ACCESS_MMIO_TYPE1_EXT,
Max = libpci_c_sys::pci_access_type_PCI_ACCESS_MAX,
}
impl From<u32> for AccessType {
fn from(value: u32) -> Self {
if value > AccessType::Max as u32 || value < AccessType::Auto as u32 {
return AccessType::Auto;
}
unsafe { mem::transmute(value) }
}
}
#[derive(Clone, Debug)]
pub struct PCIAccess<'a> {
handle: *mut libpci_c_sys::pci_access,
_phantom: PhantomData<&'a ()>,
}
pub struct PCIDevice<'a>(*mut libpci_c_sys::pci_dev, PhantomData<&'a ()>);
unsafe impl<'a> Send for PCIAccess<'a> {}
unsafe impl<'a> Send for PCIDevice<'a> {}
impl<'a> Drop for PCIDevice<'a> {
fn drop(&mut self) {}
}
impl<'a> Drop for PCIAccess<'a> {
fn drop(&mut self) {
if self.handle.is_null() {
return;
}
unsafe {
libpci_c_sys::pci_cleanup(self.handle);
}
}
}
impl<'a> PCIAccess<'a> {
pub fn try_new(do_scan: bool) -> Option<Self> {
let ptr: *mut libpci_c_sys::pci_access = unsafe { libpci_c_sys::pci_alloc() };
if ptr.is_null() {
None
} else {
unsafe {
libpci_c_sys::pci_init(ptr);
}
if do_scan {
unsafe {
libpci_c_sys::pci_scan_bus(ptr);
}
}
Some(Self { handle: ptr, _phantom: PhantomData })
}
}
pub fn new(do_scan: bool) -> Self {
Self::try_new(do_scan).expect("returned null pointer when allocating memory")
}
pub fn scan_bus(&mut self) {
if !self.handle.is_null() {
unsafe {
libpci_c_sys::pci_scan_bus(self.handle);
}
}
}
pub fn devices(&mut self) -> Option<PCIDevice> {
if self.handle.is_null() {
None
} else {
Some(unsafe { PCIDevice::from_raw((*self.handle).devices) })
}
}
}
impl<'a> Default for PCIDevice<'a> {
fn default() -> Self {
Self::new()
}
}
impl<'a> PCIDevice<'a> {
pub fn new() -> Self {
Self(ptr::null_mut::<libpci_c_sys::pci_dev>(), PhantomData)
}
pub unsafe fn from_raw(data: *mut libpci_c_sys::pci_dev) -> Self {
Self(data, PhantomData)
}
pub fn fill_info(&mut self, fill: u32) {
if !self.0.is_null() {
unsafe {
libpci_c_sys::pci_fill_info(self.0, fill as _);
}
}
}
pub fn class(&self) -> Option<String> {
if self.0.is_null() {
None
} else {
let mut class = vec![0_u8; 1024];
let size = (class.len() * mem::size_of::<u8>()) as usize;
unsafe {
libpci_c_sys::pci_lookup_class_helper(
(*self.0).access,
class.as_mut_ptr() as _,
size,
self.0,
);
}
Some(String::from(unsafe { c_char_to_str(class.as_ptr() as _) }))
}
}
pub fn vendor(&self) -> Option<String> {
if self.0.is_null() {
None
} else {
let mut vendor = vec![0_u8; 256];
let size = (vendor.len() * mem::size_of::<u8>()) as usize;
unsafe {
libpci_c_sys::pci_lookup_vendor_helper(
(*self.0).access,
vendor.as_mut_ptr() as _,
size,
self.0,
);
}
Some(String::from(unsafe { c_char_to_str(vendor.as_ptr() as _) }))
}
}
pub fn device(&self) -> Option<String> {
if self.0.is_null() {
None
} else {
let mut device = vec![0_u8; 256];
let size = (device.len() * mem::size_of::<u8>()) as usize;
unsafe {
libpci_c_sys::pci_lookup_device_helper(
(*self.0).access,
device.as_mut_ptr() as _,
size,
self.0,
);
}
Some(String::from(unsafe { c_char_to_str(device.as_ptr() as _) }))
}
}
pub fn class_id(&self) -> Option<u16> {
if self.0.is_null() {
None
} else {
let inner_obj = unsafe { *self.0 };
Some(inner_obj.device_class)
}
}
pub fn vendor_id(&self) -> Option<u16> {
if self.0.is_null() {
None
} else {
let inner_obj = unsafe { *self.0 };
Some(inner_obj.vendor_id)
}
}
pub fn device_id(&self) -> Option<u16> {
if self.0.is_null() {
None
} else {
let inner_obj = unsafe { *self.0 };
Some(inner_obj.device_id)
}
}
pub fn domain(&self) -> Option<i32> {
if self.0.is_null() {
None
} else {
let inner_obj = unsafe { *self.0 };
Some(inner_obj.domain)
}
}
pub fn bus(&self) -> Option<u8> {
if self.0.is_null() {
None
} else {
let inner_obj = unsafe { *self.0 };
Some(inner_obj.bus)
}
}
pub fn dev(&self) -> Option<u8> {
if self.0.is_null() {
None
} else {
let inner_obj = unsafe { *self.0 };
Some(inner_obj.dev)
}
}
pub fn func(&self) -> Option<u8> {
if self.0.is_null() {
None
} else {
let inner_obj = unsafe { *self.0 };
Some(inner_obj.func)
}
}
pub fn iter_mut(&self) -> IterMut<'_> {
IterMut { ptr: self.0 as _, _phantom: PhantomData }
}
}
pub struct IterMut<'a> {
ptr: *mut libpci_c_sys::pci_dev,
_phantom: PhantomData<&'a mut ()>,
}
impl<'a> Iterator for IterMut<'a> {
type Item = PCIDevice<'a>;
fn next(&mut self) -> Option<PCIDevice<'a>> {
let ptr = self.ptr;
self.ptr = unsafe { libpci_c_sys::pci_get_next_device_mut(self.ptr) };
if ptr.is_null() {
None
} else {
Some(unsafe { PCIDevice::from_raw(ptr) })
}
}
}
unsafe fn c_char_to_str(text: *const c_char) -> &'static str {
if text.is_null() {
return "";
}
#[cfg(not(feature = "std"))]
{
let len = libc::strlen(text);
let slice = core::slice::from_raw_parts(text as *mut u8, len);
str::from_utf8(slice).expect("bad error message from libpci")
}
#[cfg(feature = "std")]
{
std::ffi::CStr::from_ptr(text).to_str().expect("bad error message from libpci")
}
}