📄 smf_lpt.c
字号:
/**********
*
* smf_lpt.c: SmartMedia File System Logical/Physical Transition part
*
* Portable File System designed for SmartMedia
*
* Created by Kim Hyung Gi (06/99) - kimhg@mmrnd.sec.samsung.co.kr
* Samsung Electronics, M/M R&D Center, Visual Communication Group
*
* Notes:
* - This layer uses the IO layer APIs, and provides for APIs that is used by FAT layer.
* - In general, input parameters of the APIs are not checked for its validities (it must be checked at FAT layer)
*
**********
* Modified by Lee Jin Woo (1999/08/17)
*
**********/
#include "smf_cmn.h"
#include "smf_lpt.h"
#include "smf_io.h"
#include "smf_fat.h"
#include "smf_buf.h"
/**********
* Macro Definitions
**********/
#define LPT_RETURN(drv_no, ret) do { sm_PostLPT((drv_no)); return (ret); } while (0)
#define SET_PB_TBL(drv_no, offset, value) do { s_pBlock[(drv_no)][(offset)] = (value); } while (0)
#define GET_PB_TBL(drv_no, offset) s_pBlock[(drv_no)][(offset)]
#define SET_LB_TBL(drv_no, offset, value) do { s_lBlock[(drv_no)][(offset)] = (value); } while (0)
#define GET_LB_TBL(drv_no, offset) s_lBlock[(drv_no)][(offset)]
/**********
* Static Variables
**********/
static const ubyte s_cis[110] = { /* 110 Byte CIS data */
0x01, 0x03, 0xd9, 0x01, 0xff, 0x18, 0x02, 0xdf,
0x01, 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x21,
0x02, 0x04, 0x01, 0x22, 0x02, 0x01, 0x01, 0x22,
0x03, 0x02, 0x04, 0x07, 0x1a, 0x05, 0x01, 0x03,
0x00, 0x02, 0x0f, 0x1b, 0x08, 0xc0, 0xc0, 0xa1,
0x01, 0x55, 0x08, 0x00, 0x20, 0x1b, 0x0a, 0xc1,
0x41, 0x99, 0x01, 0x55, 0x64, 0xf0, 0xff, 0xff,
0x20, 0x1b, 0x0c, 0x82, 0x41, 0x18, 0xea, 0x61,
0xf0, 0x01, 0x07, 0xf6, 0x03, 0x01, 0xee, 0x1b,
0x0c, 0x83, 0x41, 0x18, 0xea, 0x61, 0x70, 0x01,
0x07, 0x76, 0x03, 0x01, 0xee, 0x15, 0x14, 0x05,
0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x00, 0x20, 0x20, 0x20, 0x20, 0x00, 0x30, 0x2e,
0x30, 0x00, 0xff, 0x14, 0x00, 0xff
};
static const sDEV_INFO s_devInfo[8] = {
/* CpV, HpC, SpH, allS, szS, PBpV, LBpV, SpB, PpB, szP */
{ 125, 4, 4, 2000, 512, 256, 250, 8, 16, 256}, /* DI_1M */
{ 125, 4, 8, 4000, 512, 512, 500, 8, 16, 256}, /* DI_2M */
{ 250, 4, 8, 8000, 512, 512, 500, 16, 16, 512}, /* DI_4M */
{ 250, 4, 16, 16000, 512, 1024, 1000, 16, 16, 512}, /* DI_8M */
{ 500, 4, 16, 32000, 512, 1024, 1000, 32, 32, 512}, /* DI_16M */
{ 500, 8, 16, 64000, 512, 2048, 2000, 32, 32, 512}, /* DI_32M */
{ 500, 8, 32, 128000, 512, 4096, 4000, 32, 32, 512}, /* DI_64M */
{ 500, 16, 32, 256000, 512, 8192, 8000, 32, 32, 512} /* DI_128M */
};
static sDEV_ID s_devID[MAX_DRIVE];
/**********
* s_pBlock[MAX_DRIVE]: contains BAA in Spare area (index indicates Physical Block Number)
* - size: 2 * zone_num * pblock_num per zone (512B <= 1M SM, 16KB <= 128M SM)
* - value:
* . 0xffff: unused block
* . 0xffee: invalid block (checked by Block Status Area in Spare area)
* . 0 : CIS block
* . else : Logical Block Number(with parity bit) in the zone => BAA value in Spare area
**********/
static uword* s_pBlock[MAX_DRIVE] = { NULL, };
/**********
* s_lBlock[MAX_DRIVE]: contains Physical Block Number (index indicates Logical Block Number)
* - size: 2 * zone_num * pblock_num per zone (used only 2 * zone_num * lblock_num)
* - value: minimum available value is 2(start of MBR)
* . 0 : unused block
* . else : Physical Block Number in the zone
**********/
static uword* s_lBlock[MAX_DRIVE] = { NULL, };
static udword s_lptFlag[MAX_DRIVE];
static ERR_CODE s_smlErr;
/**********
* For wear leveling, this variable is set to random value in LPTInit(),
* and from then on, it is used for start index of search for the free LBlock.
* After searched the free LBLock, this variable is set to that block number
**********/
static udword s_lBlockSearchStart[MAX_DRIVE];
/**********
* For wear leveling, this variable is set to random value when a zone is
* changed in sm_SearchFreePBlock(), and from then on, it is used for start
* index of search for the free PBlock.
* After searched the free PBLock, this variable is set to that block number.
**********/
static udword s_pBlockSearchStart[MAX_DRIVE];
/********************************************************************/
/* static funtions */
static ERR_CODE sm_LPTInit(udword drv_no);
static ERR_CODE sm_PreLPT(udword drv_no);
static ERR_CODE sm_PostLPT(udword drv_no);
/* static ERR_CODE sm_SearchFreeLBlock(udword drv_no, udword* pLba); */
static ERR_CODE sm_SearchFreePBlock(udword drv_no, udword lba, udword* pPba);
static ERR_CODE sm_ECCEncode(const ubyte* p_buf, ubyte* p_ecc);
static ERR_CODE sm_ECCDecode(const ubyte* p_buf, const ubyte* p_ecc);
static ERR_CODE sm_ReadBlock(udword drv_no, udword lblock_no, udword offset, void* p_buf, udword read_size);
static ERR_CODE sm_ReadPhysBlock(udword drv_no, udword pba, udword offset, void* p_buf, udword read_size);
static ERR_CODE sm_WriteCISBlock(udword drv_no, udword pblock_no);
static uword sm_MakeBAA(udword lblock_no);
static ERR_CODE sm_processBadBlock(udword drv_no, udword lblock_no);
static void sm_markBadBlock(udword drv_no, udword pba);
/**********
* LPT API definitions
**********/
#if 0 /* smlGetNewBlock function is obsolete */
/**********
* Function: smlGetNewBlock
* Remarks:
* - finds unused pLBlock & pPBlock entry , connects them to each other
* , and then returns the pLBlock number through the parameter
* Notes:
* - higher layer must call this function to get a new block for use
* (before the start of smlWriteBlock()'s in a new block)
**********/
SML_EXPORT ERR_CODE smlGetNewBlock(udword drv_no, udword* p_lblock_no)
{
udword pba, lba;
s_smlErr = sm_PreLPT(drv_no);
if (s_smlErr != SM_OK)
LPT_RETURN(drv_no, s_smlErr);
/*
* in a zone, max LBlock = 1000, max PBlock = 1024,
* and both must be in the same zone => free LBLock is searched first
*/
s_smlErr = sm_SearchFreeLBlock(drv_no, &lba);
if (s_smlErr != SM_OK)
LPT_RETURN(drv_no, s_smlErr);
s_smlErr = sm_SearchFreePBlock(drv_no, lba, &pba);
if (s_smlErr != SM_OK)
LPT_RETURN(drv_no, s_smlErr);
SET_LB_TBL(drv_no, lba, (uword)pba);
SET_PB_TBL(drv_no, pba, sm_MakeBAA(lba));
*p_lblock_no = lba;
LPT_RETURN(drv_no, SM_OK);
}
#endif
/**********
* Function: smlGetNewSpecifiedBlock
* Remarks:
* - finds unused pPBlock entry, connects it to the specified lblock_no
* - updates pPBlock, pLBlock table
* - This API is used for the case that higher layer must use the specified lblock
* ex) when formatting, MBR must be located in 0th block
* Parameters
* . lblock_no: logical block number that is to be ready
* . p_changed_pblock(result): physical block number that was mapped before.
* UNUSED_LBLOCK if the specified lblock_no was in free state.
* Notes:
* - If the specified lblock_no is not free, the block is remapped to the
* other free block, updates pPBlock, pLBlock according to the new mapping,
* and then returns the old physical block with p_changed_pblock
* *** the higher layer must call smlDiscardPhysBlock() with the parameter of pba
* that is returned through p_changed_pblock after updating the contents
* if the value is not UNUSED_LBLOCK
**********/
SML_EXPORT ERR_CODE smlGetNewSpecifiedBlock(udword drv_no, udword lblock_no, udword* p_changed_pblock)
{
udword pba;
udword addr;
bool done = FALSE;
s_smlErr = sm_PreLPT(drv_no);
if (s_smlErr != SM_OK)
LPT_RETURN(drv_no, s_smlErr);
*p_changed_pblock = GET_LB_TBL(drv_no, lblock_no);
SET_LB_TBL(drv_no, lblock_no, UNUSED_LBLOCK);
while (!done)
{
done = TRUE;
s_smlErr = sm_SearchFreePBlock(drv_no, lblock_no, &pba);
if (s_smlErr != SM_OK)
LPT_RETURN(drv_no, s_smlErr);
addr = pba * SECTOR_SIZE * s_devInfo[s_devID[drv_no].device].SpB;
s_smlErr = smpFlashEraseBlock(drv_no, addr);
if (s_smlErr != SM_OK)
{
if (s_smlErr == ERR_FLASH_STATUS)
{
sm_markBadBlock(drv_no, pba);
done = FALSE;
continue;
}
LPT_RETURN(drv_no, s_smlErr);
}
SET_LB_TBL(drv_no, lblock_no, (uword)pba);
SET_PB_TBL(drv_no, pba, sm_MakeBAA(lblock_no));
}
LPT_RETURN(drv_no, SM_OK);
}
/**********
* Function: smlDiscardBlock
* Remarks:
* - erases the pba physical block , and makes the entries(in PLBlock & pPBlock) to unused state
* Notes:
* - higher layer must call this function when the block is no more used
**********/
SML_EXPORT ERR_CODE smlDiscardPhysBlock(udword drv_no, udword pba)
{
udword addr;
s_smlErr = sm_PreLPT(drv_no);
if (s_smlErr != SM_OK)
LPT_RETURN(drv_no, s_smlErr);
if ((pba >= s_devInfo[s_devID[drv_no].device].PBpV) || (pba == UNUSED_LBLOCK))
LPT_RETURN(drv_no, ERR_INVALID_BLOCK);
addr = pba * SECTOR_SIZE * s_devInfo[s_devID[drv_no].device].SpB;
smpFlashEraseBlock(drv_no, addr);
SET_PB_TBL(drv_no, pba, UNUSED_PBLOCK);
LPT_RETURN(drv_no, SM_OK);
}
/**********
* Function: smlWriteBlock
* Remarks:
* - write data within 1 block
* - cannot write beyond the block boundary
* - offset and write_size must be on the sector boundary
* Notes:
* - Prior to using this API, higher layer must prepare 1 block using smlGetNewBlock().
* - It is strongly recommended that higher layer use this API sequentially from 1st sector to last sector.
* * If this API is used to the written sector, it returns error.
**********/
SML_EXPORT ERR_CODE smlWriteBlock(udword drv_no, udword lblock_no,
udword offset, const void* p_buf, udword write_size)
{
udword i;
udword pba;
udword converted_lba;
udword sector;
udword page_size;
ubyte spare[16];
bool done = FALSE;
s_smlErr = sm_PreLPT(drv_no);
if (s_smlErr != SM_OK)
LPT_RETURN(drv_no, s_smlErr);
/*
* check if the offset and the write_size are on the sector boundary
if ((offset & (SECTOR_SIZE - 1))
|| (write_size & (SECTOR_SIZE - 1))) {
LPT_RETURN(drv_no, ERR_INVALID_PARAM);
}
* check if the (offset + write_size) is beyond the block boundary
if (offset + write_size >
SECTOR_SIZE * conDevInfo[sDevID[drv_no].device].SpB) {
LPT_RETURN(drv_no, ERR_INVALID_PARAM);
}
*/
while (!done)
{
done = TRUE;
pba = GET_LB_TBL(drv_no, lblock_no);
if ((pba >= s_devInfo[s_devID[drv_no].device].PBpV)
|| (pba == UNUSED_LBLOCK)) {
LPT_RETURN(drv_no, ERR_INVALID_BLOCK);
}
converted_lba = sm_MakeBAA(lblock_no);
sector = pba * s_devInfo[s_devID[drv_no].device].SpB + offset / SECTOR_SIZE;
page_size = s_devInfo[s_devID[drv_no].device].szP;
SM_MEMSET(spare, 0xff, 16);
spare[5] = 0xff;
spare[6] = spare[11] = (byte)((converted_lba & 0xff00) >> 8);
spare[7] = spare[12] = (byte)(converted_lba & 0xff);
for (i = 0; i < write_size; i += SECTOR_SIZE, ++sector, p_buf = (const ubyte*)p_buf + SECTOR_SIZE)
{
#ifndef ECC_ENCODE_DISABLE
sm_ECCEncode((const ubyte*)p_buf, spare + 13);
sm_ECCEncode((const ubyte*)p_buf + 256, spare + 8);
#endif
s_smlErr = smpFlashWriteSector(drv_no, sector, (const ubyte*)p_buf, spare);
if (s_smlErr != SM_OK)
{
if (s_smlErr == ERR_FLASH_STATUS)
{
s_smlErr = sm_processBadBlock(drv_no, lblock_no);
if (s_smlErr != SM_OK)
LPT_RETURN(drv_no, s_smlErr);
done = FALSE;
break;
}
LPT_RETURN(drv_no, s_smlErr);
}
}
}
LPT_RETURN(drv_no, SM_OK);
}
ERR_CODE sm_processBadBlock(udword drv_no, udword lblock_no)
{
udword old_pblock;
udword sectorsPerBlock;
udword i;
ubyte* buf;
udword read_offset;
// copy the contents
s_smlErr = smlGetNewSpecifiedBlock(drv_no, lblock_no, &old_pblock);
if (s_smlErr != SM_OK)
return s_smlErr;
buf = SMB_ALLOC_BUF();
if (!buf)
{
smlDiscardPhysBlock(drv_no, old_pblock);
return ERR_OUT_OF_MEMORY;
}
s_smlErr = SM_OK;
sectorsPerBlock = s_devInfo[s_devID[drv_no].device].SpB;
for (i = 0, read_offset = 0; i < sectorsPerBlock; ++i, read_offset += SECTOR_SIZE)
{
if (!smlIsPhysSectorErased(drv_no, old_pblock, i))
{
s_smlErr = smlReadPhysBlock(drv_no, old_pblock, read_offset, buf, SECTOR_SIZE);
if (s_smlErr != SM_OK)
break;
s_smlErr = smlWriteBlock(drv_no, lblock_no, read_offset, buf, SECTOR_SIZE);
if (s_smlErr != SM_OK)
break;
}
}
SMB_FREE_BUF(buf);
// mark as a bad block
sm_markBadBlock(drv_no, old_pblock);
return s_smlErr;
}
void sm_markBadBlock(udword drv_no, udword pba)
{
ubyte* buf;
ubyte spare_buf[16];
udword sectorsPerBlock;
udword i;
udword sector;
buf = SMB_ALLOC_BUF();
if (!buf)
return;
SM_MEMSET(buf, 0xff, SECTOR_SIZE);
SM_MEMSET(spare_buf, 0xff, 16);
spare_buf[5] = SM_INVALID_BLOCK_MARK;
sectorsPerBlock = s_devInfo[s_devID[drv_no].device].SpB;
sector = pba * sectorsPerBlock;
smpFlashEraseBlock(drv_no, sector * SECTOR_SIZE);
for (i = 0; i < sectorsPerBlock; ++i)
{
smpFlashWriteSector(drv_no, sector + i, buf, spare_buf);
}
SET_PB_TBL(drv_no, pba, INVALID_PBLOCK);
SMB_FREE_BUF(buf);
}
/**********
* Function: smlReadBlock
* Remarks:
* - read data within 1 block
* - cannot read beyond the block boundary
* - if there is an ECC error, p_buf is filled with flash data as in the normal case, but return value is ERR_ECC
**********/
SML_EXPORT ERR_CODE smlReadBlock(udword drv_no, udword lblock_no, udword offset, void* p_buf, udword read_size)
{
s_smlErr = sm_PreLPT(drv_no);
if (s_smlErr != SM_OK)
LPT_RETURN(drv_no, s_smlErr);
s_smlErr = sm_ReadBlock(drv_no, lblock_no, offset, p_buf, read_size);
LPT_RETURN(drv_no, s_smlErr);
}
/**********
* Function: smlReadPhysBlock
* Remarks:
* - read data within 1 block
* - cannot read beyond the block boundary
* - if there is an ECC error, p_buf is filled with flash data as in the normal case, but return value is ERR_ECC
**********/
SML_EXPORT ERR_CODE smlReadPhysBlock(udword drv_no, udword pba, udword offset, void* p_buf, udword read_size)
{
s_smlErr = sm_PreLPT(drv_no);
if (s_smlErr != SM_OK)
LPT_RETURN(drv_no, s_smlErr);
s_smlErr = sm_ReadPhysBlock(drv_no, pba, offset, p_buf, read_size);
LPT_RETURN(drv_no, s_smlErr);
}
/**********
* Function: smlEraseBlock
* Remarks:
* - erase 1 block
* - this function is not needed in normal read & write
* (normally, erase function is implemented in smlDiscardBlock)
**********/
SML_EXPORT ERR_CODE smlEraseBlock(udword drv_no, udword lblock_no)
{
udword addr;
udword pba;
s_smlErr = sm_PreLPT(drv_no);
if (s_smlErr != SM_OK)
LPT_RETURN(drv_no, s_smlErr);
pba = GET_LB_TBL(drv_no, lblock_no);
if ((pba >= s_devInfo[s_devID[drv_no].device].PBpV) || (pba == UNUSED_LBLOCK))
{
LPT_RETURN(drv_no, ERR_INVALID_PARAM);
}
addr = pba * SECTOR_SIZE * s_devInfo[s_devID[drv_no].device].SpB;
s_smlErr = smpFlashEraseBlock(drv_no, addr);
if (s_smlErr != SM_OK)
LPT_RETURN(drv_no, s_smlErr);
LPT_RETURN(drv_no, SM_OK);
}
/**********
* Function: smlReadSector
* Remarks:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -