1use anyhow::Result;
6use std::os::unix::io::AsRawFd;
7use std::path::Path;
8use tokio::fs;
9use tokio::io::{AsyncReadExt, AsyncWriteExt};
10
11#[derive(Debug)]
15pub struct Uio {
16 num: usize,
17 file: fs::File,
18}
19
20#[derive(Debug, Clone)]
25pub struct Mapping {
26 base: *mut libc::c_void,
27 effective: *mut libc::c_void,
28 map_size: usize,
29}
30
31impl Uio {
32 pub async fn from_num(num: usize) -> Result<Uio> {
37 let file = fs::OpenOptions::new()
38 .read(true)
39 .write(true)
40 .open(format!("/dev/uio{num}"))
41 .await?;
42 Ok(Uio { num, file })
43 }
44
45 pub async fn from_name(name: &str) -> Result<Uio> {
50 match Self::find_by_name(name).await? {
51 Some(num) => Self::from_num(num).await,
52 None => anyhow::bail!("UIO device not found"),
53 }
54 }
55
56 async fn find_by_name(name: &str) -> Result<Option<usize>> {
57 let mut entries = fs::read_dir(Path::new("/sys/class/uio")).await?;
58 while let Some(entry) = entries.next_entry().await? {
59 let file_name = entry.file_name();
60 let uio = file_name
61 .to_str()
62 .ok_or_else(|| anyhow::anyhow!("file name is not valid UTF8"))?;
63 if let Some(num) = uio
64 .strip_prefix("uio")
65 .and_then(|a| a.parse::<usize>().ok())
66 {
67 let mut path = entry.path();
68 path.push("name");
69 let this_name = fs::read_to_string(path).await?;
70 if this_name.trim_end() == name {
71 return Ok(Some(num));
72 }
73 }
74 }
75 Ok(None)
76 }
77
78 pub async fn map_mapping(&self, mapping: usize) -> Result<Mapping> {
85 let offset = mapping * page_size::get();
86 let fd = self.file.as_raw_fd();
87 let map_size = self.map_size(mapping).await?;
88
89 let base = unsafe {
90 match libc::mmap(
91 std::ptr::null_mut::<libc::c_void>(),
92 map_size,
93 libc::PROT_READ | libc::PROT_WRITE,
94 libc::MAP_SHARED,
95 fd,
96 offset as libc::off_t,
97 ) {
98 libc::MAP_FAILED => anyhow::bail!("mmap UIO failed"),
99 x => x,
100 }
101 };
102 let effective_offset = isize::try_from(self.map_offset(mapping).await?)?;
103 let effective = unsafe { base.offset(effective_offset) };
104 Ok(Mapping {
105 base,
106 effective,
107 map_size,
108 })
109 }
110
111 async fn read_mapping_hex(&self, mapping: usize, fname: &str) -> Result<usize> {
112 let n = fs::read_to_string(format!(
113 "/sys/class/uio/uio{}/maps/map{}/{}",
114 self.num, mapping, fname
115 ))
116 .await?;
117 Ok(usize::from_str_radix(
118 n.strip_prefix("0x")
119 .ok_or_else(|| anyhow::anyhow!("prefix 0x not present"))?
120 .trim_end(),
121 16,
122 )?)
123 }
124
125 pub async fn map_size(&self, mapping: usize) -> Result<usize> {
130 self.read_mapping_hex(mapping, "size").await
131 }
132
133 pub async fn map_offset(&self, mapping: usize) -> Result<usize> {
138 self.read_mapping_hex(mapping, "offset").await
139 }
140
141 pub async fn map_addr(&self, mapping: usize) -> Result<usize> {
146 self.read_mapping_hex(mapping, "addr").await
147 }
148
149 pub async fn irq_enable(&mut self) -> Result<()> {
154 let bytes = 1u32.to_ne_bytes();
155 self.file.write_all(&bytes).await?;
156 Ok(())
157 }
158
159 pub async fn irq_disable(&mut self) -> Result<()> {
164 let bytes = 0u32.to_ne_bytes();
165 self.file.write_all(&bytes).await?;
166 Ok(())
167 }
168
169 pub async fn irq_wait(&mut self) -> Result<u32> {
174 let mut bytes = [0; 4];
175 self.file.read_exact(&mut bytes).await?;
176 Ok(u32::from_ne_bytes(bytes))
177 }
178}
179
180impl Mapping {
181 pub fn addr(&self) -> *mut libc::c_void {
186 self.effective
187 }
188}
189
190impl Drop for Mapping {
192 fn drop(&mut self) {
193 unsafe {
194 libc::munmap(self.base, self.map_size);
196 }
197 }
198}