use crate::{
alloc,
alloc::allocator::VmallocPageIter,
bindings,
device::{Bound, Device},
devres::Devres,
dma, error,
io::ResourceSize,
page,
prelude::*,
sync::aref::ARef,
types::Opaque,
};
use core::{ops::Deref, ptr::NonNull};
#[repr(transparent)]
pub struct SGEntry(Opaque<bindings::scatterlist>);
unsafe impl Send for SGEntry {}
unsafe impl Sync for SGEntry {}
impl SGEntry {
#[inline]
unsafe fn from_raw<'a>(ptr: *mut bindings::scatterlist) -> &'a Self {
unsafe { &*ptr.cast() }
}
#[inline]
fn as_raw(&self) -> *mut bindings::scatterlist {
self.0.get()
}
#[inline]
pub fn dma_address(&self) -> dma::DmaAddress {
unsafe { bindings::sg_dma_address(self.as_raw()) }
}
#[inline]
pub fn dma_len(&self) -> ResourceSize {
#[allow(clippy::useless_conversion)]
unsafe { bindings::sg_dma_len(self.as_raw()) }.into()
}
}
#[repr(transparent)]
pub struct Borrowed(Opaque<bindings::sg_table>);
unsafe impl Send for Borrowed {}
unsafe impl Sync for Borrowed {}
#[repr(transparent)]
#[pin_data]
pub struct SGTable<T: private::Sealed = Borrowed> {
#[pin]
inner: T,
}
impl SGTable {
#[inline]
pub unsafe fn from_raw<'a>(ptr: *mut bindings::sg_table) -> &'a Self {
unsafe { &*ptr.cast() }
}
#[inline]
fn as_raw(&self) -> *mut bindings::sg_table {
self.inner.0.get()
}
pub fn iter(&self) -> SGTableIter<'_> {
let nents = unsafe { (*self.as_raw()).nents };
let pos = if nents > 0 {
let ptr = unsafe { (*self.as_raw()).sgl };
Some(unsafe { SGEntry::from_raw(ptr) })
} else {
None
};
SGTableIter { pos, nents }
}
}
struct DmaMappedSgt {
sgt: NonNull<bindings::sg_table>,
dev: ARef<Device>,
dir: dma::DataDirection,
}
unsafe impl Send for DmaMappedSgt {}
unsafe impl Sync for DmaMappedSgt {}
impl DmaMappedSgt {
unsafe fn new(
sgt: NonNull<bindings::sg_table>,
dev: &Device<Bound>,
dir: dma::DataDirection,
) -> Result<Self> {
error::to_result(unsafe {
bindings::dma_map_sgtable(dev.as_raw(), sgt.as_ptr(), dir.into(), 0)
})?;
Ok(Self {
sgt,
dev: dev.into(),
dir,
})
}
}
impl Drop for DmaMappedSgt {
#[inline]
fn drop(&mut self) {
unsafe {
bindings::dma_unmap_sgtable(self.dev.as_raw(), self.sgt.as_ptr(), self.dir.into(), 0)
};
}
}
#[repr(transparent)]
struct RawSGTable(Opaque<bindings::sg_table>);
unsafe impl Send for RawSGTable {}
unsafe impl Sync for RawSGTable {}
impl RawSGTable {
unsafe fn new(
pages: &mut [*mut bindings::page],
size: usize,
max_segment: u32,
flags: alloc::Flags,
) -> Result<Self> {
if pages.is_empty() {
return Err(EINVAL);
}
let sgt = Opaque::zeroed();
error::to_result(unsafe {
bindings::sg_alloc_table_from_pages_segment(
sgt.get(),
pages.as_mut_ptr(),
pages.len().try_into()?,
0,
size,
max_segment,
flags.as_raw(),
)
})?;
Ok(Self(sgt))
}
#[inline]
fn as_raw(&self) -> *mut bindings::sg_table {
self.0.get()
}
}
impl Drop for RawSGTable {
#[inline]
fn drop(&mut self) {
unsafe { bindings::sg_free_table(self.0.get()) };
}
}
#[pin_data]
pub struct Owned<P> {
#[pin]
dma: Devres<DmaMappedSgt>,
sgt: RawSGTable,
_pages: P,
}
unsafe impl<P: Send> Send for Owned<P> {}
unsafe impl<P: Sync> Sync for Owned<P> {}
impl<P> Owned<P>
where
for<'a> P: page::AsPageIter<Iter<'a> = VmallocPageIter<'a>> + 'static,
{
fn new(
dev: &Device<Bound>,
mut pages: P,
dir: dma::DataDirection,
flags: alloc::Flags,
) -> Result<impl PinInit<Self, Error> + '_> {
let page_iter = pages.page_iter();
let size = page_iter.size();
let mut page_vec: KVec<*mut bindings::page> =
KVec::with_capacity(page_iter.page_count(), flags)?;
for page in page_iter {
page_vec.push(page.as_ptr(), flags)?;
}
let max_segment = match unsafe { bindings::dma_max_mapping_size(dev.as_raw()) } {
0 => u32::MAX,
max_segment => u32::try_from(max_segment).unwrap_or(u32::MAX),
};
Ok(try_pin_init!(&this in Self {
sgt: unsafe { RawSGTable::new(&mut page_vec, size, max_segment, flags) }?,
dma <- {
let sgt = unsafe { &raw mut (*this.as_ptr()).sgt }.cast();
let sgt = unsafe { NonNull::new_unchecked(sgt) };
Devres::new(dev, unsafe { DmaMappedSgt::new(sgt, dev, dir) })
},
_pages: pages,
}))
}
}
impl<P> SGTable<Owned<P>>
where
for<'a> P: page::AsPageIter<Iter<'a> = VmallocPageIter<'a>> + 'static,
{
pub fn new(
dev: &Device<Bound>,
pages: P,
dir: dma::DataDirection,
flags: alloc::Flags,
) -> impl PinInit<Self, Error> + '_ {
try_pin_init!(Self {
inner <- Owned::new(dev, pages, dir, flags)?
})
}
}
impl<P> Deref for SGTable<Owned<P>> {
type Target = SGTable;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { SGTable::from_raw(self.inner.sgt.as_raw()) }
}
}
mod private {
pub trait Sealed {}
impl Sealed for super::Borrowed {}
impl<P> Sealed for super::Owned<P> {}
}
pub struct SGTableIter<'a> {
pos: Option<&'a SGEntry>,
nents: c_uint,
}
impl<'a> Iterator for SGTableIter<'a> {
type Item = &'a SGEntry;
fn next(&mut self) -> Option<Self::Item> {
let entry = self.pos?;
self.nents = self.nents.saturating_sub(1);
let next = unsafe { bindings::sg_next(entry.as_raw()) };
self.pos = (!next.is_null() && self.nents > 0).then(|| {
unsafe { SGEntry::from_raw(next) }
});
Some(entry)
}
}