blob: 057558e2e01c138148591078a96ff43015149abe [file] [log] [blame]
/*
* FreeRTOS+FAT SL V1.0.1 (C) 2014 HCC Embedded
*
* The FreeRTOS+FAT SL license terms are different to the FreeRTOS license
* terms.
*
* FreeRTOS+FAT SL uses a dual license model that allows the software to be used
* under a standard GPL open source license, or a commercial license. The
* standard GPL license (unlike the modified GPL license under which FreeRTOS
* itself is distributed) requires that all software statically linked with
* FreeRTOS+FAT SL is also distributed under the same GPL V2 license terms.
* Details of both license options follow:
*
* - Open source licensing -
* FreeRTOS+FAT SL is a free download and may be used, modified, evaluated and
* distributed without charge provided the user adheres to version two of the
* GNU General Public License (GPL) and does not remove the copyright notice or
* this text. The GPL V2 text is available on the gnu.org web site, and on the
* following URL: http://www.FreeRTOS.org/gpl-2.0.txt.
*
* - Commercial licensing -
* Businesses and individuals who for commercial or other reasons cannot comply
* with the terms of the GPL V2 license must obtain a commercial license before
* incorporating FreeRTOS+FAT SL into proprietary software for distribution in
* any form. Commercial licenses can be purchased from
* http://shop.freertos.org/fat_sl and do not require any source files to be
* changed.
*
* FreeRTOS+FAT SL is distributed in the hope that it will be useful. You
* cannot use FreeRTOS+FAT SL unless you agree that you use the software 'as
* is'. FreeRTOS+FAT SL is provided WITHOUT ANY WARRANTY; without even the
* implied warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. Real Time Engineers Ltd. and HCC Embedded disclaims all
* conditions and terms, be they implied, expressed, or statutory.
*
* http://www.FreeRTOS.org
* http://www.FreeRTOS.org/FreeRTOS-Plus
*
*/
#include "../../api/fat_sl.h"
#include "fat.h"
#include "util.h"
#include "volume.h"
#include "drv.h"
#include "../../version/ver_fat_sl.h"
#if VER_FAT_SL_MAJOR != 5 || VER_FAT_SL_MINOR != 2
#error Incompatible FAT_SL version number!
#endif
/****************************************************************************
*
* _f_writefatsector
*
* writing fat sector into volume, this function check if fat was modified
* and writes data
*
* RETURNS
*
* error code or zero if successful
*
***************************************************************************/
unsigned char _f_writefatsector ( void )
{
unsigned char a;
if ( gl_volume.modified )
{
unsigned long fatsector = gl_volume.firstfat.sector + gl_volume.fatsector;
if ( gl_volume.fatsector >= gl_volume.firstfat.num )
{
return F_ERR_INVALIDSECTOR;
}
for ( a = 0 ; a < gl_volume.bootrecord.number_of_FATs ; a++ )
{
unsigned char ret;
ret = _f_writeglsector( fatsector );
if ( ret )
{
return ret;
}
fatsector += gl_volume.firstfat.num;
}
gl_volume.modified = 0;
}
return F_NO_ERROR;
} /* _f_writefatsector */
/****************************************************************************
*
* _f_getfatsector
*
* read a fat sector from media
*
* INPUTS
*
* sector - which fat sector is needed, this sector number is zero based
*
* RETURNS
*
* error code or zero if successful
*
***************************************************************************/
unsigned char _f_getfatsector ( unsigned long sector )
{
unsigned char a;
if ( gl_volume.fatsector != sector )
{
unsigned long fatsector;
gl_volume.fatsector = sector;
if ( gl_volume.fatsector >= gl_volume.firstfat.num )
{
return F_ERR_INVALIDSECTOR;
}
fatsector = gl_volume.firstfat.sector + gl_volume.fatsector;
for ( a = 0 ; a < gl_volume.bootrecord.number_of_FATs ; a++ )
{
if ( !_f_readglsector( fatsector ) )
{
return F_NO_ERROR;
}
fatsector += gl_volume.firstfat.num;
}
return F_ERR_READ;
}
return F_NO_ERROR;
} /* _f_getfatsector */
/****************************************************************************
*
* _f_setclustervalue
*
* set a cluster value in the FAT
*
* INPUTS
*
* cluster - which cluster's value need to be modified
* data - new value of the cluster
*
* RETURNS
*
* error code or zero if successful
*
***************************************************************************/
unsigned char _f_setclustervalue ( unsigned long cluster, unsigned long _tdata )
{
unsigned char ret;
switch ( gl_volume.mediatype )
{
case F_FAT16_MEDIA:
{
unsigned long sector = cluster;
unsigned short s_data = (unsigned short)( _tdata & 0xffff ); /*keep 16 bit only*/
sector /= ( F_SECTOR_SIZE / 2 );
cluster -= sector * ( F_SECTOR_SIZE / 2 );
ret = _f_getfatsector( sector );
if ( ret )
{
return ret;
}
if ( _f_getword( &gl_sector[cluster << 1] ) != s_data )
{
_f_setword( &gl_sector[cluster << 1], s_data );
gl_volume.modified = 1;
}
}
break;
case F_FAT12_MEDIA:
{
unsigned char f12new[2];
unsigned long sector = cluster;
unsigned short pos;
unsigned short s_data = (unsigned short)( _tdata & 0x0fff ); /*keep 12 bit only*/
if ( cluster & 1 )
{
s_data <<= 4;
}
_f_setword( f12new, s_data ); /*create new data*/
sector += sector / 2; /*1.5 bytes*/
pos = (unsigned short)( sector % F_SECTOR_SIZE );
sector /= F_SECTOR_SIZE;
ret = _f_getfatsector( sector );
if ( ret )
{
return ret;
}
if ( cluster & 1 )
{
f12new[0] |= gl_sector[pos] & 0x0f;
}
if ( gl_sector[pos] != f12new[0] )
{
gl_sector[pos] = f12new[0];
gl_volume.modified = 1;
}
pos++;
if ( pos >= 512 )
{
ret = _f_getfatsector( sector + 1 );
if ( ret )
{
return ret;
}
pos = 0;
}
if ( !( cluster & 1 ) )
{
f12new[1] |= gl_sector[pos] & 0xf0;
}
if ( gl_sector[pos] != f12new[1] )
{
gl_sector[pos] = f12new[1];
gl_volume.modified = 1;
}
}
break;
case F_FAT32_MEDIA:
{
unsigned long sector = cluster;
unsigned long oldv;
sector /= ( F_SECTOR_SIZE / 4 );
cluster -= sector * ( F_SECTOR_SIZE / 4 );
ret = _f_getfatsector( sector );
if ( ret )
{
return ret;
}
oldv = _f_getlong( &gl_sector[cluster << 2] );
_tdata &= 0x0fffffff;
_tdata |= oldv & 0xf0000000; /*keep 4 top bits*/
if ( _tdata != oldv )
{
_f_setlong( &gl_sector[cluster << 2], _tdata );
gl_volume.modified = 1;
}
}
break;
default:
return F_ERR_INVALIDMEDIA;
} /* switch */
return F_NO_ERROR;
} /* _f_setclustervalue */
/****************************************************************************
*
* _f_getclustervalue
*
* get a cluster value from FAT
*
* INPUTS
*
* cluster - which cluster value is requested
* pvalue - where to store data
*
* RETURNS
*
* error code or zero if successful
*
***************************************************************************/
unsigned char _f_getclustervalue ( unsigned long cluster, unsigned long * pvalue )
{
unsigned long val;
unsigned char ret;
switch ( gl_volume.mediatype )
{
case F_FAT16_MEDIA:
{
unsigned long sector = cluster;
sector /= ( F_SECTOR_SIZE / 2 );
cluster -= sector * ( F_SECTOR_SIZE / 2 );
ret = _f_getfatsector( sector );
if ( ret )
{
return ret;
}
val = _f_getword( &gl_sector[cluster << 1] );
if ( val >= ( F_CLUSTER_RESERVED & 0xffff ) )
{
val |= 0x0ffff000; /*extends it*/
}
if ( pvalue )
{
*pvalue = val;
}
}
break;
case F_FAT12_MEDIA:
{
unsigned char dataf12[2];
unsigned long sector = cluster;
unsigned short pos;
sector += sector / 2; /*1.5 bytes*/
pos = (unsigned short)( sector % F_SECTOR_SIZE );
sector /= F_SECTOR_SIZE;
ret = _f_getfatsector( sector );
if ( ret )
{
return ret;
}
dataf12[0] = gl_sector[pos++];
if ( pos >= 512 )
{
ret = _f_getfatsector( sector + 1 );
if ( ret )
{
return ret;
}
pos = 0;
}
dataf12[1] = gl_sector[pos];
val = _f_getword( dataf12 );
if ( cluster & 1 )
{
val = val >> 4;
}
else
{
val = val & 0xfff;
}
if ( val >= ( F_CLUSTER_RESERVED & 0xfff ) )
{
val |= 0x0ffff000; /*extends it*/
}
if ( pvalue )
{
*pvalue = val;
}
}
break;
case F_FAT32_MEDIA:
{
unsigned long sector = cluster;
sector /= ( F_SECTOR_SIZE / 4 );
cluster -= sector * ( F_SECTOR_SIZE / 4 );
ret = _f_getfatsector( sector );
if ( ret )
{
return ret;
}
if ( pvalue )
{
*pvalue = _f_getlong( &gl_sector[cluster << 2] ) & 0x0fffffff; /*28bit*/
}
}
break;
default:
return F_ERR_INVALIDMEDIA;
} /* switch */
return F_NO_ERROR;
} /* _f_getclustervalue */
/****************************************************************************
*
* _f_clustertopos
*
* convert a cluster position into physical sector position
*
* INPUTS
*
* cluster - original cluster position
* pos - position structure to fills the position
*
***************************************************************************/
void _f_clustertopos ( unsigned long cluster, F_POS * pos )
{
pos->cluster = cluster;
if ( !cluster )
{
pos->sector = gl_volume.root.sector;
pos->sectorend = pos->sector + gl_volume.root.num;
}
else
{
unsigned long sectorcou = gl_volume.bootrecord.sector_per_cluster;
pos->sector = ( pos->cluster - 2 ) * sectorcou + gl_volume._tdata.sector;
pos->sectorend = pos->sector + sectorcou;
}
if ( cluster >= F_CLUSTER_RESERVED )
{
pos->sectorend = 0;
}
pos->pos = 0;
} /* _f_clustertopos */
/****************************************************************************
*
* _f_getcurrsector
*
* read current sector according in file structure
*
* INPUTS
* f - internal file pointer
*
* RETURNS
* error code or zero if successful
*
***************************************************************************/
unsigned char _f_getcurrsector ( void )
{
unsigned char ret;
unsigned long cluster;
if ( gl_file.pos.sector == gl_file.pos.sectorend )
{
gl_volume.fatsector = (unsigned long)-1;
ret = _f_getclustervalue( gl_file.pos.cluster, &cluster );
if ( ret )
{
return ret;
}
if ( cluster >= F_CLUSTER_RESERVED )
{
return F_ERR_EOF;
}
_f_clustertopos( cluster, &gl_file.pos );
}
return _f_readglsector( gl_file.pos.sector );
} /* _f_getcurrsector */
/****************************************************************************
*
* _f_alloccluster
*
* allocate cluster from FAT
*
* INPUTS
* pcluster - where to store the allocated cluster number
*
* RETURNS
*
* error code or zero if successful
*
***************************************************************************/
unsigned char _f_alloccluster ( unsigned long * pcluster )
{
unsigned long maxcluster = gl_volume.maxcluster;
unsigned long cou;
unsigned long cluster = gl_volume.lastalloccluster;
unsigned long value;
unsigned char ret;
for ( cou = 0 ; cou < maxcluster ; cou++ )
{
if ( cluster >= maxcluster )
{
cluster = 0;
}
ret = _f_getclustervalue( cluster, &value );
if ( ret )
{
return ret;
}
if ( !value )
{
gl_volume.lastalloccluster = cluster + 1; /*set next one*/
*pcluster = cluster;
return F_NO_ERROR;
}
cluster++;
}
return F_ERR_NOMOREENTRY;
} /* _f_alloccluster */
/****************************************************************************
*
* _f_removechain
*
* remove cluster chain from fat
*
* INPUTS
* cluster - first cluster in the cluster chain
*
* RETURNS
*
* error code or zero if successful
*
***************************************************************************/
unsigned char _f_removechain ( unsigned long cluster )
{
gl_volume.fatsector = (unsigned long)-1;
if ( cluster < gl_volume.lastalloccluster ) /*this could be the begining of alloc*/
{
gl_volume.lastalloccluster = cluster;
}
while ( cluster < F_CLUSTER_RESERVED && cluster >= 2 )
{
unsigned long nextcluster;
unsigned char ret = _f_getclustervalue( cluster, &nextcluster );
if ( ret )
{
return ret;
}
ret = _f_setclustervalue( cluster, F_CLUSTER_FREE );
if ( ret )
{
return ret;
}
cluster = nextcluster;
}
return _f_writefatsector();
} /* _f_removechain */