/* * fatfs.c - FATFS file system API implementation. * * Author Alain Greiner (2016,2017,2018,2019,2020) * * Copyright (c) UPMC Sorbonne Universites * * This file is part of ALMOS-MKH. * * ALMOS-MKH is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2.0 of the License. * * ALMOS-MKH is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ALMOS-MKH; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define LITTLE_ENDIAN 1 ////////////////////////////////////////////////////////////////////////////////////////// // Extern variables ////////////////////////////////////////////////////////////////////////////////////////// extern vfs_ctx_t fs_context[FS_TYPES_NR]; // allocated in kernel_init.c file ////////////////////////////////////////////////////////////////////////////////////////// // FATFS specific static functions ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// // These functions return the "offset" and "length" values of an // [offset,length] constant defined in the fatfs.h file. ////////////////////////////////////////////////////////////////////////////////////////// static inline int get_length( int offset __attribute__((unused)), int length ) { return length; } static inline int get_offset( int offset, int length __attribute__((unused)) ) { return offset; } ////////////////////////////////////////////////////////////////////////////////////////// // This static function returns the LBA of the first sector of a FAT cluster. // This function can be called by any thread running in any cluster. ////////////////////////////////////////////////////////////////////////////////////////// // @ ctx : pointer on FATFS context. // @ cluster : cluster index in FATFS. // @ return the lba value. ////////////////////////////////////////////////////////////////////////////////////////// static inline uint32_t fatfs_lba_from_cluster( fatfs_ctx_t * ctx, uint32_t cluster ) { return (ctx->cluster_begin_lba + ((cluster - 2) << 3)); } ////////////////////////////////////////////////////////////////////////////////////////// // This function return an integer record value (one, two, or four bytes) from a local // array of bytes, taking into account the global LITTLE_ENDIAN parameter: // if LITTLE_ENDIAN is true, the most significant byte has the highest address. ////////////////////////////////////////////////////////////////////////////////////////// // @ offset : first byte in array. // @ nbytes : record length in bytes (1/2/4). // @ buffer : local pointer on byte array. // @ return the integer value in a 32 bits word. ////////////////////////////////////////////////////////////////////////////////////////// static uint32_t fatfs_get_record( uint32_t offset, uint32_t nbytes, uint8_t * buffer ) { uint32_t i; uint32_t res = 0; if ( LITTLE_ENDIAN ) { for( i = nbytes ; i > 0 ; i-- ) res = (res<<8) | buffer[offset+i-1]; } else { for( i = 0 ; i < nbytes ; i++ ) res = (res<<8) | buffer[offset+i]; } return res; } // end fatfs_get_record() ////////////////////////////////////////////////////////////////////////////////////////// // This function return an integer record value (one, two, or four bytes) from a remote // array of bytes, taking into account the global LITTLE_ENDIAN parameter: // if LITTLE_ENDIAN is true, the most significant byte has the highest address. ////////////////////////////////////////////////////////////////////////////////////////// // @ offset : first byte in array. // @ nbytes : record length in bytes (1/2/4). // @ buffer_xp : extended pointer on byte array. // @ return the integer value in a 32 bits word. ////////////////////////////////////////////////////////////////////////////////////////// static uint32_t fatfs_get_remote_record( uint32_t offset, uint32_t nbytes, xptr_t buffer_xp ) { uint32_t i; uint32_t res = 0; if ( LITTLE_ENDIAN ) { for( i = nbytes ; i > 0 ; i-- ) { res = (res<<8) | hal_remote_lb( buffer_xp + offset + i-1 ); } } else { for( i = 0 ; i < nbytes ; i++ ) { res = (res<<8) | hal_remote_lb( buffer_xp + offset + i ); } } return res; } // end fatfs_get_remote_record() /* ////////////////////////////////////////////////////////////////////////////////////////// // This function writes one, two, or four bytes from a 32 bits integer to a local // array of bytes, taking into account the global LITTLE_ENDIAN parameter: // if LITTLE_ENDIAN is true, the most significant byte has the highest address. ////////////////////////////////////////////////////////////////////////////////////////// // @ offset : first byte in array. // @ nbytes : record length in bytes (1/2/4). // @ buffer : local pointer on byte array. // @ value : 32 bits integer value. ////////////////////////////////////////////////////////////////////////////////////////// static void fatfs_set_record( uint32_t offset, uint32_t nbytes, uint8_t * buffer, uint32_t value ) { uint32_t i; if ( LITTLE_ENDIAN ) { for( i = nbytes ; i > 0 ; i-- ) buffer[offset+i-1] = (uint8_t)(value>>((i-1)<<3)); } else { for( i = 0 ; i < nbytes ; i++ ) buffer[offset+i] = (uint8_t)(value>>((nbytes-1-i)<<3)); } } // end fatfs_set_record() */ ////////////////////////////////////////////////////////////////////////////////////////// // This function writes one, two, or four bytes from a 32 bits integer to a remote // array of bytes, taking into account the global LITTLE_ENDIAN parameter: // if LITTLE_ENDIAN is true, the most significant byte has the highest address. ////////////////////////////////////////////////////////////////////////////////////////// // @ offset : first byte in array. // @ nbytes : record length in bytes (1/2/4). // @ buffer_xp : extended pointer on byte array. // @ value : 32 bits integer value. ////////////////////////////////////////////////////////////////////////////////////////// static void fatfs_set_remote_record( uint32_t offset, uint32_t nbytes, xptr_t buffer_xp, uint32_t value ) { uint32_t i; if ( LITTLE_ENDIAN ) { for( i = nbytes ; i > 0 ; i-- ) { hal_remote_sb( (buffer_xp + offset + i-1 ) , (uint8_t)(value>>((i-1)<<3)) ); } } else { for( i = 0 ; i < nbytes ; i++ ) { hal_remote_sb( (buffer_xp + offset + i) , (uint8_t)(value>>((nbytes-1-i)<<3)) ); } } } // end fatfs_set_record() ////////////////////////////////////////////////////////////////////////////////////////// // This static function retun in the buffer a short name stored in // a SFN FATFS directory entry. /////////////////////////i//////////////////////////////////////////////////////////////// // @ buffer : pointer on buffer containing the directory entry. // @ name : [out] buffer allocated by the caller. ////////////////////////////////////////////////////////////////////////////////////////// static void fatfs_get_name_from_short( uint8_t * buffer, char * name ) { uint32_t i; uint32_t j = 0; // get name for ( i = 0; i < 8 && buffer[i] != ' '; i++ ) { name[j] = to_lower( buffer[i] ); j++; } // get extension for ( i = 8; i < 8 + 3 && buffer[i] != ' '; i++ ) { // we entered the loop so there is an extension. add the dot if ( i == 8 ) { name[j] = '.'; j++; } name[j] = to_lower( buffer[i] ); j++; } name[j] = '\0'; } // fatfs_get_name_from_short() ////////////////////////////////////////////////////////////////////////////////////////// // This static function retun in the buffer a partial name stored in // a LFN FATFS directory entry. /////////////////////////i//////////////////////////////////////////////////////////////// // @ buffer : pointer on buffer containing the directory entry. // @ name : [out] buffer allocated by the caller. ////////////////////////////////////////////////////////////////////////////////////////// static void fatfs_get_name_from_long( uint8_t * buffer, char * name ) { uint32_t name_offset = 0; uint32_t buffer_offset = get_length(LDIR_ORD); uint32_t l_name_1 = get_length(LDIR_NAME_1); uint32_t l_name_2 = get_length(LDIR_NAME_2); uint32_t l_name_3 = get_length(LDIR_NAME_3); uint32_t l_attr = get_length(LDIR_ATTR); uint32_t l_type = get_length(LDIR_TYPE); uint32_t l_chksum = get_length(LDIR_CHKSUM); uint32_t l_rsvd = get_length(LDIR_RSVD); uint32_t j = 0; uint32_t eof = 0; while ( (buffer_offset != DIR_ENTRY_SIZE) && (!eof) ) { while (j != l_name_1 && !eof ) { if ( (buffer[buffer_offset] == 0x00) || (buffer[buffer_offset] == 0xFF) ) { eof = 1; continue; } name[name_offset] = buffer[buffer_offset]; buffer_offset += 2; j += 2; name_offset++; } buffer_offset += (l_attr + l_type + l_chksum); j = 0; while (j != l_name_2 && !eof ) { if ( (buffer[buffer_offset] == 0x00) || (buffer[buffer_offset] == 0xFF) ) { eof = 1; continue; } name[name_offset] = buffer[buffer_offset]; buffer_offset += 2; j += 2; name_offset++; } buffer_offset += l_rsvd; j = 0; while (j != l_name_3 && !eof ) { if ( (buffer[buffer_offset] == 0x00) || (buffer[buffer_offset] == 0xFF) ) { eof = 1; continue; } name[name_offset] = buffer[buffer_offset]; buffer_offset += 2; j += 2; name_offset++; } } name[name_offset] = 0; } // end fatfs_get_name_from_long() ////////////////////////////////////////////////////////////////////////////////////////// // This static function analyse the input argument, and returns in other // output arguments various informations required to store the name in FATFS directory. // The length cannot be larger than 31 characters : // - Short name (less than 13 characters) require 1 LFN entry. // - Medium names (from 14 to 26 characters require 2 LFN entries. // - Large names (up to 31 characters) require 3 LFN entries. ////////////////////////////////////////////////////////////////////////////////////////// // @ name : [in] complete directory entry name. // @ length : [out] total number of characters in name. // @ nb_lfn : [out] number of LFN entries required to store the name. // @ sfn : [out] a legal SFN name extracted from name / upper case and 8-3 format. // @ checksum : [out] checksum to be stored in SFN. // @ returns 0 on success / returns 1 if the name length is larger than 31 characters. ////////////////////////////////////////////////////////////////////////////////////////// static error_t fatfs_name_format( const char * name, uint32_t * length, uint32_t * nb_lfn, char * sfn, uint8_t * checksum ) { // compute name length uint32_t name_length = strlen( name ); *length = name_length; uint32_t suffix_length = 0; uint32_t prefix_length = 0; uint32_t dot_found = 0; uint32_t i; // compute prefix and suffix length // only the last '.' is taken into account for ( i=0 ; i>1)) + sfn[i]; } *checksum = sum; // set nb_lfn and length values if ( name_length <= 13 ) { *nb_lfn = 1; return 0; } else if ( name_length <= 26 ) { *nb_lfn = 2; return 0; } else if ( name_length <= 31 ) { *nb_lfn = 3; return 0; } else { return 1; } } // end fatfs_name_format() ////////////////////////////////////////////////////////////////////////////////////////// // This static function synchronously updates the FAT on IOC device. // It scan the FAT mapper to copy on IOC device all dirty pages in the interval // defined by the & arguments. // It can be called by a thread running in any cluster. // WARNING : We don't take the lock protecting the FAT mapper, because the FAT lock // (in FATFS context) must be taken by the calling function. ////////////////////////////////////////////////////////////////////////////////////////// // @ fatfs_ctx_xp : extended pointer on FATFS context in FAT cluster. // @ page_min : first page to be checked. // @ page_max : last page to be checked // @ return 0 if success, return -1 if the FS_INFO sector cannot be updated. ////////////////////////////////////////////////////////////////////////////////////////// static error_t fatfs_update_ioc_fat( xptr_t fatfs_ctx_xp, uint32_t page_min, uint32_t page_max ) { #if DEBUG_FATFS_UPDATE_IOC uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; if( DEBUG_FATFS_UPDATE_IOC < cycle ) printk("\n[%s] thread[%x,%x] enter / page_min %d / page_max %d / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, page_min, page_max, cycle ); #endif error_t error; cxy_t fat_cxy; // FAT cluster identifier fatfs_ctx_t * fatfs_ctx; // local pointer on FATFS context in FAT cluster mapper_t * fat_mapper_ptr; // local pointer on FAT mapper uint32_t page_id; // current page index in FAT mapper xptr_t rt_xp; // extended pointer on FAT mapper radix tree xptr_t page_xp; // extended pointer on current page in FAT mapper page_t * page_ptr; // local pointer on current page uint32_t flags; // current page flags // get pointer and cluster on FATFS context in FAT cluster fat_cxy = GET_CXY( fatfs_ctx_xp ); fatfs_ctx = GET_PTR( fatfs_ctx_xp ); // get FAT mapper pointer from FATFS context fat_mapper_ptr = hal_remote_lpt( XPTR( fat_cxy , &fatfs_ctx->fat_mapper ) ); // build extended pointer on FAT mapper radix tree rt_xp = XPTR( fat_cxy , &fat_mapper_ptr->rt ); // scan all pages in [min,max] interval for( page_id = page_min ; page_id <= page_max ; page_id++ ) { // get extended pointer on page descriptor from FAT mapper page_xp = grdxt_remote_lookup( rt_xp , page_id ); // check only existing pages if ( page_xp != XPTR_NULL ) { page_ptr = GET_PTR( page_xp ); flags = hal_remote_l32( XPTR( fat_cxy , &page_ptr->flags ) ); // copy only dirty pages if ( flags & PG_DIRTY ) { #if (DEBUG_FATFS_UPDATE_IOC & 1) if( DEBUG_FATFS_UPDATE_IOC < cycle ) printk("\n[%s] thread[%x,%x] copy page %d from FAT mapper to IOC device\n", __FUNCTION__, page_id ); #endif // move page from mapper to device error = fatfs_move_page( page_xp , IOC_SYNC_WRITE ); if ( error ) return -1; // reset page dirty flag ppm_page_undo_dirty( page_xp ); } } } // end loop on pages #if DEBUG_FATFS_UPDATE_IOC cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_UPDATE_IOC < cycle ) printk("\n[%s] thread[%x,%x] exit / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, cycle ); #endif return 0; } // end fatfs_update_ioc_fat() ////////////////////////////////////////////////////////////////////////////////////////// // This static function synchronously updates the FS_INFO sector on IOC device, // from values contained in the FATFS context in FAT cluster. // It uses and updates the FS_INFO buffer allocated in the FAT cluster. // It can be called by a thread running in any cluster. ////////////////////////////////////////////////////////////////////////////////////////// // @ fatfs_ctx_xp : extended pointer on FATFS context in FAT cluster. // @ return 0 if success, return -1 if the FS_INFO sector cannot be updated. ////////////////////////////////////////////////////////////////////////////////////////// static error_t fatfs_update_ioc_fsinfo( xptr_t fatfs_ctx_xp ) { cxy_t fat_cxy; // FAT cluster identifier fatfs_ctx_t * fatfs_ctx_ptr; // local pointer on fatfs context in FAT cluster uint32_t free_clusters; // current vale of "free_clusters" in fatfs context uint32_t free_cluster_hint; // current vale of "free_cluster_hint" in fatfs context uint8_t * fs_info_buffer_ptr; // local pointer on FS_INFO buffer in FAT cluster xptr_t fs_info_buffer_xp; // extended pointer on FS_INFO buffer in FAT cluster uint32_t fs_info_lba; // FS_INFO sector lba on IOC device // get cluster and local pointer on FAT cluster context fat_cxy = GET_CXY( fatfs_ctx_xp ); fatfs_ctx_ptr = GET_PTR( fatfs_ctx_xp ); // force FATFS context update hal_fence(); // get relevant info from fatfs context in FAT cluster fs_info_lba = hal_remote_l32( XPTR( fat_cxy , &fatfs_ctx_ptr->fs_info_lba ) ); free_clusters = hal_remote_l32( XPTR( fat_cxy , &fatfs_ctx_ptr->free_clusters ) ); free_cluster_hint = hal_remote_l32( XPTR( fat_cxy , &fatfs_ctx_ptr->free_cluster_hint ) ); fs_info_buffer_ptr = hal_remote_lpt( XPTR( fat_cxy , &fatfs_ctx_ptr->fs_info_buffer ) ); // build extended pointer on FS_INFO buffer in FAT cluster fs_info_buffer_xp = XPTR( fat_cxy , fs_info_buffer_ptr ); // update the FS_INFO buffer in FAT cluster fatfs_set_remote_record( FS_FREE_CLUSTERS , fs_info_buffer_xp , free_clusters ); fatfs_set_remote_record( FS_FREE_CLUSTER_HINT , fs_info_buffer_xp , free_cluster_hint ); // update the FS_INFO sector on IOC device return dev_ioc_sync_write( fs_info_buffer_xp , fs_info_lba , 1 ); } // end fatfs_update_ioc_fsinfo() ////////////////////////////////////////////////////////////////////////////////////////// // This static function decrements the "free_clusters" variable, and updates the // "free_cluster_hint" variable in the FATFS context in FAT cluster, when a new // has been allocated from the FAT. // It synchronously updates the FS_INFO sector on the IOC device. // The FATFS context in FAT cluster is identified by the argument. // It can be called by a thead running in any cluster. // It scan all slots in the FAT mapper seen as an array of 32 bits words, looking for the // first free slot larger than the . // The lock protecting exclusive access to the FAT must be taken by the calling function. ////////////////////////////////////////////////////////////////////////////////////////// // @ fatfs_ctx_xp : extended pointer on FATFS context in FAT cluster. // @ cluster_id : recently allocated cluster index in FAT. // @ return 0 if success, return -1 if the FS_INFO sector cannot be updated. ////////////////////////////////////////////////////////////////////////////////////////// static error_t fatfs_free_clusters_decrement( xptr_t fatfs_ctx_xp, uint32_t cluster_id ) { error_t error; cxy_t fat_cxy; // FAT cluster identifier fatfs_ctx_t * fat_ctx_ptr; // local pointer on fatfs context in FAT cluster mapper_t * fat_mapper_ptr; // local pointer on FAT mapper xptr_t fat_mapper_xp; // extended pointer on FAT mapper xptr_t hint_xp; // extended pointer on "free_cluster_hint" shared variable xptr_t numb_xp; // extended pointer on "free_clusters" shared variable uint32_t page_id; // page index in FAT mapper uint32_t slot_id; // slot index in one page of FAT (1024 slots per page) uint32_t page_max; // max number of pages in FAT mapper xptr_t page_xp; // extended pointer on current page in FAT mapper xptr_t base_xp; // extended pointer on current page base xptr_t slot_xp; // extended pointer on current slot in FAT mapper uint32_t found; // free slot found when non zero #if DEBUG_FATFS_FREE_CLUSTERS uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; if( DEBUG_FATFS_FREE_CLUSTERS < cycle ) printk("\n[%s] thread[%x,%x] enter for allocated cluster_id %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cluster_id , cycle ); #endif // get FAT cluster an local pointer on fatfs context in FAT cluster fat_cxy = GET_CXY( fatfs_ctx_xp ); fat_ctx_ptr = GET_PTR( fatfs_ctx_xp ); // build extended pointers on free_clusters, and free_cluster_hint in fatfs context hint_xp = XPTR( fat_cxy , &fat_ctx_ptr->free_cluster_hint ); numb_xp = XPTR( fat_cxy , &fat_ctx_ptr->free_clusters ); // get pointers on FAT mapper from FATFS context fat_mapper_ptr = hal_remote_lpt( XPTR( fat_cxy , &fat_ctx_ptr->fat_mapper ) ); fat_mapper_xp = XPTR( fat_cxy , fat_mapper_ptr ); // initialise the loop variables to scan the FAT mapper page_id = (cluster_id + 1) >> 10; slot_id = (cluster_id + 1) & 0x3FF; page_max = hal_remote_l32( XPTR( fat_cxy, &fat_ctx_ptr->fat_sectors_count ) ) >> 3; found = 0; // scan FAT mapper : first loop on pages while ( (page_id < page_max) && (found == 0) ) { // get current page from mapper page_xp = mapper_get_fat_page( fat_mapper_xp , page_id ); if( page_xp == XPTR_NULL ) { printk("\n[ERROR] in %s : cannot access FAT mapper\n", __FUNCTION__ ); return -1; } // get extended pointer on page base_xp = ppm_page2base( page_xp ); // scan the FAT mapper : second loop on slots while ( (slot_id < 1024) && (found == 0) ) { // get extended pointer on current slot slot_xp = base_xp + (slot_id << 2); // test slot value if ( hal_remote_l32( slot_xp ) == FREE_CLUSTER ) { // exit both loops found = 1; } else { // update slot_id if not found slot_id++; } } // end loop on slots // update page_id & slot_id variables if not found if( found == 0 ) { page_id++; slot_id = 0; } } // end loop on pages if( found ) // free cluster found { // update "free_clusters" and "free_cluster_hint" value in FATFS context hal_remote_atomic_add( numb_xp , -1 ); hal_remote_s32( hint_xp , (page_id << 10) + slot_id - 1 ); // update FS_INFO sector on IOC device error = fatfs_update_ioc_fsinfo( fatfs_ctx_xp ); if( error ) { printk("\n[ERROR] in %s : cannot update FS_INFO on IOC\n", __FUNCTION__ ); return -1; } #if DEBUG_FATFS_FREE_CLUSTERS if( DEBUG_FATFS_FREE_CLUSTERS < cycle ) printk("\n[%s] thread[%x,%x] exit / hint %x / free %x\n", __FUNCTION__, this->process->pid, this->trdid, hal_remote_l32(hint_xp), hal_remote_l32(numb_xp) ); #endif return 0; } else // free cluster not found { printk("\n[ERROR] in %s : No free cluster_id found\n", __FUNCTION__ ); return -1; } } // end fatfs_free_clusters_decrement() ////////////////////////////////////////////////////////////////////////////////////////// // This static function increments the "free_clusters" variable, and updates the // "free_cluster_hint" variables in the FATFS context in FAT cluster, identified // by the argument, when a FATFS has been released. // If the released cluster index is smaller than the current (hint) value, // it set "free_cluster_hint" <= cluster. // It does NOT update the FS_INFO sector on the IOC device, as this is done by the // calling fatfs_release_inode() function. // It can be called by a thead running in any cluster. // The lock protecting exclusive access to the FAT must be taken by the calling function. ////////////////////////////////////////////////////////////////////////////////////////// // @ fatfs_ctx_xp : extended pointer on FATFS context in FAT cluster. // @ cluster_id : recently released cluster index in FAT. // @ return 0 if success, return -1 if the FS_INFO sector cannot be updated. ////////////////////////////////////////////////////////////////////////////////////////// static error_t fatfs_free_clusters_increment( xptr_t fatfs_ctx_xp, uint32_t cluster_id ) { cxy_t fat_cxy; // FAT cluster identifier fatfs_ctx_t * fat_ctx_ptr; // local pointer on fatfs context in FAT cluster xptr_t hint_xp; // extended pointer on "free_cluster_hint" shared variable xptr_t numb_xp; // extended pointer on "free_clusters" shared variable uint32_t hint; // "free_cluster_hint" variable current value uint32_t numb; // "free_clusters" variable current value #if DEBUG_FATFS_FREE_CLUSTERS uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; if( DEBUG_FATFS_FREE_CLUSTERS < cycle ) printk("\n[%s] thread[%x,%x] enter for released cluster_id %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cluster_id , cycle ); #endif // get FAT cluster an local pointer on fatfs context in FAT cluster fat_cxy = GET_CXY( fatfs_ctx_xp ); fat_ctx_ptr = GET_PTR( fatfs_ctx_xp ); // build extended pointers on free_clusters, and free_cluster_hint hint_xp = XPTR( fat_cxy , &fat_ctx_ptr->free_cluster_hint ); numb_xp = XPTR( fat_cxy , &fat_ctx_ptr->free_clusters ); // get current value of free_cluster_hint and free_clusters hint = hal_remote_l32( hint_xp ); numb = hal_remote_l32( numb_xp ); // update "numb" and "hint" variables as required numb++; if ( (cluster_id - 1) < hint ) hint = cluster_id - 1; // update free_clusters hal_remote_s32( numb_xp , numb ); hal_remote_s32( hint_xp , hint ); #if DEBUG_FATFS_FREE_CLUSTERS if( DEBUG_FATFS_FREE_CLUSTERS < cycle ) printk("\n[%s] thread[%x,%x] exit / hint %x / free %x\n", __FUNCTION__, this->process->pid, this->trdid, hal_remote_l32( hint_xp ), hal_remote_l32( numb_xp ) ); #endif return 0; } // end fatfs_free_clusters_increment() ////////////////////////////////////////////////////////////////////////////////////////// // This recursive function is called by the generic function fatfs_release_inode(). // It release all FATFS clusters allocated to a given inode to the FAT mapper. // It can be called by a thread running in any cluster, as it use remote pointers // to access both the FAT mapper and the FATFS context in the FAT cluster, defined // by the and arguments. // The removal is done in reverse order of the linked list (from last cluster to first). // It returns in the and buffers the indexes of the // modified pages in the FAT mapper. // It updates the FAT mapper and the free_cluster info in the FATFS context, but does NOT // update the FAT and the FS_INFO on IOC device. ////////////////////////////////////////////////////////////////////////////////////////// // @ fat_mapper_xp : [in] extended pointer on FAT mapper. // @ fatfs_ctx_xp : [in] extended pointer on FATFS context in FAT cluster. // @ cluster : [in] index of cluster to be released from FAT mapper. // @ dirty_page_min : [out] pointer on buffer for min dirty page index. // @ dirty_page_max : [out] pointer on buffer for max dirty page index. // @ return 0 if success / return -1 if error. ////////////////////////////////////////////////////////////////////////////////////////// static error_t fatfs_recursive_release( xptr_t fat_mapper_xp, xptr_t fatfs_ctx_xp, uint32_t cluster, uint32_t * dirty_page_min, uint32_t * dirty_page_max ) { uint32_t next; // next cluster index uint32_t page_id; // page index in FAT mapper uint32_t word_id; // word index in page // get page index and word index from cluster page_id = cluster >> 10; word_id = cluster & 0x3FF; // get next cluster index from FAT mapper if ( mapper_remote_get_32( fat_mapper_xp, page_id, word_id, &next ) ) return -1; #if (DEBUG_FATFS_RELEASE_INODE & 1) thread_t * this = CURRENT_THREAD; if ( DEBUG_FATFS_RELEASE_INODE < (uint32_t)hal_get_cycles() ) printk("\n[%s] thread[%x,%x] access FAT for cluster %x / next %x\n", __FUNCTION__, this->process->pid, this->trdid, cluster, next ); #endif if ( next < END_OF_CHAIN_CLUSTER_MIN ) // non terminal case { // call fatfs_recursive_release() on next cluster if ( fatfs_recursive_release( fat_mapper_xp, fatfs_ctx_xp, next, dirty_page_min, dirty_page_max ) ) return -1; } // update FAT mapper if ( mapper_remote_set_32( fat_mapper_xp, page_id, word_id, FREE_CLUSTER ) ) return -1; // update dirty_page_min / dirty_page_max buffers if( page_id < *dirty_page_min ) *dirty_page_min = page_id; if( page_id > *dirty_page_max ) *dirty_page_max = page_id; // Update free_cluster info in FATFS context return fatfs_free_clusters_increment( fatfs_ctx_xp , cluster ); } // end fatfs_recursive_release() ////////////////////////////////////////////////////////////////////////////////////////// // This static function access the FAT mapper to allocate a new cluster in the FATFS, // and returns in the FATFS cluster index of a free cluster. // It updates the FAT mapper (handling miss from IOC device if required) : // - if the is zero, the new cluster is the first allocated cluster, // and the FAT slot is set to END_OF_CHAIN_CLUSTER. // - if the argument is not zero, the new cluster is not the first, // the FAT slot is set to , // the FAT slot is set to END_OF_CHAIN_CLUSTER. // This function also updates the two "free_cluster_hint" and "free_clusters" variables // stored in the FATFS context. It takes the rwlock stored in the FATFS context in the // FAT cluster to get exclusive access to the FAT. // This function synchronously updates the FAT region on IOC device. // It can be called by a thread running in any cluster as it uses remote accesses. ////////////////////////////////////////////////////////////////////////////////////////// // @ last_cluster_id : [in] previous last cluster index. // @ searched_cluster_id : [out] allocated cluster index. // @ return 0 if success / return -1 if no more free clusters on IOC device. ////////////////////////////////////////////////////////////////////////////////////////// static error_t fatfs_cluster_alloc( uint32_t last_cluster_id, uint32_t * searched_cluster_id ) { error_t error; uint32_t free_clusters; // total number of free clusters uint32_t hint; // hint + 1 is the first free cluster uint32_t new_cluster_id; // allocated cluster index in FAT uint32_t new_page_id; // allocated cluster page index in FAT mapper uint32_t new_slot_id; // allocated cluster slot index in page (1024 slots per page) xptr_t new_page_xp; // extended pointer on FAT page for allocated cluster xptr_t new_slot_xp; // extended pointer on allocated cluster slot in FAT uint32_t last_page_id; // last cluster page index in FAT mapper uint32_t last_slot_id; // last cluster slot index in page (1024 slots per page) xptr_t last_slot_xp; // extended pointer on last cluster slot in FAT xptr_t last_page_xp; // extended pointer on FAT page for last cluster vfs_ctx_t * vfs_ctx; // local pointer on VFS context (same in all clusters) fatfs_ctx_t * loc_fatfs_ctx; // local pointer on local FATFS context fatfs_ctx_t * fat_fatfs_ctx; // local pointer on FATFS context in FAT cluster mapper_t * fat_mapper_ptr; // local pointer on FAT mapper xptr_t fat_mapper_xp; // extended pointer on FAT mapper cxy_t fat_cxy; // FAT mapper cluster identifier xptr_t lock_xp; // extended pointer on lock protecting free clusters info xptr_t hint_xp; // extended pointer on free_cluster_hint in FAT cluster xptr_t free_xp; // extended pointer on free_clusters_number in FAT cluster #if DEBUG_FATFS_CLUSTER_ALLOC uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; if( DEBUG_FATFS_CLUSTER_ALLOC < cycle ) printk("\n[%s] thread[%x,%x] enter / lats_cluster_id %x / cycle = %d\n", __FUNCTION__, this->process->pid, this->trdid, last_cluster_id, cycle ); #endif // get local pointer on VFS context (same in all clusters) vfs_ctx = &fs_context[FS_TYPE_FATFS]; // get local pointer on local FATFS context loc_fatfs_ctx = vfs_ctx->extend; // get FAT cluster fat_cxy = CONFIG_VFS_ROOT_CXY; // get pointers on FAT mapper fat_mapper_ptr = loc_fatfs_ctx->fat_mapper; fat_mapper_xp = XPTR( fat_cxy , fat_mapper_ptr ); // get local pointer on FATFS context in FAT cluster fat_fatfs_ctx = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) ); // build relevant extended pointers on free clusters info in FAT cluster lock_xp = XPTR( fat_cxy , &fat_fatfs_ctx->lock ); hint_xp = XPTR( fat_cxy , &fat_fatfs_ctx->free_cluster_hint ); free_xp = XPTR( fat_cxy , &fat_fatfs_ctx->free_clusters ); // take the FAT lock in write mode remote_rwlock_wr_acquire( lock_xp ); // get hint and free_clusters values from FATFS context in FAT cluster hint = hal_remote_l32( hint_xp ); free_clusters = hal_remote_l32( free_xp ); #if (DEBUG_FATFS_CLUSTER_ALLOC & 1) if( DEBUG_FATFS_CLUSTER_ALLOC < cycle ) printk("\n[%s] thread[%x,%x] get free info : hint %x / free_clusters %x\n", __FUNCTION__, this->process->pid, this->trdid, hint, free_clusters ); #endif // check "free_clusters" if ( free_clusters == 0 ) { printk("\n[ERROR] in %s : no more free FATFS clusters\n", __FUNCTION__ ); remote_rwlock_wr_release( lock_xp ); return -1; } else if ( free_clusters < CONFIG_VFS_FREE_CLUSTERS_MIN ) { printk("\n[WARNING] in %s : only %d free FATFS clusters\n", __FUNCTION__, CONFIG_VFS_FREE_CLUSTERS_MIN ); } // get new cluster, page & slot indexes in FAT new_cluster_id = hint + 1; new_page_id = new_cluster_id >> 10; new_slot_id = new_cluster_id & 0x3FF; // get relevant FAT page descriptor from FAT mapper new_page_xp = mapper_get_fat_page( fat_mapper_xp , new_page_id ); if( new_page_xp == XPTR_NULL ) { printk("\n[ERROR] in %s : cannot acces FAT mapper\n", __FUNCTION__ ); remote_rwlock_wr_release( lock_xp ); return -1; } // build extended pointer on new cluster slot in FAT mapper new_slot_xp = ppm_page2base( new_page_xp ) + (new_slot_id << 2); // check selected cluster actually free if( hal_remote_l32( new_slot_xp ) != FREE_CLUSTER ) { printk("\n[ERROR] in %s : selected cluster_id %x not free\n", __FUNCTION__, new_cluster_id ); remote_rwlock_wr_release( lock_xp ); return -1; } // update new_cluster slot in FAT mapper hal_remote_s32( new_slot_xp , END_OF_CHAIN_CLUSTER_MIN ); // handle last_cluster_id argument if non zero if( last_cluster_id ) { // get last cluster page & slot indexes in FAT last_page_id = last_cluster_id >> 10; last_slot_id = last_cluster_id & 0x3FF; // get relevant FAT page descriptor from FAT mapper last_page_xp = mapper_get_fat_page( fat_mapper_xp , last_page_id ); if( last_page_xp == XPTR_NULL ) { printk("\n[ERROR] in %s : cannot acces FAT mapper\n", __FUNCTION__ ); remote_rwlock_wr_release( lock_xp ); return -1; } // build extended pointer on new cluster slot in FAT mapper last_slot_xp = ppm_page2base( last_page_xp ) + (last_slot_id << 2); // check last cluster actually end of chain if( hal_remote_l32( last_slot_xp ) != END_OF_CHAIN_CLUSTER_MIN ) { printk("\n[ERROR] in %s : last_cluster_id %x not END_OF_CHAIN\n", __FUNCTION__, last_cluster_id ); remote_rwlock_wr_release( lock_xp ); return -1; } // update last_cluster slot in FAT mapper hal_remote_s32( last_slot_xp , new_cluster_id ); } else { last_page_xp = XPTR_NULL; } // update the FAT new_page on device error = fatfs_move_page( new_page_xp , IOC_SYNC_WRITE ); if( error ) { printk("\n[ERROR] in %s : cannot update FAT on IOC device\n", __FUNCTION__ ); remote_rwlock_wr_release( lock_xp ); return -1; } // update the FAT last_page on device when required if( (last_page_xp != XPTR_NULL) && (last_page_xp != new_page_xp) ) { error = fatfs_move_page( last_page_xp , IOC_SYNC_WRITE ); if( error ) { printk("\n[ERROR] in %s : cannot update FAT on IOC device\n", __FUNCTION__ ); remote_rwlock_wr_release( lock_xp ); return -1; } } // update free cluster info in FATFS context and in FS_INFO sector error = fatfs_free_clusters_decrement( XPTR( fat_cxy , fat_fatfs_ctx ) , new_cluster_id ); if( error ) { printk("\n[ERROR] in %s : cannot update free cluster info\n", __FUNCTION__ ); remote_rwlock_wr_release( lock_xp ); return -1; } // release FAT lock remote_rwlock_wr_release( lock_xp ); #if DEBUG_FATFS_CLUSTER_ALLOC cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_CLUSTER_ALLOC < cycle ) printk("\n[%s] thread[%x,%x] exit / allocated cluster_id %x in FAT / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, new_cluster_id, cycle ); #endif *searched_cluster_id = new_cluster_id; return 0; } // end fatfs_cluster_alloc() ////////////////////////////////////////////////////////////////////////////////////////// // This static function access the FAT (File Allocation Table), stored in the FAT mapper, // and returns in the FATFS cluster_id for a given page of a given // inode, identified by the argument that is the page index in file // (i.e. the page index in file mapper). The entry point in the FAT is defined by the // argument, that is the cluster_id of an already allocated cluster. // It can be the cluster_id of the first page of the file (always registered in the // fatfs_inode extension), or any page of the file whose argument // is smaller than the searched argument. // This function can be called by a thread running in any cluster. // The FAT mapper being a WRITE-THROUGH cache, this function updates the FAT mapper // from informations stored on IOC device in case of miss when scanning the FAT mapper. // The searched inode mapper being a WRITE-BACK cache, this function allocates a new // cluster_id when the searched page exist in the inode mapper, and there is no FATFS // cluster allocated yet for this page. It updates the FAT, but it does NOT copy the // mapper page content to the File System. ////////////////////////////////////////////////////////////////////////////////////////// // @ first_page_id : [in] index in file mapper for an existing page. // @ first_cluster_id : [in] cluster_id for this existing page. // @ searched_page_id : [in] index in file mapper for the searched page. // @ searched_cluster_id : [out] cluster_id for the searched page. // @ return 0 if success / return -1 if a FAT mapper miss cannot be solved, // or if a missing cluster_id cannot be allocated. ////////////////////////////////////////////////////////////////////////////////////////// static error_t fatfs_get_cluster( uint32_t first_page_id, uint32_t first_cluster_id, uint32_t searched_page_id, uint32_t * searched_cluster_id ) { uint32_t current_page_id; // index of page in file mapper uint32_t current_cluster_id; // index of cluster in FATFS xptr_t lock_xp; // extended pointer on FAT lock xptr_t fat_mapper_xp; // extended pointer on FAT mapper mapper_t * fat_mapper_ptr; // local pointer on FAT mapper cxy_t fat_cxy; // FAT cluster error_t error; assert( __FUNCTION__, (searched_page_id > first_page_id) , "searched_page_id must be larger than first_page_id\n"); #if DEBUG_FATFS_GET_CLUSTER uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; if( DEBUG_FATFS_GET_CLUSTER < cycle ) printk("\n[%s] thread[%x,%x] enter / frst_pid %x / frst_cid %x / srch_pid %d / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, first_page_id, first_cluster_id, searched_page_id, cycle ); #endif // get local pointer on VFS context (same in all clusters) vfs_ctx_t * vfs_ctx = &fs_context[FS_TYPE_FATFS]; // get local pointer on local FATFS context fatfs_ctx_t * loc_fatfs_ctx = vfs_ctx->extend; // get FAT cluster fat_cxy = CONFIG_VFS_ROOT_CXY; // get pointers on FAT mapper fat_mapper_ptr = loc_fatfs_ctx->fat_mapper; fat_mapper_xp = XPTR( fat_cxy , fat_mapper_ptr ); // get local pointer on FATFS context in FAT cluster fatfs_ctx_t * fat_fatfs_ctx = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) ); // build extended pointer on FAT lock in FAT cluster lock_xp = XPTR( fat_cxy , &fat_fatfs_ctx->lock ); // take FAT lock in read mode remote_rwlock_rd_acquire( lock_xp ); // initialize loop variables current_page_id = first_page_id; current_cluster_id = first_cluster_id; // scan FAT mapper (i.e. traverse FAT linked list) // starting from first_page_id until searched_page_id // each iteration in this loop can change both // the FAT page index and the slot index in FAT while( current_page_id < searched_page_id ) { // FAT mapper page and slot indexes (1024 slots per FAT page) uint32_t fat_page_index = current_cluster_id >> 10; uint32_t fat_slot_index = current_cluster_id & 0x3FF; // get pointer on current page descriptor in FAT mapper xptr_t current_page_xp = mapper_get_fat_page( fat_mapper_xp , fat_page_index ); if( current_page_xp == XPTR_NULL ) { printk("\n[ERROR] in %s : cannot get page %d from FAT mapper\n", __FUNCTION__ , fat_page_index ); remote_rwlock_rd_release( lock_xp ); return -1; } // get pointer on buffer containing the FAT mapper page xptr_t base_xp = ppm_page2base( current_page_xp ); uint32_t * buffer = (uint32_t *)GET_PTR( base_xp ); // get next_cluster_id from FAT slot uint32_t next_cluster_id = hal_remote_l32( XPTR( fat_cxy, &buffer[fat_slot_index] ) ); // allocate a new FAT cluster when END_OF_CHAIN found if( next_cluster_id >= END_OF_CHAIN_CLUSTER_MIN ) { // release the FAT lock in read mode, remote_rwlock_rd_release( lock_xp ); // allocate a new cluster_id (and update both FAT mapper and FAT on device). error = fatfs_cluster_alloc( current_cluster_id, &next_cluster_id ); if( error ) { printk("\n[ERROR] in %s : cannot allocate cluster on FAT32 for page %d\n", __FUNCTION__ , current_page_id ); remote_rwlock_wr_release( lock_xp ); return -1; } #if (DEBUG_FATFS_GET_CLUSTER & 1) if( DEBUG_FATFS_GET_CLUSTER < cycle ) printk("\n[%s] allocated a new cluster_id %x in FATFS\n", __FUNCTION__, next_cluster_id ); #endif // take the FAT lock in read mode, remote_rwlock_rd_acquire( lock_xp ); } #if (DEBUG_FATFS_GET_CLUSTER & 1) if( DEBUG_FATFS_GET_CLUSTER < cycle ) printk("\n[%s] traverse FAT / current_cluster_id %x / next_cluster_id %x\n", __FUNCTION__, current_cluster_id , next_cluster_id ); #endif // update loop variables current_cluster_id = next_cluster_id; current_page_id++; } // release FAT lock remote_rwlock_rd_release( lock_xp ); #if DEBUG_FATFS_GET_CLUSTER if( DEBUG_FATFS_GET_CLUSTER < cycle ) printk("\n[%s] thread[%x,%x] exit / frst_pid %d / frst_cid %x / srch_pid %d / srch_cid %x\n", __FUNCTION__, this->process->pid, this->trdid, first_page_id, first_cluster_id, searched_page_id, current_cluster_id ); #endif *searched_cluster_id = current_cluster_id; return 0; } // end fatfs_get_cluster() ////////////////////////////////////////////////////////////////////////////////////////////// // This static function scan the pages of a directory mapper, identified by the // argument, to find the directory entry identified by the argument, and returns // a pointer on the directory entry, described as an array of 32 bytes, and the index of // this entry in the FAT32 mapper, seen as an array of 32 bytes entries. // It makes a local copy of each directory entry to reduce the number of remote accesses. // It is called by the fatfs_new_dentry_from_mapper() function. // It can be called by a thread running in any cluster. ////////////////////////////////////////////////////////////////////////////////////////////// // @ mapper_xp : [in] extended pointer on directory mapper. // @ name : [in] searched directory entry name. // @ entry : [out] buffer for the pointer on the 32 bytes directory entry (when found). // @ index : [out] buffer for the directory entry index in mapper. // @ return 0 if found / return 1 if not found / return -1 if mapper access error. ////////////////////////////////////////////////////////////////////////////////////////////// static error_t fatfs_scan_directory( xptr_t mapper_xp, char * name, uint8_t ** entry, uint32_t * index ) { uint8_t buf[32]; // local buffer for one FAT32 directory entry // check arguments assert( __FUNCTION__, (mapper_xp != XPTR_NULL) , "mapper pointer is NULL\n" ); assert( __FUNCTION__, (name != NULL ) , "child name is undefined\n" ); #if DEBUG_FATFS_SCAN_DIRECTORY char parent_name[CONFIG_VFS_MAX_NAME_LENGTH]; uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; mapper_t * mapper_ptr = GET_PTR( mapper_xp ); cxy_t mapper_cxy = GET_CXY( mapper_xp ); vfs_inode_t * inode_ptr = hal_remote_lpt( XPTR( mapper_cxy , &mapper_ptr->inode ) ); vfs_inode_get_name( XPTR( mapper_cxy , inode_ptr ) , parent_name ); if( DEBUG_FATFS_SCAN_DIRECTORY < cycle ) printk("\n[%s] thread[%x,%x] enter to search child <%s> in parent <%s> / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, name , parent_name , cycle ); #endif char cname[CONFIG_VFS_MAX_NAME_LENGTH]; // name extracted from directory entry char lfn1[16]; // buffer for one partial cname char lfn2[16]; // buffer for one partial cname char lfn3[16]; // buffer for one partial cname xptr_t page_xp; // extended pointer on one page descriptor xptr_t base_xp; // extended pointer on one page base uint8_t attr; // directory entry ATTR field uint8_t ord; // directory entry ORD field uint32_t seq; // sequence index uint32_t lfn = 0; // LFN entries number int32_t found = 0; // not yet = 0 / success = 1 / not found = 2 / error = -1 uint32_t page_id = 0; // page index in mapper uint32_t offset = 0; // byte offset in page // Two embedded loops to scan the directory mapper: // - scan the parent directory mapper pages // - scan the directory entries in each 4 Kbytes page // scan the mapper pages while ( found == 0 ) { // get one page page_xp = mapper_get_page( mapper_xp , page_id ); if( page_xp == XPTR_NULL) { found = -1; } // get page base base_xp = ppm_page2base( page_xp ); #if (DEBUG_FATFS_SCAN_DIRECTORY & 1) if( DEBUG_FATFS_SCAN_DIRECTORY < cycle ) mapper_display_page( mapper_xp , page_xp , 256 ); #endif // scan this page until end of directory, end of page, or name found while( (offset < 4096) && (found == 0) ) { // makes a local copy of current directory entry (32 bytes) hal_remote_memcpy( XPTR( local_cxy , buf ) , base_xp + offset , 32 ); // get attr and ord from local buffer attr = fatfs_get_record( DIR_ATTR , buf ); ord = fatfs_get_record( LDIR_ORD , buf ); if (ord == NO_MORE_ENTRY) // no more entry => break { found = 2; } else if ( ord == FREE_ENTRY ) // free entry => skip { offset = offset + 32; } else if ( attr == 0x28 ) // volune_id => skip { offset = offset + 32; } else if ( attr == ATTR_LONG_NAME_MASK ) // LFN entry => get partial cname { seq = ord & 0x3; lfn = (seq > lfn) ? seq : lfn; if ( seq == 1 ) fatfs_get_name_from_long( buf , lfn1 ); else if ( seq == 2 ) fatfs_get_name_from_long( buf , lfn2 ); else if ( seq == 3 ) fatfs_get_name_from_long( buf , lfn3 ); offset = offset + 32; } else // NORMAL entry { // build the extracted name if ( lfn == 0 ) { fatfs_get_name_from_short( buf , cname ); } else if ( lfn == 1 ) { strcpy( cname , lfn1 ); } else if ( lfn == 2 ) { strcpy( cname , lfn1 ); strcpy( cname + 13 , lfn2 ); } else if ( lfn == 3 ) { strcpy( cname , lfn1 ); strcpy( cname + 13 , lfn2 ); strcpy( cname + 26 , lfn3 ); } // get dentry arguments if extracted cname == searched name if ( strcmp( name , cname ) == 0 ) { uint8_t * base = GET_PTR( base_xp ); *entry = base + offset; *index = ( (page_id << 12) + offset ) >> 5; found = 1; } offset = offset + 32; lfn = 0; } } // end loop on directory entries in page page_id++; offset = 0; } // end loop on pages if( found == 1 ) { #if DEBUG_FATFS_SCAN_DIRECTORY if( DEBUG_FATFS_SCAN_DIRECTORY < cycle ) printk("\n[%s] thread[%x,%x] exit / found child <%s> in <%s>\n", __FUNCTION__, this->process->pid, this->trdid, name, parent_name ); #endif return 0; } else if( found == 2 ) { #if DEBUG_FATFS_SCAN_DIRECTORY if( DEBUG_FATFS_SCAN_DIRECTORY < cycle ) printk("\n[%s] thread[%x,%x] exit / child <%s> in <%s> not found\n", __FUNCTION__, this->process->pid, this->trdid, name, parent_name ); #endif return 1; } else { printk("\n[ERROR] in %s : cannot get page %d from mapper\n", __FUNCTION__, page_id ); return -1; } } // end fatfs_scan_directory() ////////////////////////////////////////////////////////////////////////////////////////// // FATFS debug functions ////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////// void fatfs_display_ctx( cxy_t cxy ) { // get pointer on local FATFS context vfs_ctx_t * vfs_ctx = &fs_context[FS_TYPE_FATFS]; fatfs_ctx_t * ctx = hal_remote_lpt( XPTR( cxy , &vfs_ctx->extend ) ); uint32_t fat_sectors = hal_remote_l32( XPTR( cxy , &ctx->fat_sectors_count ) ); uint32_t sector_size = hal_remote_l32( XPTR( cxy , &ctx->bytes_per_sector ) ); uint32_t sec_per_clus = hal_remote_l32( XPTR( cxy , &ctx->sectors_per_cluster ) ); uint32_t fat_lba = hal_remote_l32( XPTR( cxy , &ctx->fat_begin_lba ) ); uint32_t data_lba = hal_remote_l32( XPTR( cxy , &ctx->cluster_begin_lba ) ); uint32_t fsinfo_lba = hal_remote_l32( XPTR( cxy , &ctx->fs_info_lba ) ); uint32_t root_dir_clus = hal_remote_l32( XPTR( cxy , &ctx->root_dir_cluster ) ); uint32_t free_clusters = hal_remote_l32( XPTR( cxy , &ctx->free_clusters ) ); uint32_t free_cluster_hint = hal_remote_l32( XPTR( cxy , &ctx->free_cluster_hint ) ); void * fat_mapper = hal_remote_lpt( XPTR( cxy , &ctx->fat_mapper ) ); void * fs_info_buffer = hal_remote_lpt( XPTR( cxy , &ctx->fs_info_buffer ) ); printk("\n*** FAT context in cluster %x\n" "- fat_sectors = %d\n" "- sector size = %d\n" "- cluster size = %d\n" "- fat_lba = %x\n" "- data_lba = %x\n" "- fsinfo_lba = %x\n" "- root_dir_cluster = %x\n" "- free_clusters = %x\n" "- free_cluster_hint = %x\n" "- fat_mapper = %x\n" "- fs_info_buffer = %x\n", cxy, fat_sectors, sector_size, sector_size * sec_per_clus, fat_lba, data_lba, fsinfo_lba, root_dir_clus, free_clusters, free_cluster_hint, fat_mapper, fs_info_buffer ); } // end fatfs_display_ctx() ////////////////////////////////////////// void fatfs_display_fat( uint32_t min_slot, uint32_t nb_slots ) { // one FAT mapper page contains 1024 slots = 128 lines of 8 slots uint32_t page_id; uint32_t line; cxy_t fat_cxy; // FAT cluster mapper_t * mapper; // local pointer on FAT mapper xptr_t mapper_xp; // extended pointer on fat_mapper uint32_t min_cluster_id; // index of min slot to be displayed uint32_t min_page_id; // index of min page to be displayed uint32_t min_line_id; // index of min line in min page ( < 128 ) uint32_t max_cluster_id; // index of max slot to be displayed uint32_t max_page_id; // index of max page to be displayed uint32_t max_line_id; // index of max line in max page ( < 128 ) // compute min values min_cluster_id = min_slot & 0xFFFFFFF8; min_line_id = (min_cluster_id & 0x3FF) >> 3; min_page_id = min_cluster_id >> 10; // compute max values max_cluster_id = min_slot + nb_slots - 1; max_line_id = (max_cluster_id & 0x3FF) >> 3; max_page_id = max_cluster_id >> 10; // get pointer on local FATFS context vfs_ctx_t * vfs_ctx = &fs_context[FS_TYPE_FATFS]; fatfs_ctx_t * loc_fatfs_ctx = (fatfs_ctx_t *)vfs_ctx->extend; // get FAT cluster fat_cxy = CONFIG_VFS_ROOT_CXY; // get pointers on FAT mapper (in FAT cluster) mapper = loc_fatfs_ctx->fat_mapper; mapper_xp = XPTR( fat_cxy , mapper ); // get pointer on FATFS context in FAT cluster fatfs_ctx_t * fat_fatfs_ctx = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) ); // get current value of hint and free_clusters uint32_t hint = hal_remote_l32( XPTR( fat_cxy , &fat_fatfs_ctx->free_cluster_hint ) ); uint32_t free = hal_remote_l32( XPTR( fat_cxy , &fat_fatfs_ctx->free_clusters ) ); printk("\n***** FAT mapper / cxy %x / free_clusters %x / hint %x\n", fat_cxy, free, hint ); // scan all pages as required by min_page_id and max_page_id for( page_id = min_page_id ; page_id <= max_page_id ; page_id++ ) { // get extended pointer on requested page descriptor in FAT mapper xptr_t page_xp = mapper_get_fat_page( mapper_xp , page_id ); // get extended pointer on requested page base xptr_t base_xp = ppm_page2base( page_xp ); // compute min_line & max_line in current page uint32_t min_line = (page_id == min_page_id) ? min_line_id : 0; uint32_t max_line = (page_id == max_page_id) ? max_line_id : 127; // loop on lines in current page for( line = min_line ; line <= max_line ; line++ ) { printk("%x : %X | %X | %X | %X | %X | %X | %X | %X\n", (page_id << 10) + (line <<3 ), hal_remote_l32( base_xp + ((line<<5) ) ), hal_remote_l32( base_xp + ((line<<5) + 4 ) ), hal_remote_l32( base_xp + ((line<<5) + 8 ) ), hal_remote_l32( base_xp + ((line<<5) + 12 ) ), hal_remote_l32( base_xp + ((line<<5) + 16 ) ), hal_remote_l32( base_xp + ((line<<5) + 20 ) ), hal_remote_l32( base_xp + ((line<<5) + 24 ) ), hal_remote_l32( base_xp + ((line<<5) + 28 ) ) ); } } } // end fatfs_display_fat() ///////////////////////////////////// error_t fatfs_check_free_info( void ) { error_t error; fatfs_ctx_t * fatfs_ctx_ptr; // local pointer on fatfs context in cluster 0 uint32_t ctx_free_clusters; // number of free clusters from fatfs context uint32_t ctx_free_cluster_hint; // free cluster hint from fatfs context uint32_t ioc_free_clusters; // number of free clusters from fatfs context uint32_t ioc_free_cluster_hint; // free cluster hint from fatfs context uint32_t fs_info_lba; // lba of FS_INFO sector on IOC device uint8_t * fs_info_buffer; // local pointer on FS_INFO buffer in cluster 0 xptr_t fs_info_buffer_xp; // extended pointer on FS_INFO buffer in cluster 0 uint8_t tmp_buf[512]; // 512 bytes temporary buffer xptr_t tmp_buf_xp; // extended pointer on temporary buffer #if DEBUG_FATFS_SYNC_FSINFO uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; if( DEBUG_FATFS_SYNC_FSINFO < cycle ) printk("\n[%s] thread[%x,%x] enter / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, cycle ); #endif // get pointer on fatfs context in cluster 0 fatfs_ctx_ptr = hal_remote_lpt( XPTR( 0 , &fs_context[FS_TYPE_FATFS].extend ) ); // get "free_clusters" and "free_cluster_hint" from fatfs context in cluster 0 ctx_free_clusters = hal_remote_l32( XPTR( 0 , &fatfs_ctx_ptr->free_clusters ) ); ctx_free_cluster_hint = hal_remote_l32( XPTR( 0 , &fatfs_ctx_ptr->free_cluster_hint ) ); // get fs_info_lba fs_info_lba = hal_remote_l32( XPTR( 0 , &fatfs_ctx_ptr->fs_info_lba ) ); // build extended pointer on temporary buffer tmp_buf_xp = XPTR( local_cxy , tmp_buf ); // copy FS_INFO sector from IOC to local buffer error = dev_ioc_sync_read( tmp_buf_xp , fs_info_lba , 1 ); if ( error ) { printk("\n[ERROR] in %s : cannot access FS_INFO on IOC device\n", __FUNCTION__ ); return -1; } // get current values of "free_clusters" and "free_cluster_hint" from FS_INFO on IOC ioc_free_clusters = fatfs_get_remote_record( FS_FREE_CLUSTERS , tmp_buf_xp ); ioc_free_cluster_hint = fatfs_get_remote_record( FS_FREE_CLUSTER_HINT , tmp_buf_xp ); #if DEBUG_FATFS_SYNC_FSINFO if( DEBUG_FATFS_SYNC_FSINFO < cycle ) printk("\n[%s] thread[%x,%x] / ctx_free %x / ioc_free %x / ctx_hint %x / ioc_hint %x\n", __FUNCTION__ , this->process->pid, this->trdid, ctx_free_clusters, ioc_free_clusters, ctx_free_cluster_hint, ioc_free_cluster_hint ); #endif // check values if( (ioc_free_clusters != ctx_free_clusters) || (ioc_free_cluster_hint != ctx_free_cluster_hint) ) { printk("\n[WARNING] in %s : unconsistent free clusters info\n" " ioc_free %x / ctx_free %x / ioc_hint %x / ctx_hint %x\n", __FUNCTION__, ioc_free_clusters, ctx_free_clusters, ioc_free_cluster_hint, ctx_free_cluster_hint ); // get pointers on FS_INFO buffer in cluster 0 fs_info_buffer = hal_remote_lpt( XPTR( 0 , &fatfs_ctx_ptr->fs_info_buffer ) ); fs_info_buffer_xp = XPTR( 0 , fs_info_buffer ); // update FS_INFO buffer in cluster 0 fatfs_set_remote_record(FS_FREE_CLUSTERS ,fs_info_buffer_xp,ctx_free_clusters ); fatfs_set_remote_record(FS_FREE_CLUSTER_HINT,fs_info_buffer_xp,ctx_free_cluster_hint); // update the FS_INFO sector on IOC device error = dev_ioc_sync_write( fs_info_buffer_xp , fs_info_lba , 1 ); if ( error ) { printk("\n[ERROR] in %s : cannot update FS_INFO on IOC device\n", __FUNCTION__ ); return -1; } } #if DEBUG_FATFS_SYNC_FSINFO cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_SYNC_FSINFO < cycle ) printk("\n[%s] thread[%x,%x] exit / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, cycle ); #endif return 0; } // end fatfs_check_free_info() /////////////////////////////////////////////////////////////////////////////////////// // Generic API : the following functions are called by the kernel VFS // and must be defined by all supported file systems. /////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////// xptr_t fatfs_ctx_alloc( cxy_t cxy ) { // allocate memory from remote cluster void * ptr = kmem_remote_alloc( cxy, bits_log2(sizeof(fatfs_ctx_t)), AF_ZERO ); if( ptr == NULL ) return XPTR_NULL; else return XPTR( cxy , ptr ); } //end faffs_ctx_alloc() /////////////////////////////////////////////// error_t fatfs_ctx_init( xptr_t fatfs_ctx_xp ) { error_t error; cxy_t cxy; // FATFS context cluster identifier fatfs_ctx_t * fatfs_ctx_ptr; // local pointer on FATFS context uint8_t * buffer; // local pointer on 512 bytes buffer xptr_t buffer_xp; // extended pointer on 512 bytes buffer xptr_t fat_mapper_xp; // extended pointer on FAT mapper mapper_t * fat_mapper; // local pointer on FAT mapper // get FATFS context cluster and local pointer cxy = GET_CXY( fatfs_ctx_xp ); fatfs_ctx_ptr = GET_PTR( fatfs_ctx_xp ); #if DEBUG_FATFS_CTX_INIT uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; if( DEBUG_FATFS_CTX_INIT < cycle ) printk("\n[%s] thread[%x,%x] enter for fatfs_ctx (%x,%x) / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, cxy, fatfs_ctx_ptr , cycle ); #endif // allocate a 512 bytes buffer in remote cluster, used to store // temporarily the BOOT sector, and permanently the FS_INFO sector buffer = kmem_remote_alloc( cxy, 9, AF_ZERO ); if( buffer == NULL ) { printk("\n[PANIC] in %s : cannot allocate buffer in cluster %x\n", __FUNCTION__ , cxy ); return -1; } // build extended pointer on buffer buffer_xp = XPTR( cxy , buffer ); // load the BOOT record from device error = dev_ioc_sync_read( buffer_xp , 0 , 1 ); if ( error ) { printk("\n[PANIC] in %s : cannot access boot record\n", __FUNCTION__ ); return -1; } #if (DEBUG_FATFS_CTX_INIT & 0x1) uint8_t bs[256]; hal_remote_memcpy( XPTR( local_cxy , bs ) , buffer_xp , 256 ); if( DEBUG_FATFS_CTX_INIT < cycle ) putb( "boot record", bs , 256 ); #endif // get sector size from boot record uint32_t sector_size = fatfs_get_remote_record( BPB_BYTSPERSEC , buffer_xp ); if ( sector_size != 512 ) { printk("\n[PANIC] in %s : sector size must be 512 bytes\n", __FUNCTION__ ); return -1; } // get cluster size from boot record uint32_t nb_sectors = fatfs_get_remote_record( BPB_SECPERCLUS , buffer_xp ); if ( nb_sectors != 8 ) { printk("\n[PANIC] in %s : cluster size must be 8 sectors\n", __FUNCTION__ ); return -1; } // get number of FAT copies from boot record uint32_t nb_fats = fatfs_get_remote_record( BPB_NUMFATS , buffer_xp ); if ( nb_fats != 1 ) { printk("\n[PANIC] in %s : number of FAT copies must be 1\n", __FUNCTION__ ); return -1; } // get number of sectors in FAT from boot record uint32_t fat_sectors = fatfs_get_remote_record( BPB_FAT32_FATSZ32 , buffer_xp ); if ( (fat_sectors & 0xF) != 0 ) { printk("\n[PANIC] in %s : FAT size not multiple of 16 sectors\n", __FUNCTION__ ); return -1; } // get root cluster from boot record uint32_t root_cluster = fatfs_get_remote_record( BPB_FAT32_ROOTCLUS , buffer_xp ); if ( root_cluster != 2 ) { printk("\n[PANIC] in %s : root cluster index must be 2\n", __FUNCTION__ ); return -1; } // get FAT lba from boot record uint32_t fat_lba = fatfs_get_remote_record( BPB_RSVDSECCNT , buffer_xp ); // get FS_INFO sector lba from boot record uint32_t fs_info_lba = fatfs_get_remote_record( BPB_FAT32_FSINFO , buffer_xp ); // load the FS_INFO record from device error = dev_ioc_sync_read( buffer_xp , fs_info_lba , 1 ); if ( error ) { printk("\n[PANIC] in %s : cannot access FS_INFO record\n", __FUNCTION__ ); return -1; } // get free_clusters number from FS_INFO record uint32_t free_clusters = fatfs_get_remote_record( FS_FREE_CLUSTERS , buffer_xp ); if ( free_clusters >= fat_sectors << 7 ) { printk("\n[PANIC] in %s : unconsistent free_clusters\n", __FUNCTION__ ); return -1; } // get free_cluster_hint from FS_INFO record uint32_t free_hint = fatfs_get_remote_record( FS_FREE_CLUSTER_HINT , buffer_xp ); if ( free_hint >= fat_sectors << 7 ) { printk("\n[PANIC] in %s : unconsistent free_cluster_hint\n", __FUNCTION__ ); return -1; } // allocate a mapper for the FAT in remote cluster fat_mapper_xp = mapper_create( cxy , FS_TYPE_FATFS ); // get local pointer on FAT mapper fat_mapper = GET_PTR( fat_mapper_xp ); if ( fat_mapper == NULL ) { printk("\n[PANIC] in %s : no memory for FAT mapper in cluster %x\n", __FUNCTION__ , cxy ); return -1; } #if (DEBUG_FATFS_CTX_INIT & 0x1) if( DEBUG_FATFS_CTX_INIT < cycle ) printk("\n[%s] sector_size %d / nb_sectors %d / fat_sectors %x / hint %x\n", __FUNCTION__, sector_size, nb_sectors, fat_sectors, free_hint ); #endif // the inode field is NULL for the FAT mapper hal_remote_spt( XPTR( cxy , &fat_mapper->inode ) , NULL ); // initialize the FATFS context hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->fat_begin_lba ), fat_lba ); hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->fat_sectors_count ), fat_sectors ); hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->bytes_per_sector ), sector_size ); hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->sectors_per_cluster ), nb_sectors ); hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->cluster_begin_lba ), fat_lba + fat_sectors ); hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->root_dir_cluster ), 2 ); hal_remote_spt( XPTR( cxy , &fatfs_ctx_ptr->fat_mapper ), fat_mapper ); hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->fs_info_lba ), fs_info_lba ); hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->free_clusters ), free_clusters ); hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->free_cluster_hint ), free_hint ); hal_remote_spt( XPTR( cxy , &fatfs_ctx_ptr->fs_info_buffer ), buffer ); // initialize FATFS lock remote_rwlock_init( XPTR( cxy , &fatfs_ctx_ptr->lock ) , LOCK_FATFS_FAT ); #if DEBUG_FATFS_CTX_INIT if( DEBUG_FATFS_CTX_INIT < cycle ) printk("\n[%s] thread[%x,%x] exit for fatfs_ctx (%x,%x)\n", __FUNCTION__, this->process->pid, this->trdid, cxy, fatfs_ctx_ptr ); #endif return 0; } // end fatfs_ctx_init() ////////////////////////////////////////////// void fatfs_ctx_destroy( xptr_t fatfs_ctx_xp ) { mapper_t * fat_mapper; uint8_t * fs_info_buffer; // get FATFS context cluster and local pointer fatfs_ctx_t * fatfs_ctx_ptr = GET_PTR( fatfs_ctx_xp ); cxy_t fatfs_ctx_cxy = GET_CXY( fatfs_ctx_xp ); // get pointer on FAT mapper fat_mapper = hal_remote_lpt( XPTR( fatfs_ctx_cxy , &fatfs_ctx_ptr->fat_mapper ) ); // release FAT mapper mapper_destroy( XPTR( fatfs_ctx_cxy , fat_mapper ) ); // get pointer on FS_INFO buffer fs_info_buffer = hal_remote_lpt( XPTR( fatfs_ctx_cxy , &fatfs_ctx_ptr->fs_info_buffer ) ); // release FS_INFO buffer (512 bytes) kmem_remote_free( fatfs_ctx_cxy, fs_info_buffer, 9 ); // release FATFS context descriptor kmem_remote_free( fatfs_ctx_cxy, fatfs_ctx_ptr, bits_log2(sizeof(fatfs_ctx_t)) ); } // end fatfs_ctx_destroy() ///////////////////////////////////////////////////////// error_t fatfs_add_dentry( xptr_t parent_inode_xp, vfs_dentry_t * dentry_ptr ) { error_t error; vfs_inode_t * parent_inode_ptr; // parent inode local pointer cxy_t parent_cxy; // pparent inode cluster xptr_t child_inode_xp; // extended pointer on child inode cxy_t child_cxy; // child inode cluster vfs_inode_t * child_inode_ptr; // child inode local pointer uint32_t length; // dentry name length uint32_t nb_lfn; // number or required LFN char sfn[11]; // buffer for SFN name uint8_t checksum; // name checksum mapper_t * mapper_ptr; // local pointer on parent directory mapper xptr_t mapper_xp; // extended pointer on parent directory mapper uint32_t size; // child inode size uint32_t type; // child inode type void * extend; // child inode extension uint32_t cluster_id; // child inode first cluster_id in FATFS char child_name[CONFIG_VFS_MAX_NAME_LENGTH]; uint8_t buf[32]; // local buffer for one FAT32 directory entry // check arguments assert( __FUNCTION__, (parent_inode_xp != XPTR_NULL) , "parent_inode_xp argument is NULL\n" ); assert( __FUNCTION__, (dentry_ptr != NULL) , "dentry_ptr argument is NULL\n" ); // get directory inode cluster and local pointer parent_cxy = GET_CXY( parent_inode_xp ); parent_inode_ptr = GET_PTR( parent_inode_xp ); // get extended pointers on child inode child_inode_xp = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) ); child_cxy = GET_CXY( child_inode_xp ); child_inode_ptr = GET_PTR( child_inode_xp ); // get a local copy of the child name vfs_inode_get_name( child_inode_xp , child_name ); #if DEBUG_FATFS_ADD_DENTRY uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; char parent_name[CONFIG_VFS_MAX_NAME_LENGTH]; vfs_inode_get_name( parent_inode_xp , parent_name ); if( DEBUG_FATFS_ADD_DENTRY < cycle ) printk("\n[%s] thread[%x,%x] enter for <%s> in <%s> directory / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cycle ); #endif // get pointers on parent directory mapper mapper_ptr = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) ); mapper_xp = XPTR( parent_cxy , mapper_ptr ); #if (DEBUG_FATFS_ADD_DENTRY & 1) mapper_display_page( mapper_xp , 0 , 512 ); #endif // get relevant infos from child inode type = hal_remote_l32( XPTR( child_cxy , &child_inode_ptr->type ) ); size = hal_remote_l32( XPTR( child_cxy , &child_inode_ptr->size ) ); extend = hal_remote_lpt( XPTR( child_cxy , &child_inode_ptr->extend ) ); cluster_id = (uint32_t)(intptr_t)extend; // analyse child name error = fatfs_name_format( child_name, &length, &nb_lfn, sfn, &checksum ); if ( error ) { printk("\n[ERROR] in %s : dentry name > 31 bytes\n", __FUNCTION__ ); return -1; } // Search end of directory with two embedded loops: // - scan the pages in the mapper // - scan the entries in each page to find NO_MORE_ENTRY xptr_t page_xp; // extended pointer on page descriptor xptr_t base_xp; // extended pointer on page base // initialise loop variables uint32_t page_id = 0; // page index in mapper uint32_t offset = 0; // position in page uint32_t found = 0; // NO_MORE_ENTRY found // loop on pages in mapper while ( found == 0 ) { // get extended pointer on page descriptor in mapper page_xp = mapper_get_page( mapper_xp , page_id ); if ( page_xp == XPTR_NULL ) { printk("\n[ERROR] in %s : cannot extend directory mapper\n", __FUNCTION__ ); return -1; } // get pointer on page base base_xp = ppm_page2base( page_xp ); // loop on directory entries in this page while ( (offset < 4096) && (found == 0) ) { if ( fatfs_get_remote_record( LDIR_ORD, (base_xp + offset) ) == NO_MORE_ENTRY ) { found = 1; } else { offset = offset + 32; } } // end loop on entries if ( found == 0 ) { page_id++; offset = 0; } } // end loop on pages #if (DEBUG_FATFS_ADD_DENTRY & 1) if( DEBUG_FATFS_ADD_DENTRY < cycle ) printk("\n[%s] thread[%x,%x] found NO_MORE_ENTRY : page_id %d / offset %d\n", __FUNCTION__, this->process->pid, this->trdid, page_id, offset ); #endif // Modify the directory mapper: depending on the name length, // the new child requires to write (3, 4, or 5) directory entries. // We build one complete directory entry in a local buffer // before copying it the remote mapper. We use use a 5 steps FSM // (one state per entry to be written), that is traversed as : // LFN3 -> LFN2 -> LFN1 -> NORMAL -> NOMORE // At most two pages are modified: // - the page containing the NO_MORE_ENTRY is always modified // - the following page can be modified if the name spread on two pages. uint32_t step; // FSM state if ( nb_lfn == 1 ) step = 3; else if ( nb_lfn == 2 ) step = 4; else if ( nb_lfn == 3 ) step = 5; uint32_t i; // byte index in one 32 bytes directory uint32_t c; // character index in name while ( step ) { // this block is only executed when the new name spread // on two pages, and we need to access a new page in mapper if ( offset >= 4096 ) { // copy the modified page to IOC device error = fatfs_move_page( page_xp , IOC_SYNC_WRITE ); if ( error ) { printk("\n[ERROR] in %s : cannot update directory on device\n", __FUNCTION__ ); return -1; } // get the next page in directory mapper page_xp = mapper_get_page( mapper_xp , page_id + 1 ); if ( page_xp == XPTR_NULL ) { printk("\n[ERROR] in %s : cannot extend directory mapper\n", __FUNCTION__ ); return -1; } // get pointer on page base base_xp = ppm_page2base( page_xp ); // update offset offset = 0; } #if (DEBUG_FATFS_ADD_DENTRY & 1) cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_ADD_DENTRY < cycle ) printk("\n[%s] FSM step = %d / offset = %x / nb_lfn = %d / cycle %d\n", __FUNCTION__, step, offset, nb_lfn, cycle ); #endif // write one FATFS directory entry (32 bytes) per iteration switch ( step ) { case 5: // write LFN3 entry { c = 26; // write 32 bytes in local buf for ( i = 0 ; i < 32 ; i++ ) { if (i == 0) { if ( nb_lfn == 3) buf[i] = 0x43; else buf[i] = 0x03; } else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1)) || ((i >= 14) && (i<=25) && ((i&1)==0)) || ((i >= 28) && (i<=31) && ((i&1)==0)) ) && ( c < length ) ) { buf[i] = child_name[c]; c++; } else if (i == 11) buf[i] = 0x0F; else if (i == 13) buf[i] = checksum; else buf[i] = 0x00; } // copy 32 bytes from local buffer to remote mapper hal_remote_memcpy( base_xp + offset , XPTR( local_cxy , buf ) , 32 ); step--; break; } case 4: // write LFN2 entry { c = 13; // write 32 bytes in local buf for ( i = 0 ; i < 32 ; i++ ) { if (i == 0) { if ( nb_lfn == 2) buf[i] = 0x42; else buf[i] = 0x02; } else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1)) || ((i >= 14) && (i<=25) && ((i&1)==0)) || ((i >= 28) && (i<=31) && ((i&1)==0)) ) && ( c < length ) ) { buf[i] = child_name[c]; c++; } else if (i == 11) buf[i] = 0x0F; else if (i == 13) buf[i] = checksum; else buf[i] = 0x00; } // copy 32 bytes from local buffer to remote mapper hal_remote_memcpy( base_xp + offset , XPTR( local_cxy , buf ) , 32 ); step--; break; } case 3: // Write LFN1 entry { c = 0; // write 32 bytes in local buf for ( i = 0 ; i < 32 ; i++ ) { if (i == 0) { if ( nb_lfn == 1) buf[i] = 0x41; else buf[i] = 0x01; } else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1)) || ((i >= 14) && (i<=25) && ((i&1)==0)) || ((i >= 28) && (i<=31) && ((i&1)==0)) ) && ( c < length ) ) { buf[i] = child_name[c]; c++; } else if (i == 11) buf[i] = 0x0F; else if (i == 13) buf[i] = checksum; else buf[i] = 0x00; } // copy 32 bytes from local buffer to remote mapper hal_remote_memcpy( base_xp + offset , XPTR( local_cxy , buf ) , 32 ); step--; break; } case 2: // write NORMAL entry { // write 32 bytes in local buf for ( i = 0 ; i < 32 ; i++ ) { if ( i < 11 ) // 8.3 SFN { buf[i] = sfn[i]; } else if (i == 11) // ATTR { if (type == FILE_TYPE_DIR) buf[i] = 0x10; else buf[i] = 0x20; } else if (i == 20) buf[i] = cluster_id>>16; // cluster.B2 else if (i == 21) buf[i] = cluster_id>>24; // cluster.B3 else if (i == 26) buf[i] = cluster_id>>0; // cluster.B0 else if (i == 27) buf[i] = cluster_id>>8; // cluster.B1 else if (i == 28) buf[i] = size>>0; // size.B0 else if (i == 29) buf[i] = size>>8; // size.B1 else if (i == 30) buf[i] = size>>16; // size.B2 else if (i == 31) buf[i] = size>>24; // size.B3 else buf[i] = 0x00; } // copy 32 bytes from local buffer to remote mapper hal_remote_memcpy( base_xp + offset , XPTR( local_cxy , buf ) , 32 ); // set the dentry "extend" field hal_remote_spt( XPTR( parent_cxy , &dentry_ptr->extend ), (void *)(intptr_t)(((page_id << 12) + offset) >> 5 ) ); step--; break; } case 1: // write NOMORE entry { hal_remote_s32( base_xp + offset , 0 ); step--; break; } } // end switch step offset += 32; } // end while // copy the modified page to the IOC device error = fatfs_move_page( page_xp , IOC_SYNC_WRITE ); if ( error ) { printk("\n[ERROR] in %s : cannot update directory on device\n", __FUNCTION__ ); return -1; } #if DEBUG_FATFS_ADD_DENTRY cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_ADD_DENTRY < cycle ) printk("\n[%s] thread[%x,%x] exit for <%s> in <%s> directory\n", __FUNCTION__, this->process->pid, this->trdid, child_name, parent_name ); #endif #if (DEBUG_FATFS_ADD_DENTRY & 1) mapper_display_page( mapper_xp , 0 , 512 ); #endif return 0; } // end fatfs_add_dentry() //////////////////////////////////////////////////////////// error_t fatfs_remove_dentry( xptr_t parent_inode_xp, vfs_dentry_t * dentry_ptr ) { error_t error; vfs_inode_t * parent_inode_ptr; // parent inode local pointer cxy_t parent_cxy; // pparent inode cluster xptr_t child_inode_xp; // extended pointer on child inode cxy_t child_cxy; // child inode cluster vfs_inode_t * child_inode_ptr; // child inode local pointer xptr_t mapper_xp; // extended pointer on mapper mapper_t * mapper_ptr; // local pointer on mapper xptr_t page_xp; // extended pointer on mapper page descriptor xptr_t base_xp; // extended pointer on mapper page base uint32_t dentry_id; // FAT32 directory entry index uint32_t page_id; // page index in directory mapper uint32_t offset; // offset in this mapper page char child_name[CONFIG_VFS_MAX_NAME_LENGTH]; // check arguments assert( __FUNCTION__, (parent_inode_xp != XPTR_NULL) , "parent_inode_xp argument is NULL\n" ); assert( __FUNCTION__, (dentry_ptr != NULL) , "dentry_ptr argument is NULL\n" ); // get directory inode cluster and local pointer parent_cxy = GET_CXY( parent_inode_xp ); parent_inode_ptr = GET_PTR( parent_inode_xp ); // get extended pointers on child inode child_inode_xp = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) ); child_cxy = GET_CXY( child_inode_xp ); child_inode_ptr = GET_PTR( child_inode_xp ); // get a local copy of the child name vfs_inode_get_name( child_inode_xp , child_name ); #if DEBUG_FATFS_REMOVE_DENTRY uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; char parent_name[CONFIG_VFS_MAX_NAME_LENGTH]; vfs_inode_get_name( parent_inode_xp , parent_name ); if( DEBUG_FATFS_REMOVE_DENTRY < cycle ) printk("\n[%s] thread[%x,%x] enter for <%s> in <%s> directory / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cycle ); #endif // get pointers on directory mapper mapper_ptr = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) ); mapper_xp = XPTR( parent_cxy , mapper_ptr ); // compute number of LFN entries uint32_t nb_lfn; uint32_t name_length = strlen( child_name ); if ( name_length <= 13 ) nb_lfn = 1; else if ( name_length <= 26 ) nb_lfn = 2; else nb_lfn = 3; // We must invalidate (2,3,4) 32 bytes entries: // - the NORMAL entry, registered in dentry->extend. // - the (1,2,3) preceding LFN entries. // At most two pages are modified: // - the page containing the NORMAL entry is always modified. // - the preceding page is modified when the name spread on two pages. // get NORMAL entry index from dentry extension dentry_id = (uint32_t)(intptr_t)hal_remote_lpt( XPTR( parent_cxy , &dentry_ptr->extend ) ); // get page index and offset in parent directory mapper page_id = dentry_id >> 7; offset = (dentry_id & 0x7F)<<5; #if DEBUG_FATFS_REMOVE_DENTRY & 1 if( DEBUG_FATFS_REMOVE_DENTRY < cycle ) printk("\n[%s] dentry_id %x / page_id %x / offset %x\n", __FUNCTION__, dentry_id, page_id, offset ); #endif // get extended pointer on page descriptor page_xp = mapper_get_page( mapper_xp , page_id ); if ( page_xp == XPTR_NULL ) { printk("\n[ERROR] in %s : cannot access directory mapper\n", __FUNCTION__ ); return -1; } // get extended pointer on page base base_xp = ppm_page2base( page_xp ); // invalidate NORMAL entry in directory cache hal_remote_sb( base_xp + offset , 0xE5 ); // invalidate LFN entries while ( nb_lfn ) { // this block is only executed when the removed name // spread on two mapper pages if (offset == 0) // we must load page (page_id - 1) { // copy the modified page to the IOC device error = fatfs_move_page( page_xp , IOC_SYNC_WRITE ); if ( error ) { printk("\n[ERROR] in %s : cannot update directory on device\n", __FUNCTION__ ); return -1; } // get extended pointer on page descriptor page_xp = mapper_get_page( mapper_xp , page_id ); if ( page_xp == XPTR_NULL ) { printk("\n[ERROR] in %s : cannot access directory mapper\n", __FUNCTION__ ); return -1; } // get extended pointer on page base base_xp = ppm_page2base( page_xp ); // update offset offset = 4096; } offset = offset - 32; // check for LFN entry assert( __FUNCTION__, (fatfs_get_remote_record( DIR_ATTR, base_xp + offset ) == ATTR_LONG_NAME_MASK ), "this directory entry must be a LFN\n"); // invalidate LFN entry hal_remote_sb( base_xp + offset , 0xE5 ); nb_lfn--; } // copy the modified page to the IOC device error = fatfs_move_page( page_xp , IOC_SYNC_WRITE ); if ( error ) { printk("\n[ERROR] in %s : cannot update directory on device\n", __FUNCTION__ ); return -1; } #if DEBUG_FATFS_REMOVE_DENTRY cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_REMOVE_DENTRY < cycle ) printk("\n[%s] thread[%x,%x] exit for <%s> in <%s> directory\n", __FUNCTION__, this->process->pid, this->trdid, child_name, parent_name ); #endif return 0; } // end fatfs_remove_dentry ///////////////////////////////////////////////////////////////////// error_t fatfs_new_dentry_from_mapper( xptr_t parent_inode_xp, vfs_dentry_t * dentry_ptr ) { uint32_t cluster_id; // directory entry first FATFS cluster uint32_t size; // directory entry size bool_t is_dir; // directory entry type (file/dir) cxy_t parent_cxy; // parent inode cluster vfs_inode_t * parent_inode_ptr; // parent inode local pointer mapper_t * parent_mapper; // pointer on parent directory mapper xptr_t child_inode_xp; // extended pointer on child inode cxy_t child_cxy; // child inode cluster vfs_inode_t * child_inode_ptr; // child inode local pointer error_t error; uint8_t buf[32]; // FAT32 directory entry local copy char parent_name[CONFIG_VFS_MAX_NAME_LENGTH]; // local parent name copy char child_name[CONFIG_VFS_MAX_NAME_LENGTH]; // local child name copy // check arguments assert( __FUNCTION__, (parent_inode_xp != XPTR_NULL) , "parent_inode_xp is NULL\n" ); assert( __FUNCTION__, (dentry_ptr != NULL ) , "dentry_ptr is NULL\n" ); // get parent inode cluster and local pointer parent_cxy = GET_CXY( parent_inode_xp ); parent_inode_ptr = GET_PTR( parent_inode_xp ); // get child inode cluster and pointers child_inode_xp = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) ); child_cxy = GET_CXY( child_inode_xp ); child_inode_ptr = GET_PTR( child_inode_xp ); // get child and parent names vfs_inode_get_name( parent_inode_xp , parent_name ); vfs_inode_get_name( child_inode_xp , child_name ); #if DEBUG_FATFS_NEW_DENTRY_FROM uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; if( DEBUG_FATFS_NEW_DENTRY_FROM < cycle ) printk("\n[%s] thread[%x,%x] enter for child <%s> in parent <%s> / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, child_name , parent_name , cycle ); #endif // get local pointer on parent mapper parent_mapper = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) ); // try to get pointer and index of directory entry in mapper uint8_t * entry = NULL; uint32_t index = 0; error = fatfs_scan_directory( XPTR( parent_cxy , parent_mapper ), child_name, &entry, &index ); // an error can be non fatal, for a new (created) entry if( error ) return -1; // get local copy of found directory entry hal_remote_memcpy( XPTR( local_cxy , buf ), XPTR( parent_cxy , entry ), 32 ); // get relevant infos from directory entry cluster_id = (fatfs_get_record( DIR_FST_CLUS_HI , buf ) << 16) | (fatfs_get_record( DIR_FST_CLUS_LO , buf ) ) ; is_dir = (fatfs_get_record( DIR_ATTR , buf ) & ATTR_DIRECTORY ); size = fatfs_get_record( DIR_FILE_SIZE , buf ); // update the child inode "type", "size", and "extend" fields vfs_file_type_t type = (is_dir) ? FILE_TYPE_DIR : FILE_TYPE_REG; hal_remote_s32( XPTR( child_cxy , &child_inode_ptr->type ) , type ); hal_remote_s32( XPTR( child_cxy , &child_inode_ptr->size ) , size ); hal_remote_s32( XPTR( child_cxy , &child_inode_ptr->extend ) , cluster_id ); // update the dentry "extend" field hal_remote_spt( XPTR( parent_cxy , &dentry_ptr->extend ) , (void *)(intptr_t)index ); #if DEBUG_FATFS_NEW_DENTRY_FROM cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_NEW_DENTRY_FROM < cycle ) printk("\n[%s] thread[%x,%x] exit for <%s> in <%s> / cluster_id %x / size %d\n", __FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cluster_id, size ); #endif #if (DEBUG_FATFS_NEW_DENTRY_FROM & 1) if( DEBUG_FATFS_NEW_DENTRY_FROM < cycle ) fatfs_display_fat( cluster_id , 32 ); #endif return 0; } // end fatfs_new_dentry_from_mapper() /////////////////////////////////////////////////////////////////// error_t fatfs_new_dentry_to_mapper( xptr_t parent_inode_xp, vfs_dentry_t * dentry_ptr ) { uint32_t cluster_id; // directory entry cluster cxy_t parent_cxy; // parent inode cluster identifier vfs_inode_t * parent_inode_ptr; // child inode local pointer xptr_t child_inode_xp; // extended pointer on child inode cxy_t child_cxy; // child inode cluster identifier vfs_inode_t * child_inode_ptr; // child inode local pointer error_t error; char parent_name[CONFIG_VFS_MAX_NAME_LENGTH]; char child_name[CONFIG_VFS_MAX_NAME_LENGTH]; // check arguments assert( __FUNCTION__, (parent_inode_xp != XPTR_NULL) , "parent_inode_xp argument is NULL\n" ); assert( __FUNCTION__, (dentry_ptr != NULL) , "dentry_ptr argument NULL\n" ); // get child inode cluster and local pointer parent_cxy = GET_CXY( parent_inode_xp ); parent_inode_ptr = GET_PTR( parent_inode_xp ); // get child inode cluster and pointers child_inode_xp = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) ); child_cxy = GET_CXY( child_inode_xp ); child_inode_ptr = GET_PTR( child_inode_xp ); // get child and parent names vfs_inode_get_name( parent_inode_xp , parent_name ); vfs_inode_get_name( child_inode_xp , child_name ); #if DEBUG_FATFS_NEW_DENTRY_TO_MAP uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; if( DEBUG_FATFS_NEW_DENTRY_TO_MAP < cycle ) printk("\n[%s] thread[%x,%x] enter for child <%s> in parent <%s> / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, child_name , parent_name , cycle ); #endif // 1. allocate one FATFS cluster (update FAT and FSINFO) error = fatfs_cluster_alloc( 0 , &cluster_id ); if( error ) { printk("\n[ERROR] in %s : cannot find a free cluster_id\n", __FUNCTION__ ); return -1; } // 2. register cluster_id in inode descriptor hal_remote_spt( XPTR( child_cxy , &child_inode_ptr->extend ), (void*)(intptr_t)cluster_id ); // 3. introduce dentry in the directory mapper error = fatfs_add_dentry( parent_inode_xp , dentry_ptr ); if( error ) { printk("\n[ERROR] in %s : cannot update parent directory mapper\n", __FUNCTION__ ); // TODO release cluster_id [AG] return -1; } #if DEBUG_FATFS_NEW_DENTRY_TO_MAP if( DEBUG_FATFS_NEW_DENTRY_TO_MAP < cycle ) printk("\n[%s] thread[%x,%x] exit for <%s> in <%s> / cluster_id %x\n", __FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cluster_id ); #endif return 0; } // end fatfs_new_dentry_to mapper() //////////////////////////////////////////////////////////// error_t fatfs_update_dentry( xptr_t parent_inode_xp, vfs_dentry_t * dentry_ptr ) { cxy_t parent_cxy; // parent directory cluster identifier vfs_inode_t * parent_inode_ptr; // extended pointer on parent directory inode mapper_t * parent_mapper_ptr; // local pointer on parent directory mapper xptr_t parent_mapper_xp; // extended pointer on parent directory mapper xptr_t child_inode_xp; // extended pointer on child inode cxy_t child_cxy; // child inode cluster identifier vfs_inode_t * child_inode_ptr; // extended pointer on child inode uint32_t current_size; // current size in directory mapper uint32_t new_size; // new size (from child inode) uint32_t entry_id; // directory entry index in parent directory mapper uint32_t page_id; // page_index in parent directory mapper uint32_t offset; // directory entry offset in page xptr_t page_xp; // extended pointer on page descriptor xptr_t base_xp; // extended pointer on page base xptr_t entry_xp; // extended pointer on directory entry error_t error; char parent_name[CONFIG_VFS_MAX_NAME_LENGTH]; char child_name[CONFIG_VFS_MAX_NAME_LENGTH]; // check arguments assert( __FUNCTION__, (parent_inode_xp != XPTR_NULL) , "parent_inode_xp argument is NULL\n" ); assert( __FUNCTION__, (dentry_ptr != NULL) , "dentry_ptr argument is NULL\n" ); // get parent directory cluster ans local pointer parent_inode_ptr = GET_PTR( parent_inode_xp ); parent_cxy = GET_CXY( parent_inode_xp ); // get extended pointer on child inode child_inode_xp = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) ); // get child and parent names vfs_inode_get_name( parent_inode_xp , parent_name ); vfs_inode_get_name( child_inode_xp , child_name ); #if DEBUG_FATFS_UPDATE_DENTRY uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; if( DEBUG_FATFS_UPDATE_DENTRY < cycle ) printk("\n[%s] thread[%x,%x] enter for <%s> in <%s> / new_size %d / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, new_size, cycle ); #endif // get child inode cluster and local pointer child_cxy = GET_CXY( child_inode_xp ); child_inode_ptr = GET_PTR( child_inode_xp ); // get size from child inode new_size = hal_remote_l32( XPTR( child_cxy , &child_inode_ptr->size ) ); // get local and extended pointers on parent directory mapper parent_mapper_ptr = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) ); parent_mapper_xp = XPTR( parent_cxy , parent_mapper_ptr ); // get directory entry index from dentry extension entry_id = (intptr_t)hal_remote_lpt( XPTR( parent_cxy , &dentry_ptr->extend ) ); // get page index and offset in parent directory mapper page_id = entry_id >> 7; offset = (entry_id & 0x7F) << 5; // get extended pointers on page descriptor and page base page_xp = mapper_get_page( parent_mapper_xp , page_id ); base_xp = ppm_page2base( page_xp ); // build extended pointer on directory entry entry_xp = base_xp + offset; // get current size from directory mapper current_size = fatfs_get_remote_record( DIR_FILE_SIZE , entry_xp ); // update dentry in mapper & device only if required if( new_size != current_size ) { // set size field in FAT32 directory entry fatfs_set_remote_record( DIR_FILE_SIZE , entry_xp , new_size ); // synchronously update the modified mapper page on device error = fatfs_move_page( page_xp , IOC_SYNC_WRITE ); if( error ) { printk("\n[ERROR] in %s : cannot update parent directory <%s> on device\n", __FUNCTION__, parent_name ); return -1; } } #if DEBUG_FATFS_UPDATE_DENTRY cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_UPDATE_DENTRY < cycle ) printk("\n[%s] thread[%x,%x] exit for <%s> in <%s> directory / size %d / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, parent_name, child->name, new_size, cycle ); #endif return 0; } // end fatfs_update_dentry() /////////////////////////////////////////////////////// error_t fatfs_get_user_dir( struct vfs_inode_s * inode, struct dirent * array, uint32_t max_dirent, uint32_t min_dentry, bool_t detailed, uint32_t * entries, bool_t * done ) { // Two embedded loops to scan the directory mapper: // - scan the parent directory mapper pages starting always from page 0 // - scan the 32 bytes NORMAL/LFN directory entries in each page // Only valid dentries are copied : dentry_id >= min_dentry && dirent_id < dirent_max #if DEBUG_FATFS_GET_USER_DIR char inode_name[CONFIG_VFS_MAX_NAME_LENGTH]; uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; vfs_inode_get_name( XPTR( local_cxy , inode ) , inode_name ); if( DEBUG_FATFS_GET_USER_DIR < cycle ) printk("\n[%s] thread[%x,%x] enter for inode <%s> / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, inode_name , cycle ); #endif mapper_t * mapper = inode->mapper; xptr_t mapper_xp = XPTR( local_cxy , mapper ); // check mapper pointer assert( __FUNCTION__, (mapper != NULL) , "mapper is NULL\n"); // TODO handle the detailed flag assert( __FUNCTION__, (detailed == false), "detailed argument not supported/n"); char cname[CONFIG_VFS_MAX_NAME_LENGTH]; // name extracted from each dentry char lfn1[16]; // buffer for one partial cname char lfn2[16]; // buffer for one partial cname char lfn3[16]; // buffer for one partial cname xptr_t page_xp; // extended pointer on page descriptor xptr_t base_xp; // extended pointer on page base uint8_t * base; // local pointer on page base uint8_t attr; // directory entry ATTR field uint8_t ord; // directory entry ORD field uint32_t seq; // sequence index uint32_t lfn = 0; // LFN entries number uint32_t offset = 0; // byte offset in page uint32_t page_id = 0; // page index in mapper uint32_t dentry_id = 0; // valid (i.e. copied) dentry index in mapper uint32_t dirent_id = 0; // slot index in dirent array to initialize bool_t end = false; // true if end of directory found // loop on mapper pages while ( (end == false) && (dirent_id < max_dirent) ) { // get one page from mapper page_xp = mapper_get_page( mapper_xp , page_id ); if( page_xp == XPTR_NULL) return -1; // get page base base_xp = ppm_page2base( page_xp ); base = (uint8_t *)GET_PTR( base_xp ); #if (DEBUG_FATFS_GET_USER_DIR & 0x1) if( DEBUG_FATFS_GET_USER_DIR < cycle ) mapper_display_page( mapper_xp , page_id , 256 ); #endif // loop on NORMAL/LFN (32 bytes) directory entries in this page while( (end == false) && (offset < 4096) ) { // compute condition to copy one dentry to dirent array bool_t valid = (dentry_id >= min_dentry) && (dirent_id < max_dirent ); attr = fatfs_get_record( DIR_ATTR , base + offset ); ord = fatfs_get_record( LDIR_ORD , base + offset ); if (ord == NO_MORE_ENTRY) // no more entry => break { end = true; } else if ( ord == FREE_ENTRY ) // free entry => skip { offset = offset + 32; } else if ( attr == 0x28 ) // volune_id => skip { offset = offset + 32; } else if ( attr == ATTR_LONG_NAME_MASK ) // LFN entry { if( valid ) { // get partial cname seq = ord & 0x3; lfn = (seq > lfn) ? seq : lfn; if ( seq == 1 ) fatfs_get_name_from_long( base + offset, lfn1 ); else if ( seq == 2 ) fatfs_get_name_from_long( base + offset, lfn2 ); else if ( seq == 3 ) fatfs_get_name_from_long( base + offset, lfn3 ); } offset = offset + 32; } else // NORMAL entry { // increment dentry_id dentry_id++; if( valid ) { // build the complete cname if ( lfn == 0 ) { fatfs_get_name_from_short( base + offset , cname ); } else if ( lfn == 1 ) { strcpy( cname , lfn1 ); } else if ( lfn == 2 ) { strcpy( cname , lfn1 ); strcpy( cname + 13 , lfn2 ); } else if ( lfn == 3 ) { strcpy( cname , lfn1 ); strcpy( cname + 13 , lfn2 ); strcpy( cname + 26 , lfn3 ); } // copy cname into dirent array strcpy( array[dirent_id].d_name , cname ); // increment dirent_id dirent_id++; } offset = offset + 32; lfn = 0; } } // end loop on directory entries in page page_id++; offset = 0; } // end loop on pages // return result of scan *done = end; *entries = dirent_id; #if DEBUG_FATFS_GET_USER_DIR cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_GET_USER_DIR < cycle ) printk("\n[%s] thread[%x,%x] exit for inode <%s> / %d entries / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, inode_name, dirent_id, cycle ); #endif return 0; } // end fatfs_get_user_dir() /////////////////////////////////////////// error_t fatfs_sync_inode( xptr_t inode_xp ) { cxy_t inode_cxy; // remote inode cluster vfs_inode_t * inode_ptr; // remote inode local pointer mapper_t * mapper; // remote inode mapper local pointer uint32_t size; // remote inode size in bytes uint32_t type; // remote inode type xptr_t rt_xp; // extended pointer on mapper radix tree uint32_t npages; // number of pages in mapper uint32_t page_id; // current page index in mapper xptr_t page_xp; // extended pointer on current page page_t * page_ptr; // local pointer on current page uint32_t flags; // current page flags error_t error; // check inode pointer and cluster index assert( __FUNCTION__, (inode_xp != XPTR_NULL) , "inode pointer undefined\n" ); #if DEBUG_FATFS_SYNC_INODE char name[CONFIG_VFS_MAX_NAME_LENGTH]; uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; vfs_inode_get_name( inode_xp , name ); if( DEBUG_FATFS_SYNC_INODE < cycle ) printk("\n[%s] thread[%x,%x] enter for <%s> / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, name, cycle ); #endif // get inode cluster and local pointer inode_cxy = GET_CXY( inode_xp ); inode_ptr = GET_PTR( inode_xp ); //get inode mapper pointer mapper = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) ); assert( __FUNCTION__, (mapper != NULL) , "mapper pointer is NULL\n" ); // get inode type and size size = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->size ) ); type = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type ) ); assert( __FUNCTION__, (type == FILE_TYPE_REG) , "inode is not a file\n" ); // compute number of pages npages = size >> CONFIG_PPM_PAGE_ORDER; if( size & CONFIG_PPM_PAGE_MASK ) npages++; // build pointers on mapper radix tree rt_xp = XPTR( inode_cxy , &mapper->rt ); // scan all pages for( page_id = 0 ; page_id < npages ; page_id++ ) { // get page descriptor from mapper page_xp = grdxt_remote_lookup( rt_xp , page_id ); // check all existing pages if ( page_xp != XPTR_NULL ) { // get page cluster and local pointer page_ptr = GET_PTR( page_xp ); // get page flags flags = hal_remote_l32( XPTR( inode_cxy , &page_ptr->flags ) ); if ( flags & PG_DIRTY ) { #if (DEBUG_FATFS_SYNC_INODE & 1) if( DEBUG_FATFS_SYNC_INODE < cycle ) printk("\n[%s] thread[%x,%x] synchronizes page %d of <%s> mapper to IOC device\n", __FUNCTION__, page_id, name ); #endif // move page from mapper to device error = fatfs_move_page( page_xp , IOC_WRITE ); if ( error ) return -1; // reset page dirty flag ppm_page_undo_dirty( page_xp ); } } } // end loop on pages #if DEBUG_FATFS_SYNC_INODE cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_SYNC_INODE < cycle ) printk("\n[%s] thread[%x,%x] exit for <%s>\n", __FUNCTION__ , this->process->pid, this->trdid, name ); #endif return 0; } // end fatfs_sync_inode() ////////////////////////////// error_t fatfs_sync_fat( void ) { fatfs_ctx_t * fatfs_ctx; cxy_t fat_cxy; mapper_t * mapper_ptr; xptr_t mapper_xp; uint32_t start_page_id; uint32_t found_page_id; page_t * page_ptr; xptr_t page_xp; uint32_t flags; error_t error; #if DEBUG_FATFS_SYNC_FAT uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; if( DEBUG_FATFS_SYNC_FAT < cycle ) printk("\n[%s] thread[%x,%x] enter / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, cycle ); #endif // get FAT cluster fat_cxy = CONFIG_VFS_ROOT_CXY; // get FAT mapper pointers fatfs_ctx = fs_context[FS_TYPE_FATFS].extend; mapper_ptr = fatfs_ctx->fat_mapper; mapper_xp = XPTR( fat_cxy , mapper_ptr ); // get pointers on remote FAT mapper radix tree grdxt_t * rt_ptr = &mapper_ptr->rt; xptr_t rt_xp = XPTR( fat_cxy , rt_ptr ); // initialise page_id start_page_id = 0; // scan FAT mapper while( 1 ) { // get one page page_xp = grdxt_remote_get_first( rt_xp , start_page_id , &found_page_id ); // exit loop when no more page found if ( page_xp != XPTR_NULL ) break; // get page flags page_ptr = GET_PTR( page_xp ); flags = hal_remote_l32( XPTR( fat_cxy , &page_ptr->flags ) ); if ( flags & PG_DIRTY ) { #if (DEBUG_FATFS_SYNC_FAT & 1) if( DEBUG_FATFS_SYNC_FAT < cycle ) printk("\n[%s] thread[%x,%x] synchronizes page %d from FAT mapper to IOC device\n", __FUNCTION__, page_id ); #endif // move page from mapper to device error = fatfs_move_page( page_xp , IOC_SYNC_WRITE ); if ( error ) return -1; // reset page dirty flag ppm_page_undo_dirty( page_xp ); } // update loop variable start_page_id = found_page_id + 1; } // end loop on pages #if DEBUG_FATFS_SYNC_FAT cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_SYNC_FAT < cycle ) printk("\n[%s] thread[%x,%x] exit\n", __FUNCTION__ , this->process->pid, this->trdid ); #endif return 0; } // end fatfs_sync_fat() ////////////////////////////////////////////// error_t fatfs_release_inode( xptr_t inode_xp ) { vfs_ctx_t * vfs_ctx; // local pointer on VFS context (same in all clusters) cxy_t fat_cxy; // FAT cluster identifier fatfs_ctx_t * fatfs_ctx_ptr; // local pointer on FATFS context in FAT cluster xptr_t fatfs_ctx_xp; // extended pointer on FATFS-context in FAT cluster mapper_t * fat_mapper_ptr; // local pointer on FAT mapper xptr_t fat_mapper_xp; // extended pointer on FAT mapper xptr_t lock_xp; // extended pointer on lock protecting FAT. xptr_t first_xp; // extended pointer on inode extension uint32_t first_cluster_id; // first cluster index for released inode vfs_inode_t * inode_ptr; // local pointer on target inode cxy_t inode_cxy; // target inode cluster identifier error_t error; // check inode pointer assert( __FUNCTION__, (inode_xp != XPTR_NULL) , "inode pointer is NULL\n" ); // get inode cluster and local pointer inode_ptr = GET_PTR( inode_xp ); inode_cxy = GET_CXY( inode_xp ); // get first_cluster_id from inode extension first_xp = XPTR( inode_cxy , &inode_ptr->extend ); first_cluster_id = (uint32_t)(intptr_t)hal_remote_lpt( first_xp ); // check first cluster index assert( __FUNCTION__, (first_cluster_id != 0) , "inode extend is NULL\n" ); #if DEBUG_FATFS_RELEASE_INODE char name[CONFIG_VFS_MAX_NAME_LENGTH]; uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; vfs_inode_get_name( inode_xp , name ); if( DEBUG_FATFS_RELEASE_INODE < cycle ) printk("\n[%s] thread[%x,%x] enter for <%s> / first_cluster_id %x / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, name, first_cluster_id, cycle ); #endif // get local pointer on VFS context (same in all clusters) vfs_ctx = &fs_context[FS_TYPE_FATFS]; // get FAT cluster fat_cxy = CONFIG_VFS_ROOT_CXY; // get pointers on FATFS context in FAT cluster fatfs_ctx_ptr = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) ); fatfs_ctx_xp = XPTR( fat_cxy , fatfs_ctx_ptr ); // get FAT mapper pointers fat_mapper_ptr = hal_remote_lpt( XPTR( fat_cxy , &fatfs_ctx_ptr->fat_mapper ) ); fat_mapper_xp = XPTR( fat_cxy , fat_mapper_ptr ); // build extended pointer on FAT lock in FAT cluster lock_xp = XPTR( fat_cxy , &fatfs_ctx_ptr->lock ); // take FAT lock in write mode remote_rwlock_wr_acquire( lock_xp ); #if (DEBUG_FATFS_RELEASE_INODE & 0x11 == 0x11) mapper_display_page( fat_mapper_xp , 0 , 4096 ); #endif // call the recursive function to release all clusters from FAT mapper uint32_t dirty_page_min = 0xFFFFFFFF; uint32_t dirty_page_max = 0; if ( fatfs_recursive_release( fat_mapper_xp, fatfs_ctx_xp, first_cluster_id, &dirty_page_min, &dirty_page_max ) ) { printk("\n[ERROR] in %s : cannot update FAT mapper\n", __FUNCTION__ ); remote_rwlock_wr_release( lock_xp ); return -1; } #if (DEBUG_FATFS_RELEASE_INODE & 1) if( DEBUG_FATFS_RELEASE_INODE < cycle ) printk("\n[%s] inode <%s> removed from FAT mapper\n", __FUNCTION__, name ); #endif #if (DEBUG_FATFS_RELEASE_INODE & 0x11 == 0x11) mapper_display_page( fat_mapper_xp , 0 , 4096 ); #endif // update FAT on IOC device (from FAT mapper) error = fatfs_update_ioc_fat( fatfs_ctx_xp, dirty_page_min, dirty_page_max ); if( error ) { printk("\n[ERROR] in %s : cannot update FAT on IOC device\n", __FUNCTION__ ); remote_rwlock_wr_release( lock_xp ); return -1; } #if (DEBUG_FATFS_RELEASE_INODE & 1) if( DEBUG_FATFS_RELEASE_INODE < cycle ) printk("\n[%s] inode <%s> removed from FAT on IOC device\n", __FUNCTION__, name ); #endif // update FS-INFO on IOC device (from FATFS context) error = fatfs_update_ioc_fsinfo( fatfs_ctx_xp ); if( error ) { printk("\n[ERROR] in %s: cannot update FSINFO on IOC device\n", __FUNCTION__ ); remote_rwlock_wr_release( lock_xp ); return -1; } #if (DEBUG_FATFS_RELEASE_INODE & 1) if( DEBUG_FATFS_RELEASE_INODE < cycle ) printk("\n[%s] updated FS_INFO on IOC device for >%s>\n", __FUNCTION__, name ); #endif // release FAT lock remote_rwlock_wr_release( lock_xp ); #if DEBUG_FATFS_RELEASE_INODE cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_RELEASE_INODE < cycle ) printk("\n[%s] thread[%x,%x] removed <%s> inode from FATFS / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, name, cycle ); #endif return 0; } // end fatfs_release_inode() ///////////////////////////////////////////////// error_t fatfs_move_page( xptr_t page_xp, ioc_cmd_type_t cmd_type ) { error_t error = 0; vfs_inode_t * inode_ptr; mapper_t * mapper_ptr; uint32_t page_id; // page index in mapper #if DEBUG_FATFS_MOVE_PAGE uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; char name[CONFIG_VFS_MAX_NAME_LENGTH]; #endif // get page cluster and local pointer cxy_t page_cxy = GET_CXY( page_xp ); page_t * page_ptr = GET_PTR( page_xp ); // get mapper pointer and page index from page descriptor mapper_ptr = hal_remote_lpt( XPTR( page_cxy , &page_ptr->mapper ) ); page_id = hal_remote_l32( XPTR( page_cxy , &page_ptr->index ) ); // get pointer on local FATFS context fatfs_ctx_t * fatfs_ctx = fs_context[FS_TYPE_FATFS].extend; // get page base address xptr_t buffer_xp = ppm_page2base( page_xp ); // get inode pointer from mapper inode_ptr = hal_remote_lpt( XPTR( page_cxy , &mapper_ptr->inode ) ); ////////////////////////////// FAT mapper ///////////////////////////////////////// if( inode_ptr == NULL ) { #if DEBUG_FATFS_MOVE_PAGE if( DEBUG_FATFS_MOVE_PAGE < cycle ) printk("\n[%s] thread[%x,%x] enters %s for page %d in FAT mapper / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, dev_ioc_cmd_str(cmd_type), page_id, cycle ); #endif // get lba from FATFS context and page_id uint32_t lba = fatfs_ctx->fat_begin_lba + (page_id << 3); // access IOC device if (cmd_type == IOC_SYNC_WRITE) error = dev_ioc_sync_write( buffer_xp, lba, 8 ); else if(cmd_type == IOC_SYNC_READ ) error = dev_ioc_sync_read( buffer_xp, lba, 8 ); else { printk("\n[ERROR] in %s : illegal asynchronous FAT access\n", __FUNCTION__ ); } if( error ) { printk("\n[ERROR] in %s : cannot access IOC device\n", __FUNCTION__ ); return -1; } #if DEBUG_FATFS_MOVE_PAGE if( DEBUG_FATFS_MOVE_PAGE < cycle ) printk("\n[%s] thread[%x,%x] exit %s for page %d in FAT mapper\n", __FUNCTION__, this->process->pid, this->trdid, dev_ioc_cmd_str(cmd_type), page_id ); #endif } ///////////////////////// inode mapper //////////////////////////////////////////// else { #if DEBUG_FATFS_MOVE_PAGE if( DEBUG_FATFS_MOVE_PAGE < cycle ) { vfs_inode_get_name( XPTR( page_cxy , inode_ptr ) , name ); printk("\n[%s] thread[%x,%x] enters %s for page %d in <%s> mapper / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, dev_ioc_cmd_str( cmd_type ), page_id, name, cycle ); } #endif uint32_t searched_cluster_id; uint32_t first_cluster_id; // get first_cluster_id from inode extension void * extend = hal_remote_lpt( XPTR( page_cxy , &inode_ptr->extend ) ); first_cluster_id = (uint32_t)(intptr_t)extend; // compute searched_cluster_id if( page_id == 0 ) // no need to access FAT mapper { // searched cluster is first cluster searched_cluster_id = first_cluster_id; } else // FAT mapper access required { // scan FAT mapper to get searched_cluster_id error = fatfs_get_cluster( 0, // first page in mapper first_cluster_id, page_id, &searched_cluster_id ); if( error ) { printk("\n[ERROR] in %s : cannot get cluster_id\n", __FUNCTION__ ); return -1; } } // get lba for searched_cluster uint32_t lba = fatfs_lba_from_cluster( fatfs_ctx , searched_cluster_id ); // access IOC device if (cmd_type == IOC_WRITE ) error = dev_ioc_write( buffer_xp, lba, 8 ); else if(cmd_type == IOC_READ ) error = dev_ioc_read( buffer_xp, lba, 8 ); else if(cmd_type == IOC_SYNC_READ ) error = dev_ioc_sync_read( buffer_xp, lba, 8 ); else if(cmd_type == IOC_SYNC_WRITE) error = dev_ioc_sync_write( buffer_xp, lba, 8 ); else { printk("\n[ERROR] in %s : illegal cmd_type\n", __FUNCTION__ ); } if( error ) { printk("\n[ERROR] in %s : cannot access device\n", __FUNCTION__ ); return -1; } #if DEBUG_FATFS_MOVE_PAGE if( DEBUG_FATFS_MOVE_PAGE < cycle ) { printk("\n[%s] thread[%x,%x] exit %s for page %d in <%s> mapper / cluster_id %x\n", __FUNCTION__, this->process->pid, this->trdid, dev_ioc_cmd_str( cmd_type ), page_id, name, searched_cluster_id ); } #endif #if (DEBUG_FATFS_MOVE_PAGE & 1) if( DEBUG_FATFS_MOVE_PAGE < cycle ) fatfs_display_fat( searched_cluster_id , 64 ); #endif } return 0; } // end fatfs_move_page()