summaryrefslogtreecommitdiff
path: root/nrf-softdevice/src/flash.rs
blob: 8a4296b1190f2f8c73e26afa1e86f547db42436a (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
use core::future::Future;
use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, Ordering};
use embassy::traits::flash::Error as FlashError;

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

/// 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 embassy::traits::flash::Flash for Flash {
    type ReadFuture<'a> = impl Future<Output = Result<(), FlashError>> + 'a;
    type WriteFuture<'a> = impl Future<Output = Result<(), FlashError>> + 'a;
    type ErasePageFuture<'a> = impl Future<Output = Result<(), FlashError>> + 'a;

    fn read<'a>(&'a mut self, address: usize, data: &'a mut [u8]) -> Self::ReadFuture<'a> {
        async move {
            // 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 write<'a>(&'a mut self, address: usize, data: &'a [u8]) -> Self::WriteFuture<'a> {
        async move {
            let data_ptr = data.as_ptr();
            let data_len = data.len() as u32;

            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
        }
    }

    fn erase<'a>(&'a mut self, address: usize) -> Self::ErasePageFuture<'a> {
        async move {
            if address % Self::PAGE_SIZE != 0 {
                return Err(FlashError::AddressMisaligned);
            }

            let page_number = address / Self::PAGE_SIZE;

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

            bomb.defuse();
            ret
        }
    }

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

    fn read_size(&self) -> usize {
        1
    }

    fn write_size(&self) -> usize {
        4
    }

    fn erase_size(&self) -> usize {
        4096
    }
}