self.count * core::mem::size_of::<T>()
}
+ /// Returns the raw pointer to the allocated region in the CPU's virtual address space.
+ #[inline]
+ pub fn as_ptr(&self) -> *const [T] {
+ core::ptr::slice_from_raw_parts(self.cpu_addr.as_ptr(), self.count)
+ }
+
+ /// Returns the raw pointer to the allocated region in the CPU's virtual address space as
+ /// a mutable pointer.
+ #[inline]
+ pub fn as_mut_ptr(&self) -> *mut [T] {
+ core::ptr::slice_from_raw_parts_mut(self.cpu_addr.as_ptr(), self.count)
+ }
+
/// Returns the base address to the allocated region in the CPU's virtual address space.
pub fn start_ptr(&self) -> *const T {
self.cpu_addr.as_ptr()
Ok(())
}
- /// Returns a pointer to an element from the region with bounds checking. `offset` is in
- /// units of `T`, not the number of bytes.
- ///
- /// Public but hidden since it should only be used from [`dma_read`] and [`dma_write`] macros.
- #[doc(hidden)]
- pub fn item_from_index(&self, offset: usize) -> Result<*mut T> {
- if offset >= self.count {
- return Err(EINVAL);
- }
- // SAFETY:
- // - The pointer is valid due to type invariant on `CoherentAllocation`
- // and we've just checked that the range and index is within bounds.
- // - `offset` can't overflow since it is smaller than `self.count` and we've checked
- // that `self.count` won't overflow early in the constructor.
- Ok(unsafe { self.cpu_addr.as_ptr().add(offset) })
- }
-
/// Reads the value of `field` and ensures that its type is [`FromBytes`].
///
/// # Safety
/// Reads a field of an item from an allocated region of structs.
///
+/// The syntax is of the form `kernel::dma_read!(dma, proj)` where `dma` is an expression evaluating
+/// to a [`CoherentAllocation`] and `proj` is a [projection specification](kernel::ptr::project!).
+///
/// # Examples
///
/// ```
/// unsafe impl kernel::transmute::AsBytes for MyStruct{};
///
/// # fn test(alloc: &kernel::dma::CoherentAllocation<MyStruct>) -> Result {
-/// let whole = kernel::dma_read!(alloc[2]);
-/// let field = kernel::dma_read!(alloc[1].field);
+/// let whole = kernel::dma_read!(alloc, [2]?);
+/// let field = kernel::dma_read!(alloc, [1]?.field);
/// # Ok::<(), Error>(()) }
/// ```
#[macro_export]
macro_rules! dma_read {
- ($dma:expr, $idx: expr, $($field:tt)*) => {{
- (|| -> ::core::result::Result<_, $crate::error::Error> {
- let item = $crate::dma::CoherentAllocation::item_from_index(&$dma, $idx)?;
- // SAFETY: `item_from_index` ensures that `item` is always a valid pointer and can be
- // dereferenced. The compiler also further validates the expression on whether `field`
- // is a member of `item` when expanded by the macro.
- unsafe {
- let ptr_field = ::core::ptr::addr_of!((*item) $($field)*);
- ::core::result::Result::Ok(
- $crate::dma::CoherentAllocation::field_read(&$dma, ptr_field)
- )
- }
- })()
+ ($dma:expr, $($proj:tt)*) => {{
+ let dma = &$dma;
+ let ptr = $crate::ptr::project!(
+ $crate::dma::CoherentAllocation::as_ptr(dma), $($proj)*
+ );
+ // SAFETY: The pointer created by the projection is within the DMA region.
+ unsafe { $crate::dma::CoherentAllocation::field_read(dma, ptr) }
}};
- ($dma:ident [ $idx:expr ] $($field:tt)* ) => {
- $crate::dma_read!($dma, $idx, $($field)*)
- };
- ($($dma:ident).* [ $idx:expr ] $($field:tt)* ) => {
- $crate::dma_read!($($dma).*, $idx, $($field)*)
- };
}
/// Writes to a field of an item from an allocated region of structs.
///
+/// The syntax is of the form `kernel::dma_write!(dma, proj, val)` where `dma` is an expression
+/// evaluating to a [`CoherentAllocation`], `proj` is a
+/// [projection specification](kernel::ptr::project!), and `val` is the value to be written to the
+/// projected location.
+///
/// # Examples
///
/// ```
/// unsafe impl kernel::transmute::AsBytes for MyStruct{};
///
/// # fn test(alloc: &kernel::dma::CoherentAllocation<MyStruct>) -> Result {
-/// kernel::dma_write!(alloc[2].member = 0xf);
-/// kernel::dma_write!(alloc[1] = MyStruct { member: 0xf });
+/// kernel::dma_write!(alloc, [2]?.member, 0xf);
+/// kernel::dma_write!(alloc, [1]?, MyStruct { member: 0xf });
/// # Ok::<(), Error>(()) }
/// ```
#[macro_export]
macro_rules! dma_write {
- ($dma:ident [ $idx:expr ] $($field:tt)*) => {{
- $crate::dma_write!($dma, $idx, $($field)*)
- }};
- ($($dma:ident).* [ $idx:expr ] $($field:tt)* ) => {{
- $crate::dma_write!($($dma).*, $idx, $($field)*)
+ (@parse [$dma:expr] [$($proj:tt)*] [, $val:expr]) => {{
+ let dma = &$dma;
+ let ptr = $crate::ptr::project!(
+ mut $crate::dma::CoherentAllocation::as_mut_ptr(dma), $($proj)*
+ );
+ let val = $val;
+ // SAFETY: The pointer created by the projection is within the DMA region.
+ unsafe { $crate::dma::CoherentAllocation::field_write(dma, ptr, val) }
}};
- ($dma:expr, $idx: expr, = $val:expr) => {
- (|| -> ::core::result::Result<_, $crate::error::Error> {
- let item = $crate::dma::CoherentAllocation::item_from_index(&$dma, $idx)?;
- // SAFETY: `item_from_index` ensures that `item` is always a valid item.
- unsafe { $crate::dma::CoherentAllocation::field_write(&$dma, item, $val) }
- ::core::result::Result::Ok(())
- })()
+ (@parse [$dma:expr] [$($proj:tt)*] [.$field:tt $($rest:tt)*]) => {
+ $crate::dma_write!(@parse [$dma] [$($proj)* .$field] [$($rest)*])
+ };
+ (@parse [$dma:expr] [$($proj:tt)*] [[$index:expr]? $($rest:tt)*]) => {
+ $crate::dma_write!(@parse [$dma] [$($proj)* [$index]?] [$($rest)*])
+ };
+ (@parse [$dma:expr] [$($proj:tt)*] [[$index:expr] $($rest:tt)*]) => {
+ $crate::dma_write!(@parse [$dma] [$($proj)* [$index]] [$($rest)*])
};
- ($dma:expr, $idx: expr, $(.$field:ident)* = $val:expr) => {
- (|| -> ::core::result::Result<_, $crate::error::Error> {
- let item = $crate::dma::CoherentAllocation::item_from_index(&$dma, $idx)?;
- // SAFETY: `item_from_index` ensures that `item` is always a valid pointer and can be
- // dereferenced. The compiler also further validates the expression on whether `field`
- // is a member of `item` when expanded by the macro.
- unsafe {
- let ptr_field = ::core::ptr::addr_of_mut!((*item) $(.$field)*);
- $crate::dma::CoherentAllocation::field_write(&$dma, ptr_field, $val)
- }
- ::core::result::Result::Ok(())
- })()
+ ($dma:expr, $($rest:tt)*) => {
+ $crate::dma_write!(@parse [$dma] [] [$($rest)*])
};
}
CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?;
for (i, value) in TEST_VALUES.into_iter().enumerate() {
- kernel::dma_write!(ca[i] = MyStruct::new(value.0, value.1))?;
+ kernel::dma_write!(ca, [i]?, MyStruct::new(value.0, value.1));
}
let size = 4 * page::PAGE_SIZE;
}
}
+impl DmaSampleDriver {
+ fn check_dma(&self) -> Result {
+ for (i, value) in TEST_VALUES.into_iter().enumerate() {
+ let val0 = kernel::dma_read!(self.ca, [i]?.h);
+ let val1 = kernel::dma_read!(self.ca, [i]?.b);
+
+ assert_eq!(val0, value.0);
+ assert_eq!(val1, value.1);
+ }
+
+ Ok(())
+ }
+}
+
#[pinned_drop]
impl PinnedDrop for DmaSampleDriver {
fn drop(self: Pin<&mut Self>) {
dev_info!(self.pdev, "Unload DMA test driver.\n");
- for (i, value) in TEST_VALUES.into_iter().enumerate() {
- let val0 = kernel::dma_read!(self.ca[i].h);
- let val1 = kernel::dma_read!(self.ca[i].b);
- assert!(val0.is_ok());
- assert!(val1.is_ok());
-
- if let Ok(val0) = val0 {
- assert_eq!(val0, value.0);
- }
- if let Ok(val1) = val1 {
- assert_eq!(val1, value.1);
- }
- }
+ assert!(self.check_dma().is_ok());
for (i, entry) in self.sgt.iter().enumerate() {
dev_info!(