📄 p30logp.nc
字号:
/*
* Copyright (c) 2005 Arch Rock Corporation
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of the Arch Rock Corporation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ARCHED
* ROCK OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/**
* @author Kaisen Lin
* @author Phil Buonadonna
*
*/
#include <P30.h>
#include <StorageVolumes.h>
module P30LogP {
provides interface LogRead as Read[ storage_volume_t block ];
provides interface LogWrite as Write[ storage_volume_t block ];
uses interface Leds;
uses interface Flash;
uses interface Get<bool> as Circular[ storage_volume_t block ];
}
implementation {
#define SEEK_BEGINNING (0x0)
#define SEEK_EOL (0xFFFFFFFF)
#define L_BASE_BLOCK(_x) (P30_VMAP[_x].base * FLASH_PARTITION_SIZE)
#define L_PARTITIONS(_x) ((P30_VMAP[_x].size * FLASH_PARTITION_SIZE) / P30_BLOCK_SIZE)
#define L_FULL_RECORD_SIZE (sizeof(record_data_t) + sizeof(record_meta_t))
#define L_RECORD_DATA_SIZE 256
#define L_MAX_RECORDS_PER_BLOCK (P30_BLOCK_SIZE / L_FULL_RECORD_SIZE) // page meta counts as one record
// _a = blockId (from parameterized interface), _b = page, _c = record
#define L_RAW_OFFSET(_a,_b,_c) (L_BASE_BLOCK(_a) + (_b * P30_BLOCK_SIZE) + (_c * L_FULL_RECORD_SIZE))
enum {
INVALID_VERSION = 0xFFFFFFFF,
NUM_VOLS = _V_NUMVOLS_, //uniqueCount("pxa27xp30.Volume"),
};
enum {
PAGE_START = 0x0000,
PAGE_USED = 0xFFF0,
PAGE_AVAILABLE = 0xFFFF,
};
typedef struct page_meta_t {
uint16_t header;
} page_meta_t;
enum {
RECORD_VALID = 0x0000,
RECORD_INVALID = 0xFFF0,
RECORD_EMPTY = 0xFFFF,
};
typedef struct record_meta_t {
uint16_t status;
uint16_t length;
} record_meta_t;
typedef struct record_data_t {
uint8_t data[L_RECORD_DATA_SIZE];
} record_data_t;
typedef enum {
S_IDLE,
S_READ,
S_APPEND,
S_SYNC,
S_ERASE,
S_SEEK,
} p30_log_state_t;
norace p30_log_state_t m_state = S_IDLE;
storage_volume_t clientId = 0xff;
void* clientBuf;
storage_len_t clientLen;
error_t clientResult;
uint32_t firstBlock[NUM_VOLS]; // 0-15 for 2 MB
uint32_t lastBlock[NUM_VOLS]; // 0-15 for 2 MB
uint32_t nextFreeRecord[NUM_VOLS]; // 0-X depending on data size
storage_cookie_t readCookieOffset[NUM_VOLS]; // this is a raw offset
bool gbOverwriteOccured = FALSE;
/* This shuffles all the blocks when we run out of space. We have to
* do it in a special order so crash recovery is possible. We also
* have to write special bytes so we can rewrite to areas without
* doing a complete erase.
*/
void shuffleBlocks(storage_volume_t block) {
page_meta_t pageMeta;
uint32_t pageCounter;
// 1. set the last block to USED, if it's already USED or START, then no effect
pageMeta.header = PAGE_USED;
call Flash.write(L_RAW_OFFSET(block, lastBlock[block], 0),
(uint8_t*) &pageMeta,
sizeof(page_meta_t));
// 2. if lastBlock + 1 is free, then set it as last block and the first record is free
pageCounter = (lastBlock[block] + 1) % L_PARTITIONS(block);
call Flash.read(L_RAW_OFFSET(block, pageCounter, 0),
(uint8_t*) &pageMeta,
sizeof(page_meta_t));
if(pageMeta.header == PAGE_AVAILABLE) {
nextFreeRecord[block] = 1;
lastBlock[block] = pageCounter;
}
else {
call Flash.erase(L_RAW_OFFSET(block, firstBlock[block], 0));
pageCounter = (firstBlock[block] + 1) % L_PARTITIONS(block);
pageMeta.header = PAGE_START;
call Flash.write(L_RAW_OFFSET(block, pageCounter, 0),
(uint8_t*) &pageMeta,
sizeof(page_meta_t));
nextFreeRecord[block] = 1;
lastBlock[block] = firstBlock[block];
firstBlock[block] = pageCounter;
gbOverwriteOccured = TRUE;
}
}
/*
* Converts a cookie to a page/record/offset tuple
*/
void cookieToTuple(uint32_t cookie, storage_volume_t block,
uint32_t *page, uint32_t *record, uint32_t *offset) {
uint32_t mypage;
uint32_t myrecord;
uint32_t myoffset;
mypage = (cookie - L_BASE_BLOCK(block)) / P30_BLOCK_SIZE;
cookie = (cookie - L_BASE_BLOCK(block)) % P30_BLOCK_SIZE;
myrecord = cookie / L_FULL_RECORD_SIZE;
myoffset = (cookie % L_FULL_RECORD_SIZE) - sizeof(record_meta_t);
*page = mypage;
*record = myrecord;
*offset = myoffset;
}
/*
* Ideally, Logstorage would require a mount too, but it doesn't so
* it's a total hack. Before any operation, we have to check if a
* mount occurred. Mount initializes the your logblock.
*/
uint8_t mountBits[NUM_VOLS];
void myMount(storage_volume_t block) {
page_meta_t pageMeta;
record_meta_t recordMeta;
uint32_t pageCounter;
uint32_t recordCounter;
uint32_t freePages = 0;
if(mountBits[block] != 0)
return;
// scan all 128k pages for page meta
// annoying corner case of all free pages, write the first page as START
for(pageCounter = 0; pageCounter < L_PARTITIONS(block); pageCounter++) {
call Flash.read(L_RAW_OFFSET(block, pageCounter, 0),
(uint8_t*)&pageMeta,
sizeof(page_meta_t));
if(pageMeta.header == PAGE_AVAILABLE)
freePages++;
}
if(freePages == L_PARTITIONS(block)) {
pageMeta.header = PAGE_START;
call Flash.write(L_RAW_OFFSET(block, 0, 0), (uint8_t*) &pageMeta, sizeof(page_meta_t));
}
// if we find a START page, then we are done
for(pageCounter = 0; pageCounter < L_PARTITIONS(block); pageCounter++) {
call Flash.read(L_RAW_OFFSET(block, pageCounter, 0),
(uint8_t*)&pageMeta,
sizeof(page_meta_t));
if(pageMeta.header == PAGE_START) {
firstBlock[block] = pageCounter;
break;
}
}
// if we didn't find a START page, first page is right after AVAILABLE
if(pageCounter == L_PARTITIONS(block)) {
for(pageCounter = 0; pageCounter < L_PARTITIONS(block); pageCounter++) {
call Flash.read(L_RAW_OFFSET(block, pageCounter, 0),
(uint8_t*)&pageMeta,
sizeof(page_meta_t));
if(pageMeta.header == PAGE_AVAILABLE) {
pageCounter = (pageCounter + 1) % L_PARTITIONS(block);
firstBlock[block] = pageCounter;
// mark that block as a START block
pageMeta.header = PAGE_START;
call Flash.write(L_RAW_OFFSET(block, pageCounter, 0),
(uint8_t*) &pageMeta,
sizeof(page_meta_t));
break;
}
}
}
// now we scan for next free record location
pageCounter = firstBlock[block];
for(recordCounter = 1; recordCounter < L_MAX_RECORDS_PER_BLOCK; recordCounter++) {
call Flash.read(L_RAW_OFFSET(block, pageCounter, recordCounter),
(uint8_t*) &recordMeta,
sizeof(record_meta_t));
if(recordMeta.status == RECORD_EMPTY) {
nextFreeRecord[block] = recordCounter;
lastBlock[block] = pageCounter;
break;
}
}
// Didn't find a free record in the START block, search the first FREE block
if(recordCounter == L_MAX_RECORDS_PER_BLOCK) {
for(pageCounter = 0; pageCounter < L_PARTITIONS(block); pageCounter++) {
call Flash.read(L_RAW_OFFSET(block, pageCounter, 0),
(uint8_t*)&pageMeta,
sizeof(page_meta_t));
if(pageMeta.header == PAGE_AVAILABLE) {
for(recordCounter = 1; recordCounter < L_MAX_RECORDS_PER_BLOCK; recordCounter++) {
call Flash.read(L_RAW_OFFSET(block, pageCounter, recordCounter),
(uint8_t*) &recordMeta,
sizeof(record_meta_t));
if(recordMeta.status == RECORD_EMPTY) {
lastBlock[block] = pageCounter;
nextFreeRecord[block] = recordCounter;
goto mount_complete;
}
}
}
}
// if here, you didn't find the last block, it must be right before the START block
// special case the wrap around
if(firstBlock[block] == 0)
lastBlock[block] = L_PARTITIONS(block) - 1;
else
lastBlock[block] = firstBlock[block] - 1;
// that last block must be full, so shuffle it
shuffleBlocks(block);
}
mount_complete:
readCookieOffset[block] = SEEK_BEGINNING;
mountBits[block] = 1;
}
task void signalDoneTask() {
switch(m_state) {
case S_APPEND:
m_state = S_IDLE;
signal Write.appendDone[clientId](clientBuf, clientLen, gbOverwriteOccured, clientResult);
gbOverwriteOccured = FALSE;
break;
case S_SYNC:
m_state = S_IDLE;
signal Write.syncDone[clientId](SUCCESS);
break;
case S_ERASE:
m_state = S_IDLE;
signal Write.eraseDone[clientId](clientResult);
break;
case S_READ:
m_state = S_IDLE;
signal Read.readDone[clientId](clientBuf, clientLen, clientResult);
break;
case S_SEEK:
m_state = S_IDLE;
signal Read.seekDone[clientId](SUCCESS);
break;
default:
break;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -