1mod address;
12mod range;
13
14use std::collections::BTreeMap;
15use std::convert::TryFrom;
16use std::fmt::{Display, Formatter};
17use std::result::Result;
18
19use address::BusAddress;
20
21pub use address::{MmioAddress, MmioAddressOffset, PioAddress, PioAddressOffset};
22pub use range::{BusRange, MmioRange, PioRange};
23
24#[derive(Debug, PartialEq)]
26pub enum Error {
27 DeviceNotFound,
29 DeviceOverlap,
31 InvalidAccessLength(usize),
33 InvalidRange,
35}
36
37impl Display for Error {
38 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
39 match self {
40 Error::DeviceNotFound => write!(f, "device not found"),
41 Error::DeviceOverlap => write!(f, "range overlaps with existing device"),
42 Error::InvalidAccessLength(len) => write!(f, "invalid access length ({})", len),
43 Error::InvalidRange => write!(f, "invalid range provided"),
44 }
45 }
46}
47
48impl std::error::Error for Error {}
49
50pub struct Bus<A: BusAddress, D> {
52 devices: BTreeMap<BusRange<A>, D>,
53}
54
55impl<A: BusAddress, D> Default for Bus<A, D> {
56 fn default() -> Self {
57 Bus {
58 devices: BTreeMap::new(),
59 }
60 }
61}
62
63impl<A: BusAddress, D> Bus<A, D> {
64 pub fn new() -> Self {
66 Self::default()
67 }
68
69 pub fn device(&self, addr: A) -> Option<(&BusRange<A>, &D)> {
71 self.devices
75 .range(..=BusRange::unit(addr))
76 .nth_back(0)
77 .filter(|pair| pair.0.last() >= addr)
78 }
79
80 pub fn device_mut(&mut self, addr: A) -> Option<(&BusRange<A>, &mut D)> {
83 self.devices
84 .range_mut(..=BusRange::unit(addr))
85 .nth_back(0)
86 .filter(|pair| pair.0.last() >= addr)
87 }
88
89 pub fn register(&mut self, range: BusRange<A>, device: D) -> Result<(), Error> {
91 for r in self.devices.keys() {
92 if range.overlaps(r) {
93 return Err(Error::DeviceOverlap);
94 }
95 }
96
97 self.devices.insert(range, device);
98
99 Ok(())
100 }
101
102 pub fn deregister(&mut self, addr: A) -> Option<(BusRange<A>, D)> {
104 let range = self.device(addr).map(|(range, _)| *range)?;
105 self.devices.remove(&range).map(|device| (range, device))
106 }
107
108 pub fn check_access(&self, addr: A, len: usize) -> Result<(&BusRange<A>, &D), Error> {
111 let access_range = BusRange::new(
112 addr,
113 A::V::try_from(len).map_err(|_| Error::InvalidAccessLength(len))?,
114 )
115 .map_err(|_| Error::InvalidRange)?;
116 self.device(addr)
117 .filter(|(range, _)| range.last() >= access_range.last())
118 .ok_or(Error::DeviceNotFound)
119 }
120}
121
122pub type MmioBus<D> = Bus<MmioAddress, D>;
124pub type PioBus<D> = Bus<PioAddress, D>;
126
127pub trait BusManager<A: BusAddress> {
129 type D;
131
132 fn bus(&self) -> &Bus<A, Self::D>;
134
135 fn bus_mut(&mut self) -> &mut Bus<A, Self::D>;
137}
138
139#[cfg(test)]
140mod test {
141 use super::*;
142
143 #[test]
144 fn test_bus() {
145 let base = MmioAddress(10);
146 let base_prev = MmioAddress(base.value().checked_sub(1).unwrap());
147 let len = 10;
148 let range = MmioRange::new(base, len).unwrap();
149 let range_next = range.last().checked_add(1).unwrap();
150
151 let mut bus = Bus::new();
152 let device = 1u8;
154
155 assert_eq!(bus.devices.len(), 0);
156
157 bus.register(range, device).unwrap();
158 assert_eq!(bus.devices.len(), 1);
159
160 assert!(bus.device(base_prev).is_none());
161 assert!(bus.device_mut(base_prev).is_none());
162 assert!(bus.device(range_next).is_none());
163 assert!(bus.device_mut(range_next).is_none());
164
165 for offset in 0..len {
166 let addr = base.checked_add(offset).unwrap();
167
168 {
169 let (r, d) = bus.device(addr).unwrap();
170 assert_eq!(range, *r);
171 assert_eq!(device, *d);
172 }
173
174 {
175 let (r, d) = bus.device_mut(addr).unwrap();
176 assert_eq!(range, *r);
177 assert_eq!(device, *d);
178 }
179
180 for start_offset in 0..offset {
182 let start_addr = base.checked_add(start_offset).unwrap();
183
184 let (r, d) = bus
185 .check_access(start_addr, usize::try_from(offset - start_offset).unwrap())
186 .unwrap();
187 assert_eq!(range, *r);
188 assert_eq!(device, *d);
189 }
190 }
191
192 assert_eq!(bus.register(range, device), Err(Error::DeviceOverlap));
194
195 {
200 let range2 = MmioRange::new(MmioAddress(1), 10).unwrap();
201 assert_eq!(bus.register(range2, device), Err(Error::DeviceOverlap));
202 assert_eq!(
203 bus.check_access(range2.base(), usize::try_from(range2.size()).unwrap()),
204 Err(Error::DeviceNotFound)
205 );
206 }
207
208 {
209 let range2 = MmioRange::new(range.last(), 10).unwrap();
210 assert_eq!(bus.register(range2, device), Err(Error::DeviceOverlap));
211 assert_eq!(
212 bus.check_access(range2.base(), usize::try_from(range2.size()).unwrap()),
213 Err(Error::DeviceNotFound)
214 );
215 }
216
217 {
218 let range2 = MmioRange::new(MmioAddress(1), range.last().value() + 100).unwrap();
219 assert_eq!(bus.register(range2, device), Err(Error::DeviceOverlap));
220 assert_eq!(
221 bus.check_access(range2.base(), usize::try_from(range2.size()).unwrap()),
222 Err(Error::DeviceNotFound)
223 );
224 }
225
226 {
227 let range2 = MmioRange::new(range.last().checked_add(1).unwrap(), 5).unwrap();
231
232 assert_eq!(
233 bus.check_access(range2.base(), usize::try_from(range2.size()).unwrap()),
234 Err(Error::DeviceNotFound)
235 );
236
237 for offset in 0..range2.size() {
239 let device2 = device + 1;
240 assert!(bus.register(range2, device2).is_ok());
241 assert_eq!(bus.devices.len(), 2);
242
243 let addr = range2.base().checked_add(offset).unwrap();
244 let (r, d) = bus.deregister(addr).unwrap();
245 assert_eq!(bus.devices.len(), 1);
246 assert_eq!(r, range2);
247 assert_eq!(d, device2);
248
249 assert!(bus.deregister(addr).is_none());
251 assert_eq!(bus.devices.len(), 1);
252 }
253
254 assert!(bus.register(range2, device).is_ok());
256 assert_eq!(bus.devices.len(), 2);
257
258 assert_eq!(
262 bus.check_access(range.base(), usize::try_from(range.size() + 1).unwrap()),
263 Err(Error::DeviceNotFound)
264 );
265 }
266
267 let pio_base = PioAddress(10);
270 let pio_len = 10;
271 let pio_range = PioRange::new(pio_base, pio_len).unwrap();
272 let mut pio_bus = Bus::new();
273 let pio_device = 1u8;
274 pio_bus.register(pio_range, pio_device).unwrap();
275 assert_eq!(
276 pio_bus.check_access(pio_base, usize::MAX),
277 Err(Error::InvalidAccessLength(usize::MAX))
278 );
279 }
280}