| 1 | |
| 2 |
|
| 3 | |
| 4 | |
| 5 | |
| 6 |
|
| 7 | use kernel::{
|
| 8 | device::Core,
|
| 9 | dma::{
|
| 10 | Coherent,
|
| 11 | DataDirection,
|
| 12 | Device,
|
| 13 | DmaMask, |
| 14 | },
|
| 15 | io::{
|
| 16 | io_project,
|
| 17 | io_read,
|
| 18 | Io, |
| 19 | },
|
| 20 | page,
|
| 21 | pci,
|
| 22 | prelude::*,
|
| 23 | scatterlist::{
|
| 24 | Owned,
|
| 25 | SGTable, |
| 26 | },
|
| 27 | sync::aref::ARef, |
| 28 | };
|
| 29 |
|
| 30 | #[pin_data(PinnedDrop)]
|
| 31 | struct DmaSampleDriver {
|
| 32 | pdev: ARef<pci::Device>,
|
| 33 | ca: Coherent<[MyStruct]>,
|
| 34 | #[pin]
|
| 35 | sgt: SGTable<Owned<VVec<u8>>>,
|
| 36 | }
|
| 37 |
|
| 38 | const TEST_VALUES: [(u32, u32); 5] = [
|
| 39 | (0xa, 0xb),
|
| 40 | (0xc, 0xd),
|
| 41 | (0xe, 0xf),
|
| 42 | (0xab, 0xba),
|
| 43 | (0xcd, 0xef),
|
| 44 | ];
|
| 45 |
|
| 46 | #[derive(FromBytes, IntoBytes)]
|
| 47 | struct MyStruct {
|
| 48 | h: u32,
|
| 49 | b: u32,
|
| 50 | }
|
| 51 |
|
| 52 | impl MyStruct {
|
| 53 | fn new(h: u32, b: u32) -> Self {
|
| 54 | Self { h, b }
|
| 55 | }
|
| 56 | }
|
| 57 | |
| 58 | unsafe impl kernel::transmute::AsBytes for MyStruct {}
|
| 59 | |
| 60 | unsafe impl kernel::transmute::FromBytes for MyStruct {}
|
| 61 |
|
| 62 | kernel::pci_device_table!(
|
| 63 | PCI_TABLE,
|
| 64 | <DmaSampleDriver as pci::Driver>::IdInfo,
|
| 65 | [(pci::DeviceId::from_id(pci::Vendor::REDHAT, 0x5), ())]
|
| 66 | );
|
| 67 |
|
| 68 | impl pci::Driver for DmaSampleDriver {
|
| 69 | type IdInfo = ();
|
| 70 | type Data<'bound> = Self;
|
| 71 | const ID_TABLE: pci::IdTable<Self::IdInfo> = &PCI_TABLE;
|
| 72 |
|
| 73 | fn probe<'bound>(
|
| 74 | pdev: &'bound pci::Device<Core<'_>>,
|
| 75 | _info: Option<&'bound Self::IdInfo>,
|
| 76 | ) -> impl PinInit<Self, Error> + 'bound {
|
| 77 | pin_init::pin_init_scope(move || {
|
| 78 | dev_info!(pdev, "Probe DMA test driver.\n");
|
| 79 |
|
| 80 | let mask = DmaMask::new::<64>();
|
| 81 |
|
| 82 | |
| 83 | unsafe { pdev.dma_set_mask_and_coherent(mask)? };
|
| 84 |
|
| 85 | let ca: Coherent<[MyStruct]> =
|
| 86 | Coherent::zeroed_slice(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?;
|
| 87 |
|
| 88 | for (i, value) in TEST_VALUES.into_iter().enumerate() {
|
| 89 | io_project!(ca, [panic: i]).copy_write(MyStruct::new(value.0, value.1));
|
| 90 | }
|
| 91 |
|
| 92 | let size = 4 * page::PAGE_SIZE;
|
| 93 | let pages = VVec::with_capacity(size, GFP_KERNEL)?;
|
| 94 |
|
| 95 | let sgt = SGTable::new(pdev.as_ref(), pages, DataDirection::ToDevice, GFP_KERNEL);
|
| 96 |
|
| 97 | Ok(try_pin_init!(Self {
|
| 98 | pdev: pdev.into(),
|
| 99 | ca,
|
| 100 | sgt <- sgt,
|
| 101 | }))
|
| 102 | })
|
| 103 | }
|
| 104 | }
|
| 105 |
|
| 106 | impl DmaSampleDriver {
|
| 107 | fn check_dma(&self) {
|
| 108 | for (i, value) in TEST_VALUES.into_iter().enumerate() {
|
| 109 | let val0 = io_read!(self.ca, [panic: i].h);
|
| 110 | let val1 = io_read!(self.ca, [panic: i].b);
|
| 111 |
|
| 112 | assert_eq!(val0, value.0);
|
| 113 | assert_eq!(val1, value.1);
|
| 114 | }
|
| 115 | }
|
| 116 | }
|
| 117 |
|
| 118 | #[pinned_drop]
|
| 119 | impl PinnedDrop for DmaSampleDriver {
|
| 120 | fn drop(self: Pin<&mut Self>) {
|
| 121 | dev_info!(self.pdev, "Unload DMA test driver.\n");
|
| 122 |
|
| 123 | self.check_dma();
|
| 124 |
|
| 125 | for (i, entry) in self.sgt.iter().enumerate() {
|
| 126 | dev_info!(
|
| 127 | self.pdev,
|
| 128 | "Entry[{}]: DMA address: {:#x}",
|
| 129 | i,
|
| 130 | entry.dma_address(),
|
| 131 | );
|
| 132 | }
|
| 133 | }
|
| 134 | }
|
| 135 |
|
| 136 | kernel::module_pci_driver! {
|
| 137 | type: DmaSampleDriver,
|
| 138 | name: "rust_dma",
|
| 139 | authors: ["Abdiel Janulgue"],
|
| 140 | description: "Rust DMA test",
|
| 141 | license: "GPL v2",
|
| 142 | }
|