twl_wrapsdk/build/libraries/fatfs/ARM7/rtlowl.c
Shirait 4fe5688d29 add SD driver and FATFS library (tentative)
git-svn-id: file:///Users/lillianskinner/Downloads/platinum/twl/twl_wrapsdk/trunk@96 4ee2a332-4b2b-5046-8439-1ba90f034370
2007-05-30 10:22:28 +00:00

712 lines
24 KiB
C

/*
* EBS - RTFS (Real Time File Manager)
*
* Copyright EBS Inc. 1987-2003
* All rights reserved.
* This code may not be redistributed in source or linkable object form
* without the consent of its author.
*/
/* RTLOWL.C - Low level functions that don't directly manipulate the fat.
Routines in this file include:
pc_i_dskopen - Mount a drive if you can
pc_read_partition_table
- Load a drive structure with partition info
pc_gblk0 - Read block zero and set up internal structures.
pc_clzero - Write zeroes to a cluster on disk.
pc_drno2dr - Convert a drive number to a drive structure.
pc_dskfree - Free resources associated with a drive.
pc_sec2cluster - Convert a sector number to a cluster value.
pc_sec2index - Convert a sector number to a cluster offset.
pc_cl2sector - Convert a cluster value to a sector number.
pc_pfinode_cluster - Assign a cluster value to a finode
pc_finode_cluster - Get the cluster value from a finode
*/
#include <rtfs.h>
/******************************************************************************
PC_I_DSKOPEN - Open a disk for business.
Description
Called by lower level code in chkmedia to open the disk
Returns
Returns TRUE if the disk was successfully initialized.
****************************************************************************/
int pc_log_base_2(word n) /*__fn__*/
{
int log;
log = 0;
if (n <= 1)
return(log);
while(n)
{
log += 1;
n >>= 1;
}
return((int)(log-1));
}
/*
* Note: This routine is called with the drive already locked so
* in several cases there is no need for critical section code handling
* This is a helper function for pc_i_dskopen()
*/
BOOLEAN pc_init_drv_fat_info(DDRIVE *pdr, struct pcblk0 *pbl0);
BOOLEAN pc_i_dskopen(int driveno) /*__fn__*/
{
DDRIVE *pdr;
struct pcblk0 bl0;
BOOLEAN ret_val;
int partition_status;
if (!prtfs_cfg)
{
/* Failed: pc_meminit() must not have been called */
pc_report_error(PCERR_INITCORE);
return (FALSE);
}
/* Check drive number */
if (!pc_validate_driveno(driveno))
{
rtfs_set_errno(PEINVALIDDRIVEID);
return(FALSE);
}
pdr = pc_drno_to_drive_struct(driveno);
/* Do not do anything on reopens */
if (pdr->mount_valid)
{
return(TRUE);
}
else
{
/* Zero the structure so all of our initial values are right */
OS_CLAIM_FSCRITICAL()
{
byte *p1, *p2;
p1 = (byte *) pdr;
p2 = (byte *) &pdr->begin_user_area;
while(p1 < p2) *p1++ = 0;
/* Set driveno now because we overwrote it */
pdr->driveno = (word)driveno;
}
OS_RELEASE_FSCRITICAL()
}
/* Set this to true now so check media does not try to mount */
pdr->mount_valid = TRUE;
partition_status = 0;
if (pdr->drive_flags & DRIVE_FLAGS_PARTITIONED)
{
partition_status = pc_read_partition_table(driveno, pdr);
if (partition_status != READ_PARTION_OK)
{
/* If we read a block but didn't see a partition table signature
and this drive structure is for the first partition number
then fall through and see if there is a BPB at block 0. */
if (partition_status == READ_PARTION_NO_TABLE && pdr->partition_number == 0)
{
rtfs_set_errno(0); /* Clear errno, we'll set it later */
pdr->partition_base = 0; /* Is BPB at 0 ? */
}
else
{
pc_report_error(PCERR_INITDEV);
return_error:
pdr->mount_valid = FALSE;
return(FALSE);
}
}
}
/* Read block 0 */
if (!pc_gblk0((word) driveno, &bl0 ))
{
/* pc_gblk0 set errno */
pc_report_error(PCERR_INITREAD);
goto return_error;
}
/* Verify that we have a good dos formatted disk */
if ( (bl0.jump != (byte) 0xE9) && (bl0.jump !=(byte) 0xEB) )
{
/* If we were checking for an MBR at physical block 0 with a driver
set for partitioned devices, set PEINVALIDMBR, otherwise
set PEINVALIDBPB */
if (pdr->drive_flags & DRIVE_FLAGS_PARTITIONED && partition_status == READ_PARTION_NO_TABLE)
rtfs_set_errno(PEINVALIDMBR);
else
rtfs_set_errno(PEINVALIDBPB); /* pc_i_dskopen Unkown values in Bios Parameter block */
pc_report_error(PCERR_INITMEDI);
goto return_error;
}
/* set up the drive structur from block 0 */
pdr->bytspsector = bl0.bytspsector; /* bytes/sector */
pdr->secpalloc = bl0.secpalloc; /* sectors / cluster */
pdr->numroot = bl0.numroot; /* Maximum number of root entries */
pdr->numsecs = (BLOCKT) bl0.numsecs; /* Total sectors on the disk */
pdr->mediadesc = bl0.mediadesc; /* Media descriptor byte */
pdr->secreserved = bl0.secreserved; /* sectors reserved */
pdr->secptrk = bl0.secptrk; /* sectors per track */
pdr->numhead = bl0.numhead; /* number of heads */
pdr->numhide =bl0.numhide; /* # hidden sectors */
copybuff(pdr->volume_label, &bl0.vollabel[0], 11);
pdr->volume_label[11] = 0;
pdr->volume_serialno = bl0.volid;
/* Check if running on a DOS (4.0) huge partition */
/* If traditional total # sectors is zero, use value in extended BPB */
if (pdr->numsecs == 0L)
pdr->numsecs = bl0.numsecs2; /* (4.0) */
/* derive some things */
pdr->bytespcluster = (int) (512 * pdr->secpalloc);
/* bits to mask in to calculate byte offset in cluster from file pointer.
AND file pointer with this to get byte offset in cluster a shift right
9 to get block offset in cluster */
pdr->byte_into_cl_mask = (dword) pdr->bytespcluster;
pdr->byte_into_cl_mask -= 1L;
/* save away log of sectors per alloc */
pdr->log2_secpalloc = (int)pc_log_base_2((word)pdr->secpalloc);
/* Get
pdr->secpfat pdr->numfats pdr->fatblock
pdr->rootblock pdr->secproot pdr->firstclblock
pdr->maxfindex pdr->infosec pdr->fasize
pdr->free_contig_base
pdr->free_contig_pointer
pdr->known_free_clusters
*/
pc_init_drv_fat_info(pdr, &bl0);
/* Load the fat driver functions and initilialize the fat */
/* Init_fat will set errno */
if (pdr->fasize == 8)
ret_val = init_fat32(pdr);
else if (pdr->fasize == 4)
ret_val = init_fat16(pdr);
else if (pdr->fasize == 3)
ret_val = init_fat12(pdr);
else
{
rtfs_set_errno(PEINVALIDBPB); /* pc_i_dskopen Unkown values in Bios Parameter block */
ret_val = 0;
}
if (!ret_val)
{
pc_report_error(PCERR_FATREAD);
goto return_error;
}
pdr->mount_valid = TRUE;
/* Save Unique id for this mount */
prtfs_cfg->drive_opencounter += 1;
pdr->drive_opencounter = prtfs_cfg->drive_opencounter;
return(TRUE);
}
/******************************************************************************
pc_read_partition_table() - Load a drive structure with partition info
Description
Read the partition table from a disk. If one is found then check the
entry in the table that is specified by pdr->partition_number. If the
entry is valid then load the fields
pdr->partition_base,pdr->partition_size and pdr->partition_type;
Returns
The following values.
READ_PARTION_OK Partition read succesfully
READ_PARTION_ERR Internal error (could not allocate buffers ?)
READ_PARTION_NO_TABLE No partition table found
READ_PARTION_NO_ENTRY Request entry not found
READ_PARTION_IOERROR Device IO error
****************************************************************************/
int pc_read_partition_table(int driveno, DDRIVE *pdr)
{
PTABLE *ppart;
BLKBUFF *buf;
byte *pbuf;
word i;
int ret_val;
/* Grab some working space */
buf = pc_scratch_blk();
if (!buf)
{
return(READ_PARTION_ERR);
}
/* Read block zero */
if (!devio_read(driveno, 0 , buf->data , 1, TRUE))
{
/* Failed reading Master boot record */
rtfs_set_errno(PEIOERRORREADMBR);
ret_val = READ_PARTION_IOERROR;
goto done;
}
/* Copy the table to a word alligned buffer */
pbuf = buf->data;
pbuf += 0x1be; /* The info starts at buf[1be] */
/* Don't use sizeof here since the structure does not pack to exact size */
copybuff(buf->data, pbuf, 0x42);
ppart = (PTABLE *) buf->data;
if (to_WORD((byte *) &ppart->signature) != 0xAA55) /*X*/
{
rtfs_set_errno(PEINVALIDMBR);
ret_val = READ_PARTION_NO_TABLE;
goto done;
}
#if (SUPPORT_EXTENDED_PARTITIONS)
{
/* Special code to support extended partitions */
/* since most applications don't use these we compile it
conditionally to save code space */
dword extended_base, ltemp;
int j, skip_count;
for (j = 0; j < 4; j++)
{
if (ppart->ents[j].p_typ == 0x5 || ppart->ents[j].p_typ == 0xF)
{
if (j <= (int)pdr->partition_number)
{
/* the partition is inside an extended partition */
/* Get the relative start and size */
pdr->partition_base = to_DWORD ((byte *) &ppart->ents[j].r_sec);
extended_base = pdr->partition_base;
pdr->partition_size = to_DWORD ((byte *) &ppart->ents[j].p_size);
skip_count = (int)pdr->partition_number - j;
for (;;)
{
/* Read the partition information in the extended partition */
if (!devio_read(driveno, pdr->partition_base , buf->data , 1, TRUE))
{
/* Failed reading Master boot record */
rtfs_set_errno(PEIOERRORREADMBR);
ret_val = READ_PARTION_IOERROR;
goto done;
}
/* Copy the table to a word alligned buffer */
pbuf = buf->data;
pbuf += 0x1be; /* The info starts at buf[1be] */
/* Don't use sizeof here since the structure does not pack to exact size */
copybuff(buf->data, pbuf, 0x42);
ppart = (PTABLE *) buf->data;
if (to_WORD((byte *) &ppart->signature) != 0xAA55) /*X*/
{
rtfs_set_errno(PEINVALIDMBR);
ret_val = READ_PARTION_NO_TABLE;
goto done;
}
if (skip_count==0)
{
if (pc_validate_partition_type(ppart->ents[0].p_typ))
{
pdr->partition_type = ppart->ents[0].p_typ;
ltemp = pdr->partition_base;
pdr->partition_base = ltemp + to_DWORD ((byte *) &ppart->ents[0].r_sec);
pdr->partition_size = to_DWORD ((byte *) &ppart->ents[0].p_size);
ret_val = READ_PARTION_OK;
goto done;
}
else
{
rtfs_set_errno(PEINVALIDMBROFFSET);
ret_val = READ_PARTION_NO_TABLE;
goto done;
}
}
else
{
if (ppart->ents[1].p_typ != 0x5 && ppart->ents[1].p_typ != 0xF)
{
rtfs_set_errno(PEINVALIDMBROFFSET);
ret_val = READ_PARTION_NO_TABLE;
goto done;
}
pdr->partition_base = extended_base + to_DWORD ((byte *) &ppart->ents[1].r_sec);
pdr->partition_size = to_DWORD ((byte *) &ppart->ents[1].p_size);
skip_count -= 1;
}
}
}
}
}
}
/* Fall through to here if not in an extended partition */
#endif /* (SUPPORT_EXTENDED_PARTITIONS) */
i = (word)pdr->partition_number;
if (pc_validate_partition_type(ppart->ents[i].p_typ))
{
/* Get the relative start and size */
pdr->partition_base = to_DWORD ((byte *) &ppart->ents[i].r_sec);
pdr->partition_size = to_DWORD ((byte *) &ppart->ents[i].p_size);
pdr->partition_type = ppart->ents[i].p_typ;
ret_val = READ_PARTION_OK;
}
else
{
rtfs_set_errno(PEINVALIDMBROFFSET);
ret_val = READ_PARTION_NO_TABLE;
}
done:
pc_free_scratch_blk(buf);
return(ret_val);
}
/****************************************************************************
PC_GBLK0 - Read block 0 and load values into a a structure
Description
Given a valid drive number, read block zero and convert
its contents from intel to native byte order.
Returns
Returns TRUE if all went well.
****************************************************************************/
/* read block zero */
BOOLEAN pc_gblk0(word driveno, struct pcblk0 *pbl0) /*__fn__*/
{
BLKBUFF *buf;
byte *b;
/* Zero fill pbl0 so we do not get any surprises */
rtfs_memset(pbl0, (byte) 0,sizeof(struct pcblk0));
/* Grab a buffer to play with */
buf = pc_scratch_blk();
if (!buf)
{
rtfs_set_errno(PERESOURCEBLOCK); /* pc_gblk0 couldn't allocate a buffer */
return(FALSE);
}
b = buf->data; /* Now we do not have to use the stack */
/* get 1 block starting at 0 from driveno */
/* READ */
if (!devio_read(driveno, 0L ,b,1, FALSE))
{
rtfs_set_errno(PEIOERRORREADBPB); /* pc_gblk0 failed reading Bios Parameter block */
pc_free_scratch_blk(buf);
return(FALSE);
}
/* Now load the structure from the buffer */
pbl0->jump = b[0];
copybuff( &pbl0->oemname[0],b+3,8);
pbl0->oemname[8] = CS_OP_ASCII('\0');
pbl0->secpalloc = b[0xd];
pbl0->numfats = b[0x10];
pbl0->mediadesc = b[0x15];
pbl0->physdrv = b[0x24]; /* Physical Drive No. (4.0) */
pbl0->xtbootsig = b[0x26]; /* Extended signt 29H if 4.0 stuf valid */
/* BUG FIX 12-1-99 - Add KS_LITTLE_ODD_PTR_OK flag to split between
big endian and little endian system. The top section works on little
endian systems that do not require even alligned word accesses like
the x86 but for example on Little endian ARM systems these assignments
derefrencing a pointer to word at an odd address which gives bad data .*/
pbl0->bytspsector = to_WORD(b+0xb); /*X*/
pbl0->secreserved = to_WORD(b+0xe); /*X*/
pbl0->numroot = to_WORD(b+0x11); /*X*/
pbl0->numsecs = to_WORD(b+0x13); /*X*/
pbl0->secpfat = to_WORD(b+0x16); /*X*/
pbl0->secptrk = to_WORD(b+0x18); /*X*/
pbl0->numhead = to_WORD(b+0x1a); /*X*/
pbl0->numhide = to_WORD(b+0x1c); /*X*/
pbl0->numhide2 = to_WORD(b+0x1e); /*X*/
pbl0->numsecs2 = to_DWORD(b+0x20);/*X*/ /* # secs if > 32M (4.0) */
pbl0->volid = to_DWORD(b+0x27);/*X*/ /* Unique number per volume (4.0) */
copybuff( &pbl0->vollabel[0],b+0x2b,11); /* Volume label (4.0) */
if (pbl0->numroot == 0 && !pc_gblk0_32(driveno, pbl0, b))
{
pc_free_scratch_blk(buf);
rtfs_set_errno(PEIOERRORREADINFO32); /* pc_gblk0_32 failed reading Bios Parameter block */
return(FALSE);
}
pc_free_scratch_blk(buf);
return(TRUE);
}
#if (RTFS_WRITE)
#if (RTFS_SUBDIRS)
/***************************************************************************
PC_CLZERO - Fill a disk cluster with zeroes
Description
Write zeroes into the cluster at clusterno on the drive pointed to by
pdrive. Used to zero out directory and data file clusters to eliminate
any residual data.
Returns
Returns FALSE on a write erro.
****************************************************************************/
/* Write zeros to all blocks in a cluster */
BOOLEAN pc_clzero(DDRIVE *pdrive, CLUSTERTYPE cluster) /*__fn__*/
{
BLKBUFF *pbuff;
CLUSTERTYPE i;
BLOCKT currbl;
currbl = pc_cl2sector(pdrive , cluster);
if (!currbl)
return (FALSE);
/*Init and write a block for each block in cl. Note: init clears the core */
for (i = 0; i < pdrive->secpalloc; i++, currbl++ )
{
pbuff = pc_init_blk( pdrive , currbl);
if (!pbuff)
{
return (FALSE);
}
if (!pc_write_blk ( pbuff ) )
{
pc_discard_buf(pbuff);
return (FALSE);
}
pc_release_buf(pbuff);
}
return (TRUE);
}
#endif
#endif
/****************************************************************************
PC_DRNO2DR - Convert a drive number to a pointer to DDRIVE
Description
Given a drive number look up the DDRIVE structure associated with it.
Returns
Returns NULL if driveno is not an open drive.
****************************************************************************/
DDRIVE *pc_drno_to_drive_struct(int driveno) /*__fn__*/
{
DDRIVE *pdr;
pdr = 0;
/* Check drive number */
if (pc_validate_driveno(driveno))
{
pdr = prtfs_cfg->drno_to_dr_map[driveno];
}
return(pdr);
}
DDRIVE *pc_drno2dr(int driveno) /*__fn__*/
{
DDRIVE *pdr;
DDRIVE *pretval;
pdr = pc_drno_to_drive_struct(driveno);
pretval = 0;
OS_CLAIM_FSCRITICAL()
/* Check drive number */
if (pdr)
{
if (pdr->mount_valid)
{
pretval = pdr;
}
}
OS_RELEASE_FSCRITICAL()
return(pretval);
}
/***************************************************************************
PC_DSKFREE - Deallocate all core associated with a disk structure
Description
Given a valid drive number. If the drive open count goes to zero, free the
file allocation table and the block zero information associated with the
drive. If unconditional is true, ignore the open count and release the
drive.
If open count reaches zero or unconditional, all future accesses to
driveno will fail until re-opened.
Returns
Returns FALSE if driveno is not an open drive.
****************************************************************************/
/* free up all core associated with the drive
called by close. A drive restart would consist of
pc_dskfree(driveno, TRUE), pc_dskopen() */
BOOLEAN pc_dskfree(int driveno) /*__fn__*/
{
DDRIVE *pdr;
/* Note this will fail unless mount_valid is true */
pdr = pc_drno2dr(driveno);
if (!pdr)
{
return(FALSE);
}
if (pdr->mount_valid)
{
/* Free the current working directory for this drive for all users */
pc_free_all_users(driveno);
/* Free all files, finodes & blocks associated with the drive */
pc_free_all_fil(pdr);
pc_free_all_i(pdr);
/* Free all drobj structures that have not yet been accessed */
pc_free_all_drobj(pdr);
pc_free_all_blk(pdr);
/* Clear the fat cache */
pc_free_all_fat_blocks(&pdr->fatcontext);
}
pdr->mount_valid = FALSE;
pdr->mount_abort = FALSE;
return (TRUE);
}
/****************************************************************************
PC_SEC2CLUSTER - Convert a block number to its cluster representation.
Description
Convert blockno to its cluster representation if it is in cluster space.
Returns
Returns 0 if the block is not in cluster space, else returns the
cluster number associated with block.
****************************************************************************/
#if (RTFS_SUBDIRS)
/* Cluster<->sector conversion routines */
/* Convert sector to cluster. 0 == s error */
CLUSTERTYPE pc_sec2cluster(DDRIVE *pdrive, BLOCKT blockno) /*__fn__*/
{
BLOCKT ltemp;
BLOCKT answer;
if ((blockno >= pdrive->numsecs) || (pdrive->firstclblock > blockno))
return (0);
else
{
/* (2 + (blockno - pdrive->firstclblock)/pdrive->secpalloc) */
ltemp = blockno - pdrive->firstclblock;
answer = ltemp;
answer = (BLOCKT) answer >> pdrive->log2_secpalloc;
answer += 2;
return ((CLUSTERTYPE)answer);
}
}
#endif
/****************************************************************************
PC_SEC2INDEX - Calculate the offset into a cluster for a block.
Description
Given a block number offset from the beginning of the drive, calculate
which block number within a cluster it will be. If the block number
coincides with a cluster boundary, the return value will be zero. If it
coincides with a cluster boundary + 1 block, the value will be 1, etc.
Returns
0,1,2 upto blockspcluster -1.
***************************************************************************/
#if (RTFS_SUBDIRS)
/* Convert sector to index into a cluster . No error detection */
word pc_sec2index(DDRIVE *pdrive, BLOCKT blockno) /*__fn__*/
{
BLOCKT answer;
/* ((blockno - pdrive->firstclblock) % pdrive->secpalloc) ); */
answer = blockno - pdrive->firstclblock;
answer = answer % pdrive->secpalloc;
return ( (word) answer);
}
#endif
/***************************************************************************
PC_CL2SECTOR - Convert a cluster number to block number representation.
Description
Convert cluster number to a blocknumber.
Returns
Returns 0 if the cluster is out of range. else returns the
block number of the beginning of the cluster.
****************************************************************************/
/* Convert cluster. to sector */
BLOCKT pc_cl2sector(DDRIVE *pdrive, CLUSTERTYPE cluster) /*__fn__*/
{
BLOCKT blockno;
dword t;
if (cluster < 2)
return (BLOCKEQ0);
else
{
t = cluster - 2;
t = t << pdrive->log2_secpalloc;
blockno = pdrive->firstclblock + t;
}
if (blockno >= pdrive->numsecs)
return (BLOCKEQ0);
else
return (blockno);
}
/* Round a size up to it's next cluster boundary and return
the length in clusters */
dword pc_chain_length(DDRIVE *pdrive, dword byte_length)
{
dword ltemp,chain_length;
ltemp = byte_length + pdrive->byte_into_cl_mask;
if (ltemp > byte_length)
{
chain_length = ltemp & ~(pdrive->byte_into_cl_mask);
chain_length >>= (int) (pdrive->log2_secpalloc + 9);
}
else
{
ltemp = (byte_length - pdrive->bytespcluster)+ pdrive->byte_into_cl_mask;
chain_length = ltemp & ~(pdrive->byte_into_cl_mask);
chain_length >>= (int) (pdrive->log2_secpalloc + 9);
chain_length += 1;
}
return(chain_length);
}