| /* | |
| * 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 */ | |