1use std::fs::File;
2use std::io::Error as IoError;
3use std::mem;
4use std::os::unix::fs::FileExt;
5
6pub const DMA_ALIGNMENT: u64 = 4096;
8
9pub type Result<T> = std::result::Result<T, Error>;
10
11#[derive(Debug)]
12pub enum Error {
13 ShellReadFailed(IoError),
14 ShellWriteFailed(IoError),
15 DmaReadFailed(IoError),
16 DmaWriteFailed(IoError),
17}
18
19#[repr(C, align(4096))]
20struct Align4K([u8; 4096]);
21
22#[derive(Debug)]
25pub struct DmaBuffer(Vec<u8>);
26
27impl DmaBuffer {
28 pub fn new(n_bytes: usize) -> Self {
29 Self(unsafe { aligned_vec(n_bytes) })
30 }
31
32 pub fn as_slice(&self) -> &[u8] {
33 self.0.as_slice()
34 }
35
36 pub fn as_mut_slice(&mut self) -> &mut [u8] {
37 self.0.as_mut_slice()
38 }
39
40 pub fn get(&self) -> &Vec<u8> {
41 &self.0
42 }
43
44 pub fn get_mut(&mut self) -> &mut Vec<u8> {
45 &mut self.0
46 }
47}
48
49unsafe fn aligned_vec(n_bytes: usize) -> Vec<u8> {
50 let n_units = (n_bytes / mem::size_of::<Align4K>()) + 1;
51
52 let mut aligned: Vec<Align4K> = Vec::with_capacity(n_units);
53
54 let ptr = aligned.as_mut_ptr();
55 let len_units = aligned.len();
56 let cap_units = aligned.capacity();
57
58 mem::forget(aligned);
59
60 Vec::from_raw_parts(
61 ptr as *mut u8,
62 len_units * mem::size_of::<Align4K>(),
63 cap_units * mem::size_of::<Align4K>(),
64 )
65}
66
67pub struct XdmaDevice {
68 pub id: u32,
70 pub user_cdev: File,
72 pub h2c_cdev: File,
74 pub c2h_cdev: File,
76 pub intc_base_addr: u32,
80 pub hbicap_base_addr: u32,
84}
85
86pub trait XdmaOps {
87 fn shell_read(&self, buf: &mut [u8], offset: u64) -> Result<()>;
88 fn shell_write(&self, buf: &[u8], offset: u64) -> Result<()>;
89 fn dma_read(&self, buf: &mut DmaBuffer, offset: u64) -> Result<()>;
90 fn dma_write(&self, buf: &DmaBuffer, offset: u64) -> Result<()>;
91}
92
93impl XdmaOps for XdmaDevice {
94 fn shell_read(&self, buf: &mut [u8], offset: u64) -> Result<()> {
95 self.user_cdev
96 .read_exact_at(buf, offset)
97 .map_err(Error::ShellReadFailed)
98 }
99
100 fn shell_write(&self, buf: &[u8], offset: u64) -> Result<()> {
101 self.user_cdev
102 .write_all_at(buf, offset)
103 .map_err(Error::ShellWriteFailed)
104 }
105
106 fn dma_read(&self, buf: &mut DmaBuffer, offset: u64) -> Result<()> {
107 self.c2h_cdev
108 .read_exact_at(buf.as_mut_slice(), offset)
109 .map_err(Error::DmaReadFailed)
110 }
111
112 fn dma_write(&self, buf: &DmaBuffer, offset: u64) -> Result<()> {
113 self.h2c_cdev
114 .write_all_at(buf.as_slice(), offset)
115 .map_err(Error::DmaWriteFailed)
116 }
117}
118
119#[cfg(test)]
120mod test {
121 use super::*;
122
123 #[test]
124 fn dma_buffer_alignment() {
125 const BUF_LEN: usize = 42;
126
127 let mut buf = DmaBuffer::new(BUF_LEN);
128 buf.get_mut().extend_from_slice(&vec![0u8; BUF_LEN]);
129
130 let ptr = buf.as_mut_slice().as_mut_ptr();
131 let len = buf.get().len();
132 let cap = buf.get().capacity();
133
134 assert_eq!(ptr as u64 % DMA_ALIGNMENT, 0);
135 assert_eq!(len, BUF_LEN);
136 assert_eq!(cap, DMA_ALIGNMENT as usize);
137 }
138
139 #[test]
140 fn file_write_all_at() {
141 let n: u64 = rand::random();
142 let f = File::create(format!("/tmp/xdma-test-{:x}", n)).expect("cannot create file");
143 let buf = vec![b'A', b'B', b'C'];
144 f.write_all_at(buf.as_slice(), 0)
145 .expect("write test failed");
146 }
147}