Skip to main content

rdif_intc/
lib.rs

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}