⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 smf_lpt.c

📁 the test file for GP32 gameboy hack
💻 C
📖 第 1 页 / 共 4 页
字号:
/**********
 *
 * 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 + -