Skip to main content

esp_emac/
interrupt.rs

1// SPDX-License-Identifier: GPL-2.0-or-later OR Apache-2.0
2// Copyright (c) Viacheslav Bocharov <v@baodeep.com> and JetHome (r)
3
4//! Interrupt status parsing for the ESP32 EMAC DMA controller.
5//!
6//! [`InterruptStatus`] decodes the raw `DMASTATUS` register value into
7//! the individual TX/RX/error flags an interrupt handler typically wants.
8
9use crate::regs::dma::status;
10
11/// Decoded DMA status flags.
12#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14pub struct InterruptStatus {
15    /// TX complete — a frame was transmitted successfully.
16    pub tx_complete: bool,
17    /// TX DMA stopped.
18    pub tx_stopped: bool,
19    /// No TX descriptors available.
20    pub tx_buf_unavailable: bool,
21    /// TX FIFO underflow.
22    pub tx_underflow: bool,
23    /// RX complete — a frame was received.
24    pub rx_complete: bool,
25    /// RX DMA stopped.
26    pub rx_stopped: bool,
27    /// No RX descriptors available (DMA suspended).
28    pub rx_buf_unavailable: bool,
29    /// RX FIFO overflow.
30    pub rx_overflow: bool,
31    /// Fatal bus error — unrecoverable DMA error.
32    pub fatal_bus_error: bool,
33    /// Normal interrupt summary.
34    pub normal_summary: bool,
35    /// Abnormal interrupt summary.
36    pub abnormal_summary: bool,
37}
38
39impl InterruptStatus {
40    /// Decode from a raw `DMASTATUS` register value.
41    #[inline]
42    #[must_use]
43    pub fn from_raw(raw: u32) -> Self {
44        Self {
45            tx_complete: (raw & status::TI) != 0,
46            tx_stopped: (raw & status::TPS) != 0,
47            tx_buf_unavailable: (raw & status::TU) != 0,
48            tx_underflow: (raw & status::UNF) != 0,
49            rx_complete: (raw & status::RI) != 0,
50            rx_stopped: (raw & status::RPS) != 0,
51            rx_buf_unavailable: (raw & status::RU) != 0,
52            rx_overflow: (raw & status::OVF) != 0,
53            fatal_bus_error: (raw & status::FBI) != 0,
54            normal_summary: (raw & status::NIS) != 0,
55            abnormal_summary: (raw & status::AIS) != 0,
56        }
57    }
58
59    /// Encode back to a raw register value, retaining only the bits
60    /// modeled by this struct.
61    ///
62    /// **Do not use this for write-1-to-clear of `DMASTATUS`** — the
63    /// struct does not represent every W1C flag (e.g. `ERI`, `ETI`,
64    /// `RWT`, `TJT`, `EBE[25:23]`), so a roundtrip silently drops
65    /// them. Use the raw `DMASTATUS` snapshot directly via
66    /// [`crate::Emac::clear_interrupts_raw`] when clearing.
67    #[inline]
68    #[must_use]
69    pub fn to_raw(&self) -> u32 {
70        let mut v = 0u32;
71        if self.tx_complete {
72            v |= status::TI;
73        }
74        if self.tx_stopped {
75            v |= status::TPS;
76        }
77        if self.tx_buf_unavailable {
78            v |= status::TU;
79        }
80        if self.tx_underflow {
81            v |= status::UNF;
82        }
83        if self.rx_complete {
84            v |= status::RI;
85        }
86        if self.rx_stopped {
87            v |= status::RPS;
88        }
89        if self.rx_buf_unavailable {
90            v |= status::RU;
91        }
92        if self.rx_overflow {
93            v |= status::OVF;
94        }
95        if self.fatal_bus_error {
96            v |= status::FBI;
97        }
98        if self.normal_summary {
99            v |= status::NIS;
100        }
101        if self.abnormal_summary {
102            v |= status::AIS;
103        }
104        v
105    }
106
107    /// True if any non-summary bit is set.
108    #[inline]
109    #[must_use]
110    pub fn any(&self) -> bool {
111        self.tx_complete
112            || self.tx_stopped
113            || self.tx_buf_unavailable
114            || self.tx_underflow
115            || self.rx_complete
116            || self.rx_stopped
117            || self.rx_buf_unavailable
118            || self.rx_overflow
119            || self.fatal_bus_error
120    }
121
122    /// True if any error flag is set.
123    #[inline]
124    #[must_use]
125    pub fn has_error(&self) -> bool {
126        self.tx_underflow || self.rx_overflow || self.fatal_bus_error
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn from_raw_zero() {
136        let s = InterruptStatus::from_raw(0);
137        assert!(!s.any());
138        assert!(!s.has_error());
139    }
140
141    #[test]
142    fn tx_rx_complete() {
143        let s = InterruptStatus::from_raw(status::TI | status::RI | status::NIS);
144        assert!(s.tx_complete);
145        assert!(s.rx_complete);
146        assert!(s.normal_summary);
147        assert!(s.any());
148        assert!(!s.has_error());
149    }
150
151    #[test]
152    fn errors() {
153        let s = InterruptStatus::from_raw(status::FBI | status::OVF);
154        assert!(s.fatal_bus_error);
155        assert!(s.rx_overflow);
156        assert!(s.has_error());
157    }
158
159    #[test]
160    fn roundtrip() {
161        let raw = status::TI | status::RI | status::NIS | status::AIS | status::FBI;
162        let s = InterruptStatus::from_raw(raw);
163        assert_eq!(s.to_raw(), raw);
164    }
165
166    #[test]
167    fn any_excludes_summary_bits() {
168        let s = InterruptStatus::from_raw(status::NIS | status::AIS);
169        assert!(!s.any());
170    }
171}