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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
//! The card: finding the FAT volume on it and mounting it.
//!
//! The filesystem is `resident-fat`, which keeps the allocation table and
//! every directory it has read in RAM. That is a trade of memory for
//! commands — a file moves in one multi-block transfer per contiguous run
//! rather than a command per block — and it is why this module needs a
//! heap. Everything here is generic over its
//! [`BlockDevice`](resident_fat::BlockDevice), so the controller the card
//! hangs off is the board's business: EMMC on one board, SDHOST on the
//! next, the same call.
//!
//! ```ignore
//! let mut volume = storage::mount(SdBlockDevice::new(sd, timer))?;
//! let loaded = config::load::<Settings, _>(&mut volume, "kickstart.toml")?;
//! ```
//!
//! # Timestamps
//!
//! With the `clock` feature, `mount` gives the volume a clock that reads
//! [`crate::clock`], so a file written once something has set the time
//! carries that time. Before then — and without the feature — it carries
//! the FAT epoch, 1980-01-01. That is not a concession: a write must not
//! depend on whether the network has answered, and a file dated 1980 beats
//! one that refuses to be written. It is also how a board reading the card
//! tells a file written by something that knew the time from one that did
//! not.
//!
//! The stamp is UTC. FAT timestamps carry no zone and operating systems
//! read them as local time, so a PC east or west of Greenwich shows these
//! files that many hours off; a board has no zone to convert with until a
//! `tz` one is read off the card, which is after the card is mounted.
//!
//! # One card, many tasks
//!
//! Once the executor runs, the card is wanted from several places at once —
//! a settings save from one web task, an update from another, a board's own
//! state from a third — and it cannot be driven from two of them.
//! [`Shared`](crate::storage::Shared) is where the mounted volume goes for
//! that: a `static` the board declares, holding the volume behind an async
//! mutex, reached with `with`.
use CriticalSectionRawMutex;
use Mutex;
use ;
use cratelogln;
/// Why [`mount`] found nothing to mount: `resident-fat`'s own error, which
/// has a case for each way a card goes wrong — including
/// `NoFatPartition`, a table with nothing FAT in it, told apart from a card
/// with no table at all — and prints as one line for the console.
pub type Error<E> = Error;
/// A mounted FAT volume.
///
/// An alias rather than a wrapper: `resident-fat`'s own API is the one to
/// read and write files through, and hiding it would mean re-exporting all
/// of it.
pub type Volume<D> = ;
/// A mounted volume that every task can reach, one at a time — declared by
/// the board as a `static`.
///
/// ```ignore
/// // SAFETY: one core runs the executor, no interrupt handler touches the
/// // card, and nothing reaches the volume except through `CARD`.
/// static CARD: storage::Shared<SdBlockDevice<'static>> = unsafe { storage::Shared::new() };
///
/// CARD.install(storage::mount(device)?); // at bring-up
/// CARD.with(|volume| config::save(volume, FILE, &settings)).await // from any task
/// ```
///
/// A type rather than a `static` of this crate's, because a `static` cannot
/// be generic and the block device is the board's.
// SAFETY: a volume holds its block device, and the devices a Pi has are not
// `Send` -- `rpi-hal` marks its peripheral handles against being shared
// across cores, and that marker is doing its job. What makes sharing one
// sound is the arrangement [`Shared::new`]'s caller promises: one core, no
// interrupt handler reaching the card, and every access through the mutex,
// so no two tasks are ever inside the driver at once.
unsafe
/// What the volume stamps on the entries it writes: [`crate::clock`]'s
/// time once something has set it, and the FAT epoch before that — see
/// the module documentation for why the fallback is the right answer.
///
/// Deliberately not uptime, which would stamp every file a few seconds
/// after 1980 with an air of precision it has not earned.
/// Mounts the FAT volume on `device`, and says on the console what it
/// found.
///
/// The volume is the first partition whose type byte says FAT, whichever
/// slot that is — imaging tools usually put the boot partition in slot 0,
/// but not always, and a card with a separate data partition has more than
/// one. A card with no partition table at all is mounted as one bare
/// volume, which is how some tools format a small card.
///
/// Mounting reads the whole allocation table into RAM — four bytes per
/// cluster, for as long as the volume is mounted — so the heap has to be
/// up first. The console line gives the figure, since it is the resource
/// this spends.