| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- // SPDX-License-Identifier: GPL-2.0
- //! Rust DMA api test (based on QEMU's `pci-testdev`).
- //!
- //! To make this driver probe, QEMU must be run with `-device pci-testdev`.
- use kernel::{
- device::Core,
- dma::{CoherentAllocation, DataDirection, Device, DmaMask},
- page, pci,
- prelude::*,
- scatterlist::{Owned, SGTable},
- sync::aref::ARef,
- };
- #[pin_data(PinnedDrop)]
- struct DmaSampleDriver {
- pdev: ARef<pci::Device>,
- ca: CoherentAllocation<MyStruct>,
- #[pin]
- sgt: SGTable<Owned<VVec<u8>>>,
- }
- const TEST_VALUES: [(u32, u32); 5] = [
- (0xa, 0xb),
- (0xc, 0xd),
- (0xe, 0xf),
- (0xab, 0xba),
- (0xcd, 0xef),
- ];
- struct MyStruct {
- h: u32,
- b: u32,
- }
- impl MyStruct {
- fn new(h: u32, b: u32) -> Self {
- Self { h, b }
- }
- }
- // SAFETY: All bit patterns are acceptable values for `MyStruct`.
- unsafe impl kernel::transmute::AsBytes for MyStruct {}
- // SAFETY: Instances of `MyStruct` have no uninitialized portions.
- unsafe impl kernel::transmute::FromBytes for MyStruct {}
- kernel::pci_device_table!(
- PCI_TABLE,
- MODULE_PCI_TABLE,
- <DmaSampleDriver as pci::Driver>::IdInfo,
- [(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())]
- );
- impl pci::Driver for DmaSampleDriver {
- type IdInfo = ();
- const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
- fn probe(pdev: &pci::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> {
- pin_init::pin_init_scope(move || {
- dev_info!(pdev, "Probe DMA test driver.\n");
- let mask = DmaMask::new::<64>();
- // SAFETY: There are no concurrent calls to DMA allocation and mapping primitives.
- unsafe { pdev.dma_set_mask_and_coherent(mask)? };
- let ca: CoherentAllocation<MyStruct> =
- 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));
- }
- let size = 4 * page::PAGE_SIZE;
- let pages = VVec::with_capacity(size, GFP_KERNEL)?;
- let sgt = SGTable::new(pdev.as_ref(), pages, DataDirection::ToDevice, GFP_KERNEL);
- Ok(try_pin_init!(Self {
- pdev: pdev.into(),
- ca,
- sgt <- sgt,
- }))
- })
- }
- }
- 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");
- assert!(self.check_dma().is_ok());
- for (i, entry) in self.sgt.iter().enumerate() {
- dev_info!(
- self.pdev,
- "Entry[{}]: DMA address: {:#x}",
- i,
- entry.dma_address(),
- );
- }
- }
- }
- kernel::module_pci_driver! {
- type: DmaSampleDriver,
- name: "rust_dma",
- authors: ["Abdiel Janulgue"],
- description: "Rust DMA test",
- license: "GPL v2",
- }
|