twl_wrapsdk/build/libraries/fatfs/ARM7/rtfatxx.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

1072 lines
37 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.
*/
/* RTFATXX.C - Low level File allocation table management functions.
Routines in this file include:
fatxx_alloc_chain - Allocate a chain from the FAT.
fatxx_find_free_cluster- Find the first free cluster in a given range.
fatxx_clalloc - Allocate a single cluster in the fragmented region.
fatxx_clgrow - Grow a directory chain in the fragmented region.
fatxx_clnext - Get the next cluster in a chain.
fatxx_clrelease - Return a cluster to the free list.
fatxx_faxx - Get a value from the FAT.
fatxx_flushfat - Make sure the FAT is up to date on disk.
fatxx_freechain - Release a chain to the free list.
fatxx_cl_truncate_dir - Truncate a cluster chain.
fatxx_get_chain - Return contiguous clusters in a chain.
fatxx_pfaxx - Put a value to the FAT.
fatxx_pfswap - Swap a block of the FAT into the cache.
fatxx_fword - Get or put a value from the swap cache.
*/
#include <rtfs.h>
CLUSTERTYPE fatxx_alloc_chain(DDRIVE *pdr, CLUSTERTYPE *pstart_cluster, CLUSTERTYPE n_clusters, BOOLEAN dolink);
CLUSTERTYPE fatxx_find_free_cluster(DDRIVE *pdr, CLUSTERTYPE startpt, CLUSTERTYPE endpt, int *is_error);
CLUSTERTYPE fatxx_clnext(DDRIVE *pdr, CLUSTERTYPE clno);
BOOLEAN fatxx_clrelease_dir(DDRIVE *pdr, CLUSTERTYPE clno);
BOOLEAN fatxx_faxx(DDRIVE *pdr, CLUSTERTYPE clno, CLUSTERTYPE *pvalue);
BOOLEAN fatxx_flushfat(int driveno);
BOOLEAN fatxx_freechain(DDRIVE *pdr, CLUSTERTYPE cluster, dword min_clusters_to_free, dword max_clusters_to_free);
CLUSTERTYPE fatxx_cl_truncate_dir(DDRIVE *pdr, CLUSTERTYPE cluster, CLUSTERTYPE l_cluster);
CLUSTERTYPE fatxx_get_chain(DDRIVE *pdr, CLUSTERTYPE start_cluster, CLUSTERTYPE *pnext_cluster, CLUSTERTYPE n_clusters, int *end_of_chain);
BOOLEAN fatxx_pfaxxterm(DDRIVE *pdr, CLUSTERTYPE clno);
BOOLEAN fatxx_pfaxx(DDRIVE *pdr, CLUSTERTYPE clno, CLUSTERTYPE value);
BOOLEAN fatxx_fword(DDRIVE *pdr, CLUSTERTYPE index, word *pvalue, BOOLEAN putting);
BOOLEAN fatxx_pfpdword(DDRIVE *pdr, dword index, dword *pvalue);
BOOLEAN fatxx_pfgdword(DDRIVE *pdr, dword index, dword *value);
/******************************************************************************
PC_ALLOC_CHAIN - Allocate as many contiguous clusters as possible.
Description
Reserve up to n_clusters contiguous clusters from the FAT and
return the number of contiguous clusters reserved.
If pstart_cluster points to a valid cluster and dolink is true
then link the new chain to it.
Returns
Returns the number of contiguous clusters found. Or zero on an error.
pstart_cluster contains the address of the start of the chain on
return.
*****************************************************************************/
#if (RTFS_WRITE)
CLUSTERTYPE fatxx_alloc_chain(DDRIVE *pdr, CLUSTERTYPE *pstart_cluster, CLUSTERTYPE n_clusters, BOOLEAN dolink) /*__fatfn__*/
{
CLUSTERTYPE start_cluster;
CLUSTERTYPE first_new_cluster;
CLUSTERTYPE clno;
CLUSTERTYPE n_contig;
CLUSTERTYPE value;
CLUSTERTYPE last_cluster; /* NEW for 5/18/97 bug fix */
int is_error;
is_error = 0;
start_cluster = *pstart_cluster;
if (start_cluster &&
( (start_cluster < 2) || (start_cluster > pdr->maxfindex) ) )
{
rtfs_set_errno(PEINVALIDCLUSTER); /* fatxx_alloc_chain: bad cluster value internal error */
return (0);
}
/* If the user provided a cluster we find the next cluster beyond that
one. Otherwise we look at the disk structure and find the next
free cluster in the free cluster region after the current best guess
of the region. If that fails we look to the beginning of the region
and if that fails we look in the non-contiguous region. */
/* Begin changes for the 5/18/97 bug fix */
clno = 0;
/* NEW */
if (start_cluster)
{
/* search from the start_cluster hint to the end of the fat */
/* use: pdr->maxfindex+1 because find_free_cluster uses < end not <= */
clno = fatxx_find_free_cluster(pdr, start_cluster, pdr->maxfindex+1, &is_error);
if (is_error) /* Error reading fat */
return(0);
/* If we search again search only to the start_cluster */
last_cluster = start_cluster;
}
else
/* When we search again search to the end */
/* use: pdr->maxfindex+1 because find_free_cluster uses < end not <= */
last_cluster = pdr->maxfindex + 1;
/* Check the most likely place to find contiguous space */
if (!clno)
{
/* NEW */
if (!start_cluster || start_cluster >= pdr->free_contig_pointer)
{
/* search from free_contig_pointer to start_cluster or maxfindex whichever
is less */
clno = fatxx_find_free_cluster(pdr, pdr->free_contig_pointer, last_cluster, &is_error);
if (is_error) /* Error reading fat */
return(0);
/* If we search again search only to the free_contig_pointer */
last_cluster = pdr->free_contig_pointer;
}
}
/* Check the area of the disk beyond where we typically write fragments */
if (!clno)
{
/* NEW */
if (!start_cluster || start_cluster > pdr->free_contig_base)
/* search from free_contig_base to start_cluster or free_contig_pointer whichever
is less */
clno = fatxx_find_free_cluster(pdr, pdr->free_contig_base, last_cluster, &is_error);
if (is_error) /* Error reading fat */
return(0);
}
/* Check the beginning of the the disk where we typically write fragments */
if (!clno)
{
clno = fatxx_find_free_cluster(pdr, 2, pdr->free_contig_base, &is_error);
if (is_error) /* Error reading fat */
return(0);
}
/* We did not find any clusters. Scan the whole fat again this should
never work but we did have a bug in this area once before ... */
if (!clno)
{
clno = fatxx_find_free_cluster(pdr, 2, pdr->maxfindex, &is_error);
if (is_error) /* Error reading fat */
return(0);
}
if (!clno)
{
rtfs_set_errno(PENOSPC); /* fatxx_alloc_chain: No free clusters */
return(0);
}
/* End changes for the 5/18/97 bug fix */
first_new_cluster = clno;
value = 0;
n_contig = 1;
/* look up the FAT. If the next cluster is free we link to it
and up the contig count. */
while ( (n_contig < n_clusters) && (clno < pdr->maxfindex) )
{
if (!fatxx_faxx(pdr,(CLUSTERTYPE)(clno+1), &value))
return(0);
/* If the next cluster is in-use we are done. */
if (value)
break;
/* Link the current cluster to the next one */
if (!fatxx_pfaxx(pdr, clno, (CLUSTERTYPE)(clno+1)))
return (0);
n_contig += (CLUSTERTYPE)1; /* Yep.. we got another */
clno += (CLUSTERTYPE)1; /* Up the FAT table */
}
/* Terminate the list we just made */
if (!fatxx_pfaxxterm(pdr, clno))
return (0);
/* Update the hint of most likeley place to find a free cluster */
if ((clno < pdr->maxfindex) && (clno >= pdr->free_contig_pointer))
pdr->free_contig_pointer = (CLUSTERTYPE)(clno+1);
/* If we were handed a starting cluster we have to stitch our new
chain after it. */
if (dolink && start_cluster)
{
if (!fatxx_pfaxx(pdr, start_cluster, first_new_cluster))
return (0);
}
*pstart_cluster = first_new_cluster;
pdr->known_free_clusters = (long)(pdr->known_free_clusters - n_contig);
return(n_contig);
}
/* Find the first free cluster in a range */
/* Note: The caller locks the fat before calling this routine */
CLUSTERTYPE fatxx_find_free_cluster(DDRIVE *pdr, CLUSTERTYPE startpt, CLUSTERTYPE endpt, int *is_error) /*__fatfn__*/
{
CLUSTERTYPE i;
CLUSTERTYPE value;
*is_error = 0;
for (i = startpt; i < endpt; i++)
{
if ( !fatxx_faxx(pdr, i, &value) )
{
*is_error = 1;
return(0);
}
if (value == 0)
{
return(i);
}
}
return(0);
}
#if (RTFS_SUBDIRS)
/***************************************************************************
PC_CLALLOC - Reserve and return the next free cluster on a drive
Description
Given a DDRIVE, mark the next available cluster in the file allocation
table as used and return the associated cluster number. Clhint provides
a means of selecting clusters that are near eachother. This should
reduce fragmentation.
NOTE: This routine is used to allocate single cluster chunks for
maintaining directories. We artificially break the disks into
two regions. The first region is where single clusters chunks
used in directory files come from. These are allocated by this
routine only. Data file clusters are allocated by fatxx_alloc_chain.
THE DISK IS NOT REALLY PARTITIONED. If this routine runs out of
space in the first region it grabs a cluster from the second
region.
Returns
Return a new cluster number or 0 if the disk is full.
****************************************************************************/
/* Note: The caller locks the fat before calling this routine */
CLUSTERTYPE fatxx_clalloc(DDRIVE *pdr, CLUSTERTYPE clhint) /*__fatfn__*/
{
CLUSTERTYPE clno;
int is_error;
if (clhint < 2)
clhint = 2;
if (clhint >= pdr->free_contig_base)
clhint = 2;
/* Look in the fragmentable region first from clhint up */
clno = fatxx_find_free_cluster(pdr, clhint, pdr->free_contig_base, &is_error);
if (is_error) /* Error reading fat */
return(0);
/* Look in the fragmentable region up to clhint */
if (!clno)
{
clno = fatxx_find_free_cluster(pdr, 2, clhint, &is_error);
if (is_error) /* Error reading fat */
return(0);
}
/* Look in the contiguos region if the fragmentable region is full */
if (!clno)
{
/* use: pdr->maxfindex+1 because find_free_cluster uses < end not <= */
clno = fatxx_find_free_cluster(pdr, pdr->free_contig_base, pdr->maxfindex+1, &is_error);
if (is_error) /* Error reading fat */
return(0);
}
if (!clno)
rtfs_set_errno(PENOSPC); /* fatxx_clalloc: No free clusters */
else
{
/* Mark the cluster in use */
if (!fatxx_pfaxxterm(pdr, clno))
return (0);
pdr->known_free_clusters = pdr->known_free_clusters - 1;
}
return(clno);
}
/****************************************************************************
PC_CLGROW - Extend a cluster chain and return the next free cluster
Description
Given a DDRIVE and a cluster, extend the chain containing the cluster
by allocating a new cluster and linking clno to it. If clno is zero
assume it is the start of a new file and allocate a new cluster.
Note: The chain is traversed to the end before linking in the new
cluster. The new cluster terminates the chain.
Returns
Return a new cluster number or 0 if the disk is full.
****************************************************************************/
/* Note: The caller locks the fat before calling this routine */
CLUSTERTYPE fatxx_clgrow(DDRIVE *pdr, CLUSTERTYPE clno) /*__fatfn__*/
{
CLUSTERTYPE nxt;
CLUSTERTYPE nextcluster;
long range_check;
/* Check the incoming argument. Should be a valid cluster */
if ((clno < 2) || (clno > pdr->maxfindex) )
{
rtfs_set_errno(PEINVALIDCLUSTER);
return (0);
}
/* Make sure we are at the end of chain */
range_check = 0;
nextcluster = fatxx_clnext(pdr , clno);
while (nextcluster != 0xffffffff && ++range_check < MAX_CLUSTERS_PER_DIR)
{
if (!nextcluster) /* fatxx_clnext - set errno */
return (0);
clno = nextcluster;
nextcluster = fatxx_clnext(pdr , clno);
}
if (get_errno())
return (0);
if (range_check == MAX_CLUSTERS_PER_DIR)
{
rtfs_set_errno(PEINVALIDCLUSTER);
return (0);
}
/* Get a cluster, clno provides a hint for more efficient cluster
allocation */
nxt = fatxx_clalloc(pdr,clno);
if (!nxt)
return((CLUSTERTYPE) 0);
/* Attach it to the current cluster if not at the begining of the chain */
if (!fatxx_pfaxx(pdr, clno, nxt))
return((CLUSTERTYPE) 0);
return(nxt);
}
/****************************************************************************
PC_CLRELEASE - Return a cluster to the pool of free space on a disk
Description
Given a DDRIVE and a cluster, mark the cluster in the file allocation
table as free. It will be used again by calls to fatxx_clalloc().
Returns
Nothing
***************************************************************************/
/* Return a cluster to the free list */
/* Note: The caller locks the fat before calling this routine */
BOOLEAN fatxx_clrelease_dir(DDRIVE *pdr, CLUSTERTYPE clno) /*__fatfn__*/
{
int current_errno;
/* This is a cleanup routine, an earlier event is the interesting errno
to the application, so we restore errno if we fail */
current_errno = get_errno();
/* No need to check clno value, pfaxx will catch it */
/* Do not catch any lower level errors here. You will catch them soon enough */
if (fatxx_pfaxx(pdr, clno, (CLUSTERTYPE) 0x0)) /* Mark it as free */
{
/* If freeing in the contiguous region reset the hint if we
free space earlier than it. */
if (clno >= pdr->free_contig_base && clno <= pdr->free_contig_pointer)
pdr->free_contig_pointer = clno;
pdr->known_free_clusters = pdr->known_free_clusters + 1;
return(TRUE);
}
else
{
rtfs_set_errno(current_errno);
return(FALSE);
}
}
/****************************************************************************
PC_FLUSHFAT - Write any dirty FAT blocks to disk
Description
Given a valid drive number. Write any fat blocks to disk that
have been modified. Updates all copies of the fat.
Returns
Returns FALSE if driveno is not an open drive. Or a write failed.
****************************************************************************/
/* Consult the dirty fat block list and write any. write all copies
of the fat */
/* Note: The caller locks the fat before calling this routine */
BOOLEAN fatxx_flushfat(int driveno) /*__fatfn__*/
{
DDRIVE *pdr;
pdr = pc_drno2dr(driveno);
if (!pdr)
{
return(FALSE);
}
if (!pdr->fat_is_dirty)
return(TRUE);
/* write the alloc hints into the info block (no-op on non-FAT32 systems) */
if (!fat_flushinfo(pdr))
return(FALSE);
if (pc_flush_fat_blocks(pdr))
{
pdr->fat_is_dirty = FALSE;
return(TRUE);
}
else
return(FALSE);
}
/****************************************************************************
PC_FREECHAIN - Free a cluster chain associated with an inode.
Description
Trace the cluster chain starting at cluster and return all the clusters to
the free state for re-use. The FAT is not flushed.
Returns
Nothing.
****************************************************************************/
/* Note: The caller locks the fat before calling this routine */
BOOLEAN fatxx_freechain(DDRIVE *pdr, CLUSTERTYPE cluster, dword min_clusters_to_free, dword max_clusters_to_free) /*__fatfn__*/
{
CLUSTERTYPE nextcluster;
dword clusters_freed;
if (max_clusters_to_free==0)
return(TRUE);
if ((cluster < 2) || (cluster > pdr->maxfindex) )
{
rtfs_set_errno(PEINVALIDCLUSTER); /* fatxx_pfaxx: bad cluster value internal error */
return (FALSE);
}
clusters_freed = 0;
nextcluster = fatxx_clnext(pdr , cluster);
while (cluster != 0xffffffff)
{
if (!nextcluster)
return (FALSE); /* Clnext set errno */
/* check if we are about to free more than we should */
if (clusters_freed >= max_clusters_to_free)
{
rtfs_set_errno(PEINVALIDCLUSTER);
return (FALSE);
}
if (fatxx_pfaxx(pdr, cluster, (CLUSTERTYPE) 0x0)) /* Mark it as free */
{
/* If freeing in the contiguous region reset the hint if we
free space earlier than it. */
if (cluster >= pdr->free_contig_base && cluster <= pdr->free_contig_pointer)
pdr->free_contig_pointer = cluster;
pdr->known_free_clusters = pdr->known_free_clusters + 1;
}
else
return(FALSE); /* pfaxx set errno */
clusters_freed += 1;
cluster = nextcluster;
if (nextcluster != 0xffffffff)
nextcluster = fatxx_clnext(pdr , nextcluster);
}
if (clusters_freed < min_clusters_to_free)
{
rtfs_set_errno(PEINVALIDCLUSTER);
return (FALSE);
}
return(TRUE);
}
/****************************************************************************
PC_CL_TRUNCATE - Truncate a cluster chain.
Description
Trace the cluster chain starting at cluster until l_cluster is reached.
Then terminate the chain and free from l_cluster on.
The FAT is not flushed.
If cluster == l_cluster does nothing. This condition should be handled
higher up by updating dirents appropriately and then calling fatxx_freechain.
Returns
The last cluster in the chain after truncation.
****************************************************************************/
/* Note: The caller locks the fat before calling this routine */
CLUSTERTYPE fatxx_cl_truncate_dir(DDRIVE *pdr, CLUSTERTYPE cluster, CLUSTERTYPE l_cluster)/*__fatfn__*/
{
CLUSTERTYPE nextcluster;
long range_check;
int current_errno;
if ((cluster < 2) || (cluster > pdr->maxfindex) )
{ /* Don't set errno in this function, it is a cleanup */
return (0);
}
/* This is a cleanup routine, an earlier event is the interesting errno
to the application, so we restore errno if we fail */
current_errno = get_errno();
nextcluster = fatxx_clnext(pdr , cluster);
range_check = 0;
while (nextcluster != 0xffffffff && ++range_check < MAX_CLUSTERS_PER_DIR)
{
if (!nextcluster) /* fatxx_clnext detected error */
{ /* Don't set errno in this function, it is a cleanup */
rtfs_set_errno(current_errno);
return (0);
}
if (nextcluster == l_cluster)
{
if (!fatxx_pfaxxterm(pdr, cluster)) /* Terminate the chain */
break; /* Error break to ret 0 */
/* We don't know the size so free between 0 and max */
if (!fatxx_freechain(pdr, l_cluster, 0, MAX_CLUSTERS_PER_DIR))
{
rtfs_set_errno(current_errno);
return(0);
}
return(cluster);
}
else
{
cluster = nextcluster;
nextcluster = fatxx_clnext(pdr , nextcluster);
}
}
rtfs_set_errno(current_errno);
return(0);
}
#endif /* (RTFS_SUBDIRS) */
/******************************************************************************
PC_PFAXXTERM - Write a terminating value to the FAT at clno.
Description
Given a DDRIVE,cluster number and value. Write the value 0xffffffff or
0xffff in the fat at clusterno. Handle 32, 16 and 12 bit fats correctly.
Returns
FALSE if an io error occurred during fat swapping, else TRUE
*****************************************************************************/
/* Given a clno & fatval Put the value in the table at the index (clno) */
/* Note: The caller locks the fat before calling this routine */
BOOLEAN fatxx_pfaxxterm(DDRIVE *pdr, CLUSTERTYPE clno) /*__fatfn__*/
{
#if (FAT32)
if (pdr->fasize == 8)
return(fatxx_pfaxx(pdr, clno, 0x0ffffffful));
else
#endif
return(fatxx_pfaxx(pdr, clno, 0xffff));
}
/******************************************************************************
PC_PFAXX - Write a value to the FAT at clno.
Description
Given a DDRIVE,cluster number and value. Write the value in the fat
at clusterno. Handle 16 and 12 bit fats correctly.
Returns
No if an io error occurred during fat swapping, else TRUE.
*****************************************************************************/
/* Given a clno & fatval Put the value in the table at the index (clno) */
/* Note: The caller locks the fat before calling this routine */
BOOLEAN fatxx_pfaxx(DDRIVE *pdr, CLUSTERTYPE clno, CLUSTERTYPE value) /*__fatfn__*/
{
union align1 {
byte wrdbuf[4]; /* Temp storage area */
word fill[2];
} u;
CLUSTERTYPE nibble,index,offset, t;
if ((clno < 2) || (clno > pdr->maxfindex) )
{
rtfs_set_errno(PEINVALIDCLUSTER); /* fatxx_pfaxx: bad cluster value internal error */
return (FALSE);
}
pdr->fat_is_dirty = TRUE;
if (pdr->fasize == 3) /* 3 nibble ? */
{
nibble = (CLUSTERTYPE)(clno * 3);
index = (CLUSTERTYPE)(nibble >> 2);
offset = (CLUSTERTYPE)(clno & 0x03);
/* Read the first word */
if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], FALSE ))
return(FALSE);
/*
| W0 | W1 | W2 |
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
xx xx xx
*/
if (offset == 0) /* (A2 << 8) | A1 A2 */
{
/* Low nibble of b[1] is hi nibble of value */
u.wrdbuf[1] &= 0xf0;
t = (CLUSTERTYPE)((value >> 8) & 0x0f);
u.wrdbuf[1] |= (byte) t;
/* b[0] is lo byte of value */
t = (CLUSTERTYPE)(value & 0x00ff);
u.wrdbuf[0] = (byte) t;
if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], TRUE ))
return (FALSE);
}
/*
| W0 | W1 | W2 |
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
xx xx xx
*/
else if (offset == 1) /* (B1 B2 << 4) | B0 */
{
/* Hi nibble of b[1] is lo nibble of value */
u.wrdbuf[1] &= 0x0f;
t = (CLUSTERTYPE)((value << 4) & 0x00f0);
u.wrdbuf[1] |= (byte)t;
if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], TRUE ))
return (FALSE);
/* b[0] is hi byte of value */
if (!fatxx_fword( pdr, (word)(index+1), (word *) &u.wrdbuf[0], FALSE ))
return(FALSE);
t = (CLUSTERTYPE)((value >> 4) & 0x00ff);
u.wrdbuf[0] = (byte) t;
if (!fatxx_fword( pdr, (word)(index+1), (word *) &u.wrdbuf[0], TRUE ))
return (FALSE);
}
/*
| W0 | W1 | W2 |
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
xx xx xx
*/
else if (offset == 2) /*(C2 << 8) | C1 C2 */
{
/* b[1] = low byte of value */
t = (CLUSTERTYPE)(value & 0x00ff);
u.wrdbuf[1] = (byte) t;
if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], TRUE ))
return (FALSE);
/* lo nibble of b[0] == hi nibble of value */
if (!fatxx_fword( pdr, (word)(index+1), (word *) &u.wrdbuf[0], FALSE ))
return(FALSE);
u.wrdbuf[0] &= 0xf0;
t = (CLUSTERTYPE)((value >> 8) & 0x0f);
u.wrdbuf[0] |= (byte) t;
if (!fatxx_fword( pdr, (word)(index+1), (word *) &u.wrdbuf[0], TRUE ))
return (FALSE);
}
/*
| W0 | W1 | W2 |
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
xx xx xx
*/
else if (offset == 3) /* (D2 D1) << 4 | D0 */
{
/* Hi nibble b[0] == low nible of value */
u.wrdbuf[0] &= 0x0f;
t = (CLUSTERTYPE)((value << 4) & 0x00f0);
u.wrdbuf[0] |= (byte) t;
t = (CLUSTERTYPE)((value >> 4) & 0x00ff);
u.wrdbuf[1] = (byte) t;
if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], TRUE ))
return (FALSE);
}
}
#if (FAT32) /* FAT32 only supported if FAT swapping is enabled */
else if (pdr->fasize == 8)
{
fr_DWORD(&u.wrdbuf[0],value); /*X*/
/* Now put the values back into the FAT */
if (!fatxx_pfpdword( pdr, clno, (dword *) &u.wrdbuf[0] ))
{
return (FALSE);
}
}
#endif /* FAT32 */
else /* 16 BIT entries */
{
fr_WORD((byte *) &u.wrdbuf[0],(word)value); /*X*/
/* Now put the values back into the FAT */
if (!fatxx_fword( pdr, clno, (word *) &u.wrdbuf[0], TRUE ))
{
return (FALSE);
}
}
return (TRUE);
}
#endif /* (RTFS_WRITE) */
/***************************************************************************
PC_CLNEXT - Return the next cluster in a cluster chain
Description
Given a DDRIVE and a cluster number, return the next cluster in the
chain containing clno. Return 0 on end of chain.
Returns
Return a new cluster number or 0xffffffff on end of chain.
****************************************************************************/
/* Return the next cluster in a chain or 0xffffffff */
CLUSTERTYPE fatxx_clnext(DDRIVE *pdr, CLUSTERTYPE clno) /*__fatfn__ */
{
CLUSTERTYPE nxt;
#if (FAT32)
dword _Oxffffffful;
#endif
/* Get the value at clno. return 0 on any io errors */
if (! fatxx_faxx(pdr,clno,&nxt) )
return (0);
if (pdr->fasize == 3) /* 3 nibble ? */
{
if ( (0xff7 < nxt) && (nxt <= 0xfff) )
nxt = 0xffffffff; /* end of chain */
}
else
#if (FAT32)
if (pdr->fasize == 8)
{
_Oxffffffful = 0x0ffffffful;
nxt &= _Oxffffffful;
if ( nxt == 0x0ffffffful )
nxt = 0xffffffff; /* end of chain */
}
else
#endif
{
#if (FAT32)
if ( (nxt >= (CLUSTERTYPE)0xfff7) && (nxt <= (CLUSTERTYPE)0xffff) )
#else
/* If fat32 is not defined the nxt is always <= 0xffff. picky compilers
notice this and emit a warning */
if (nxt >= (CLUSTERTYPE)0xfff7)
#endif
nxt = 0xffffffff; /* end of chain */
}
if (nxt != 0xffffffff && (nxt < 2 || nxt > pdr->maxfindex) )
{
rtfs_set_errno(PEINVALIDCLUSTER);
return (0);
}
return(nxt);
}
/***************************************************************************
PC_FAXX - Get the value store in the FAT at clno.
Description
Given a DDRIVE and a cluster number. Get the value in the fat at clusterno
(the next cluster in a chain.) Handle 16 and 12 bit fats correctly.
Returns
Returns the the value at clno. In pvalue.
If any error occured while FAT swapping return FALSE else return TRUE.
***************************************************************************/
/* Retrieve a value from the fat */
BOOLEAN fatxx_faxx(DDRIVE *pdr, CLUSTERTYPE clno, CLUSTERTYPE *pvalue) /*__fatfn__*/
{
CLUSTERTYPE nibble,index, offset, result;
byte c;
union align1 {
byte wrdbuf[4]; /* Temp storage area */
word fill[2];
} u;
union align2 {
byte wrdbuf2[4]; /* Temp storage area */
word fill[2];
} u2;
if ((clno < 2) || (clno > pdr->maxfindex) )
{
rtfs_set_errno(PEINVALIDCLUSTER); /* fatxx_faxx: bad cluster value internal error */
return (FALSE);
}
result = 0;
if (pdr->fasize == 3) /* 3 nibble ? */
{
nibble = (word)(clno * 3);
index = (word)(nibble >> 2);
offset = (word)(clno & 0x03);
/* Read the first word */
if (!fatxx_fword( pdr, index, (word *) &u.wrdbuf[0], FALSE ))
return(FALSE);
/*
| W0 | W1 | W2 |
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
xx xx xx
*/
if (offset == 0) /* (A2 << 8) | A1 A2 */
{
/* BYTE 0 == s Low byte, byte 1 low nibble == s high byte */
u.wrdbuf[1] &= 0x0f;
result = u.wrdbuf[1];
result <<= 8;
result |= u.wrdbuf[0];
}
/*
| W0 | W1 | W2 |
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
xx xx xx
*/
else if (offset == 1) /* (B1 B2 << 4) | B0 */
{
/* BYTE 2 == High byte Byte 1 high nibb == low nib */
if (!fatxx_fword( pdr, (word)(index+1), (word *) &u2.wrdbuf2[0], FALSE ))
return(FALSE);
c = (byte) (u.wrdbuf[1] >> 4);
result = u2.wrdbuf2[0];
result <<= 4;
result |= c;
}
/*
| W0 | W1 | W2 |
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
xx xx xx
*/
else if (offset == 2) /*(C2 << 8) | C1 C2 */
{
if (!fatxx_fword( pdr, (word)(index+1), (word *) &u2.wrdbuf2[0], FALSE ))
return(FALSE);
/* BYTE 1 == s Low byte, byte 2 low nibble == s high byte */
result = (word) (u2.wrdbuf2[0] & 0x0f);
result <<= 8;
result |= u.wrdbuf[1];
}
/*
| W0 | W1 | W2 |
A1 A0 B0 A2 B2 B1 C1 C0 D0 C2 D2 D1
xx xx xx
*/
else if (offset == 3) /* (D2 D1) << 4 | D0 */
{
result = u.wrdbuf[1];
result <<= 4;
c = u.wrdbuf[0];
c >>= 4;
result |= c;
}
}
else if (pdr->fasize == 8) /* 32 BIT fat. ret the value at 4 * clno */
{
#if (FAT32)
#if KS_LITTLE_ENDIAN
if (!fatxx_pfgdword( pdr, clno, (dword *) &result ))
return (FALSE);
#else
if ( fatxx_pfgdword( pdr, clno, (dword *) &u.wrdbuf[0] ))
result = (CLUSTERTYPE) to_DWORD(&u.wrdbuf[0]);
else
return (FALSE);
#endif
#else
return (FALSE);
#endif
}
else /* 16 BIT fat. ret the value at 2 * clno */
{
if ( fatxx_fword( pdr, clno, (word *) &u.wrdbuf[0], FALSE ))
result = (CLUSTERTYPE) to_WORD(&u.wrdbuf[0]); /*X*/ /* And use the product as index */
else
return (FALSE);
}
*pvalue = result;
return (TRUE);
}
/******************************************************************************
PC_GET_CHAIN - Return as many contiguous clusters as possible.
Description
Starting at start_cluster return the number of contiguous clusters
allocated in the chain containing start_cluster or n_clusters,
whichever is less.
Returns
Returns the number of contiguous clusters found. Or zero on an error.
This function should always return at least one. (start_cluster). Unless
an error occurs.
The word at *pnext_cluster is filled with on of the following:
. If we went beyond a contiguous section it contains
the first cluster in the next segment of the chain.
. If we are still in a section it contains
the next cluster in the current segment of the chain.
. If we are at the end of the chain it contains the last cluster
in the chain.
****************************************************************************/
/* Note: The caller locks the fat before calling this routine */
CLUSTERTYPE fatxx_get_chain(DDRIVE *pdr, CLUSTERTYPE start_cluster, CLUSTERTYPE *pnext_cluster, CLUSTERTYPE n_clusters, int *end_of_chain)
{
CLUSTERTYPE clno, next_cluster;
CLUSTERTYPE n_contig;
CLUSTERTYPE value;
value = 0;
clno = start_cluster;
n_contig = 1;
*pnext_cluster = 0;
/* Get each FAT entry. If its value points to the next contiguous entry
continue. Otherwise we have reached the end of the contiguous chain.
At which point we return the number of contig s found and by reference
the address of the FAT entry beginning the next chain segment.
*/
*end_of_chain = 0;
for (;;)
{
next_cluster = fatxx_clnext(pdr, clno);
if (!next_cluster) /* clnext detected an error */
return(0);
/* check for end markers set next cluster to the last
cluster in the chain if we are at the end */
if (next_cluster == 0xffffffff) /* clnext detected end */
{
*end_of_chain = 1;
value = clno;
break;
}
else if (next_cluster == ++clno)
{
value = next_cluster;
if (n_contig >= n_clusters)
break;
n_contig++;
}
else /* (next_cluster != ++clno) and we know it is not an error condition */
{
value = next_cluster;
break;
}
}
*pnext_cluster = value;
return (n_contig);
}
/***************************************************************************
PC_FATSW - Map in a page of the FAT
****************************************************************************/
/* Swap in the page containing index */
/* Note: The caller locks the fat before calling this routine */
byte * fatxx_pfswap(DDRIVE *pdr, CLUSTERTYPE index, BOOLEAN for_write) /*__fatfn__*/
{
dword block_offset_in_fat;
dword flags;
if (pdr->fasize == 8)
block_offset_in_fat = (dword)(index >> 7);
else
block_offset_in_fat = (word) (index >> 8);
if (block_offset_in_fat >= pdr->secpfat) /* Check range */
return (0);
if (for_write)
flags = 0x80000000ul;
else
flags = 0x00000000ul;
return (pc_map_fat_block(pdr,pdr->fatblock+block_offset_in_fat, flags));
}
/* Put or get a WORD value into the fat at index */
BOOLEAN fatxx_fword(DDRIVE *pdr, CLUSTERTYPE index, word *pvalue, BOOLEAN putting) /*__fatfn__*/
{
word *ppage;
word offset;
/* Make sure we have access to the page. Mark it for writing (if a put) */
ppage = (word *)fatxx_pfswap(pdr,index,putting);
if (!ppage)
return(FALSE);
else
{
/* there are 256 entries per page */
offset = (word) (index & 0xff);
if (putting)
ppage[offset] = *pvalue;
else
*pvalue = ppage[offset];
}
return(TRUE);
}
FAT_DRIVER fatxx_d;
static BOOLEAN init_fat(DDRIVE *pdr)
{
FAT_DRIVER *pfd;
/* int driveno;
driveno = pdr->driveno;*/
pfd = &fatxx_d;
pfd->fatop_alloc_chain = fatxx_alloc_chain;
pfd->fatop_clnext = fatxx_clnext;
pfd->fatop_clrelease_dir = fatxx_clrelease_dir;
pfd->fatop_faxx = fatxx_faxx;
pfd->fatop_flushfat = fatxx_flushfat;
pfd->fatop_freechain = fatxx_freechain;
pfd->fatop_cl_truncate_dir = fatxx_cl_truncate_dir;
pfd->fatop_get_chain = fatxx_get_chain;
pfd->fatop_pfaxxterm = fatxx_pfaxxterm;
pfd->fatop_pfaxx = fatxx_pfaxx;
pdr->fad = (void *) pfd;
return(TRUE);
}
BOOLEAN faxx_check_free_space(DDRIVE *pdr)
{
CLUSTERTYPE i;
CLUSTERTYPE nxt;
long freecount = 0;
for (i = 2 ; i <= pdr->maxfindex; i++)
{
if (!FATOP(pdr)->fatop_faxx(pdr, i, &nxt)) /* Any (ifree) */
return(FALSE);
if (nxt == 0)
freecount++;
}
pdr->known_free_clusters = freecount;
return(TRUE);
}
BOOLEAN init_fat32(DDRIVE *pdr)
{
return(init_fat(pdr) && faxx_check_free_space(pdr));
}
BOOLEAN init_fat16(DDRIVE *pdr)
{
return(init_fat(pdr) && faxx_check_free_space(pdr));
}
BOOLEAN init_fat12(DDRIVE *pdr)
{
return(init_fat(pdr) && faxx_check_free_space(pdr));
}