summaryrefslogtreecommitdiff
path: root/nrf-softdevice/src/flash.rs
blob: afee4832d035e14f91b7ee5916ec0d9ff323b94b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
use core::future::Future;
use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, Ordering};
use embedded_storage::nor_flash::{ErrorType, NorFlashError, NorFlashErrorKind, ReadNorFlash};
use embedded_storage_async::nor_flash::{AsyncNorFlash, AsyncReadNorFlash};

use crate::raw;
use crate::util::{DropBomb, Signal};
use crate::{RawError, Softdevice};

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum FlashError {
    Failed,
    AddressMisaligned,
    BufferMisaligned,
}

impl NorFlashError for FlashError {
    fn kind(&self) -> NorFlashErrorKind {
        match self {
            Self::Failed => NorFlashErrorKind::Other,
            Self::AddressMisaligned => NorFlashErrorKind::NotAligned,
            Self::BufferMisaligned => NorFlashErrorKind::NotAligned,
        }
    }
}

/// Singleton instance of the Flash softdevice functionality.
pub struct Flash {
    // Prevent Send, Sync
    _private: PhantomData<*mut ()>,
}

static FLASH_TAKEN: AtomicBool = AtomicBool::new(false);

impl Flash {
    const PAGE_SIZE: usize = 4096;

    /// Takes the Flash instance from the softdevice.
    ///
    /// # Panics
    ///
    /// Panics if called more than once.
    pub fn take(_sd: &Softdevice) -> Flash {
        if FLASH_TAKEN
            .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
            .is_err()
        {
            panic!("nrf_softdevice::Softdevice::take_flash() called multiple times.")
        }

        Flash {
            _private: PhantomData,
        }
    }
}

static SIGNAL: Signal<Result<(), FlashError>> = Signal::new();

pub(crate) fn on_flash_success() {
    SIGNAL.signal(Ok(()))
}

pub(crate) fn on_flash_error() {
    SIGNAL.signal(Err(FlashError::Failed))
}

impl ErrorType for Flash {
    type Error = FlashError;
}

impl ReadNorFlash for Flash {
    const READ_SIZE: usize = 1;

    fn read(&mut self, address: u32, data: &mut [u8]) -> Result<(), Self::Error> {
        // Reading is simple since SoC flash is memory-mapped :)
        // TODO check addr/len is in bounds.

        data.copy_from_slice(unsafe {
            core::slice::from_raw_parts(address as *const u8, data.len())
        });

        Ok(())
    }

    fn capacity(&self) -> usize {
        256 * 4096
    }
}

impl AsyncReadNorFlash for Flash {
    const READ_SIZE: usize = 1;

    type ReadFuture<'a> = impl Future<Output = Result<(), FlashError>> + 'a;
    fn read<'a>(&'a mut self, address: u32, data: &'a mut [u8]) -> Self::ReadFuture<'a> {
        async move { <Self as ReadNorFlash>::read(self, address, data) }
    }

    fn capacity(&self) -> usize {
        <Self as ReadNorFlash>::capacity(self)
    }
}

impl AsyncNorFlash for Flash {
    const WRITE_SIZE: usize = 4;
    const ERASE_SIZE: usize = 4096;

    type WriteFuture<'a> = impl Future<Output = Result<(), FlashError>> + 'a;
    fn write<'a>(&'a mut self, offset: u32, data: &'a [u8]) -> Self::WriteFuture<'a> {
        async move {
            let data_ptr = data.as_ptr();
            let data_len = data.len() as u32;

            let address = offset as usize;
            if address % 4 != 0 {
                return Err(FlashError::AddressMisaligned);
            }
            if (data_ptr as u32) % 4 != 0 || data_len % 4 != 0 {
                return Err(FlashError::BufferMisaligned);
            }

            // This is safe because we've checked ptr and len is aligned above
            let words_ptr = data_ptr as *const u32;
            let words_len = data_len / 4;

            let bomb = DropBomb::new();
            let ret = unsafe { raw::sd_flash_write(address as _, words_ptr, words_len) };
            let ret = match RawError::convert(ret) {
                Ok(()) => SIGNAL.wait().await,
                Err(_e) => {
                    warn!("sd_flash_write err {:?}", _e);
                    Err(FlashError::Failed)
                }
            };

            bomb.defuse();
            ret
        }
    }

    type EraseFuture<'a> = impl Future<Output = Result<(), FlashError>> + 'a;
    fn erase<'a>(&'a mut self, from: u32, to: u32) -> Self::EraseFuture<'a> {
        async move {
            if from as usize % Self::PAGE_SIZE != 0 {
                return Err(FlashError::AddressMisaligned);
            }
            if to as usize % Self::PAGE_SIZE != 0 {
                return Err(FlashError::AddressMisaligned);
            }

            let bomb = DropBomb::new();
            for address in (from as usize..to as usize).step_by(Self::PAGE_SIZE) {
                let page_number = (address / Self::PAGE_SIZE) as u32;
                let ret = unsafe { raw::sd_flash_page_erase(page_number) };
                match RawError::convert(ret) {
                    Ok(()) => match SIGNAL.wait().await {
                        Err(_e) => {
                            warn!("sd_flash_page_erase err {:?}", _e);
                            bomb.defuse();
                            return Err(_e);
                        }
                        _ => {}
                    },
                    Err(_e) => {
                        warn!("sd_flash_page_erase err {:?}", _e);
                        bomb.defuse();
                        return Err(FlashError::Failed);
                    }
                }
            }

            bomb.defuse();
            Ok(())
        }
    }
}