📄 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 + -