📄 uffs_buf.c~
字号:
/*
Copyright (C) 2005-2008 Ricky Zheng <ricky_gz_zheng@yahoo.co.nz>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* \file uffs_buf.c
* \brief uffs page buffers manipulations
* \author Ricky Zheng
* \note Created in 11th May, 2005
*/
#include "uffs/uffs_types.h"
#include "uffs/uffs_buf.h"
#include "uffs/uffs_device.h"
#include "uffs/uffs_os.h"
#include "uffs/uffs_public.h"
#include "uffs/ubuffer.h"
#include "uffs/uffs_ecc.h"
#include "uffs/uffs_badblock.h"
#include <string.h>
#define PFX "pg buf:"
/**
* \brief inspect (print) uffs page buffers.
* \param[in] dev uffs device to be inspected.
*/
void uffs_BufInspect(uffs_Device *dev)
{
struct uffs_pageBufsSt *pb = &dev->buf;
uffs_Buf *buf;
uffs_Perror(UFFS_ERR_NORMAL, "------------- page buffer inspect ---------\n");
uffs_Perror(UFFS_ERR_NORMAL, "all buffers: \n");
for (buf = pb->bufHead; buf; buf = buf->next) {
uffs_Perror(UFFS_ERR_NORMAL, "\tF:%04x S:%04x P:%02d R:%02d D:%03d M:%d\n",
buf->father, buf->serial, buf->pageID, buf->refCount, buf->dataLen, buf->mark);
}
uffs_Perror(UFFS_ERR_NORMAL, "--------------------------------------------\n");
}
/**
* \brief initialize page buffers for device
* in UFFS, each device has one buffer pool
* \param[in] dev uffs device
* \param[in] maxBuf maximum buffer number, normally use #MAX_PAGE_BUFFERS
* \param[in] maxDirtyBuf maximum dirty buffer allowed, if the dirty buffer over this number,
* than need to be flush to flash
*/
URET uffs_BufInit(uffs_Device *dev, int maxBuf, int maxDirtyBuf)
{
void *pool;
u8 *data;
uffs_Buf *buf;
int size;
int i;
if(!dev) return U_FAIL;
//init device common parameters, which are needed by page buffers
dev->com.pgSize = dev->attr->page_data_size;
dev->com.eccSize = dev->flash->GetEccSize(dev);
dev->com.pgDataSize = dev->com.pgSize - dev->com.eccSize;
if(dev->buf.pool != NULL) {
uffs_Perror(UFFS_ERR_NORMAL, PFX"buf.pool is not NULL, buf already inited ?\n");
return U_FAIL;
}
size = (sizeof(uffs_Buf) + dev->com.pgSize) * maxBuf;
if (dev->mem.page_buffer_size == 0) {
if (dev->mem.malloc) {
dev->mem.page_buffer = dev->mem.malloc(dev, size);
if (dev->mem.page_buffer) dev->mem.page_buffer_size = size;
}
}
if (size > dev->mem.page_buffer_size) {
uffs_Perror(UFFS_ERR_DEAD, PFX"page buffers require %d but only %d available.\n", size, dev->mem.page_buffer_size);
return U_FAIL;
}
pool = dev->mem.page_buffer;
uffs_Perror(UFFS_ERR_NOISY, PFX"allcate %d bytes.\n", size);
dev->buf.pool = pool;
data = (u8 *)pool + (sizeof(uffs_Buf) * maxBuf);
for(i = 0; i < maxBuf; i++) {
buf = (uffs_Buf *)((u8 *)pool + (sizeof(uffs_Buf) * i));
memset(buf, 0, sizeof(uffs_Buf));
data = (u8 *)pool + (sizeof(uffs_Buf) * maxBuf) + (dev->com.pgSize * i);
buf->data = data;
buf->ecc = data + dev->com.pgDataSize;
buf->mark = UFFS_BUF_EMPTY;
if(i == 0) {
buf->prev = NULL;
dev->buf.bufHead = buf;
}
else {
buf->prev = (uffs_Buf *)((u8 *)buf - sizeof(uffs_Buf));
}
if(i == maxBuf - 1) {
buf->next = NULL;
dev->buf.bufTail = buf;
}
else {
buf->next = (uffs_Buf *)((u8 *)buf + sizeof(uffs_Buf));
}
}
dev->buf.maxBuf = maxBuf;
dev->buf.maxDirtyBuf = (maxDirtyBuf > dev->attr->pages_per_block ? dev->attr->pages_per_block : maxDirtyBuf);
dev->buf.dirty = NULL;
dev->buf.dirtyCount = 0;
return U_SUCC;
}
/**
* \brief release all page buffer, this function should be called
when unmounting a uffs device
* \param[in] dev uffs device
* \note if there are page buffers in used, it may cause fail to release
*/
URET uffs_BufReleaseAll(uffs_Device *dev)
{
uffs_Buf *p;
if(!dev) return U_FAIL;
//now release all buffer
p = dev->buf.bufHead;
while(p) {
if(p->refCount != 0) {
uffs_Perror(UFFS_ERR_NORMAL,
PFX "can't release buffers, \
father:%d, serial:%d, pageID:%d still in used.\n", p->father, p->serial, p->pageID);
return U_FAIL;
}
p = p->next;
}
if(uffs_BufFlush(dev) != U_SUCC) {
uffs_Perror(UFFS_ERR_NORMAL, PFX"can't release buf, fail to flush buffer\n");
return U_FAIL;
}
if (dev->mem.free) dev->mem.free(dev, dev->buf.pool);
dev->buf.pool = NULL;
dev->buf.bufHead = dev->buf.bufTail = NULL;
return U_SUCC;
}
static void _BreakFromBufList(uffs_Device *dev, uffs_Buf *buf)
{
if(buf->next) {
buf->next->prev = buf->prev;
}
if(buf->prev) {
buf->prev->next = buf->next;
}
if(dev->buf.bufHead == buf) {
dev->buf.bufHead = buf->next;
}
if(dev->buf.bufTail == buf) {
dev->buf.bufTail = buf->prev;
}
}
static void _LinkToBufListHead(uffs_Device *dev, uffs_Buf *buf)
{
if(buf == dev->buf.bufHead) return;
buf->prev = NULL;
buf->next = dev->buf.bufHead;
if(dev->buf.bufHead) dev->buf.bufHead->prev = buf;
if(dev->buf.bufTail == NULL) dev->buf.bufTail = buf;
dev->buf.bufHead = buf;
}
static void _LinkToBufListTail(uffs_Device *dev, uffs_Buf *buf)
{
if(dev->buf.bufTail == buf) return;
buf->prev = dev->buf.bufTail;
buf->next = NULL;
if(dev->buf.bufTail) dev->buf.bufTail->next = buf;
if(dev->buf.bufHead == NULL) dev->buf.bufHead = buf;
dev->buf.bufTail = buf;
}
//move a node which linked in the list to the head of list
static void _MoveNodeToHead(uffs_Device *dev, uffs_Buf *p)
{
if(p == dev->buf.bufHead) return;
//break from list
_BreakFromBufList(dev, p);
//link to head
_LinkToBufListHead(dev, p);
}
static void _LinkToDirtyList(uffs_Device *dev, uffs_Buf *buf)
{
if (buf == NULL) {
uffs_Perror(UFFS_ERR_SERIOUS, PFX"Try to insert a NULL node into dirty list ?\n");
return;
}
buf->mark = UFFS_BUF_DIRTY;
buf->prevDirty = NULL;
buf->nextDirty = dev->buf.dirty;
if(dev->buf.dirty) dev->buf.dirty->prevDirty = buf;
dev->buf.dirty = buf;
dev->buf.dirtyCount++;
}
static uffs_Buf * _FindFreeBuf(uffs_Device *dev)
{
uffs_Buf *buf;
#if 1
buf = dev->buf.bufHead;
while(buf) {
if(buf->refCount == 0 &&
buf->mark != UFFS_BUF_DIRTY) return buf;
buf = buf->next;
}
#else
buf = dev->buf.bufTail;
while(buf) {
if(buf->refCount == 0 &&
buf->mark != UFFS_BUF_DIRTY) return buf;
buf = buf->prev;
}
#endif
return buf;
}
/**
* load psychical page data into buf and do ecc check
* \param[in] dev uffs device
* \param[in] buf buf to be load in
* \param[in] block psychical block number
* \param[in] page psychical page number
* \return return U_SUCC if no error, return U_FAIL if I/O error or ecc check fail
*/
URET uffs_LoadPhiDataToBuf(uffs_Device *dev, uffs_Buf *buf, u32 block, u32 page)
{
URET ret;
ret = dev->ops->ReadPageData(dev, block, page, buf->data, 0, dev->com.pgSize);
if(ret == U_SUCC) {
if (uffs_CheckBadBlock(dev, buf, block) == U_SUCC) {
buf->mark = UFFS_BUF_VALID; // the data is valid now
}
else {
buf->mark = UFFS_BUF_EMPTY;
ret = U_FAIL;
}
}
else {
buf->mark = UFFS_BUF_EMPTY; // the data is not valid
}
return ret;
}
/**
* load psychical page data into buf and try ecc check
* \param[in] dev uffs device
* \param[in] buf buf to be load in
* \param[in] block psychical block number
* \param[in] page psychical page number
* \return return U_SUCC if no error, return U_FAIL if I/O error
* \note this function should be only used when doing bad block recover.
*/
URET uffs_LoadPhiDataToBufEccUnCare(uffs_Device *dev, uffs_Buf *buf, u32 block, u32 page)
{
URET ret;
ret = dev->ops->ReadPageData(dev, block, page, buf->data, 0, dev->com.pgSize);
if(ret == U_SUCC) {
if (uffs_CheckBadBlock(dev, buf, block) == U_SUCC) {
//ECC check fail, but we return 'successful' anyway !!
buf->mark = UFFS_BUF_VALID;
}
return U_SUCC;
}
else {
buf->mark = UFFS_BUF_EMPTY; // the data is not valid, I/O error ?
}
return ret;
}
/**
* find a buffer in the pool
* \param[in] dev uffs device
* \param[in] father father serial num
* \param[in] serial serial num
* \param[in] pageID pageID
* \return return found buffer, return NULL if buffer not found
*/
uffs_Buf * uffs_BufFind(uffs_Device *dev, u16 father, u16 serial, u16 pageID)
{
uffs_Buf *p = dev->buf.bufHead;
while(p) {
if( p->father == father &&
p->serial == serial &&
p->pageID == pageID &&
p->mark != UFFS_BUF_EMPTY) {
//they have match one
return p;
}
p = p->next;
}
return NULL; //buffer not found
}
static uffs_Buf * _FindBufInDirtyList(uffs_Buf *dirty, u16 pageID)
{
while(dirty) {
if(dirty->pageID == pageID) return dirty;
dirty = dirty->nextDirty;
}
return NULL;
}
static URET _BreakFromDirty(uffs_Device *dev, uffs_Buf *dirtyBuf)
{
if(dirtyBuf->mark != UFFS_BUF_DIRTY) {
uffs_Perror(UFFS_ERR_NORMAL, PFX"try to break a non-dirty buf from dirty list ?\n");
return U_FAIL;
}
if(dev->buf.dirty == NULL) {
uffs_Perror(UFFS_ERR_NORMAL, PFX"no dirty list exit ?\n");
return U_FAIL;
}
// break from the link
if(dirtyBuf->nextDirty) {
dirtyBuf->nextDirty->prevDirty = dirtyBuf->prevDirty;
}
if(dirtyBuf->prevDirty) {
dirtyBuf->prevDirty->nextDirty = dirtyBuf->nextDirty;
}
// check if it's the link head ...
if(dev->buf.dirty == dirtyBuf) {
dev->buf.dirty = dirtyBuf->nextDirty;
}
dirtyBuf->nextDirty = dirtyBuf->prevDirty = NULL; // clear dirty link
dev->buf.dirtyCount--;
return U_SUCC;
}
static u16 _GetDataSum(uffs_Device *dev, uffs_Buf *buf)
{
u16 dataSum = 0; //default: 0
uffs_fileInfo *fi;
dev = dev;
//FIXME: We use the same schema for both dir and file.
if(buf->type == UFFS_TYPE_FILE || buf->type == UFFS_TYPE_DIR) {
if(buf->pageID == 0) {
fi = (uffs_fileInfo *)(buf->data);
dataSum = uffs_MakeSum16(fi->name, fi->name_len);
}
}
return dataSum;
}
static URET _CheckDirtyList(uffs_Device *dev)
{
uffs_Buf *dirty = dev->buf.dirty;
u16 father;
u16 serial;
if(dirty == NULL) {
return U_SUCC;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -