/* * 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 "../../psp/include/psp_string.h" #include "volume.h" #include "util.h" #include "drv.h" #include "fat.h" #include "dir.h" #include "file.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 #if F_FS_THREAD_AWARE == 1 #include "f_lock.h" #endif F_VOLUME gl_volume; /* only one volume */ F_FILE gl_file; /* file */ char gl_sector[F_SECTOR_SIZE]; /* actual sector */ #if F_FILE_CHANGED_EVENT F_FILE_CHANGED_EVENTFUNC f_filechangedevent; #endif /* Defines the number of sectors per cluster on a sector number basis */ typedef struct { unsigned long max_sectors; unsigned char sector_per_cluster; } t_FAT32_CS; static const t_FAT32_CS FAT32_CS[] = { { 0x00020000, 1 } /* ->64MB */ , { 0x00040000, 2 } /* ->128MB */ , { 0x00080000, 4 } /* ->256MB */ , { 0x01000000, 8 } /* ->8GB */ , { 0x02000000, 16 } /* ->16GB */ , { 0x0ffffff0, 32 } /* -> ... */ }; /**************************************************************************** * * _f_writebootrecord * * writing boot record onto a volume, it uses number of hidden sector variable * * INPUTS * phy - media physical descriptor * * RETURNS * error code or zero if successful * ***************************************************************************/ static unsigned char _f_writebootrecord ( F_PHY * phy ) { unsigned char jump_code[] = { 0xeb, 0x3c, 0x90 }; unsigned char oem_name[] = "MSDOS5.0"; unsigned char executable_marker[] = { 0x55, 0xaa }; unsigned char * ptr = (unsigned char *)gl_sector; unsigned char rs; unsigned short mre; unsigned char ret; unsigned char _n = 0; if ( gl_volume.mediatype == F_FAT32_MEDIA ) { /*write FS_INFO*/ unsigned char a; rs = 32 + 4; mre = 0; psp_memset( ptr, 0, F_SECTOR_SIZE ); for ( a = 0 ; a < rs ; a++ ) { ret = _f_writeglsector( a ); /*erase reserved area*/ if ( ret ) { return ret; } } ptr = _setlong( 0x41615252, ptr ); /*signature*/ ptr = _setcharzero( 480, ptr ); /*reserved*/ ptr = _setlong( 0x61417272, ptr ); /*signature*/ ptr = _setlong( 0xffffffff, ptr ); /*no last*/ ptr = _setlong( 0xffffffff, ptr ); /*no hint*/ ptr = _setcharzero( 12, ptr ); /*reserved*/ ptr = _setlong( 0xaa550000, ptr ); /*trail*/ ret = _f_writeglsector( 1 ); /*write FSINFO*/ if ( ret ) { return ret; } ret = _f_writeglsector( 1 + 6 ); /*write FSINFO*/ if ( ret ) { return ret; } } else { rs = 1; mre = 512; } ptr = (unsigned char *)gl_sector; ptr = _setchar( jump_code, sizeof( jump_code ), ptr ); ptr = _setchar( oem_name, sizeof( oem_name ) - 1, ptr ); ptr = _setword( F_SECTOR_SIZE, ptr ); *ptr++ = gl_volume.bootrecord.sector_per_cluster; ptr = _setword( rs, ptr ); /* reserved sectors */ *ptr++ = 2; /* number of FATs */ ptr = _setword( mre, ptr ); /* max root entry */ if ( phy->number_of_sectors < 0x10000 ) { ptr = _setword( (unsigned short)phy->number_of_sectors, ptr ); } else { ptr = _setword( 0, ptr ); } *ptr++ = 0xf0; /* media descriptor */ ptr = _setword( (unsigned short)gl_volume.bootrecord.sector_per_FAT, ptr ); ptr = _setword( phy->sector_per_track, ptr ); ptr = _setword( phy->number_of_heads, ptr ); ptr = _setlong( 0, ptr ); /* number of hidden sectors */ if ( phy->number_of_sectors >= 0x10000 ) { ptr = _setlong( phy->number_of_sectors, ptr ); } else { ptr = _setlong( 0, ptr ); /* number of sectors */ } if ( gl_volume.mediatype == F_FAT32_MEDIA ) { ptr = _setlong( gl_volume.bootrecord.sector_per_FAT32, ptr ); ptr = _setword( 0, ptr ); ptr = _setword( 0, ptr ); ptr = _setlong( 2, ptr ); ptr = _setword( 1, ptr ); ptr = _setword( 6, ptr ); ptr = _setchar( NULL, 12, ptr ); _n = 28; } ptr = _setword( 0, ptr ); /* logical drive num */ *ptr++ = 0x29; /* extended signature */ ptr = _setlong( 0x11223344, ptr ); ptr = _setchar( (const unsigned char *)"NO NAME ", 11, ptr ); /* volume name */ switch ( gl_volume.mediatype ) { case F_FAT12_MEDIA: ptr = _setchar( (const unsigned char *)"FAT12 ", 8, ptr ); break; case F_FAT16_MEDIA: ptr = _setchar( (const unsigned char *)"FAT16 ", 8, ptr ); break; case F_FAT32_MEDIA: ptr = _setchar( (const unsigned char *)"FAT32 ", 8, ptr ); break; default: return F_ERR_INVALIDMEDIA; } /* switch */ ptr = _setchar( 0, 448 - _n, ptr ); ptr = _setchar( executable_marker, sizeof( executable_marker ), ptr ); if ( _n ) { ret = _f_writeglsector( 6 ); if ( ret ) { return ret; } } return _f_writeglsector( 0 ); /*write bootrecord*/ } /* _f_writebootrecord */ /**************************************************************************** * * _f_buildsectors * * INPUTS * phy - media physical descriptor * * calculate relative sector position from boot record * ***************************************************************************/ static unsigned char _f_buildsectors ( F_PHY * phy ) { gl_volume.mediatype = F_UNKNOWN_MEDIA; if ( gl_volume.bootrecord.sector_per_FAT ) { gl_volume.firstfat.sector = 1; gl_volume.firstfat.num = gl_volume.bootrecord.sector_per_FAT; gl_volume.root.sector = gl_volume.firstfat.sector + ( gl_volume.firstfat.num * (unsigned long)( gl_volume.bootrecord.number_of_FATs ) ); gl_volume.root.num = ( 512 * sizeof( F_DIRENTRY ) ) / F_SECTOR_SIZE; gl_volume._tdata.sector = gl_volume.root.sector + gl_volume.root.num; gl_volume._tdata.num = 0; /*??*/ } else { gl_volume.firstfat.sector = ( 32 + 4 ); gl_volume.firstfat.num = gl_volume.bootrecord.sector_per_FAT32; gl_volume._tdata.sector = gl_volume.firstfat.sector; gl_volume._tdata.sector += gl_volume.firstfat.num * (unsigned long)( gl_volume.bootrecord.number_of_FATs ); gl_volume._tdata.num = 0; /*??*/ { unsigned long sectorcou = gl_volume.bootrecord.sector_per_cluster; gl_volume.root.sector = ( ( gl_volume.bootrecord.rootcluster - 2 ) * sectorcou ) + gl_volume._tdata.sector; gl_volume.root.num = gl_volume.bootrecord.sector_per_cluster; } } { unsigned long maxcluster; maxcluster = phy->number_of_sectors; maxcluster -= gl_volume._tdata.sector; maxcluster /= gl_volume.bootrecord.sector_per_cluster; gl_volume.maxcluster = maxcluster; } if ( gl_volume.maxcluster < ( F_CLUSTER_RESERVED & 0xfff ) ) { gl_volume.mediatype = F_FAT12_MEDIA; } else if ( gl_volume.maxcluster < ( F_CLUSTER_RESERVED & 0xffff ) ) { gl_volume.mediatype = F_FAT16_MEDIA; } else { gl_volume.mediatype = F_FAT32_MEDIA; } return F_NO_ERROR; } /* _f_buildsectors */ /**************************************************************************** * * _f_prepareformat * * preparing boot record for formatting, it sets and calculates values * * INPUTS * phy - media physical descriptor * f_bootrecord - which bootrecord need to be prepare * number_of_hidden_sectors - where boot record starts * fattype - one of this definitions F_FAT12_MEDIA,F_FAT16_MEDIA,F_FAT32_MEDIA * * RETURNS * error code or zero if successful * ***************************************************************************/ static unsigned char _f_prepareformat ( F_PHY * phy, unsigned char fattype ) { if ( !phy->number_of_sectors ) { return F_ERR_INVALIDSECTOR; } gl_volume.bootrecord.number_of_FATs = 2; gl_volume.bootrecord.media_descriptor = 0xf0; if ( fattype != F_FAT32_MEDIA ) { unsigned long _n; switch ( fattype ) { case F_FAT12_MEDIA: _n = F_CLUSTER_RESERVED & 0xfff; break; case F_FAT16_MEDIA: _n = F_CLUSTER_RESERVED & 0xffff; break; default: return F_ERR_INVFATTYPE; } gl_volume.bootrecord.sector_per_cluster = 1; while ( gl_volume.bootrecord.sector_per_cluster ) { if ( phy->number_of_sectors / gl_volume.bootrecord.sector_per_cluster < _n ) { break; } gl_volume.bootrecord.sector_per_cluster <<= 1; } if ( !gl_volume.bootrecord.sector_per_cluster ) { return F_ERR_MEDIATOOLARGE; } } else { unsigned char i; for ( i = 0 ; i<( sizeof( FAT32_CS ) / sizeof( t_FAT32_CS ) ) - 1 && phy->number_of_sectors>FAT32_CS[i].max_sectors ; i++ ) { } gl_volume.bootrecord.sector_per_cluster = FAT32_CS[i].sector_per_cluster; } if ( !gl_volume.bootrecord.sector_per_cluster ) { return F_ERR_INVALIDMEDIA; /*fat16 cannot be there*/ } { long secpercl = gl_volume.bootrecord.sector_per_cluster; long nfat = gl_volume.bootrecord.number_of_FATs; unsigned long roots; unsigned long fatsec; roots = ( 512 * sizeof( F_DIRENTRY ) ) / F_SECTOR_SIZE; switch ( fattype ) { case F_FAT32_MEDIA: { unsigned long _n = (unsigned long)( 128 * secpercl + nfat ); fatsec = ( phy->number_of_sectors - ( 32 + 4 ) + 2 * secpercl ); fatsec += ( _n - 1 ); fatsec /= _n; gl_volume.bootrecord.sector_per_FAT32 = fatsec; gl_volume.bootrecord.sector_per_FAT = 0; } break; case F_FAT16_MEDIA: { unsigned long _n = (unsigned long)( 256 * secpercl + nfat ); fatsec = ( phy->number_of_sectors - 1 - roots + 2 * secpercl ); fatsec += ( _n - 1 ); fatsec /= _n; gl_volume.bootrecord.sector_per_FAT = (unsigned short)( fatsec ); } break; case F_FAT12_MEDIA: { unsigned long _n = (unsigned long)( 1024 * secpercl + 3 * nfat ); fatsec = ( phy->number_of_sectors - 1 - roots + 2 * secpercl ); fatsec *= 3; fatsec += ( _n - 1 ); fatsec /= _n; gl_volume.bootrecord.sector_per_FAT = (unsigned short)( fatsec ); } break; default: return F_ERR_INVALIDMEDIA; } /* switch */ return F_NO_ERROR; } } /* _f_prepareformat */ /**************************************************************************** * * _f_postformat * * erase fats, erase root directory, reset variables after formatting * * INPUTS * phy - media physical descriptor * fattype - one of this definitions F_FAT12_MEDIA,F_FAT16_MEDIA,F_FAT32_MEDIA * * RETURNS * error code or zero if successful * ***************************************************************************/ static unsigned char _f_postformat ( F_PHY * phy, unsigned char fattype ) { unsigned long a; unsigned char ret; _f_buildsectors( phy ); /*get positions*/ if ( gl_volume.mediatype != fattype ) { return F_ERR_MEDIATOOSMALL; } gl_volume.fatsector = (unsigned long)( -1 ); { unsigned char * ptr = (unsigned char *)gl_sector; unsigned char j = 2; unsigned long i; psp_memset( ptr, 0, F_SECTOR_SIZE ); switch ( gl_volume.mediatype ) { case F_FAT16_MEDIA: j = 3; break; case F_FAT32_MEDIA: j = 11; break; } *ptr = gl_volume.bootrecord.media_descriptor; psp_memset( ptr + 1, 0xff, j ); if ( gl_volume.mediatype == F_FAT32_MEDIA ) { *( ptr + 8 ) = (unsigned char)( F_CLUSTER_LAST & 0xff ); } (void)_f_writeglsector( gl_volume.firstfat.sector ); (void)_f_writeglsector( gl_volume.firstfat.sector + gl_volume.firstfat.num ); psp_memset( ptr, 0, ( j + 1 ) ); for ( i = 1 ; i < gl_volume.firstfat.num ; i++ ) { (void)_f_writeglsector( gl_volume.firstfat.sector + i ); (void)_f_writeglsector( gl_volume.firstfat.sector + i + gl_volume.firstfat.num ); } } for ( a = 0 ; a < gl_volume.root.num ; a++ ) /*reset root direntries*/ { ret = _f_writeglsector( gl_volume.root.sector + a ); if ( ret ) { return ret; } } return _f_writebootrecord( phy ); } /* _f_postformat */ /**************************************************************************** * * fn_hardformat * * Making a complete format on media, independently from master boot record, * according to media physical * * INPUTS * fattype - one of this definitions F_FAT12_MEDIA,F_FAT16_MEDIA,F_FAT32_MEDIA * * RETURNS * error code or zero if successful * ***************************************************************************/ unsigned char fn_hardformat ( unsigned char fattype ) { unsigned char ret; int mdrv_ret; F_PHY phy; ret = _f_getvolume(); if ( ret && ( ret != F_ERR_NOTFORMATTED ) ) { return ret; } gl_volume.state = F_STATE_NEEDMOUNT; psp_memset( &phy, 0, sizeof( F_PHY ) ); mdrv_ret = mdrv->getphy( mdrv, &phy ); if ( mdrv_ret ) { return F_ERR_ONDRIVE; } ret = _f_prepareformat( &phy, fattype ); /*no partition*/ if ( ret ) { return ret; } return _f_postformat( &phy, fattype ); } /* fn_hardformat */ /**************************************************************************** * * _f_readbootrecord * * read boot record from a volume, it detects if there is MBR on the media * * RETURNS * * error code or zero if successful * ***************************************************************************/ static unsigned char _f_readbootrecord ( void ) { unsigned char ret; unsigned char * ptr = (unsigned char *)gl_sector; unsigned long maxcluster, _n; unsigned long first_sector = 0; gl_volume.mediatype = F_UNKNOWN_MEDIA; ret = _f_readglsector( 0 ); if ( ret ) { return ret; } if ( ( ptr[0x1fe] != 0x55 ) || ( ptr[0x1ff] != 0xaa ) ) { return F_ERR_NOTFORMATTED; /*??*/ } if ( ( ptr[0] != 0xeb ) && ( ptr[0] != 0xe9 ) ) { first_sector = _f_getlong( &ptr[0x08 + 0x1be] ); /*start sector for 1st partioon*/ ret = _f_readglsector( first_sector ); if ( ret ) { return ret; } if ( ( ptr[0x1fe] != 0x55 ) || ( ptr[0x1ff] != 0xaa ) ) { return F_ERR_NOTFORMATTED; /*??*/ } if ( ( ptr[0] != 0xeb ) && ( ptr[0] != 0xe9 ) ) { return F_ERR_NOTFORMATTED; /*??*/ } } ptr += 11; if ( _f_getword( ptr ) != F_SECTOR_SIZE ) { return F_ERR_NOTSUPPSECTORSIZE; } ptr += 2; gl_volume.bootrecord.sector_per_cluster = *ptr++; gl_volume.firstfat.sector = _f_getword( ptr ); ptr += 2; gl_volume.bootrecord.number_of_FATs = *ptr++; gl_volume.root.num = _f_getword( ptr ); ptr += 2; gl_volume.root.num *= sizeof( F_DIRENTRY ); gl_volume.root.num /= F_SECTOR_SIZE; maxcluster = _f_getword( ptr ); ptr += 2; gl_volume.bootrecord.media_descriptor = *ptr++; gl_volume.firstfat.num = _f_getword( ptr ); ptr += 6; _n = _f_getlong( ptr ); ptr += 4; if ( _n < first_sector ) { _n = first_sector; } gl_volume.firstfat.sector += _n; if ( !maxcluster ) { maxcluster = _f_getlong( ptr ); } ptr += 4; if ( gl_volume.firstfat.num ) { gl_volume.root.sector = gl_volume.firstfat.sector + ( gl_volume.firstfat.num * gl_volume.bootrecord.number_of_FATs ); gl_volume._tdata.sector = gl_volume.root.sector + gl_volume.root.num; gl_volume._tdata.num = 0; ptr += 3; } else { gl_volume.firstfat.num = _f_getlong( ptr ); ptr += 8; gl_volume._tdata.sector = gl_volume.firstfat.sector; gl_volume._tdata.sector += gl_volume.firstfat.num * gl_volume.bootrecord.number_of_FATs; gl_volume._tdata.num = 0; gl_volume.bootrecord.rootcluster = _f_getlong( ptr ); ptr += 23; gl_volume.root.num = gl_volume.bootrecord.sector_per_cluster; gl_volume.root.sector = ( ( gl_volume.bootrecord.rootcluster - 2 ) * gl_volume.root.num ) + gl_volume._tdata.sector; } gl_volume.bootrecord.serial_number = _f_getlong( ptr ); maxcluster -= gl_volume._tdata.sector; maxcluster += _n; gl_volume.maxcluster = maxcluster / gl_volume.bootrecord.sector_per_cluster; if ( gl_volume.maxcluster < ( F_CLUSTER_RESERVED & 0xfff ) ) { gl_volume.mediatype = F_FAT12_MEDIA; } else if ( gl_volume.maxcluster < ( F_CLUSTER_RESERVED & 0xffff ) ) { gl_volume.mediatype = F_FAT16_MEDIA; } else { gl_volume.mediatype = F_FAT32_MEDIA; } if ( gl_volume.bootrecord.media_descriptor != 0xf8 ) /*fixdrive*/ { if ( gl_volume.bootrecord.media_descriptor != 0xf0 ) /*removable*/ { return F_ERR_NOTFORMATTED; /*??*/ } } return F_NO_ERROR; } /* _f_readbootrecord */ /**************************************************************************** * * _f_getvolume * * getting back a volume info structure of a given drive, it try to mounts * drive if it was not mounted before * * RETURNS * * error code or zero if successful * ***************************************************************************/ unsigned char _f_getvolume ( void ) { switch ( gl_volume.state ) { case F_STATE_NONE: return F_ERR_ONDRIVE; case F_STATE_WORKING: if ( !_f_checkstatus() ) { return F_NO_ERROR; } /* here we don't stop case flow, */ /* because we have to clean up this volume! */ case F_STATE_NEEDMOUNT: { gl_file.modified = 0; gl_volume.modified = 0; gl_volume.lastalloccluster = 0; gl_volume.actsector = (unsigned long)( -1 ); gl_volume.fatsector = (unsigned long)( -1 ); gl_file.mode = F_FILE_CLOSE; gl_volume.cwd[0] = 0; /*reset cwd*/ gl_volume.mediatype = F_UNKNOWN_MEDIA; if ( mdrv->getstatus != NULL ) { if ( mdrv->getstatus( mdrv ) & F_ST_MISSING ) { gl_volume.state = F_STATE_NEEDMOUNT; /*card missing*/ return F_ERR_CARDREMOVED; } } if ( !_f_readbootrecord() ) { gl_volume.state = F_STATE_WORKING; return F_NO_ERROR; } gl_volume.mediatype = F_UNKNOWN_MEDIA; return F_ERR_NOTFORMATTED; } } /* switch */ return F_ERR_ONDRIVE; } /* _f_getvolume */ /**************************************************************************** * * fn_getfreespace * * get total/free/used/bad diskspace * * INPUTS * pspace - pointer where to store the information * * RETURNS * error code * ***************************************************************************/ unsigned char fn_getfreespace ( F_SPACE * pspace ) { unsigned char ret; unsigned long a; unsigned long clustersize; ret = _f_getvolume(); if ( ret ) { return ret; } psp_memset( pspace, 0, sizeof( F_SPACE ) ); pspace->total = gl_volume.maxcluster; gl_volume.fatsector = (unsigned long)-1; for ( a = 2 ; a < gl_volume.maxcluster + 2 ; a++ ) { unsigned long value; ret = _f_getclustervalue( a, &value ); if ( ret ) { return ret; } if ( !value ) { ++( pspace->free ); } else if ( value == F_CLUSTER_BAD ) { ++( pspace->bad ); } else { ++( pspace->used ); } } clustersize = (unsigned long)( gl_volume.bootrecord.sector_per_cluster * F_SECTOR_SIZE ); for ( a = 0 ; ( clustersize & 1 ) == 0 ; a++ ) { clustersize >>= 1; } pspace->total_high = ( pspace->total ) >> ( 32 - a ); pspace->total <<= a; pspace->free_high = ( pspace->free ) >> ( 32 - a ); pspace->free <<= a; pspace->used_high = ( pspace->used ) >> ( 32 - a ); pspace->used <<= a; pspace->bad_high = ( pspace->bad ) >> ( 32 - a ); pspace->bad <<= a; return F_NO_ERROR; } /* fn_getfreespace */ /**************************************************************************** * * fn_getserial * * get serial number * * INPUTS * serial - pointer where to store the serial number * * RETURNS * error code * ***************************************************************************/ unsigned char fn_getserial ( unsigned long * serial ) { unsigned char ret; ret = _f_getvolume(); if ( ret ) { return ret; } *serial = gl_volume.bootrecord.serial_number; return 0; } /* ** fs_init ** ** Initialize STHIN file system ** ** RETURN: F_NO_ERROR on success, other if error. */ unsigned char fs_init ( void ) { unsigned char rc = F_NO_ERROR; #if RTOS_SUPPORT rc = fsr_init(); if ( rc ) { return rc; } #endif return rc; } /* fs_init */ /* ** fs_delete ** ** Delete STHIN file system ** ** RETURN: F_NO_ERROR on success, other if error. */ unsigned char fs_delete ( void ) { unsigned char rc = F_NO_ERROR; #if RTOS_SUPPORT rc = fsr_delete(); if ( rc ) { return rc; } #endif return rc; } /* fs_delete */ /**************************************************************************** * * fn_initvolume * * initiate a volume, this function has to be called 1st to set physical * driver function to a given volume * * RETURNS * * error code or zero if successful * ***************************************************************************/ unsigned char fn_initvolume ( F_DRIVERINIT initfunc ) { #if F_FS_THREAD_AWARE == 1 { extern xSemaphoreHandle fs_lock_semaphore; if( fs_lock_semaphore == NULL ) { fs_lock_semaphore = xSemaphoreCreateMutex(); if( fs_lock_semaphore == NULL ) { return F_ERR_OS; } } } #endif /* F_FS_THREAD_AWARE */ gl_volume.state = F_STATE_NONE; mdrv = initfunc( 0 ); if ( mdrv == NULL ) { return F_ERR_INITFUNC; } gl_volume.state = F_STATE_NEEDMOUNT; #if F_FILE_CHANGED_EVENT f_filechangedevent = 0; #endif return _f_getvolume(); } /* fn_initvolume */ /**************************************************************************** * * fn_delvolume * ***************************************************************************/ unsigned char fn_delvolume ( void ) { if ( mdrv->release ) { (void)mdrv->release( mdrv ); } return 0; }