/* * fatfs.c - FATFS file system API implementation. * * Author Alain Greiner (2016,2017,2018) * * 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 ////////////////////////////////////////////////////////////////////////////////////////// // 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 memory buffer, taking into account endianness. ////////////////////////////////////////////////////////////////////////////////////////// // @ offset : first byte of record in buffer. // @ size : record length in bytes (1/2/4). // @ buffer : pointer on buffer base. // @ little endian : the most significant byte has the highest address when true. // @ return the integer value in a 32 bits word. ////////////////////////////////////////////////////////////////////////////////////////// static uint32_t fatfs_get_record( uint32_t offset, uint32_t size, uint8_t * buffer, uint32_t little_endian ) { uint32_t n; uint32_t res = 0; if ( little_endian ) { for( n = size ; n > 0 ; n-- ) res = (res<<8) | buffer[offset+n-1]; } else // big_endian { for( n = 0 ; n < size ; n++ ) res = (res<<8) | buffer[offset+n]; } return res; } // end fatfs_get_record() ////////////////////////////////////////////////////////////////////////////////////////// // This function writes one, two, or four bytes from a 32 bits integer to a memory buffer, // taking into account endianness. ////////////////////////////////////////////////////////////////////////////////////////// // @ offset : first byte of record in buffer. // @ size : record length in bytes (1/2/4). // @ buffer : pointer on buffer base. // @ little endian : the most significant byte has the highest address when true. // @ return the integer value in a 32 bits word. ////////////////////////////////////////////////////////////////////////////////////////// static void fatfs_set_record( uint32_t offset, uint32_t size, uint8_t * buffer, uint32_t little_endian, uint32_t value ) { uint32_t n; if ( little_endian ) { for( n = size ; n > 0 ; n-- ) buffer[offset+n-1] = (uint8_t)(value>>((n-1)<<3)); } else // big_endian { for( n = 0 ; n < size ; n++ ) buffer[offset+n] = (uint8_t)(value>>((size-1-n)<<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 - atomically - decrements "free_clusters", and updates // the "free_cluster_hint" shared variables in the FATFS context in the FAT cluster. // It scan the FAT to find the first free slot larger than the argument, // and set "free_cluster_hint" <= (free - 1). // // WARNING : The free_lock protecting exclusive access to these variables // must be taken by the calling function. ////////////////////////////////////////////////////////////////////////////////////////// // @ cluster : recently allocated cluster index in FAT. ////////////////////////////////////////////////////////////////////////////////////////// static error_t fatfs_free_clusters_decrement( uint32_t cluster ) { fatfs_ctx_t * loc_ctx; // local pointer on local FATFS context fatfs_ctx_t * fat_ctx; // local pointer on FATFS in cluster containing FAT mapper cxy_t mapper_cxy; // cluster identifier for cluster containing FAT mapper xptr_t 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 numb; // "free_clusters" variable current value 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 slot_xp; // extended pointer on current slot in FAT mapper #if DEBUG_FATFS_FREE_CLUSTERS uint32_t cycle = (uint32_t)hal_get_cycles(); thread_t * this = CURRENT_THREAD; if( DEBUG_FATFS_FREE_CLUSTERS < (uint32_t)hal_get_cycles() ) printk("\n[%s] thread[%x,%x] enter for allocated cluster %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, cluster , cycle ); #endif // get local pointer on local FATFS context loc_ctx = fs_context[FS_TYPE_FATFS].extend; // get cluster containing FAT mapper mapper_xp = loc_ctx->fat_mapper_xp; mapper_cxy = GET_CXY( mapper_xp ); // get local pointer on FATFS context in FAT cluster fat_ctx = hal_remote_lpt( XPTR( mapper_cxy , &fs_context[FS_TYPE_FATFS].extend ) ); // build extended pointers on free_clusters, and free_cluster_hint hint_xp = XPTR( mapper_cxy , &fat_ctx->free_cluster_hint ); numb_xp = XPTR( mapper_cxy , &fat_ctx->free_clusters ); // update "free_clusters" numb = hal_remote_l32( numb_xp ); hal_remote_s32( numb_xp , numb - 1 ); // scan FAT mapper to find the first free slot > cluster // and update "free_cluster_hint" as (free - 1) page_id = (cluster + 1) >> 10; slot_id = (cluster + 1) & 0x3FF; page_max = (loc_ctx->fat_sectors_count >> 3); // scan FAT mapper / loop on pages while ( page_id < page_max ) { // get current page from mapper page_xp = mapper_remote_get_page( mapper_xp , page_id ); if( page_xp == XPTR_NULL ) { printk("\n[ERROR] in %s : cannot access FAT mapper\n", __FUNCTION__ ); return -1; } // scan FAT mapper / loop on slots while ( slot_id < 1024 ) { // get extended pointer on current slot slot_xp = ppm_page2base( page_xp ) + (slot_id << 2); // test FAT slot value if ( hal_remote_l32( slot_xp ) == FREE_CLUSTER ) { // update "free_cluster_hint" <= (free - 1) hal_remote_s32( hint_xp , (page_id << 10) + slot_id - 1 ); #if DEBUG_FATFS_FREE_CLUSTERS cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_FREE_CLUSTERS < (uint32_t)hal_get_cycles() ) printk("\n[%s] thread[%x,%x] exit / hint %x / free_clusters %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, hal_remote_l32(hint_xp), hal_remote_l32(numb_xp), cycle ); #endif return 0; } // increment slot_id slot_id++; } // end loop on slots // update loop variables page_id++; slot_id = 0; } // end loop on pages // return error if no free cluster found printk("\n[ERROR] in %s : No free cluster found\n", __FUNCTION__ ); return -1; } // end fatfs_free_clusters_decrement() ////////////////////////////////////////////////////////////////////////////////////////// // This static function atomically increments , and updates // the shared variables in the FATFS context in the FAT cluster. // If the released cluster index is smaller than the current (hint + 1) value, // it set "free_cluster_hint" <= cluster - 1. // // WARNING : The free_lock protecting exclusive access to these variables // must be taken by the calling function. ////////////////////////////////////////////////////////////////////////////////////////// // @ cluster : recently released cluster index in FAT. ////////////////////////////////////////////////////////////////////////////////////////// static void fatfs_free_clusters_increment( uint32_t cluster ) { fatfs_ctx_t * loc_ctx; // local pointer on local FATFS context fatfs_ctx_t * fat_ctx; // local pointer on FATFS in cluster containing FAT mapper cxy_t fat_cxy; // cluster identifier for cluster containing 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 hint; // "free_cluster_hint" variable current value uint32_t numb; // "free_clusters" variable current value // get local pointer on local FATFS context loc_ctx = fs_context[FS_TYPE_FATFS].extend; // get cluster containing FAT mapper fat_cxy = GET_CXY( loc_ctx->fat_mapper_xp ); // get local pointer on FATFS context in FAT cluster fat_ctx = hal_remote_lpt( XPTR( fat_cxy , &fs_context[FS_TYPE_FATFS].extend ) ); // build extended pointers free_lock, free_clusters, and free_cluster_hint hint_xp = XPTR( fat_cxy , &fat_ctx->free_cluster_hint ); numb_xp = XPTR( fat_cxy , &fat_ctx->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 free_cluster_hint if required if ( cluster < (hint + 1) ) hal_remote_s32( hint_xp , (cluster - 1) ); // update free_clusters hal_remote_s32( numb_xp , numb + 1 ); #if DEBUG_FATFS_FREE_CLUSTERS thread_t * this = CURRENT_THREAD; if( DEBUG_FATFS_FREE_CLUSTERS < (uint32_t)hal_get_cycles() ) printk("\n[%s] thread[%x,%x] updates free cluster info : hint %x / number %x\n", __FUNCTION__, this->process->pid, this->trdid, hal_remote_l32( hint_xp ), hal_remote_l32( numb_xp ) ); #endif } // end fatfs_free_clusters_increment() ////////////////////////////////////////////////////////////////////////////////////////// // This recursive function is called by the generic function fatfs_release_all_clusters() // It release all clusters allocated to a given inode in the FAT mapper. // The removal is done in reverse order of the linked list (from last to first). // It does NOT update the FS on the IOC device. ////////////////////////////////////////////////////////////////////////////////////////// // @ fat_mapper_xp : extended pointer on FAT mapper. // @ cluster : cluster index in FAT. // @ return 0 if success / return -1 if error (cannot access FAT) ////////////////////////////////////////////////////////////////////////////////////////// static error_t fatfs_recursive_release( xptr_t fat_mapper_xp, uint32_t cluster ) { uint32_t next; // get next cluster from FAT mapper if ( mapper_remote_get_32( fat_mapper_xp , cluster , &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 , next ) ) return -1; } // update current cluster in FAT mapper if ( mapper_remote_set_32( fat_mapper_xp, cluster , FREE_CLUSTER ) ) return -1; // Update free_cluster_hint and free_clusters in FAT context fatfs_free_clusters_increment( cluster ); return 0; } // end fatfs_recursive_release() ////////////////////////////////////////////////////////////////////////////////////////// // FATFS specific extern functions ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////// void fatfs_ctx_display( void ) { // get pointer on local FATFS context vfs_ctx_t * vfs_ctx = &fs_context[FS_TYPE_FATFS]; fatfs_ctx_t * fatfs_ctx = (fatfs_ctx_t *)vfs_ctx->extend; printk("\n*** FAT context ***\n" "- fat_sectors = %d\n" "- sector size = %d\n" "- cluster size = %d\n" "- fat_first_lba = %d\n" "- data_first_lba = %d\n" "- root_dir_cluster = %d\n" "- free_clusters = %d\n" "- free_cluster_hint = %d\n" "- fat_mapper_xp = %l\n", fatfs_ctx->fat_sectors_count, fatfs_ctx->bytes_per_sector, fatfs_ctx->sectors_per_cluster * fatfs_ctx->bytes_per_sector, fatfs_ctx->fat_begin_lba, fatfs_ctx->cluster_begin_lba, fatfs_ctx->root_dir_cluster, fatfs_ctx->free_clusters, fatfs_ctx->free_cluster_hint, fatfs_ctx->fat_mapper_xp ); } // end fatfs_ctx_display() ////////////////////////////////////////// void fatfs_display_fat( uint32_t page_id, uint32_t nentries ) { uint32_t line; uint32_t maxline; // copute numner of lines to display maxline = nentries >> 3; if( nentries & 0x7 ) maxline++; // get pointer on local FATFS context vfs_ctx_t * vfs_ctx = &fs_context[FS_TYPE_FATFS]; fatfs_ctx_t * fatfs_ctx = (fatfs_ctx_t *)vfs_ctx->extend; // get extended pointer on FAT mapper xptr_t fat_mapper_xp = fatfs_ctx->fat_mapper_xp; // get extended pointer and cluster of FAT mapper requested page xptr_t page_xp = mapper_remote_get_page( fat_mapper_xp , page_id ); // get extended pointer on requested page base xptr_t base_xp = ppm_page2base( page_xp ); printk("\n***** FAT content / page %d *****\n", page_id ); for( line = 0 ; line < maxline ; line++ ) { printk("%x : %X | %X | %X | %X | %X | %X | %X | %X\n", (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_get_cluster( uint32_t first_cluster_id, uint32_t searched_page_index, uint32_t * searched_cluster_id ) { xptr_t current_page_xp; // pointer on current page descriptor uint32_t * buffer; // pointer on current page (array of uint32_t) uint32_t current_page_index; // index of current page in FAT uint32_t current_page_offset; // offset of slot in current page uint32_t page_count_in_file; // index of page in file (index in linked list) uint32_t next_cluster_id; // content of current FAT slot assert( (searched_page_index > 0) , "no FAT access required for first page\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 / first_cluster_id %d / searched_index / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, first_cluster_id, searched_page_index, cycle ); #endif // get local pointer on local FATFS context fatfs_ctx_t * ctx = fs_context[FS_TYPE_FATFS].extend; // get extended pointer and cluster on FAT mapper xptr_t mapper_xp = ctx->fat_mapper_xp; cxy_t mapper_cxy = GET_CXY( mapper_xp ); // initialize loop variable (1024 slots per page) current_page_index = first_cluster_id >> 10; current_page_offset = first_cluster_id & 0x3FF; page_count_in_file = 0; next_cluster_id = 0xFFFFFFFF; // scan FAT (i.e. traverse FAT linked list) while( page_count_in_file < searched_page_index ) { // get pointer on current page descriptor current_page_xp = mapper_remote_get_page( mapper_xp , current_page_index ); if( current_page_xp == XPTR_NULL ) { // TODO return -1; } // get pointer on buffer for current page xptr_t base_xp = ppm_page2base( current_page_xp ); buffer = (uint32_t *)GET_PTR( base_xp ); // get FAT slot content next_cluster_id = hal_remote_l32( XPTR( mapper_cxy , &buffer[current_page_offset] ) ); #if (DEBUG_FATFS_GET_CLUSTER & 1) if( DEBUG_FATFS_GET_CLUSTER < cycle ) printk("\n[%s] traverse FAT / current_page_index = %d\n" "current_page_offset = %d / next_cluster_id = %d\n", __FUNCTION__, current_page_index, current_page_offset , next_cluster_id ); #endif // update loop variables current_page_index = next_cluster_id >> 10; current_page_offset = next_cluster_id & 0x3FF; page_count_in_file++; } if( next_cluster_id == 0xFFFFFFFF ) return -1; #if DEBUG_FATFS_GET_CLUSTER cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_GET_CLUSTER < cycle ) printk("\n[%s] thread[%x,%x] exit / searched_cluster_id = %d / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, next_cluster_id / cycle ); #endif *searched_cluster_id = next_cluster_id; return 0; } // end fatfs_get_cluster() /////////////////////////////////////////////////////////////////////////////////////// // Generic API : the following functions are called by the kernel VFS // and must be defined by all supported file systems. /////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////// fatfs_ctx_t * fatfs_ctx_alloc( void ) { kmem_req_t req; req.type = KMEM_FATFS_CTX; req.size = sizeof(fatfs_ctx_t); req.flags = AF_KERNEL | AF_ZERO; return (fatfs_ctx_t *)kmem_alloc( &req ); } ////////////////////////////////////////////// void fatfs_ctx_init( fatfs_ctx_t * fatfs_ctx ) { error_t error; kmem_req_t req; uint8_t * buffer; #if DEBUG_FATFS_CTX_INIT uint32_t cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_CTX_INIT < cycle ) printk("\n[%s] thread[%x,%x] enter for fatfs_ctx = %x / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, fatfs_ctx , cycle ); #endif // check argument assert( (fatfs_ctx != NULL) , "pointer on FATFS context is NULL\n" ); // check only cluster 0 does FATFS init assert( (local_cxy == 0) , "only cluster 0 can initialize FATFS\n"); // allocate a 512 bytes buffer to store the boot record req.type = KMEM_512_BYTES; req.flags = AF_KERNEL | AF_ZERO; buffer = (uint8_t *)kmem_alloc( &req ); if( buffer == NULL ) { printk("\n[PANIC] in %s : cannot allocate buffer\n", __FUNCTION__ ); hal_core_sleep(); } // load the BOOT record from device error = dev_ioc_sync_read( buffer , 0 , 1 ); if ( error ) { printk("\n[PANIC] in %s : cannot access boot record\n", __FUNCTION__ ); hal_core_sleep(); } #if (DEBUG_FATFS_CTX_INIT & 0x1) if( DEBUG_FATFS_CTX_INIT < cycle ) { uint32_t line; uint32_t byte = 0; printk("\n***** %s : FAT boot record\n", __FUNCTION__ ); for ( line = 0 ; line < 32 ; line++ ) { printk(" %X | %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x |\n", byte, buffer[byte+ 0],buffer[byte+ 1],buffer[byte+ 2],buffer[byte+ 3], buffer[byte+ 4],buffer[byte+ 5],buffer[byte+ 6],buffer[byte+ 7], buffer[byte+ 8],buffer[byte+ 9],buffer[byte+10],buffer[byte+11], buffer[byte+12],buffer[byte+13],buffer[byte+14],buffer[byte+15] ); byte += 16; } } #endif // get sector size from boot record uint32_t sector_size = fatfs_get_record( BPB_BYTSPERSEC , buffer , 1 ); if ( sector_size != 512 ) { printk("\n[PANIC] in %s : sector size must be 512 bytes\n", __FUNCTION__ ); hal_core_sleep(); } // get cluster size from boot record uint32_t nb_sectors = fatfs_get_record( BPB_SECPERCLUS , buffer , 1 ); if ( nb_sectors != 8 ) { printk("\n[PANIC] in %s : cluster size must be 8 sectors\n", __FUNCTION__ ); hal_core_sleep(); } // get number of FAT copies from boot record uint32_t nb_fats = fatfs_get_record( BPB_NUMFATS , buffer , 1 ); if ( nb_fats != 1 ) { printk("\n[PANIC] in %s : number of FAT copies must be 1\n", __FUNCTION__ ); hal_core_sleep(); } // get number of sectors in FAT from boot record uint32_t fat_sectors = fatfs_get_record( BPB_FAT32_FATSZ32 , buffer , 1 ); if ( (fat_sectors & 0xF) != 0 ) { printk("\n[PANIC] in %s : FAT size not multiple of 16 sectors\n", __FUNCTION__ ); hal_core_sleep(); } // get root cluster from boot record uint32_t root_cluster = fatfs_get_record( BPB_FAT32_ROOTCLUS , buffer , 1 ); if ( root_cluster != 2 ) { printk("\n[PANIC] in %s : root cluster index must be 2\n", __FUNCTION__ ); hal_core_sleep(); } // get FAT lba from boot record uint32_t fat_lba = fatfs_get_record( BPB_RSVDSECCNT , buffer , 1 ); // get FS_INFO sector lba from boot record uint32_t fs_info_lba = fatfs_get_record( BPB_FAT32_FSINFO , buffer , 1 ); // load the FS_INFO record from device error = dev_ioc_sync_read( buffer , fs_info_lba , 1 ); if ( error ) { printk("\n[PANIC] in %s : cannot access FS_INFO record\n", __FUNCTION__ ); hal_core_sleep(); } // get free clusters number from FS_INFO record uint32_t free_clusters = fatfs_get_record( FS_FREE_CLUSTERS , buffer , 1 ); if ( free_clusters >= fat_sectors << 7 ) { printk("\n[PANIC] in %s : unconsistent free_clusters\n", __FUNCTION__ ); hal_core_sleep(); } // get cluster hint from FS_INFO record uint32_t free_cluster_hint = fatfs_get_record( FS_FREE_CLUSTER_HINT , buffer , 1 ); if ( free_cluster_hint >= fat_sectors << 7 ) { printk("\n[PANIC] in %s : unconsistent free_cluster_hint\n", __FUNCTION__ ); hal_core_sleep(); } // release the 512 bytes buffer req.type = KMEM_512_BYTES; req.ptr = buffer; kmem_free( &req ); // allocate a mapper for the FAT itself mapper_t * fat_mapper = mapper_create( FS_TYPE_FATFS ); if ( fat_mapper == NULL ) { printk("\n[PANIC] in %s : no memory for FAT mapper\n", __FUNCTION__ ); hal_core_sleep(); } // WARNING : the inode field MUST be NULL for the FAT mapper fat_mapper->inode = NULL; // initialize the FATFS context fatfs_ctx->fat_begin_lba = fat_lba; fatfs_ctx->fat_sectors_count = fat_sectors; fatfs_ctx->bytes_per_sector = sector_size; fatfs_ctx->sectors_per_cluster = nb_sectors; fatfs_ctx->cluster_begin_lba = fat_lba + fat_sectors; fatfs_ctx->root_dir_cluster = 2; fatfs_ctx->fat_mapper_xp = XPTR( local_cxy , fat_mapper ); fatfs_ctx->free_clusters = free_clusters; fatfs_ctx->free_cluster_hint = free_cluster_hint; remote_queuelock_init( XPTR( local_cxy , &fatfs_ctx->free_lock ) , LOCK_FATFS_FREE ); #if DEBUG_FATFS_CTX_INIT cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_CTX_INIT < cycle ) printk("\n[%s] thread[%x,%x] exit for fatfs_ctx = %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, fatfs_ctx, cycle ); #endif } // end fatfs_ctx_init() ///////////////////////////////////////////////// void fatfs_ctx_destroy( fatfs_ctx_t * fatfs_ctx ) { kmem_req_t req; req.type = KMEM_FATFS_CTX; req.ptr = fatfs_ctx; kmem_free( &req ); } /////////////////////////////////////////////// error_t fatfs_add_dentry( vfs_inode_t * inode, vfs_dentry_t * dentry ) { error_t error; 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; // loal pointer on parent inode mapper xptr_t mapper_xp; // extended pointer on parent inode mapper xptr_t child_xp; // extended pointer on child inode cxy_t child_cxy; // child inode cluster vfs_inode_t * child_ptr; // child inode local pointer uint32_t size; // child inode size uint32_t type; // child inode type uint32_t cluster; // child inode cluster index #if DEBUG_FATFS_ADD_DENTRY char dir_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 ) , dir_name ); if( DEBUG_FATFS_ADD_DENTRY < cycle ) printk("\n[%s] thread[%x,%x] enter / parent <%s> / child <%s> / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle ); #endif // check arguments assert( (inode != NULL) , "inode pointer is NULL\n" ); assert( (dentry != NULL) , "dentry pointer is NULL\n" ); assert( (inode->type == INODE_TYPE_DIR) , "inode is not a directory\n" ); assert( (inode->mapper != NULL ) , "mapper pointer is NULL\n" ); // get pointers on directory mapper mapper = inode->mapper; mapper_xp = XPTR( local_cxy , mapper ); // get extended pointers on remote child inode child_xp = dentry->child_xp; child_cxy = GET_CXY( child_xp ); child_ptr = GET_PTR( child_xp ); // get relevant infos from child inode type = hal_remote_l32( XPTR( child_cxy , &child_ptr->type ) ); size = hal_remote_l32( XPTR( child_cxy , &child_ptr->size ) ); cluster = (uint32_t)(intptr_t)hal_remote_lpt( XPTR( child_cxy , &child_ptr->extend ) ); // analyse dentry name error = fatfs_name_format( dentry->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 uint8_t * base; // local pointer on page base (array of bytes) 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_remote_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 ); base = GET_PTR( base_xp ); // loop on directory entries in this page while ( (offset < 4096) && (found == 0) ) { if ( fatfs_get_record( LDIR_ORD, (base + offset), 0 ) == NO_MORE_ENTRY ) { found = 1; } else { offset = offset + 32; } } // end loop on entries if ( found == 0 ) { page_id++; offset = 0; } } // end loop on pages // Modify the directory mapper: depending on the name length, // the new child requires to write (3, 4, or 5) directory entries. // To actually register the new child, we 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 to pages. char * name = dentry->name; 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; uint8_t * entry; // pointer on directory entry to be written uint32_t i; // byte index in one 32 bytes directory uint32_t c; // character index in name while ( step ) { // when the new child is split on two pages, // we need to access a new page in mapper if ( offset >= 4096 ) { // copy the modified page to IOC device fatfs_move_page( page_xp , false ); // get the next page in FAT mapper page_xp = mapper_remote_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 ); base = GET_PTR( base_xp ); // update offset offset = 0; } // compute directory entry address entry = base + offset; #if (DEBUG_FATFS_ADD_DENTRY & 1) if( DEBUG_FATFS_ADD_DENTRY < cycle ) printk("\n[%s] FSM step = %d / offset = %x / nb_lfn = %d\n", __FUNCTION__, step, offset, nb_lfn ); #endif // write 32 bytes (one directory entry) per iteration switch ( step ) { case 5: // write LFN3 entry { c = 26; // scan the 32 bytes in dir_entry for ( i = 0 ; i < 32 ; i++ ) { if (i == 0) { if ( nb_lfn == 3) entry[i] = 0x43; else entry[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 ) ) { entry[i] = name[c]; c++; } else if (i == 11) entry[i] = 0x0F; else if (i == 13) entry[i] = checksum; else entry[i] = 0x00; } step--; break; } case 4: // write LFN2 entry { c = 13; // scan the 32 bytes in dir_entry for ( i = 0 ; i < 32 ; i++ ) { if (i == 0) { if ( nb_lfn == 2) entry[i] = 0x42; else entry[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 ) ) { entry[i] = name[c]; c++; } else if (i == 11) entry[i] = 0x0F; else if (i == 13) entry[i] = checksum; else entry[i] = 0x00; } step--; break; } case 3: // Write LFN1 entry { c = 0; // scan the 32 bytes in dir_entry for ( i = 0 ; i < 32 ; i++ ) { if (i == 0) { if ( nb_lfn == 1) entry[i] = 0x41; else entry[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 ) ) { entry[i] = name[c]; c++; } else if (i == 11) entry[i] = 0x0F; else if (i == 13) entry[i] = checksum; else entry[i] = 0x00; } step--; break; } case 2: // write NORMAL entry { // scan the 32 bytes in dir_entry for ( i = 0 ; i < 32 ; i++ ) { if ( i < 11 ) // 8.3 SFN { entry[i] = sfn[i]; } else if (i == 11) // ATTR { if (type == INODE_TYPE_DIR) entry[i] = 0x10; else entry[i] = 0x20; } else if (i == 20) entry[i] = cluster>>16; // cluster.B2 else if (i == 21) entry[i] = cluster>>24; // cluster.B3 else if (i == 26) entry[i] = cluster>>0; // cluster.B0 else if (i == 27) entry[i] = cluster>>8; // cluster.B1 else if (i == 28) entry[i] = size>>0; // size.B0 else if (i == 29) entry[i] = size>>8; // size.B1 else if (i == 30) entry[i] = size>>16; // size.B2 else if (i == 31) entry[i] = size>>24; // size.B3 else entry[i] = 0x00; } // update the "extend" field in dentry descriptor dentry->extend = (void*)(intptr_t)(((page_id<<12) + offset)>>5); step--; break; } case 1: // write NOMORE entry { entry [0] = 0x00; step--; break; } } // end switch step offset += 32; } // exit while // copy the modified page to the IOC device fatfs_move_page( page_xp , false ); #if DEBUG_FATFS_ADD_DENTRY cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_ADD_DENTRY < cycle ) printk("\n[%s] thread[%x,%x] exit / parent %s / child %s / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle ); #endif return 0; } // end fatfs_add_dentry() ////////////////////////////////////////////////// error_t fatfs_remove_dentry( vfs_inode_t * inode, vfs_dentry_t * dentry ) { xptr_t mapper_xp; // extended pointer on mapper mapper_t * mapper; // local pointer on mapper xptr_t page_xp; // extended pointer on mapper page descriptor xptr_t base_xp; // extended pointer on mapper page base uint8_t * base; // local pointer on mapper page base #if DEBUG_FATFS_REMOVE_DENTRY char dir_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 ) , dir_name ); if( DEBUG_FATFS_REMOVE_DENTRY < cycle ) printk("\n[%s] thread[%x,%x] enter / parent <%s> / child <%s> / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle ); #endif // check arguments assert( (inode != NULL) , "inode pointer is NULL\n" ); assert( (dentry != NULL) , "dentry pointer is NULL\n" ); assert( (inode->type == INODE_TYPE_DIR) , "inode is not a directory\n" ); assert( (inode->mapper != NULL ) , "mapper pointer is NULL\n" ); // get pointers on directory mapper mapper = inode->mapper; mapper_xp = XPTR( local_cxy , mapper ); // compute number of LFN entries uint32_t nb_lfn; uint32_t name_length = strlen( dentry->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 or 4) 32 bytes entries: // the NORMAL entry (registered in dentry->extend) and all 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 32 bytes directory entry index from dentry->extend uint32_t dentry_id = (uint32_t)(intptr_t)dentry->extend; // get page index and offset in parent directory mapper uint32_t page_id = dentry_id >> 7; uint32_t 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 from parent directory mapper page_xp = mapper_remote_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 pointers on page base base_xp = ppm_page2base( page_xp ); base = GET_PTR( base_xp ); // invalidate NORMAL entry in directory cache base[offset] = 0xE5; // invalidate LFN entries while ( nb_lfn ) { if (offset == 0) // we must load page (page_id - 1) { // check page_id assert( (page_id > 0), "page_id and offset cannot be both 0\n" ); // copy the modified page to the IOC device fatfs_move_page( page_xp , false ); // get extended pointer on page descriptor from parent directory mapper page_xp = mapper_remote_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 pointers on page base base_xp = ppm_page2base( page_xp ); base = GET_PTR( base_xp ); // update offset offset = 4096; } offset = offset - 32; // check for LFN entry assert( (fatfs_get_record( DIR_ATTR, base + offset, 0 ) == ATTR_LONG_NAME_MASK ), "this directory entry must be a LFN\n"); // invalidate LFN entry base[offset] = 0xE5; nb_lfn--; } // copy the modified page to the IOC device fatfs_move_page( page_xp , false ); #if DEBUG_FATFS_REMOVE_DENTRY cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_REMOVE_DENTRY < cycle ) printk("\n[%s] thread[%x,%x] exit / parent %s / child %s / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle ); #endif return 0; } // end fatfs_remove_dentry //////////////////////////////////////////////////////////////// error_t fatfs_child_init( vfs_inode_t * parent_inode, char * name, xptr_t child_inode_xp ) { // Two embedded loops to scan the directory mapper: // - scan the parent directory mapper pages // - scan the directory entries in each 4 Kbytes page #if DEBUG_FATFS_CHILD_INIT char parent_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 , parent_inode ) , parent_name ); if( DEBUG_FATFS_CHILD_INIT < cycle ) printk("\n[%s] thread[%x,%x] enter for child <%s> in parent <%s> / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, name , parent_name , cycle ); #endif // check parent_inode and child_inode assert( (parent_inode != NULL) , "parent_inode is NULL\n" ); assert( (child_inode_xp != XPTR_NULL ) , "child_inode is XPTR_NULL\n" ); mapper_t * mapper = parent_inode->mapper; xptr_t mapper_xp = XPTR( local_cxy , mapper ); // check parent mapper assert( (mapper != NULL) , "parent mapper is NULL\n"); char cname[CONFIG_VFS_MAX_NAME_LENGTH]; // name extracter from each 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 page descriptor xptr_t base_xp; // extended pointer on page base uint8_t * base; // local pointer on page base uint32_t attr; // directory entry ATTR field uint32_t ord; // directory entry ORD field uint32_t seq; // sequence index uint32_t lfn = 0; // LFN entries number uint32_t size = 0; // searched file/dir size (bytes) uint32_t cluster = 0; // searched file/dir cluster index uint32_t is_dir = 0; // searched file/dir type int32_t found = 0; // not found (0) / name found (1) / end of dir (-1) uint32_t page_id = 0; // page index in mapper uint32_t dentry_id = 0; // directory entry index uint32_t offset = 0; // byte offset in page // scan the parent directory mapper while ( found == 0 ) { // get one page page_xp = mapper_remote_get_page( mapper_xp , page_id ); if( page_xp == XPTR_NULL) return EIO; // get page base base_xp = ppm_page2base( page_xp ); base = (uint8_t *)GET_PTR( base_xp ); #if (DEBUG_FATFS_CHILD_INIT & 0x1) if( DEBUG_FATFS_CHILD_INIT < cycle ) { uint32_t * buf = (uint32_t *)base; uint32_t line , word; printk("\n[%s] First 16 dentries for <%s>\n", __FUNCTION__ , parent_name ); for( line = 0 ; line < 16 ; line++ ) { printk("%X : ", line ); for( word = 0 ; word < 8 ; word++ ) printk("%X ", buf[(line<<4) + word] ); printk("\n"); } } #endif // scan this page until end of directory, end of page, or name found while( (offset < 4096) && (found == 0) ) { attr = fatfs_get_record( DIR_ATTR , base + offset , 0 ); ord = fatfs_get_record( LDIR_ORD , base + offset , 0 ); if (ord == NO_MORE_ENTRY) // no more entry => break { found = -1; } else if ( ord == FREE_ENTRY ) // free entry => 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( 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 { // build the extracted name 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 ); } // get dentry arguments if extracted cname == searched name if ( strcmp( name , cname ) == 0 ) { cluster = (fatfs_get_record( DIR_FST_CLUS_HI , base + offset , 1 ) << 16) | (fatfs_get_record( DIR_FST_CLUS_LO , base + offset , 1 ) ) ; dentry_id = ((page_id<<12) + offset)>>5; is_dir = ((attr & ATTR_DIRECTORY) == ATTR_DIRECTORY); size = fatfs_get_record( DIR_FILE_SIZE , base + offset , 1 ); found = 1; } offset = offset + 32; lfn = 0; } } // end loop on directory entries in page page_id++; offset = 0; } // end loop on pages // analyse the result of scan if ( found == -1 ) // found end of directory => failure { #if DEBUG_FATFS_CHILD_INIT cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_CHILD_INIT < cycle ) printk("\n[%s] thread[%x,%x] exit / child <%s> not found / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, name, cycle ); #endif return -1; } // get child inode cluster and local pointer cxy_t inode_cxy = GET_CXY( child_inode_xp ); vfs_inode_t * inode_ptr = GET_PTR( child_inode_xp ); // build extended pointer on parent dentried root xptr_t parents_root_xp = XPTR( inode_cxy , &inode_ptr->parents ); // check child inode has at least one parent assert( (xlist_is_empty( parents_root_xp ) == false ), "child inode must have one parent\n"); // get dentry pointers and cluster xptr_t dentry_xp = XLIST_FIRST( parents_root_xp , vfs_dentry_t , parents ); vfs_dentry_t * dentry_ptr = GET_PTR( dentry_xp ); cxy_t dentry_cxy = GET_CXY( dentry_xp ); // check dentry descriptor in same cluster as parent inode assert( (dentry_cxy == local_cxy) , "illegal dentry cluster\n" ); // update the child inode "type", "size", and "extend" fields vfs_inode_type_t type = (is_dir) ? INODE_TYPE_DIR : INODE_TYPE_FILE; hal_remote_s32( XPTR( inode_cxy , &inode_ptr->type ) , type ); hal_remote_s32( XPTR( inode_cxy , &inode_ptr->size ) , size ); hal_remote_s32( XPTR( inode_cxy , &inode_ptr->extend ) , cluster ); // update the dentry "extend" field dentry_ptr->extend = (void *)(intptr_t)dentry_id; #if DEBUG_FATFS_CHILD_INIT cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_CHILD_INIT < cycle ) printk("\n[%s] thread[%x,%x] exit / child <%s> loaded in <%s> / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, name, parent_name, cycle ); #endif return 0; } // end fatfs_child_init() /////////////////////////////////////////////// error_t fatfs_sync_inode( vfs_inode_t * inode ) { // check inode pointer and cluster index assert( (inode != NULL) , "inode pointer undefined\n" ); assert( (inode->mapper != NULL ) , "mapper pointer undefined\n" ); assert( (inode->type == INODE_TYPE_FILE) , "inode must be a file\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( XPTR( local_cxy , inode ) , 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 error_t error; mapper_t * mapper; page_t * page; uint32_t page_id; // get mapper from inode mapper = inode->mapper; // compute max number of pages in mapper from file size uint32_t size = inode->size; uint32_t pages = size >> CONFIG_PPM_PAGE_SHIFT; if( size & CONFIG_PPM_PAGE_MASK ) pages++; // get pointer on mapper radix tree grdxt_t * rt = &mapper->rt; // scan all pages for( page_id = 0 ; page_id < pages ; page_id++ ) { // get page descriptor from mapper page = grdxt_lookup( rt , page_id ); // check all existing pages if ( page != NULL ) { if ( page->flags & PG_DIRTY ) { #if (DEBUG_FATFS_SYNC_INODE & 1) if( DEBUG_FATFS_SYNC_INODE < cycle ) printk("\n[%s] thread[%x,%x] synchronizes page %d from <%s> mapper to IOC device\n", __FUNCTION__, page_id, name ); #endif // build extended pointer on page descriptor xptr_t page_xp = XPTR( local_cxy , page ); // move page from mapper to device error = fatfs_move_page( page_xp , false ); 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> / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, name, cycle ); #endif return 0; } // end fatfs_sync_inode() ////////////////////////////// error_t fatfs_sync_fat( void ) { #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 uint32_t page_id; error_t error; // get FAT mapper pointers an cluster fatfs_ctx_t * fatfs_ctx = fs_context[FS_TYPE_FATFS].extend; xptr_t mapper_xp = fatfs_ctx->fat_mapper_xp; cxy_t mapper_cxy = GET_CXY( mapper_xp ); mapper_t * mapper_ptr = GET_PTR( mapper_xp ); // compute max number of 4 Kbytes pages in FAT mapper // TODO : this could be improved (see fatfs.h) [AG] uint32_t pages = fatfs_ctx->fat_sectors_count >> 3; // get pointers on remote FAT mapper radix tree grdxt_t * rt_ptr = &mapper_ptr->rt; xptr_t rt_xp = XPTR( mapper_cxy , rt_ptr ); // scan all pages for( page_id = 0 ; page_id < pages ; page_id++ ) { // get extended pointer on page descriptor from FAT mapper xptr_t page_xp = grdxt_remote_lookup( rt_xp , page_id ); // check all existing pages if ( page_xp != XPTR_NULL ) { page_t * page_ptr = GET_PTR( page_xp ); uint32_t flags = hal_remote_l32( XPTR( mapper_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 , false ); if ( error ) return -1; // reset page dirty flag ppm_page_undo_dirty( page_xp ); } } } // 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 / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, cycle ); #endif return 0; } // end fatfs_sync_fat() //////////////////////////////////// error_t fatfs_sync_free_info( void ) { #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 uint8_t * buffer; // dynamically allocated aligned 512 bytes buffer kmem_req_t req; error_t error; // get FS_INFO lba, free_ from FATFS context fatfs_ctx_t * fatfs_ctx = fs_context[FS_TYPE_FATFS].extend; uint32_t lba = fatfs_ctx->fs_info_lba; uint32_t hint = fatfs_ctx->free_cluster_hint; uint32_t number = fatfs_ctx->free_clusters; // allocate buffer to store the FS_INFO sector req.type = KMEM_512_BYTES; req.flags = AF_KERNEL | AF_ZERO; buffer = (uint8_t *)kmem_alloc( &req ); if( buffer == NULL ) { printk("\n[PANIC] in %s : cannot allocate buffer\n", __FUNCTION__ ); return ENOMEM; } // load the FS_INFO sector from device to buffer error = dev_ioc_read( buffer , lba , 1 ); if ( error ) { printk("\n[PANIC] in %s : cannot read FS_INFO record\n", __FUNCTION__ ); return EIO; } // update buffer fatfs_set_record( FS_FREE_CLUSTERS , buffer , 1 , number ); fatfs_set_record( FS_FREE_CLUSTER_HINT , buffer , 1 , hint ); // write modified FS_INFO sector from buffer to device error = dev_ioc_write( buffer , lba , 1 ); if ( error ) { printk("\n[PANIC] in %s : cannot write FS_INFO record\n", __FUNCTION__ ); return EIO; } // release the 512 bytes buffer req.type = KMEM_512_BYTES; req.ptr = buffer; kmem_free( &req ); #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_sync_fs_info() ////////////////////////////////////////////////////////// error_t fatfs_cluster_alloc( uint32_t * searched_cluster ) { uint32_t page_id; // page index in mapper uint32_t slot_id; // slot index in page (1024 slots per page) uint32_t hint; // first free cluster index in FAT uint32_t free_clusters; // total number of free clusters 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 xptr_t mapper_xp; // extended pointer on FAT mapper cxy_t mapper_cxy; // Fat mapper cluster identifier xptr_t page_xp; // extended pointer on current page descriptor in mapper xptr_t slot_xp; // extended pointer on FAT slot defined by hint 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 numb_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 / cycle = %d\n", __FUNCTION__, this->process->pid, this->trdid, 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 extended pointer and cluster on FAT mapper mapper_xp = loc_fatfs_ctx->fat_mapper_xp; mapper_cxy = GET_CXY( mapper_xp ); // get local pointer on FATFS context in FAT cluster fat_fatfs_ctx = hal_remote_lpt( XPTR( mapper_cxy , &vfs_ctx->extend ) ); // build relevant extended pointers in on free clusters info in FAT cluster lock_xp = XPTR( mapper_cxy , &fat_fatfs_ctx->free_lock ); hint_xp = XPTR( mapper_cxy , &fat_fatfs_ctx->free_cluster_hint ); numb_xp = XPTR( mapper_cxy , &fat_fatfs_ctx->free_clusters ); // take the lock protecting free clusters remote_queuelock_acquire( lock_xp ); // get hint and free_clusters values from FATFS context hint = hal_remote_l32( hint_xp ); free_clusters = hal_remote_l32( numb_xp ); // get page index & slot index for the first free cluster page_id = (hint + 1) >> 10; slot_id = (hint + 1) & 0x3FF; // get relevant page from mapper page_xp = mapper_remote_get_page( mapper_xp , page_id ); if( page_xp == XPTR_NULL ) { printk("\n[ERROR] in %s : cannot acces FAT mapper\n", __FUNCTION__ ); return -1; } // build extended pointer on free cluster slot slot_xp = ppm_page2base( page_xp ) + (slot_id<<2); #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_queuelock_acquire( lock_xp ); return -1; } else if ( free_clusters < CONFIG_VFS_FREE_CLUSTERS_MIN ) { printk("\n[WARNING] in %s : only %n free FATFS clusters\n", __FUNCTION__, CONFIG_VFS_FREE_CLUSTERS_MIN ); } // check "hint" if( hal_remote_l32( slot_xp ) != FREE_CLUSTER ) { printk("\n[ERROR] in %s : illegal hint cluster\n", __FUNCTION__ ); remote_queuelock_acquire( lock_xp ); return -1; } // update allocated cluster in FAT mapper hal_remote_s32( slot_xp , END_OF_CHAIN_CLUSTER_MAX ); // update free cluster info fatfs_free_clusters_decrement( hint + 1 ); // release free clusters busylock remote_queuelock_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 / cluster %x / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, hint + 1, cycle ); #endif *searched_cluster = hint + 1; return 0; } // end fat_cluster_alloc() ////////////////////////////////////////////// error_t fatfs_release_inode( xptr_t inode_xp ) { 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 xptr_t mapper_xp; // extended pointer on FAT mapper cxy_t mapper_cxy; // Fat mapper cluster identifier xptr_t lock_xp; // extended pointer on lock protecting free clusters info. xptr_t first_xp; // extended pointer on inode extension uint32_t first_cluster; // first cluster index for released inode vfs_inode_t * inode_ptr; cxy_t inode_cxy; // check inode pointer assert( (inode_xp != XPTR_NULL) , "inode pointer is NULL\n" ); // get first_cluster from inode extension inode_ptr = GET_PTR( inode_xp ); inode_cxy = GET_CXY( inode_xp ); first_xp = XPTR( inode_cxy , &inode_ptr->extend ); first_cluster = (uint32_t)(intptr_t)hal_remote_lpt( first_xp ); // check first cluster index assert( (first_cluster != 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 %x / cycle %d\n", __FUNCTION__ , this->process->pid, this->trdid, name, first_cluster, 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 extended pointer and cluster on FAT mapper mapper_xp = loc_fatfs_ctx->fat_mapper_xp; mapper_cxy = GET_CXY( mapper_xp ); // get local pointer on FATFS context in FAT cluster fat_fatfs_ctx = hal_remote_lpt( XPTR( mapper_cxy , &vfs_ctx->extend ) ); // get extended pointer on free clusters lock in FAT cluster lock_xp = XPTR( mapper_cxy , &fat_fatfs_ctx->free_lock ); // take lock protecting free clusters remote_queuelock_acquire( lock_xp ); // call the recursive function to release all clusters from FAT mapper if ( fatfs_recursive_release( mapper_xp , first_cluster ) ) { printk("\n[ERROR] in %s : cannot update FAT mapper\n", __FUNCTION__ ); remote_queuelock_release( lock_xp ); return -1; } // release lock protecting free cluster remote_queuelock_release( lock_xp ); #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 // update FAT on IOC device (from FAT mapper) if ( fatfs_sync_fat() ) { printk("\n[ERROR] in %s : cannot update FAT on device\n", __FUNCTION__ ); 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 sector on IOC device (from FATFS context) if ( fatfs_sync_free_info() ) { printk("\n[ERROR] in %s: cannot update FS_INFO on device\n", __FUNCTION__ ); return -1; } #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, bool_t to_mapper ) { error_t error; 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 an 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 base_xp = ppm_page2base( page_xp ); uint8_t * buffer = (uint8_t *)GET_PTR( base_xp ); // get inode pointer from mapper inode_ptr = hal_remote_lpt( XPTR( page_cxy , &mapper_ptr->inode ) ); ////////////////////////////// it is the FAT mapper if( inode_ptr == NULL ) { // get lba from FATFS context and page_id uint32_t lba = fatfs_ctx->fat_begin_lba + (page_id << 3); // access device if( to_mapper ) error = dev_ioc_sync_read ( buffer , lba , 8 ); else error = dev_ioc_write( buffer , lba , 8 ); if( error ) return EIO; #if (DEBUG_FATFS_MOVE_PAGE & 0x1) if( DEBUG_FATFS_MOVE_PAGE < cycle ) { uint32_t * tab = (uint32_t *)buffer; uint32_t line , word; printk("\n***** %s : First 64 words of page %d in FAT mapper\n", __FUNCTION__ , page_id ); for( line = 0 ; line < 8 ; line++ ) { printk("%X : ", line ); for( word = 0 ; word < 8 ; word++ ) printk("%X ", tab[(line<<3) + word] ); printk("\n"); } } #endif #if DEBUG_FATFS_MOVE_PAGE cycle = (uint32_t)hal_get_cycles(); if( DEBUG_FATFS_MOVE_PAGE < cycle ) { if (to_mapper) printk("\n[%s] thread[%x,%x] load page %d of FAT / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, page_id, cycle ); else printk("\n[%s] thread[%x,%x] sync page %d of FAT / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, page_id, cycle ); } #endif } ///////////////////////// it is an inode mapper else { #if DEBUG_FATFS_MOVE_PAGE vfs_inode_get_name( XPTR( page_cxy , inode_ptr ) , name ); #endif uint32_t searched_cluster; uint32_t first_cluster; // get first_cluster from inode extension void * extend = hal_remote_lpt( XPTR( page_cxy , &inode_ptr->extend ) ); first_cluster = (uint32_t)(intptr_t)extend; // compute searched_cluster if( page_id == 0 ) // no need to access FAT mapper { // searched cluster is first cluster searched_cluster = first_cluster; } else // FAT mapper access required { // access FAT mapper to get searched cluster error = fatfs_get_cluster( first_cluster, page_id, &searched_cluster ); if( error ) return EIO; } // get lba from searched_cluster uint32_t lba = fatfs_lba_from_cluster( fatfs_ctx , searched_cluster ); // access device if( to_mapper ) error = dev_ioc_sync_read ( buffer , lba , 8 ); else error = dev_ioc_write( buffer , lba , 8 ); if( error ) return EIO; #if (DEBUG_FATFS_MOVE_PAGE & 0x1) if( DEBUG_FATFS_MOVE_PAGE < cycle ) { uint32_t * tab = (uint32_t *)buffer; uint32_t line , word; printk("\n***** %s : First 64 words of page %d in <%s> mapper\n", __FUNCTION__, page_id, name ); for( line = 0 ; line < 8 ; line++ ) { printk("%X : ", line ); for( word = 0 ; word < 8 ; word++ ) printk("%X ", tab[(line<<3) + word] ); printk("\n"); } } #endif #if DEBUG_FATFS_MOVE_PAGE cycle = (uint32_t)hal_get_cycles(); if(DEBUG_FATFS_MOVE_PAGE < cycle) { if(to_mapper) printk("\n[%s] thread[%x,%x] load page %d of <%s> inode / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, page_id, name, cycle ); else printk("\n[%s] thread[%x,%x] sync page %d of <%s> inode / cycle %d\n", __FUNCTION__, this->process->pid, this->trdid, page_id, name, cycle ); } #endif } return 0; } // end fatfs_move_page()