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
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! The Flash Controller interface with hardware

use crate::error_codes::ErrorCode;

/// Implementation required for the flash controller hardware. This
/// should read, write and erase flash from the hardware using the
/// flash controller.
///
/// This is the public trait for the Flash controller implementation.
///
/// The size of the regions (pages) must be the smallest size that can be
/// erased in a single operation. This is specified as the constant `S`
/// when implementing `FlashController` and `TicKV` and it must match
/// the length of the `read_buffer`.
///
/// The start and end address of the FlashController must be aligned
/// to the size of regions.
/// All `region_number`s and `address`es are offset from zero. If you
/// want to use flash that doesn't start at zero, or is a partition
/// offset from the start of flash you will need to add that offset
/// to the values in your implementation.
///
/// The boiler plate for an implementation will look something like this
///
/// ```rust
/// use tickv::error_codes::ErrorCode;
/// use tickv::flash_controller::FlashController;
///
/// #[derive(Default)]
/// struct FlashCtrl {}
///
/// impl FlashCtrl {
///     fn new() -> Self {
///         Self { /* fields */ }
///     }
/// }
///
/// impl FlashController<1024> for FlashCtrl {
///     fn read_region(&self, region_number: usize, buf: &mut [u8; 1024]) -> Result<(), ErrorCode> {
///         unimplemented!()
///     }
///
///     fn write(&self, address: usize, buf: &[u8]) -> Result<(), ErrorCode> {
///         unimplemented!()
///     }
///
///     fn erase_region(&self, region_number: usize) -> Result<(), ErrorCode> {
///         unimplemented!()
///     }
/// }
/// ```
pub trait FlashController<const S: usize> {
    /// This function must read the data from the flash region specified by
    /// `region_number` into `buf`. The length of the data read should be the
    /// same length as buf.
    ///
    /// On success it should return nothing, on failure it
    /// should return ErrorCode::ReadFail.
    ///
    /// If the read operation is to be complete asynchronously then
    /// `read_region()` can return `ErrorCode::ReadNotReady(region_number)`.
    /// By returning `ErrorCode::ReadNotReady(region_number)`
    /// `read_region()` can indicate that the operation should be retried in
    /// the future.
    /// After running the `continue_()` functions after a async
    /// `read_region()` has returned `ErrorCode::ReadNotReady(region_number)`
    /// the `read_region()` function will be called again and this time should
    /// return the data.
    fn read_region(&self, region_number: usize, buf: &mut [u8; S]) -> Result<(), ErrorCode>;

    /// This function must write the length of `buf` to the specified address
    /// in flash.
    /// If the length of `buf` is smaller then the minimum supported write size
    /// the implementation can write a larger value. This should be done by first
    /// reading the value, making the changed from `buf` and then writing it back.
    ///
    /// On success it should return nothing, on failure it
    /// should return ErrorCode::WriteFail.
    ///
    /// If the write operation is to be complete asynchronously then
    /// `write()` can return `ErrorCode::WriteNotReady(region_number)`.
    /// By returning `ErrorCode::WriteNotReady(region_number)`
    /// `read_region()` can indicate that the operation should be retried in
    /// the future. Note that that region will not be written
    /// again so the write must occur otherwise the operation fails.
    fn write(&self, address: usize, buf: &[u8]) -> Result<(), ErrorCode>;

    /// This function must erase the region specified by `region_number`.
    ///
    /// On success it should return nothing, on failure it
    /// should return ErrorCode::WriteFail.
    ///
    /// If the erase is going to happen asynchronously then this should return
    /// `EraseNotReady(region_number)`. Note that that region will not be erased
    /// again so the erasure must occur otherwise the operation fails.
    fn erase_region(&self, region_number: usize) -> Result<(), ErrorCode>;
}