1#![no_std]
2
3extern crate alloc;
4
5use alloc::boxed::Box;
6use core::ops::{Deref, DerefMut};
7
8pub use irq_framework::{HwIrq, IrqDomainId, IrqError, IrqId};
9pub use rdif_base::{
10 DriverGeneric, KError, io,
11 irq::{AcpiGsiController, AcpiGsiRoute, AcpiIrqPolarity, AcpiIrqTrigger, IrqConfig, Trigger},
12};
13
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15pub struct IrqTranslation {
16 pub id: IrqId,
17 pub trigger: Option<Trigger>,
18}
19
20impl IrqTranslation {
21 pub const fn new(id: IrqId) -> Self {
22 Self { id, trigger: None }
23 }
24
25 pub const fn with_trigger(id: IrqId, trigger: Trigger) -> Self {
26 Self {
27 id,
28 trigger: Some(trigger),
29 }
30 }
31
32 pub const fn from_controller(
33 domain: IrqDomainId,
34 translation: ControllerIrqTranslation,
35 ) -> Self {
36 Self {
37 id: IrqId::new(domain, translation.hwirq),
38 trigger: translation.trigger,
39 }
40 }
41}
42
43#[derive(Clone, Copy, Debug, Eq, PartialEq)]
44pub struct ControllerIrqTranslation {
45 pub hwirq: HwIrq,
46 pub trigger: Option<Trigger>,
47}
48
49impl ControllerIrqTranslation {
50 pub const fn new(hwirq: HwIrq) -> Self {
51 Self {
52 hwirq,
53 trigger: None,
54 }
55 }
56
57 pub const fn with_trigger(hwirq: HwIrq, trigger: Trigger) -> Self {
58 Self {
59 hwirq,
60 trigger: Some(trigger),
61 }
62 }
63}
64
65pub trait Interface: DriverGeneric {
66 fn translate_fdt(&self, _irq_prop: &[u32]) -> Result<ControllerIrqTranslation, IrqError> {
67 Err(IrqError::Unsupported)
68 }
69
70 fn supports_acpi_gsi(&self, _route: &AcpiGsiRoute) -> bool {
71 false
72 }
73
74 fn translate_acpi(&self, _route: &AcpiGsiRoute) -> Result<ControllerIrqTranslation, IrqError> {
75 Err(IrqError::Unsupported)
76 }
77
78 fn configure(&mut self, _translation: &IrqTranslation) -> Result<(), IrqError> {
79 Ok(())
80 }
81
82 fn configure_acpi(
83 &mut self,
84 translation: &IrqTranslation,
85 _route: &AcpiGsiRoute,
86 ) -> Result<(), IrqError> {
87 self.configure(translation)
88 }
89
90 fn set_enabled(&mut self, _hwirq: HwIrq, _enabled: bool) -> Result<(), IrqError> {
91 Err(IrqError::Unsupported)
92 }
93}
94
95pub struct Intc {
96 domain: IrqDomainId,
97 inner: Box<dyn Interface>,
98}
99
100impl Intc {
101 pub fn new<T: Interface>(domain: IrqDomainId, driver: T) -> Self {
102 Self {
103 domain,
104 inner: Box::new(driver),
105 }
106 }
107
108 pub const fn domain(&self) -> IrqDomainId {
109 self.domain
110 }
111
112 pub fn translate_fdt(&self, irq_prop: &[u32]) -> Result<IrqTranslation, IrqError> {
113 self.inner
114 .translate_fdt(irq_prop)
115 .map(|translation| IrqTranslation::from_controller(self.domain, translation))
116 }
117
118 pub fn supports_acpi_gsi(&self, route: &AcpiGsiRoute) -> bool {
119 self.inner.supports_acpi_gsi(route)
120 }
121
122 pub fn translate_acpi(&self, route: &AcpiGsiRoute) -> Result<IrqTranslation, IrqError> {
123 self.inner
124 .translate_acpi(route)
125 .map(|translation| IrqTranslation::from_controller(self.domain, translation))
126 }
127
128 pub fn configure(&mut self, translation: &IrqTranslation) -> Result<(), IrqError> {
129 if translation.id.domain != self.domain {
130 return Err(IrqError::InvalidIrq);
131 }
132 self.inner.configure(translation)
133 }
134
135 pub fn configure_acpi(
136 &mut self,
137 translation: &IrqTranslation,
138 route: &AcpiGsiRoute,
139 ) -> Result<(), IrqError> {
140 if translation.id.domain != self.domain {
141 return Err(IrqError::InvalidIrq);
142 }
143 let expected = self.translate_acpi(route)?;
144 if expected.id != translation.id {
145 return Err(IrqError::InvalidIrq);
146 }
147 self.inner.configure_acpi(translation, route)
148 }
149
150 pub fn set_enabled(&mut self, hwirq: HwIrq, enabled: bool) -> Result<(), IrqError> {
151 self.inner.set_enabled(hwirq, enabled)
152 }
153
154 pub fn typed_ref<T: Interface>(&self) -> Option<&T> {
155 self.raw_any()?.downcast_ref()
156 }
157
158 pub fn typed_mut<T: Interface>(&mut self) -> Option<&mut T> {
159 self.raw_any_mut()?.downcast_mut()
160 }
161}
162
163impl DriverGeneric for Intc {
164 fn name(&self) -> &str {
165 self.inner.name()
166 }
167
168 fn raw_any(&self) -> Option<&dyn core::any::Any> {
169 Some(self.inner.as_ref() as &dyn core::any::Any)
170 }
171
172 fn raw_any_mut(&mut self) -> Option<&mut dyn core::any::Any> {
173 Some(self.inner.as_mut() as &mut dyn core::any::Any)
174 }
175}
176
177impl Deref for Intc {
178 type Target = dyn Interface;
179
180 fn deref(&self) -> &Self::Target {
181 self.inner.as_ref()
182 }
183}
184
185impl DerefMut for Intc {
186 fn deref_mut(&mut self) -> &mut Self::Target {
187 self.inner.as_mut()
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 extern crate std;
194
195 use std::{
196 sync::{Arc, Mutex},
197 vec,
198 vec::Vec,
199 };
200
201 use super::*;
202
203 struct MockIntc {
204 hwirq: HwIrq,
205 enabled_calls: Arc<Mutex<Vec<(HwIrq, bool)>>>,
206 }
207
208 impl DriverGeneric for MockIntc {
209 fn name(&self) -> &str {
210 "mock-intc"
211 }
212 }
213
214 impl Interface for MockIntc {
215 fn translate_fdt(&self, _irq_prop: &[u32]) -> Result<ControllerIrqTranslation, IrqError> {
216 Ok(ControllerIrqTranslation::new(self.hwirq))
217 }
218
219 fn translate_acpi(
220 &self,
221 _route: &AcpiGsiRoute,
222 ) -> Result<ControllerIrqTranslation, IrqError> {
223 Ok(ControllerIrqTranslation::new(self.hwirq))
224 }
225
226 fn set_enabled(&mut self, hwirq: HwIrq, enabled: bool) -> Result<(), IrqError> {
227 self.enabled_calls.lock().unwrap().push((hwirq, enabled));
228 Ok(())
229 }
230 }
231
232 #[test]
233 fn intc_translation_uses_its_own_domain() {
234 let intc_a = Intc::new(
235 IrqDomainId(11),
236 MockIntc {
237 hwirq: HwIrq(5),
238 enabled_calls: Arc::new(Mutex::new(Vec::new())),
239 },
240 );
241 let intc_b = Intc::new(
242 IrqDomainId(12),
243 MockIntc {
244 hwirq: HwIrq(5),
245 enabled_calls: Arc::new(Mutex::new(Vec::new())),
246 },
247 );
248
249 assert_eq!(
250 intc_a.translate_fdt(&[5]).unwrap().id,
251 IrqId::new(IrqDomainId(11), HwIrq(5))
252 );
253 assert_eq!(
254 intc_b.translate_fdt(&[5]).unwrap().id,
255 IrqId::new(IrqDomainId(12), HwIrq(5))
256 );
257 }
258
259 #[test]
260 fn set_enabled_passes_controller_local_hwirq() {
261 let calls = Arc::new(Mutex::new(Vec::new()));
262 let mut intc = Intc::new(
263 IrqDomainId(11),
264 MockIntc {
265 hwirq: HwIrq(5),
266 enabled_calls: Arc::clone(&calls),
267 },
268 );
269
270 intc.set_enabled(HwIrq(5), true).unwrap();
271
272 assert_eq!(*calls.lock().unwrap(), vec![(HwIrq(5), true)]);
273 }
274
275 #[test]
276 fn configure_acpi_rejects_route_translation_mismatch() {
277 let mut intc = Intc::new(
278 IrqDomainId(11),
279 MockIntc {
280 hwirq: HwIrq(5),
281 enabled_calls: Arc::new(Mutex::new(Vec::new())),
282 },
283 );
284 let route = AcpiGsiRoute {
285 gsi: 5,
286 vector: 37,
287 controller: AcpiGsiController::IoApic,
288 controller_id: 0,
289 controller_address: 0xfec0_0000,
290 controller_input: 5,
291 trigger: AcpiIrqTrigger::Level,
292 polarity: AcpiIrqPolarity::ActiveLow,
293 };
294 let mismatched = IrqTranslation::new(IrqId::new(IrqDomainId(11), HwIrq(6)));
295
296 assert_eq!(
297 intc.configure_acpi(&mismatched, &route),
298 Err(IrqError::InvalidIrq)
299 );
300 }
301}