1
#[cfg(place_rmt_driver_in_ram)]
2
use procmacros::ram;
3

            
4
use super::{DynChannelAccess, Error, PulseCode, Rx};
5

            
6
#[derive(Debug, PartialEq)]
7
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8
pub(super) enum ReaderState {
9
    Active,
10

            
11
    Error(Error),
12

            
13
    Done,
14
}
15

            
16
#[derive(Debug)]
17
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18
pub(super) struct RmtReader {
19
    // The position in channel RAM to continue reading from; must be either
20
    // 0 or half the available RAM size if there's further data.
21
    // The position may be invalid if there's no data left.
22
    offset: u16,
23

            
24
    pub total: usize,
25

            
26
    pub state: ReaderState,
27
}
28

            
29
impl RmtReader {
30
    pub(super) fn new() -> Self {
31
        Self {
32
            offset: 0,
33
            total: 0,
34
            state: ReaderState::Active,
35
        }
36
    }
37

            
38
    // Copy from the hardware buffer to `data`, advancing the `data` slice accordingly.
39
    //
40
    // If `final_` is set, read a full buffer length, potentially wrapping around. Otherwise, fetch
41
    // half the buffer's length.
42
    //
43
    // The return value is only valuable if `final_` is set.
44
    // In that case `true` indicates that the read completed successfully, while `false` indicates
45
    // that the read was incomplete and an error occurred.
46
    // This was only observed on ESP32 (and probably ESP32-S2) - other chips indicated an error in
47
    // that case.
48
    #[cfg_attr(place_rmt_driver_in_ram, ram)]
49
    pub(super) fn read(
50
        &mut self,
51
        data: &mut &mut [PulseCode],
52
        raw: DynChannelAccess<Rx>,
53
        final_: bool,
54
    ) -> bool {
55
        if self.state != ReaderState::Active {
56
            return true;
57
        }
58

            
59
        let ram_start = raw.channel_ram_start();
60
        let memsize = raw.memsize().codes();
61
        let offset = self.offset as usize;
62

            
63
        let max_count = if final_ {
64
            let hw_offset = raw.hw_offset();
65

            
66
            // self.offset -> next code we would read
67
            // hw_offset -> next code the hardware would write
68
            // => If both are the same, we're done, max_count = 0
69
            let max_count = (if offset <= hw_offset { 0 } else { memsize }) + hw_offset - offset;
70

            
71
            if !(max_count == 0 && self.total == 0
72
                    // We always enable wrapping if it is available. If it's unavailable, rx might
73
                    // stop when the buffer is full, without an end marker present!
74
                    // (Checking for two value of hw_offset here, because it's not documented what
75
                    // happens with the pointer in that case.)
76
                    || !property!("rmt.has_rx_wrap") && (hw_offset == 0 || hw_offset == memsize)
77
                    || unsafe {
78
                        raw.channel_ram_start()
79
                            .add(hw_offset.checked_sub(1).unwrap_or(memsize - 1))
80
                            .read_volatile()
81
                    }
82
                    .is_end_marker())
83
            {
84
                return false;
85
            }
86

            
87
            max_count
88
        } else {
89
            memsize / 2
90
        };
91

            
92
        let count = data.len().min(max_count);
93
        let mut count0 = count.min(memsize - offset);
94
        let mut count1 = count - count0;
95

            
96
        // Read in up to 2 chunks to allow wrapping around the buffer end. This is more efficient
97
        // than checking in each iteration of the inner loop whether we reached the buffer end.
98
        let mut ram_ptr = unsafe { ram_start.add(self.offset as usize) };
99

            
100
        let mut data_ptr = data.as_mut_ptr();
101

            
102
        loop {
103
            let data_end = unsafe { data_ptr.add(count0) };
104
            while data_ptr < data_end {
105
                // SAFETY: The iteration count `count0` is smaller than both `max_count` and
106
                // `data.len()` such that incrementing both pointers cannot advance them beyond
107
                // their allocation's end.
108
                unsafe {
109
                    data_ptr.write(ram_ptr.read_volatile());
110
                    ram_ptr = ram_ptr.add(1);
111
                    data_ptr = data_ptr.add(1);
112
                }
113
            }
114

            
115
            if count1 == 0 {
116
                break;
117
            }
118

            
119
            count0 = count1;
120
            count1 = 0;
121
            ram_ptr = ram_start;
122
        }
123

            
124
        // Update offset as
125
        //
126
        // | offset      | new offset  |
127
        // | ----------- + ----------- |
128
        // | 0           | memsize / 2 |
129
        // | memsize / 2 | 0           |
130
        //
131
        // If `count < max_count` or if `final_` is set, the new offset will not correspond to
132
        // where we stopped reading, but the new offset will not be used again since further calls
133
        // will immediately return due to `self.state != Active`.
134
        self.offset = (memsize / 2) as u16 - self.offset;
135
        self.total += count;
136

            
137
        // The panic can never trigger since count <= data.len()!
138
        data.split_off_mut(..count).unwrap();
139

            
140
        if count < max_count {
141
            // `data` exhausted
142
            self.state = ReaderState::Error(Error::ReceiverError);
143
        } else if final_ {
144
            // Caller indicated that we're done
145
            self.state = ReaderState::Done;
146
        }
147

            
148
        debug_assert!(self.offset == 0 || self.offset as usize == memsize / 2);
149

            
150
        true
151
    }
152
}