]> oss.titaniummirror.com Git - tinyos-2.x.git/blob - tos/chips/at45db/ConfigStorageP.nc
Updates to 119.
[tinyos-2.x.git] / tos / chips / at45db / ConfigStorageP.nc
1 // $Id$
2
3 /* tab:4
4 * Copyright (c) 2002-2006 Intel Corporation
5 * All rights reserved.
6 *
7 * This file is distributed under the terms in the attached INTEL-LICENSE
8 * file. If you do not find these files, copies can be found by writing to
9 * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA,
10 * 94704. Attention: Intel License Inquiry.
11 */
12
13 /**
14 * Private component of the AT45DB implementation of the config storage
15 * abstraction.
16 *
17 * @author: David Gay <dgay@acm.org>
18 */
19
20 #include "Storage.h"
21 #include "crc.h"
22
23 module ConfigStorageP {
24 provides {
25 interface Mount[uint8_t id];
26 interface ConfigStorage[uint8_t id];
27 interface At45dbBlockConfig as BConfig[uint8_t id];
28 }
29 uses {
30 interface At45db;
31 interface BlockRead[uint8_t id];
32 interface BlockWrite[uint8_t id];
33 }
34 }
35 implementation
36 {
37 /* A config storage is built on top of a block storage volume, with
38 the block storage volume divided into two and the first 4 bytes of
39 each half holding a (>0) version number. The valid half with the
40 highest version number is the current version.
41
42 Transactional behaviour is achieved by copying the current half
43 into the other, then increment its version number. Writes then
44 proceed in that new half until a commit, which just uses the
45 underlying BlockStorage commit's operation.
46
47 Note: all of this depends on the at45db's implementation of
48 BlockStorageP. It will not work over an arbitrary BlockStorageP
49 implementation (additionally, it uses hooks in BlockStorageP to
50 support the half-volume operation). Additionally, the code assumes
51 that the config volumes all have lower ids than the block volumes.
52 */
53
54 enum {
55 S_STOPPED,
56 S_MOUNT,
57 S_COMMIT,
58 S_CLEAN,
59 S_DIRTY,
60 S_INVALID
61 };
62
63 enum {
64 N = uniqueCount(UQ_CONFIG_STORAGE),
65 NO_CLIENT = 0xff,
66 };
67
68 /* Per-client state. We could keep just the state and current version
69 in an array, but this requires more complex arbitration (don't
70 release block storage during mount or commit). As I don't expect
71 many config volumes, this doesn't seem worth the trouble. */
72 struct {
73 uint8_t state : 3;
74 uint8_t committing : 1;
75 } s[N];
76 nx_struct {
77 nx_uint16_t crc;
78 nx_uint32_t version;
79 } low[N], high[N];
80
81
82 /* Bit n is true if client n is using upper block */
83 uint8_t flipState[(N + 7) / 8];
84
85 uint8_t client = NO_CLIENT;
86 at45page_t nextPage;
87
88 void setFlip(uint8_t id, bool flip) {
89 if (flip)
90 flipState[id >> 3] |= 1 << (id & 7);
91 else
92 flipState[id >> 3] &= ~(1 << (id & 7));
93 }
94
95 bool flipped(uint8_t id) {
96 return call BConfig.flipped[id]();
97 }
98
99 void flip(uint8_t id) {
100 setFlip(id, !flipped(id));
101 }
102
103 storage_len_t volumeSize(uint8_t id) {
104 return call BlockRead.getSize[id]();
105 }
106
107 /* ------------------------------------------------------------------ */
108 /* Mounting */
109 /* ------------------------------------------------------------------ */
110
111 command error_t Mount.mount[uint8_t id]() {
112 /* Read version on both halves. Validate higher. Validate lower if
113 higher invalid. Use lower if both invalid. */
114 if (s[id].state != S_STOPPED)
115 return FAIL;
116
117 s[id].state = S_MOUNT;
118 setFlip(id, FALSE);
119 call BlockRead.read[id](0, &low[id], sizeof low[id]);
120
121 return SUCCESS;
122 }
123
124 void computeCrc(uint8_t id) {
125 call BlockRead.computeCrc[id](sizeof(nx_uint16_t),
126 volumeSize(id) - sizeof(nx_uint16_t),
127 0);
128 }
129
130 void mountReadDone(uint8_t id, error_t error) {
131 if (error != SUCCESS)
132 {
133 s[id].state = S_STOPPED;
134 signal Mount.mountDone[id](FAIL);
135 }
136 else if (!call BConfig.flipped[id]())
137 {
138 /* Just read low-half version. Read high-half version */
139 setFlip(id, TRUE);
140 call BlockRead.read[id](0, &high[id], sizeof high[id]);
141 }
142 else
143 {
144 /* Verify the half with the largest version */
145 setFlip(id, high[id].version > low[id].version);
146 computeCrc(id);
147 }
148 }
149
150 void mountCrcDone(uint8_t id, uint16_t crc, error_t error) {
151 bool isflipped = call BConfig.flipped[id]();
152
153 if (error == SUCCESS &&
154 crc == (isflipped ? high[id].crc : low[id].crc))
155 {
156 /* We just use the low data once mounted */
157 if (isflipped)
158 low[id].version = high[id].version;
159 s[id].state = S_CLEAN;
160 }
161 else
162 {
163 // try the other half?
164 if ((high[id].version > low[id].version) == isflipped)
165 {
166 /* Verification of the half with the highest version failed. Try
167 the other half. */
168 setFlip(id, !isflipped);
169 computeCrc(id);
170 return;
171 }
172 /* Both halves bad, terminate. Reads will fail. */
173 s[id].state = S_INVALID;
174 low[id].version = 0;
175 }
176 signal Mount.mountDone[id](SUCCESS);
177 }
178
179 /* ------------------------------------------------------------------ */
180 /* Read */
181 /* ------------------------------------------------------------------ */
182
183 command error_t ConfigStorage.read[uint8_t id](storage_addr_t addr, void* buf, storage_len_t len) {
184 /* Read from current half using BlockRead */
185 if (s[id].state < S_CLEAN)
186 return EOFF;
187 if (s[id].state == S_INVALID) // nothing to read
188 return FAIL;
189
190 return call BlockRead.read[id](addr + sizeof low[0], buf, len);
191 }
192
193 void readReadDone(uint8_t id, storage_addr_t addr, void* buf, storage_len_t len, error_t error) {
194 signal ConfigStorage.readDone[id](addr - sizeof low[0], buf, len, error);
195 }
196
197 /* ------------------------------------------------------------------ */
198 /* Write */
199 /* ------------------------------------------------------------------ */
200
201 command error_t ConfigStorage.write[uint8_t id](storage_addr_t addr, void* buf, storage_len_t len) {
202 /* 1: If first write:
203 copy to other half with incremented version number
204 2: Write to other half using BlockWrite */
205
206 if (s[id].state < S_CLEAN)
207 return EOFF;
208 return call BlockWrite.write[id](addr + sizeof low[0], buf, len);
209 }
210
211 void copyCopyPageDone(error_t error);
212 void writeContinue(error_t error);
213
214 command int BConfig.writeHook[uint8_t id]() {
215 if (s[id].committing)
216 return FALSE;
217
218 flip(id); /* We write to the non-current half... */
219 if (s[id].state != S_CLEAN) // no copy if dirty or invalid
220 return FALSE;
221
222 /* Time to do the copy dance */
223 client = id;
224 nextPage = signal BConfig.npages[id]();
225 copyCopyPageDone(SUCCESS);
226
227 return TRUE;
228 }
229
230 void copyCopyPageDone(error_t error) {
231 if (error != SUCCESS)
232 writeContinue(error);
233 else if (nextPage == 0) // copy done
234 {
235 s[client].state = S_DIRTY;
236 writeContinue(SUCCESS);
237 }
238 else
239 {
240 // copy next page
241 at45page_t from, to, npages = signal BConfig.npages[client]();
242
243 to = from = signal BConfig.remap[client](--nextPage);
244 if (flipped(client))
245 from -= npages;
246 else
247 from += npages;
248
249 call At45db.copyPage(from, to);
250 }
251 }
252
253 void writeContinue(error_t error) {
254 uint8_t id = client;
255
256 client = NO_CLIENT;
257 signal BConfig.writeContinue[id](error);
258 }
259
260 void writeWriteDone(uint8_t id, storage_addr_t addr, void* buf, storage_len_t len, error_t error) {
261 flip(id); // flip back to current half
262 signal ConfigStorage.writeDone[id](addr - sizeof low[0], buf, len, error);
263 }
264
265 /* ------------------------------------------------------------------ */
266 /* Commit */
267 /* ------------------------------------------------------------------ */
268
269 void commitSyncDone(uint8_t id, error_t error);
270
271 command error_t ConfigStorage.commit[uint8_t id]() {
272 error_t ok;
273 uint16_t crc;
274 uint8_t i;
275
276 if (s[id].state < S_CLEAN)
277 return EOFF;
278
279 if (s[id].state == S_CLEAN)
280 /* A dummy CRC call to avoid signaling a completion event from here */
281 return call BlockRead.computeCrc[id](0, 1, 0);
282
283 /* Compute CRC for new version and current contents */
284 flip(id);
285 low[id].version++;
286 for (crc = 0, i = 0; i < sizeof low[id].version; i++)
287 crc = crcByte(crc, ((uint8_t *)&low[id] + sizeof(nx_uint16_t))[i]);
288 ok = call BlockRead.computeCrc[id](sizeof low[id],
289 volumeSize(id) - sizeof low[id],
290 crc);
291 if (ok == SUCCESS)
292 s[id].committing = TRUE;
293
294 return ok;
295 }
296
297 void commitCrcDone(uint8_t id, uint16_t crc, error_t error) {
298 /* Weird commit of clean volume hack: we just complete now, w/o
299 really doing anything. Ideally we should short-circuit out in the
300 commit call, but that would break the "no-signal-from-command"
301 rule. So we just waste the CRC computation effort instead - the
302 assumption is people don't regularly commit clean volumes. */
303 if (s[id].state == S_CLEAN)
304 signal ConfigStorage.commitDone[id](error);
305 else if (error != SUCCESS)
306 commitSyncDone(id, error);
307 else
308 {
309 low[id].crc = crc;
310 call BlockWrite.write[id](0, &low[id], sizeof low[id]);
311 }
312 }
313
314 void commitWriteDone(uint8_t id, error_t error) {
315 if (error != SUCCESS)
316 commitSyncDone(id, error);
317 else
318 call BlockWrite.sync[id]();
319 }
320
321 void commitSyncDone(uint8_t id, error_t error) {
322 s[id].committing = FALSE;
323 if (error == SUCCESS)
324 s[id].state = S_CLEAN;
325 else
326 flip(id); // revert to old block
327 signal ConfigStorage.commitDone[id](error);
328 }
329
330 /* ------------------------------------------------------------------ */
331 /* Get Size */
332 /* ------------------------------------------------------------------ */
333
334 command storage_len_t ConfigStorage.getSize[uint8_t id]() {
335 return volumeSize(id) - sizeof low[0];
336 }
337
338 /* ------------------------------------------------------------------ */
339 /* Valid */
340 /* ------------------------------------------------------------------ */
341
342 command bool ConfigStorage.valid[uint8_t id]() {
343 return s[id].state != S_INVALID;
344 }
345
346 /* ------------------------------------------------------------------ */
347 /* Interface with BlockStorageP */
348 /* ------------------------------------------------------------------ */
349
350 /* The config volumes use the low block volume numbers. So a volume is a
351 config volume iff its its id is less than N */
352
353 command int BConfig.isConfig[uint8_t id]() {
354 return id < N;
355 }
356
357 inline command int BConfig.flipped[uint8_t id]() {
358 return (flipState[id >> 3] & (1 << (id & 7))) != 0;
359 }
360
361 event void BlockRead.readDone[uint8_t id](storage_addr_t addr, void* buf, storage_len_t len, error_t error) {
362 if (id < N)
363 if (s[id].state == S_MOUNT)
364 mountReadDone(id, error);
365 else
366 readReadDone(id, addr, buf, len, error);
367 }
368
369 event void BlockWrite.writeDone[uint8_t id]( storage_addr_t addr, void* buf, storage_len_t len, error_t error ) {
370 if (id < N)
371 if (s[id].committing)
372 commitWriteDone(id, error);
373 else
374 writeWriteDone(id, addr, buf, len, error);
375 }
376
377 event void BlockWrite.syncDone[uint8_t id]( error_t error ) {
378 if (id < N)
379 commitSyncDone(id, error);
380 }
381
382 event void BlockRead.computeCrcDone[uint8_t id]( storage_addr_t addr, storage_len_t len, uint16_t crc, error_t error ) {
383 if (id < N)
384 if (s[id].state == S_MOUNT)
385 mountCrcDone(id, crc, error);
386 else
387 commitCrcDone(id, crc, error);
388 }
389
390 event void At45db.copyPageDone(error_t error) {
391 if (client != NO_CLIENT)
392 copyCopyPageDone(error);
393 }
394
395 event void BlockWrite.eraseDone[uint8_t id](error_t error) {}
396 event void At45db.eraseDone(error_t error) {}
397 event void At45db.syncDone(error_t error) {}
398 event void At45db.flushDone(error_t error) {}
399 event void At45db.readDone(error_t error) {}
400 event void At45db.computeCrcDone(error_t error, uint16_t crc) {}
401 event void At45db.writeDone(error_t error) {}
402
403 default event void Mount.mountDone[uint8_t id](error_t error) { }
404 default event void ConfigStorage.readDone[uint8_t id](storage_addr_t addr, void* buf, storage_len_t len, error_t error) {}
405 default event void ConfigStorage.writeDone[uint8_t id](storage_addr_t addr, void* buf, storage_len_t len, error_t error) {}
406 default event void ConfigStorage.commitDone[uint8_t id](error_t error) {}
407 }