]> oss.titaniummirror.com Git - tinyos-2.x.git/blob - tos/chips/at45db/LogStorageP.nc
Updates to 119.
[tinyos-2.x.git] / tos / chips / at45db / LogStorageP.nc
1 /* tab:4
2 * "Copyright (c) 2000-2004 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation for any purpose, without fee, and without written agreement is
7 * hereby granted, provided that the above copyright notice, the following
8 * two paragraphs and the author appear in all copies of this software.
9 *
10 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
11 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
12 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
13 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14 *
15 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
16 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17 * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
18 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
19 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
20 *
21 * Copyright (c) 2002-2006 Intel Corporation
22 * All rights reserved.
23 *
24 * This file is distributed under the terms in the attached INTEL-LICENSE
25 * file. If you do not find these files, copies can be found by writing to
26 * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA,
27 * 94704. Attention: Intel License Inquiry.
28 */
29
30 #include <Storage.h>
31 #include <crc.h>
32
33 /**
34 * Private component of the AT45DB implementation of the log storage
35 * abstraction.
36 *
37 * @author: David Gay <dgay@acm.org>
38 * @author: Jonathan Hui <jwhui@cs.berkeley.edu>
39 */
40
41 module LogStorageP {
42 provides {
43 interface LogRead[uint8_t logId];
44 interface LogWrite[uint8_t logId];
45 }
46 uses {
47 interface At45db;
48 interface At45dbVolume[uint8_t logId];
49 interface Resource[uint8_t logId];
50 }
51 }
52 implementation
53 {
54 /* Some design notes.
55
56 - The logId's in the LogRead and LogWrites are shifted left by 1 bit.
57 The low-order bit is 1 for circular logs, 0 for linear ones
58 (see newRequest and endRequest, and the LogStorageC configuration)
59
60 - Data is written sequentially to the pages of a log volume. Each page
61 ends with a footer (nx_struct pageinfo) recording metadata on the
62 current page:
63 o a cookie
64 o the "position" of the current page in the log (see below)
65 o the offset of the last record on this page (i.e., the offset
66 at which the last append ended) - only valid if flags & F_LASTVALID
67 o flags:
68 x F_SYNC page was synchronised - data after lastRecordOffset
69 is not log data; implies F_LASTVALID
70 x F_CIRCLED this page is not from the first run through the log's
71 pages (never set in linear logs)
72 x F_LASTVALID not set if no record ended on this page
73 o a CRC
74
75 - "Positions" are stored in the metadata, used as cookies by
76 currentOffset and seek, and stored in the wpos and rpos fields of the
77 volume state structure. They represent the number of bytes that
78 writing has advanced in the log since the log was erased, with
79 PAGE_SIZE added. Note that this is basically the number of bytes
80 written, except that when a page is synchronised unused bytes in the
81 page count towards increasing the position.
82
83 As a result, on page p, the following equation holds:
84 (metadata(p).pos - PAGE_SIZE) % volume-size == p * PAGE_SIZE
85 (this also means that the "position" metadata field could be replaced
86 by a count of the number of times writing has cycled through the log,
87 reducing the metadata size)
88
89 The PAGE_SIZE offset on positions is caused by Invariant 2 below: to
90 ensure that Invariant 2 is respected, at flash erase time, we write a
91 valid page with position 0 to the last block of the flash. As a result,
92 the first writes to the flash, in page 0, are at "position" PAGE_SIZE.
93
94 - This code is designed to deal with "one-at-a-time" failures (i.e.,
95 the system will not modify any blocks after a previous failed
96 write). This should allow recovery from:
97 o arbitrary reboots
98 o write failure (the underlying PageEEPROM shuts down after any
99 write fails; all pages are flushed before moving on to the next
100 page)
101 It will not recover from arbitrary data corruption
102
103 - When sync is called, the current write page is written to flash with an
104 F_SYNC flag and writing continues on the next page (wasting on average
105 half a flasg page)
106
107 - We maintain the following invariants on log volumes, even in the face
108 of the "one-at-a-time" failures described above:
109 1) at least one of the first and last blocks are valid
110 2) the last block, if valid, has the F_SYNC flag
111
112 - Locating the log boundary page (the page with the greatest position):
113
114 Invariant 1, the one-at-a-time failure model and the metadata position
115 definition guarantees that the physical flash pages have the following
116 properties:
117 an initial set of V1 valid pages,
118 followed by a set of I invalid pages,
119 followed by a set of V2 valid pages
120 with V1+i+V2=total-number-of-pages, and V1, V2, I >= 0
121 Additionally, the position of all pages in V1 is greater than in V2,
122 and consecutive pages in V1 (respectively V2) have greater positions
123 than their predecessors.
124
125 From this, it's possible to locate the log boundary page (the page with
126 the greatest position) using the following algorithm:
127 o let basepos=metadata(lastpage).pos, or 0 if the last page is invalid
128 o locate (using a binary search) the page p with the largest position
129 greater than basepos
130 invalid pages can be assumed to have positions less than basepos
131 if there is no such page p, let p = lastpage
132
133 Once the log boundary page is known, we resume writing at the last
134 page before p with a record boundary (Invariant 2, combined with
135 limiting individual records to volumesize - PAGE_SIZE ensures there
136 will be such a page).
137
138 - The read pointer has a special "invalid" state which represents the
139 current beginning of the log. In that state, LogRead.currentOffset()
140 returns SEEK_BEGINNING rather than a regular position.
141
142 The read pointer is invalidated:
143 o at boot time
144 o after the volume is erased
145 o after the write position "catches up" with the read position
146 o after a failed seek
147
148 Reads from an invalid pointer:
149 o start reading from the beginning of the flash if we are on the
150 first run through the log volume
151 o start reading at the first valid page after the write page with
152 an F_LASTVALID flag; the read offset is set to the lastRecordOffset
153 value
154 if this page has the SYNC flag, we start at the beginning of the
155 next page
156 */
157
158
159 enum {
160 F_SYNC = 1,
161 F_CIRCLED = 2,
162 F_LASTVALID = 4
163 };
164
165 nx_struct pageinfo {
166 nx_uint16_t magic;
167 nx_uint32_t pos;
168 nx_uint8_t lastRecordOffset;
169 nx_uint8_t flags;
170 nx_uint16_t crc;
171 };
172
173 enum {
174 N = uniqueCount(UQ_LOG_STORAGE),
175 NO_CLIENT = 0xff,
176 PAGE_SIZE = AT45_PAGE_SIZE - sizeof(nx_struct pageinfo),
177 PERSISTENT_MAGIC = 0x4256,
178 };
179
180 enum {
181 R_IDLE,
182 R_ERASE,
183 R_APPEND,
184 R_SYNC,
185 R_READ,
186 R_SEEK
187 };
188
189 enum {
190 META_IDLE,
191 META_LOCATEFIRST,
192 META_LOCATE,
193 META_LOCATELAST,
194 META_SEEK,
195 META_READ,
196 META_WRITE
197 };
198
199 uint8_t client = NO_CLIENT;
200 uint8_t metaState;
201 bool recordsLost;
202 at45page_t firstPage, lastPage;
203 storage_len_t len;
204 nx_struct pageinfo metadata;
205
206 struct {
207 /* The latest request made for this client, and it's arguments */
208 uint8_t request;
209 uint8_t *buf;
210 storage_len_t len;
211
212 /* Log r/w positions */
213 bool positionKnown : 1;
214 bool circular : 1;
215 bool circled : 1;
216 bool rvalid : 1;
217 uint32_t wpos; /* Bytes since start of logging */
218 at45page_t wpage; /* Current write page */
219 at45pageoffset_t woffset; /* Offset on current write page */
220 uint32_t rpos; /* Bytes since start of logging */
221 at45page_t rpage; /* Current read page */
222 at45pageoffset_t roffset; /* Offset on current read page */
223 at45pageoffset_t rend; /* Last valid offset on current read page */
224 } s[N];
225
226 at45page_t firstVolumePage() {
227 return call At45dbVolume.remap[client](0);
228 }
229
230 at45page_t npages() {
231 return call At45dbVolume.volumeSize[client]();
232 }
233
234 at45page_t lastVolumePage() {
235 return call At45dbVolume.remap[client](npages());
236 }
237
238 void setWritePage(at45page_t page) {
239 if (s[client].circular && page == lastVolumePage())
240 {
241 s[client].circled = TRUE;
242 page = firstVolumePage();
243 }
244 s[client].wpage = page;
245 s[client].woffset = 0;
246 }
247
248 void invalidateReadPointer() {
249 s[client].rvalid = FALSE;
250 }
251
252 void crcPage(at45page_t page) {
253 call At45db.computeCrc(page, 0,
254 PAGE_SIZE + offsetof(nx_struct pageinfo, crc), 0);
255 }
256
257 void readMetadata(at45page_t page) {
258 call At45db.read(page, PAGE_SIZE, &metadata, sizeof metadata);
259 }
260
261 void writeMetadata(at45page_t page) {
262 call At45db.write(page, PAGE_SIZE, &metadata, sizeof metadata);
263 }
264
265 void wmetadataStart();
266
267 void sync() {
268 metadata.flags = F_SYNC | F_LASTVALID;
269 metadata.lastRecordOffset = s[client].woffset;
270 /* rend is now no longer the end of the page */
271 if (s[client].rpage == s[client].wpage)
272 s[client].rend = s[client].woffset;
273 wmetadataStart();
274 }
275
276 /* ------------------------------------------------------------------ */
277 /* Queue and initiate user requests */
278 /* ------------------------------------------------------------------ */
279
280 void eraseStart();
281 void appendStart();
282 void syncStart();
283 void readStart();
284 void locateStart();
285 void rmetadataStart();
286 void seekStart();
287
288 void startRequest() {
289 if (!s[client].positionKnown && s[client].request != R_ERASE)
290 {
291 locateStart();
292 return;
293 }
294
295 metaState = META_IDLE;
296 switch (s[client].request)
297 {
298 case R_ERASE: eraseStart(); break;
299 case R_APPEND: appendStart(); break;
300 case R_SYNC: syncStart(); break;
301 case R_READ: readStart(); break;
302 case R_SEEK: seekStart(); break;
303 }
304 }
305
306 void endRequest(error_t ok) {
307 uint8_t c = client;
308 uint8_t request = s[c].request;
309 storage_len_t actualLen = s[c].len - len;
310 void *ptr = s[c].buf - actualLen;
311
312 client = NO_CLIENT;
313 s[c].request = R_IDLE;
314 call Resource.release[c]();
315
316 c = c << 1 | s[c].circular;
317 switch (request)
318 {
319 case R_ERASE: signal LogWrite.eraseDone[c](ok); break;
320 case R_APPEND: signal LogWrite.appendDone[c](ptr, actualLen, recordsLost, ok); break;
321 case R_SYNC: signal LogWrite.syncDone[c](ok); break;
322 case R_READ: signal LogRead.readDone[c](ptr, actualLen, ok); break;
323 case R_SEEK: signal LogRead.seekDone[c](ok); break;
324 }
325 }
326
327 /* Enqueue request and request the underlying flash */
328 error_t newRequest(uint8_t newRequest, uint8_t id,
329 uint8_t *buf, storage_len_t length) {
330 s[id >> 1].circular = id & 1;
331 id >>= 1;
332
333 if (s[id].request != R_IDLE)
334 return EBUSY;
335
336 s[id].request = newRequest;
337 s[id].buf = buf;
338 s[id].len = length;
339 call Resource.request[id]();
340
341 return SUCCESS;
342 }
343
344 event void Resource.granted[uint8_t id]() {
345 client = id;
346 len = s[client].len;
347 startRequest();
348 }
349
350 command error_t LogWrite.append[uint8_t id](void* buf, storage_len_t length) {
351 if (len > call LogRead.getSize[id]() - PAGE_SIZE)
352 /* Writes greater than the volume size are invalid.
353 Writes equal to the volume size could break the log volume
354 invariant (see next comment).
355 Writes that span the whole volume could lead to problems
356 at boot time (no valid block with a record boundary).
357 Refuse them all. */
358 return EINVAL;
359 else
360 return newRequest(R_APPEND, id, buf, length);
361 }
362
363 command storage_cookie_t LogWrite.currentOffset[uint8_t id]() {
364 return s[id >> 1].wpos;
365 }
366
367 command error_t LogWrite.erase[uint8_t id]() {
368 return newRequest(R_ERASE, id, NULL, 0);
369 }
370
371 command error_t LogWrite.sync[uint8_t id]() {
372 return newRequest(R_SYNC, id, NULL, 0);
373 }
374
375 command error_t LogRead.read[uint8_t id](void* buf, storage_len_t length) {
376 return newRequest(R_READ, id, buf, length);
377 }
378
379 command storage_cookie_t LogRead.currentOffset[uint8_t id]() {
380 id >>= 1;
381 return s[id].rvalid ? s[id].rpos : SEEK_BEGINNING;
382 }
383
384 command error_t LogRead.seek[uint8_t id](storage_cookie_t offset) {
385 return newRequest(R_SEEK, id, (void *)(offset >> 16), offset);
386 }
387
388 command storage_len_t LogRead.getSize[uint8_t id]() {
389 return call At45dbVolume.volumeSize[id >> 1]() * (storage_len_t)PAGE_SIZE;
390 }
391
392 /* ------------------------------------------------------------------ */
393 /* Erase */
394 /* ------------------------------------------------------------------ */
395
396 void eraseMetadataDone() {
397 /* Set write pointer to the beginning of the flash */
398 s[client].wpos = PAGE_SIZE; // last page has offset 0 and is before us
399 s[client].circled = FALSE;
400 setWritePage(firstVolumePage());
401
402 invalidateReadPointer();
403
404 s[client].positionKnown = TRUE;
405 endRequest(SUCCESS);
406 }
407
408 void eraseEraseDone() {
409 if (firstPage == lastPage - 1)
410 {
411 /* We create a valid, synced last page (see invariants) */
412 metadata.flags = F_SYNC | F_LASTVALID;
413 metadata.lastRecordOffset = 0;
414 setWritePage(firstPage);
415 s[client].circled = FALSE;
416 s[client].wpos = 0;
417 wmetadataStart();
418 }
419 else
420 call At45db.erase(firstPage++, AT45_ERASE);
421 }
422
423 void eraseStart() {
424 s[client].positionKnown = FALSE; // in case erase fails
425 firstPage = firstVolumePage();
426 lastPage = lastVolumePage();
427 eraseEraseDone();
428 }
429
430 /* ------------------------------------------------------------------ */
431 /* Locate log boundaries */
432 /* ------------------------------------------------------------------ */
433
434 void locateLastRecord();
435
436 void locateLastCrcDone(uint16_t crc) {
437 if (crc != metadata.crc)
438 {
439 locateLastRecord();
440 return;
441 }
442
443 /* We've found the last valid page with a record-end. Set up
444 the read and write positions. */
445 invalidateReadPointer();
446
447 if (metadata.flags & F_SYNC) /* must start on next page */
448 {
449 /* We need to special case the empty log, as we don't want
450 to wrap around in the case of a full, non-circular log
451 with a sync on its last page. */
452 if (firstPage == lastPage && !metadata.pos)
453 setWritePage(firstVolumePage());
454 else
455 setWritePage(firstPage + 1);
456 s[client].wpos = metadata.pos + PAGE_SIZE;
457 }
458 else
459 {
460 s[client].wpage = firstPage;
461 s[client].woffset = metadata.lastRecordOffset;
462 s[client].wpos = metadata.pos + metadata.lastRecordOffset;
463 }
464
465 s[client].circled = (metadata.flags & F_CIRCLED) != 0;
466 if (s[client].circled && !s[client].circular) // oops
467 {
468 endRequest(FAIL);
469 return;
470 }
471
472 /* And we can now proceed to the real request */
473 s[client].positionKnown = TRUE;
474 startRequest();
475 }
476
477 void locateLastReadDone() {
478 if (metadata.magic == PERSISTENT_MAGIC && metadata.flags & F_LASTVALID)
479 crcPage(firstPage);
480 else
481 locateLastRecord();
482 }
483
484 void locateLastRecord() {
485 if (firstPage == lastPage)
486 {
487 /* We walked all the way back to the last page, and it's not
488 valid. The log-volume invariant is not holding. Fail out. */
489 endRequest(FAIL);
490 return;
491 }
492
493 if (firstPage == firstVolumePage())
494 firstPage = lastPage;
495 else
496 firstPage--;
497
498 readMetadata(firstPage);
499 }
500
501 void located() {
502 metaState = META_LOCATELAST;
503 /* firstPage is one after last valid page, but the last page with
504 a record end may be some pages earlier. Search for it. */
505 lastPage = lastVolumePage() - 1;
506 locateLastRecord();
507 }
508
509 at45page_t locateCurrentPage() {
510 return firstPage + ((lastPage - firstPage) >> 1);
511 }
512
513 void locateBinarySearch() {
514 if (lastPage <= firstPage)
515 located();
516 else
517 readMetadata(locateCurrentPage());
518 }
519
520 void locateGreaterThan() {
521 firstPage = locateCurrentPage() + 1;
522 locateBinarySearch();
523 }
524
525 void locateLessThan() {
526 lastPage = locateCurrentPage();
527 locateBinarySearch();
528 }
529
530 void locateCrcDone(uint16_t crc) {
531 if (crc == metadata.crc)
532 {
533 s[client].wpos = metadata.pos;
534 locateGreaterThan();
535 }
536 else
537 locateLessThan();
538 }
539
540 void locateReadDone() {
541 if (metadata.magic == PERSISTENT_MAGIC && s[client].wpos < metadata.pos)
542 crcPage(locateCurrentPage());
543 else
544 locateLessThan();
545 }
546
547 void locateFirstCrcDone(uint16_t crc) {
548 if (metadata.magic == PERSISTENT_MAGIC && crc == metadata.crc)
549 s[client].wpos = metadata.pos;
550 else
551 s[client].wpos = 0;
552
553 metaState = META_LOCATE;
554 locateBinarySearch();
555 }
556
557 void locateFirstReadDone() {
558 crcPage(lastPage);
559 }
560
561 /* Locate log beginning and ending. See description at top of file. */
562 void locateStart() {
563 metaState = META_LOCATEFIRST;
564 firstPage = firstVolumePage();
565 lastPage = lastVolumePage() - 1;
566 readMetadata(lastPage);
567 }
568
569 /* ------------------------------------------------------------------ */
570 /* Append */
571 /* ------------------------------------------------------------------ */
572
573 void appendContinue() {
574 uint8_t *buf = s[client].buf;
575 at45pageoffset_t offset = s[client].woffset, count;
576
577 if (len == 0)
578 {
579 endRequest(SUCCESS);
580 return;
581 }
582
583 if (s[client].wpage == lastVolumePage())
584 {
585 /* We reached the end of a linear log */
586 endRequest(ESIZE);
587 return;
588 }
589
590 if (offset + len <= PAGE_SIZE)
591 count = len;
592 else
593 count = PAGE_SIZE - offset;
594
595 s[client].buf += count;
596 s[client].wpos += count;
597 s[client].woffset += count;
598 len -= count;
599
600 /* We normally lose data at the point we make the first write to a
601 page in a log that has circled. */
602 if (offset == 0 && s[client].circled)
603 recordsLost = TRUE;
604
605 call At45db.write(s[client].wpage, offset, buf, count);
606 }
607
608 void appendWriteDone() {
609 if (s[client].woffset == PAGE_SIZE) /* Time to write metadata */
610 wmetadataStart();
611 else
612 endRequest(SUCCESS);
613 }
614
615 void appendMetadataDone() { // metadata of previous page flushed
616 /* Setup metadata in case we overflow this page too */
617 metadata.flags = 0;
618 appendContinue();
619 }
620
621 void appendSyncDone() {
622 s[client].wpos = metadata.pos + PAGE_SIZE;
623 appendStart();
624 }
625
626 void appendStart() {
627 storage_len_t vlen = (storage_len_t)npages() * PAGE_SIZE;
628
629 recordsLost = FALSE;
630
631 /* If request would span the end of the flash, sync, to maintain the
632 invariant that the last flash page is synced and that either
633 the first or last pages are valid.
634
635 Note that >= in the if below means we won't write a record that
636 would end on the last byte of the last page, as this would mean that
637 we would not sync the last page, breaking the log volume
638 invariant */
639 if ((s[client].wpos - PAGE_SIZE) % vlen >= vlen - len)
640 sync();
641 else
642 {
643 /* Set lastRecordOffset in case we need to write metadata (see
644 wmetadataStart) */
645 metadata.lastRecordOffset = s[client].woffset;
646 metadata.flags = F_LASTVALID;
647 appendContinue();
648 }
649 }
650
651 /* ------------------------------------------------------------------ */
652 /* Sync */
653 /* ------------------------------------------------------------------ */
654
655 void syncStart() {
656 if (s[client].woffset == 0) /* we can't lose any writes */
657 endRequest(SUCCESS);
658 else
659 sync();
660 }
661
662 void syncMetadataDone() {
663 /* Write position reflect the absolute position in the flash, not
664 user-bytes written. So update wpos to reflect sync effects. */
665 s[client].wpos = metadata.pos + PAGE_SIZE;
666 endRequest(SUCCESS);
667 }
668
669 /* ------------------------------------------------------------------ */
670 /* Write block metadata */
671 /* ------------------------------------------------------------------ */
672
673 void wmetadataStart() {
674 /* The caller ensures that metadata.flags (except F_CIRCLED) and
675 metadata.lastRecordOffset are set correctly. */
676 metaState = META_WRITE;
677 firstPage = s[client].wpage; // remember page to commit
678 metadata.pos = s[client].wpos - s[client].woffset;
679 metadata.magic = PERSISTENT_MAGIC;
680 if (s[client].circled)
681 metadata.flags |= F_CIRCLED;
682
683 call At45db.computeCrc(firstPage, 0, PAGE_SIZE, 0);
684
685 /* We move to the next page now. If writing the metadata fails, we'll
686 simply leave the invalid page in place. Trying to recover seems
687 complicated, and of little benefit (note that in practice, At45dbC
688 shuts down after a failed write, so nothing is really going to
689 happen after that anyway). */
690 setWritePage(s[client].wpage + 1);
691
692 /* Invalidate read pointer if we reach it's page */
693 if (s[client].wpage == s[client].rpage)
694 invalidateReadPointer();
695 }
696
697 void wmetadataCrcDone(uint16_t crc) {
698 uint8_t i, *md;
699
700 // Include metadata in crc
701 md = (uint8_t *)&metadata;
702 for (i = 0; i < offsetof(nx_struct pageinfo, crc); i++)
703 crc = crcByte(crc, md[i]);
704 metadata.crc = crc;
705
706 // And save it
707 writeMetadata(firstPage);
708 }
709
710 void wmetadataWriteDone() {
711 metaState = META_IDLE;
712 if (metadata.flags & F_SYNC)
713 call At45db.sync(firstPage);
714 else
715 call At45db.flush(firstPage);
716 }
717
718 /* ------------------------------------------------------------------ */
719 /* Read */
720 /* ------------------------------------------------------------------ */
721
722 void readContinue() {
723 uint8_t *buf = s[client].buf;
724 at45pageoffset_t offset = s[client].roffset, count;
725 at45pageoffset_t end = s[client].rend;
726
727 if (len == 0)
728 {
729 endRequest(SUCCESS);
730 return;
731 }
732
733 if (!s[client].rvalid)
734 {
735 if (s[client].circled)
736 /* Find a valid page after wpage, skipping invalid pages */
737 s[client].rpage = s[client].wpage;
738 else
739 {
740 /* resume reading at the beginning of the first page */
741 s[client].rvalid = TRUE;
742 s[client].rpage = lastVolumePage() - 1;
743 }
744
745 rmetadataStart();
746 return;
747 }
748
749 if (s[client].rpage == s[client].wpage)
750 end = s[client].woffset;
751
752 if (offset == end)
753 {
754 if ((s[client].rpage + 1 == lastVolumePage() && !s[client].circular) ||
755 s[client].rpage == s[client].wpage)
756 endRequest(SUCCESS); // end of log
757 else
758 rmetadataStart();
759 return;
760 }
761
762 if (offset + len <= end)
763 count = len;
764 else
765 count = end - offset;
766
767 s[client].buf += count;
768 len -= count;
769 s[client].rpos += count;
770 s[client].roffset = offset + count;
771
772 call At45db.read(s[client].rpage, offset, buf, count);
773 }
774
775 void readStart() {
776 readContinue();
777 }
778
779 /* ------------------------------------------------------------------ */
780 /* Read block metadata */
781 /* ------------------------------------------------------------------ */
782
783 void continueReadAt(at45pageoffset_t roffset) {
784 /* Resume reading at firstPage whose metadata is currently available
785 in the metadata variable */
786 metaState = META_IDLE;
787 s[client].rpos = metadata.pos + roffset;
788 s[client].rpage = firstPage;
789 s[client].roffset = roffset;
790 s[client].rend =
791 metadata.flags & F_SYNC ? metadata.lastRecordOffset : PAGE_SIZE;
792 s[client].rvalid = TRUE;
793 readContinue();
794 }
795
796 void rmetadataContinue() {
797 if (++firstPage == lastVolumePage())
798 firstPage = firstVolumePage();
799 if (firstPage == s[client].wpage)
800 if (!s[client].rvalid)
801 /* We cannot find a record boundary to start at (we've just
802 walked through the whole log...). Give up. */
803 endRequest(SUCCESS);
804 else
805 {
806 /* The current write page has no metadata yet, so we fake it */
807 metadata.flags = 0;
808 metadata.pos = s[client].wpos - s[client].woffset;
809 continueReadAt(0);
810 }
811 else
812 readMetadata(firstPage);
813 }
814
815 void rmetadataReadDone() {
816 if (metadata.magic == PERSISTENT_MAGIC)
817 crcPage(firstPage);
818 else
819 endRequest(SUCCESS);
820 }
821
822 void rmetadataCrcDone(uint16_t crc) {
823 if (!s[client].rvalid)
824 if (crc == metadata.crc && metadata.flags & F_LASTVALID)
825 continueReadAt(metadata.lastRecordOffset);
826 else
827 rmetadataContinue();
828 else
829 if (crc == metadata.crc)
830 continueReadAt(0);
831 else
832 endRequest(SUCCESS);
833 }
834
835 void rmetadataStart() {
836 metaState = META_READ;
837 firstPage = s[client].rpage;
838 rmetadataContinue();
839 }
840
841 /* ------------------------------------------------------------------ */
842 /* Seek. */
843 /* ------------------------------------------------------------------ */
844
845 void seekCrcDone(uint16_t crc) {
846 if (metadata.magic == PERSISTENT_MAGIC && crc == metadata.crc &&
847 metadata.pos == s[client].rpos - s[client].roffset)
848 {
849 s[client].rvalid = TRUE;
850 if (metadata.flags & F_SYNC)
851 s[client].rend = metadata.lastRecordOffset;
852 }
853 endRequest(SUCCESS);
854 }
855
856 void seekReadDone() {
857 crcPage(s[client].rpage);
858 }
859
860 /* Move to position specified by cookie. */
861 void seekStart() {
862 uint32_t offset = (uint32_t)(uint16_t)s[client].buf << 16 | s[client].len;
863
864 invalidateReadPointer(); // default to beginning of log
865
866 /* The write positions are offset by PAGE_SIZE (see emptyLog) */
867
868 if (offset == SEEK_BEGINNING)
869 offset = PAGE_SIZE;
870
871 if (offset > s[client].wpos || offset < PAGE_SIZE)
872 {
873 endRequest(EINVAL);
874 return;
875 }
876
877 /* Cookies are just flash positions which continue incrementing as
878 you circle around and around. So we can just check the requested
879 page's metadata.pos field matches the cookie's value */
880 s[client].rpos = offset;
881 s[client].roffset = (offset - PAGE_SIZE) % PAGE_SIZE;
882 s[client].rpage = firstVolumePage() + ((offset - PAGE_SIZE) / PAGE_SIZE) % npages();
883 s[client].rend = PAGE_SIZE; // default to no sync flag
884
885 // The last page's metadata isn't written to flash yet. Special case it.
886 if (s[client].rpage == s[client].wpage)
887 {
888 /* If we're seeking within the current write page, just go there.
889 Otherwise, we're asking for an old version of the current page
890 so just keep the invalidated read pointer, i.e., read from
891 the beginning. */
892 if (offset >= s[client].wpos - s[client].woffset)
893 s[client].rvalid = TRUE;
894 endRequest(SUCCESS);
895 }
896 else
897 {
898 metaState = META_SEEK;
899 readMetadata(s[client].rpage);
900 }
901 }
902
903 /* ------------------------------------------------------------------ */
904 /* Dispatch HAL operations to current user op */
905 /* ------------------------------------------------------------------ */
906
907 event void At45db.eraseDone(error_t error) {
908 if (client != NO_CLIENT)
909 if (error != SUCCESS)
910 endRequest(FAIL);
911 else
912 eraseEraseDone();
913 }
914
915 event void At45db.writeDone(error_t error) {
916 if (client != NO_CLIENT)
917 if (error != SUCCESS)
918 endRequest(FAIL);
919 else
920 switch (metaState)
921 {
922 case META_WRITE: wmetadataWriteDone(); break;
923 case META_IDLE: appendWriteDone(); break;
924 }
925 }
926
927 event void At45db.syncDone(error_t error) {
928 if (client != NO_CLIENT)
929 if (error != SUCCESS)
930 endRequest(FAIL);
931 else switch (s[client].request)
932 {
933 case R_ERASE: eraseMetadataDone(); break;
934 case R_APPEND: appendSyncDone(); break;
935 case R_SYNC: syncMetadataDone(); break;
936 }
937 }
938
939 event void At45db.flushDone(error_t error) {
940 if (client != NO_CLIENT)
941 if (error != SUCCESS)
942 endRequest(FAIL);
943 else
944 appendMetadataDone();
945 }
946
947 event void At45db.readDone(error_t error) {
948 if (client != NO_CLIENT)
949 if (error != SUCCESS)
950 endRequest(FAIL);
951 else
952 switch (metaState)
953 {
954 case META_LOCATEFIRST: locateFirstReadDone(); break;
955 case META_LOCATE: locateReadDone(); break;
956 case META_LOCATELAST: locateLastReadDone(); break;
957 case META_SEEK: seekReadDone(); break;
958 case META_READ: rmetadataReadDone(); break;
959 case META_IDLE: readContinue(); break;
960 }
961 }
962
963 event void At45db.computeCrcDone(error_t error, uint16_t crc) {
964 if (client != NO_CLIENT)
965 if (error != SUCCESS)
966 endRequest(FAIL);
967 else
968 switch (metaState)
969 {
970 case META_LOCATEFIRST: locateFirstCrcDone(crc); break;
971 case META_LOCATE: locateCrcDone(crc); break;
972 case META_LOCATELAST: locateLastCrcDone(crc); break;
973 case META_SEEK: seekCrcDone(crc); break;
974 case META_WRITE: wmetadataCrcDone(crc); break;
975 case META_READ: rmetadataCrcDone(crc); break;
976 }
977 }
978
979 event void At45db.copyPageDone(error_t error) { }
980
981 default event void LogWrite.appendDone[uint8_t logId](void* buf, storage_len_t l, bool rLost, error_t error) { }
982 default event void LogWrite.eraseDone[uint8_t logId](error_t error) { }
983 default event void LogWrite.syncDone[uint8_t logId](error_t error) { }
984 default event void LogRead.readDone[uint8_t logId](void* buf, storage_len_t l, error_t error) { }
985 default event void LogRead.seekDone[uint8_t logId](error_t error) {}
986
987 default command at45page_t At45dbVolume.remap[uint8_t logId](at45page_t volumePage) {return 0;}
988 default command at45page_t At45dbVolume.volumeSize[uint8_t logId]() {return 0;}
989 default async command error_t Resource.request[uint8_t logId]() {return SUCCESS;}
990 default async command error_t Resource.release[uint8_t logId]() { return FAIL; }
991 }