use crate::ibverbs::device::{Context, Guid};
use crate::ibverbs::error::{IbvError, IbvResult};
use ibverbs_sys::*;
use std::ffi::CStr;
use std::io;
use std::marker::PhantomData;
pub fn open_device(name: impl AsRef<str>) -> IbvResult<Context> {
let name = name.as_ref();
let devices = list_devices()?;
let device = devices
.iter()
.find(|d| d.name() == Some(name))
.ok_or_else(|| IbvError::NotFound(format!("Device '{name}' not found")))?;
device.open()
}
pub fn list_devices() -> IbvResult<DeviceList> {
let mut num_devices = 0i32;
let devices_ptr = unsafe { ibv_get_device_list(&mut num_devices as *mut _) };
if devices_ptr.is_null() {
let errno = io::Error::last_os_error()
.raw_os_error()
.expect("ibv_get_device_list should set errno on error");
if errno != 0 {
return Err(IbvError::from_errno_with_msg(
errno,
"Failed to list devices",
));
}
}
log::debug!("DeviceList created");
#[allow(clippy::cast_sign_loss)]
Ok(DeviceList {
devices_ptr,
num_devices: num_devices as usize,
})
}
#[doc(alias = "ibv_device")]
#[doc(alias = "ibv_get_device_list")]
pub struct DeviceList {
devices_ptr: *mut *mut ibv_device,
num_devices: usize,
}
unsafe impl Sync for DeviceList {}
unsafe impl Send for DeviceList {}
impl Drop for DeviceList {
fn drop(&mut self) {
if !self.devices_ptr.is_null() {
log::debug!("DeviceList dropped");
unsafe { ibv_free_device_list(self.devices_ptr) };
}
}
}
impl DeviceList {
pub fn iter(&self) -> DeviceListIter<'_> {
DeviceListIter { list: self, i: 0 }
}
pub fn len(&self) -> usize {
self.num_devices
}
pub fn is_empty(&self) -> bool {
self.num_devices == 0
}
pub fn get(&self, index: usize) -> Option<Device<'_>> {
if index >= self.num_devices {
return None;
}
Some(unsafe { Device::from_ptr(*self.devices_ptr.add(index)) })
}
}
impl<'a> IntoIterator for &'a DeviceList {
type Item = <DeviceListIter<'a> as Iterator>::Item;
type IntoIter = DeviceListIter<'a>;
fn into_iter(self) -> Self::IntoIter {
DeviceListIter { list: self, i: 0 }
}
}
pub struct DeviceListIter<'iter> {
list: &'iter DeviceList,
i: usize,
}
impl<'iter> Iterator for DeviceListIter<'iter> {
type Item = Device<'iter>;
fn next(&mut self) -> Option<Self::Item> {
let opt_device = self.list.get(self.i);
if opt_device.is_some() {
self.i += 1;
}
opt_device
}
}
impl std::fmt::Debug for DeviceList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
pub struct Device<'a> {
pub(super) device_ptr: *mut ibv_device,
_dev_list: PhantomData<&'a DeviceList>,
}
unsafe impl Sync for Device<'_> {}
unsafe impl Send for Device<'_> {}
impl Device<'_> {
pub fn open(&self) -> IbvResult<Context> {
Context::from_device(self)
}
pub fn name(&self) -> Option<&str> {
let name_ptr = unsafe { ibv_get_device_name(self.device_ptr) };
if name_ptr.is_null() {
None
} else {
unsafe { CStr::from_ptr(name_ptr).to_str().ok() }
}
}
pub fn guid(&self) -> IbvResult<Guid> {
let guid_int = unsafe { ibv_get_device_guid(self.device_ptr) };
let guid: Guid = guid_int.into();
if guid.is_reserved() {
Err(IbvError::from_errno_with_msg(
io::Error::last_os_error()
.raw_os_error()
.expect("ibv_get_device_guid should set errno on error"),
"GUID is reserved or invalid",
))
} else {
Ok(guid)
}
}
}
impl std::fmt::Debug for Device<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name_str = self.name().unwrap_or("<unknown>");
let guid_str = match self.guid() {
Ok(g) => format!("{:?}", g),
Err(_) => "<error>".to_string(),
};
f.debug_struct("Device")
.field("name", &name_str)
.field("guid", &guid_str)
.finish()
}
}
impl Device<'_> {
pub(super) unsafe fn from_ptr(device_ptr: *mut ibv_device) -> Self {
Self {
device_ptr,
_dev_list: PhantomData,
}
}
}