source: trunk/kernel/fs/fatfs.c @ 626

Last change on this file since 626 was 626, checked in by alain, 5 years ago

This version has been tested on the sort multithreaded application
for TSAR_IOB architectures ranging from 1 to 8 clusters.
It fixes three bigs bugs:
1) the dev_ioc device API has been modified: the dev_ioc_sync_read()
and dev_ioc_sync_write() function use now extended pointers on the
kernel buffer to access a mapper stored in any cluster.
2) the hal_uspace API has been modified: the hal_copy_to_uspace()
and hal_copy_from_uspace() functions use now a (cxy,ptr) couple
to identify the target buffer (equivalent to an extended pointer.
3) an implementation bug has been fixed in the assembly code contained
in the hal_copy_to_uspace() and hal_copy_from_uspace() functions.

File size: 99.4 KB
RevLine 
[1]1/*
2 * fatfs.c - FATFS file system API implementation.
3 *
[625]4 * Author    Alain Greiner (2016,2017,2018,2019)
[1]5 *
6 * Copyright (c) UPMC Sorbonne Universites
7 *
8 * This file is part of ALMOS-MKH.
9 *
10 * ALMOS-MKH is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; version 2.0 of the License.
13 *
14 * ALMOS-MKH is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24
[457]25#include <hal_kernel_types.h>
[1]26#include <hal_special.h>
27#include <printk.h>
[401]28#include <thread.h>
[1]29#include <kmem.h>
30#include <ppm.h>
31#include <vfs.h>
[238]32#include <string.h>
[1]33#include <rpc.h>
34#include <mapper.h>
[23]35#include <cluster.h>
[1]36#include <dev_ioc.h>
37#include <fatfs.h>
38
[626]39#define LITTLE_ENDIAN  1
[50]40
[23]41//////////////////////////////////////////////////////////////////////////////////////////
42//          Extern  variables         
43//////////////////////////////////////////////////////////////////////////////////////////
[1]44
[602]45extern vfs_ctx_t     fs_context[FS_TYPES_NR];   // allocated in kernel_init.c file
[23]46
[1]47//////////////////////////////////////////////////////////////////////////////////////////
[602]48//              FATFS specific static functions
[1]49//////////////////////////////////////////////////////////////////////////////////////////
50
[188]51//////////////////////////////////////////////////////////////////////////////////////////
[238]52// These functions return the "offset" and "length" values of an
53// [offset,length] constant defined in the fatfs.h file.
54//////////////////////////////////////////////////////////////////////////////////////////
55
[568]56static inline int get_length( int offset __attribute__((unused)), int length ) { return length; }
[238]57
[568]58static inline int get_offset( int offset, int length __attribute__((unused)) ) { return offset; }
[238]59
60//////////////////////////////////////////////////////////////////////////////////////////
[440]61// This static function returns the LBA of the first sector of a FAT cluster.
[188]62// This function can be called by any thread running in any cluster.
63//////////////////////////////////////////////////////////////////////////////////////////
64// @ ctx          :     pointer on FATFS context.
65// @ cluster  : cluster index in FATFS.
66// @ return the lba value.
67//////////////////////////////////////////////////////////////////////////////////////////
68static inline uint32_t fatfs_lba_from_cluster( fatfs_ctx_t * ctx,
69                                               uint32_t      cluster )
[1]70{
[23]71    return (ctx->cluster_begin_lba + ((cluster - 2) << 3));
[1]72}
73
[246]74//////////////////////////////////////////////////////////////////////////////////////////
[626]75// This function return an integer record value (one, two, or four bytes) from a local
76// array of bytes, taking into account the global LITTLE_ENDIAN parameter:
77// if LITTLE_ENDIAN is true, the most significant byte has the highest address.
[238]78//////////////////////////////////////////////////////////////////////////////////////////
[626]79// @ offset        : first byte in array.
80// @ nbytes        : record length in bytes (1/2/4).
81// @ buffer        : local pointer on byte array.
[23]82// @ return the integer value in a 32 bits word.
[238]83//////////////////////////////////////////////////////////////////////////////////////////
84static uint32_t fatfs_get_record( uint32_t    offset,
[626]85                                  uint32_t    nbytes,
86                                  uint8_t   * buffer )
[23]87{
[626]88    uint32_t i;
89    uint32_t res = 0;
[1]90
[626]91    if ( LITTLE_ENDIAN )
[23]92    {
[626]93        for( i = nbytes ; i > 0 ; i-- ) res = (res<<8) | buffer[offset+i-1];
[23]94    }
[626]95    else 
[23]96    {
[626]97        for( i = 0 ; i < nbytes ; i++ ) res = (res<<8) | buffer[offset+i];
[23]98    }
99    return res;
100
[238]101}  // end fatfs_get_record()
[23]102
[238]103//////////////////////////////////////////////////////////////////////////////////////////
[626]104// This function return an integer record value (one, two, or four bytes) from a remote
105// array of bytes, taking into account the global LITTLE_ENDIAN parameter:
106// if LITTLE_ENDIAN is true, the most significant byte has the highest address.
[602]107//////////////////////////////////////////////////////////////////////////////////////////
[626]108// @ offset        : first byte in array.
109// @ nbytes        : record length in bytes (1/2/4).
110// @ buffer_xp     : extended pointer on byte array.
[602]111// @ return the integer value in a 32 bits word.
112//////////////////////////////////////////////////////////////////////////////////////////
[626]113static uint32_t fatfs_get_remote_record( uint32_t   offset,
114                                         uint32_t   nbytes,
115                                         xptr_t     buffer_xp )
116{
117    uint32_t i;
118    uint32_t res = 0;
119
120    if ( LITTLE_ENDIAN )
121    {
122        for( i = nbytes ; i > 0 ; i-- )
123        {
124            res = (res<<8) | hal_remote_lb( buffer_xp+offset+i-1 );
125        }
126    }
127    else 
128    {
129        for( i = 0 ; i < nbytes ; i++ )
130        {
131            res = (res<<8) | hal_remote_lb( buffer_xp+offset+i );
132        }
133    }
134    return res;
135
136}  // end fatfs_get_remote_record()
137
138//////////////////////////////////////////////////////////////////////////////////////////
139// This function writes one, two, or four bytes from a 32 bits integer to a local
140// array of bytes, taking into account the global LITTLE_ENDIAN parameter:
141// if LITTLE_ENDIAN is true, the most significant byte has the highest address.
142//////////////////////////////////////////////////////////////////////////////////////////
143// @ offset        : first byte in array.
144// @ nbytes        : record length in bytes (1/2/4).
145// @ buffer        : local pointer on byte array.
146// @ value         : 32 bits integer value.
147//////////////////////////////////////////////////////////////////////////////////////////
[602]148static void fatfs_set_record( uint32_t    offset,
[626]149                              uint32_t    nbytes,
[602]150                              uint8_t   * buffer,
151                              uint32_t    value )
152{
[626]153    uint32_t i;
[602]154
[626]155    if ( LITTLE_ENDIAN )
[602]156    {
[626]157        for( i = nbytes ; i > 0 ; i-- ) buffer[offset+i-1] = (uint8_t)(value>>((i-1)<<3));
[602]158    }
[626]159    else
[602]160    {
[626]161        for( i = 0 ; i < nbytes ; i++ ) buffer[offset+i] = (uint8_t)(value>>((nbytes-1-i)<<3));
[602]162    }
163
164}  // end fatfs_set_record()
165
166//////////////////////////////////////////////////////////////////////////////////////////
[626]167// This function writes one, two, or four bytes from a 32 bits integer to a remote
168// array of bytes, taking into account the global LITTLE_ENDIAN parameter:
169// if LITTLE_ENDIAN is true, the most significant byte has the highest address.
170//////////////////////////////////////////////////////////////////////////////////////////
171// @ offset        : first byte in array.
172// @ nbytes        : record length in bytes (1/2/4).
173// @ buffer_xp     : extended pointer on byte array.
174// @ value         : 32 bits integer value.
175//////////////////////////////////////////////////////////////////////////////////////////
176static void fatfs_set_remote_record( uint32_t    offset,
177                                     uint32_t    nbytes,
178                                     xptr_t      buffer_xp,
179                                     uint32_t    value )
180{
181    uint32_t i;
182
183    if ( LITTLE_ENDIAN )
184    {
185        for( i = nbytes ; i > 0 ; i-- )
186        {
187            hal_remote_sb( (buffer_xp+offset+i-1) , (uint8_t)(value>>((i-1)<<3)) );
188        }
189    }
190    else
191    {
192        for( i = 0 ; i < nbytes ; i++ )
193        {
194            hal_remote_sb( (buffer_xp+offset+i) , (uint8_t)(value>>((nbytes-1-i)<<3)) );
195        }
196    }
197
198}  // end fatfs_set_record()
199
200//////////////////////////////////////////////////////////////////////////////////////////
[238]201// This static function retun in the <name> buffer a short name stored in
202// a SFN FATFS directory entry.
203/////////////////////////i////////////////////////////////////////////////////////////////
204// @ buffer   : pointer on buffer containing the directory entry.
205// @ name     : [out] buffer allocated by the caller.
206//////////////////////////////////////////////////////////////////////////////////////////
207static void fatfs_get_name_from_short( uint8_t * buffer,
208                                       char    * name )
209{
210    uint32_t i;
211    uint32_t j = 0;
[23]212
[238]213    // get name
214    for ( i = 0; i < 8 && buffer[i] != ' '; i++ )
215    {
216        name[j] = to_lower( buffer[i] );
217        j++;
218    }
[23]219
[238]220    // get extension
221    for ( i = 8; i < 8 + 3 && buffer[i] != ' '; i++ )
222    {
223        // we entered the loop so there is an extension. add the dot
224        if ( i == 8 )
225        {
226            name[j] = '.';
227            j++;
228        }
229
230        name[j] = to_lower( buffer[i] );
231        j++;
232    }
233
234    name[j] = '\0';
235
[602]236}  // fatfs_get_name_from_short()
237
[238]238//////////////////////////////////////////////////////////////////////////////////////////
239// This static function retun in the <name> buffer a partial name stored in
240// a LFN FATFS directory entry.
241/////////////////////////i////////////////////////////////////////////////////////////////
242// @ buffer   : pointer on buffer containing the directory entry.
243// @ name     : [out] buffer allocated by the caller.
244//////////////////////////////////////////////////////////////////////////////////////////
245static void fatfs_get_name_from_long( uint8_t * buffer,
246                                      char    * name )
247{
248    uint32_t   name_offset   = 0;
249    uint32_t   buffer_offset = get_length(LDIR_ORD);
250    uint32_t   l_name_1      = get_length(LDIR_NAME_1);
251    uint32_t   l_name_2      = get_length(LDIR_NAME_2);
252    uint32_t   l_name_3      = get_length(LDIR_NAME_3);
253    uint32_t   l_attr        = get_length(LDIR_ATTR);
254    uint32_t   l_type        = get_length(LDIR_TYPE);
255    uint32_t   l_chksum      = get_length(LDIR_CHKSUM);
256    uint32_t   l_rsvd        = get_length(LDIR_RSVD);
257
258    uint32_t   j             = 0;
259    uint32_t   eof           = 0;
260
261    while ( (buffer_offset != DIR_ENTRY_SIZE)  && (!eof) )
262    {
263        while (j != l_name_1 && !eof )
264        {
265            if ( (buffer[buffer_offset] == 0x00) || 
266                 (buffer[buffer_offset] == 0xFF) )
267            {
268                eof = 1;
269                continue;
270            }
271            name[name_offset] = buffer[buffer_offset];
272            buffer_offset += 2;
273            j += 2;
274            name_offset++;
275        }
276
277        buffer_offset += (l_attr + l_type + l_chksum);
278        j = 0;
279
280        while (j != l_name_2 && !eof )
281        {
282            if ( (buffer[buffer_offset] == 0x00) || 
283                 (buffer[buffer_offset] == 0xFF) )
284            {
285                eof = 1;
286                continue;
287            }
288            name[name_offset] = buffer[buffer_offset];
289            buffer_offset += 2;
290            j += 2;
291            name_offset++;
292        }
293
294        buffer_offset += l_rsvd;
295        j = 0;
296
297        while (j != l_name_3 && !eof )
298        {
299            if ( (buffer[buffer_offset] == 0x00) || 
300                 (buffer[buffer_offset] == 0xFF) )
301            {
302                eof = 1;
303                continue;
304            }
305            name[name_offset] = buffer[buffer_offset];
306            buffer_offset += 2;
307            j += 2;
308            name_offset++;
309        }
310    }
311    name[name_offset] = 0;
312
[602]313} // end fatfs_get_name_from_long()
[238]314
[602]315//////////////////////////////////////////////////////////////////////////////////////////
316// This static function analyse the <name> input argument, and returns in other
317// output arguments various informations required to store the name in FATFS directory.
318// The <name> length cannot be larger than 31 characters :
319// - Short name (less than 13 characters) require 1 LFN entry.
320// - Medium names (from 14 to 26 characters require 2 LFN entries.
321// - Large names (up to 31 characters) require 3 LFN entries.
322//////////////////////////////////////////////////////////////////////////////////////////
323// @ name     : [in]  complete directory entry name.
324// @ length   : [out] total number of characters in name.
325// @ nb_lfn   : [out] number of LFN entries required to store the name.
326// @ sfn      : [out] a legal SFN name extracted from name / upper case and 8-3 format.
327// @ checksum : [out] checksum to be stored in SFN.
328// @ returns 0 on success / returns 1 if the name length is larger than 31 characters.
329//////////////////////////////////////////////////////////////////////////////////////////
330static error_t fatfs_name_format( const char  * name,
331                                  uint32_t    * length,
332                                  uint32_t    * nb_lfn,
333                                  char        * sfn,
334                                  uint8_t     * checksum )
335{
336    // compute name length
337    uint32_t name_length = strlen( name );
338    *length = name_length;
[1]339
[602]340    uint32_t suffix_length = 0;
341    uint32_t prefix_length = 0;
342    uint32_t dot_found     = 0;
343    uint32_t i;
344
345    // compute prefix and suffix length
346    // only the last '.' is taken into account
347    for ( i=0 ; i<name_length ; i++ )
348    {
349        if (name[i] == '.' )
350        {
351            if ( dot_found ) 
352            {
353                prefix_length += suffix_length + 1;
354                suffix_length =  0;
355            }
356            else
357            {
358                dot_found = 1;
359            }
360        }
361        else
362        { 
363            if ( dot_found)  suffix_length++;
364            else             prefix_length++;
365        }
366    } 
367
368    // build SFN prefix (8bits)
369    if (prefix_length <= 8)
370    {
371        for( i=0 ; i<8 ; i++)
372        {
373            if ( i<prefix_length ) sfn[i] = to_upper( name[i] );
374            else                   sfn[i] = 0x20;
375        }
376    }
377    else
378    {
379        for( i=0 ; i<6 ; i++)
380        {
381            sfn[i] = to_upper( name[i] );
382        }
383        sfn[6] = 0x7E;
384        sfn[7] = 0x31;
385    }
386
387    // build SFN suffix (3 bits)
388    if ( suffix_length == 0 )
389    {
390        sfn[8]  = 0x20;
391        sfn[9]  = 0x20;
392        sfn[10] = 0x20;
393    }
394    else if ( suffix_length == 1 )
395    {
396        sfn[8]  = to_upper( name[name_length-1] );
397        sfn[9]  = 0x20;
398        sfn[10] = 0x20;
399    }
400    else if ( suffix_length == 2 )
401    {
402        sfn[8]  = to_upper( name[name_length-2] );
403        sfn[9]  = to_upper( name[name_length-1] );
404        sfn[10] = 0x20;
405    }
406    else
407    {
408        sfn[8]  = to_upper( name[name_length-suffix_length] );
409        sfn[9]  = to_upper( name[name_length-suffix_length+1] );
410        sfn[10] = to_upper( name[name_length-suffix_length+2] );
411    }
412
413    // compute 8 bits checksum
414    uint8_t sum = 0;
415    for ( i=0 ; i<11 ; i++ )
416    {
417        sum = (((sum & 0x01)<<7) | ((sum & 0xFE)>>1)) + sfn[i];
418    }
419    *checksum = sum;
420
421    // set nb_lfn and length values
422    if      ( name_length <= 13 )
423    {
424        *nb_lfn  = 1;
425        return 0;
426    }
427    else if ( name_length <= 26 )
428    {
429        *nb_lfn  = 2;
430        return 0;
431    }
432    else if ( name_length <= 31 )
433    {
434        *nb_lfn  = 3;
435        return 0;
436    }
437    else
438    {
439        return 1;
440    }
441}   // end fatfs_name_format() 
442
[238]443//////////////////////////////////////////////////////////////////////////////////////////
[626]444// This static function is called by both the fatfs_free_clusters_increment(),
445// and the fatfs_free_cluster_decrement() functions defined below.
446// It synchronously updates the  "free_clusters" and "free_cluster_hint" variables
447// in FS_INFO sector on the IOC device, each times these variables are modified.
448//////////////////////////////////////////////////////////////////////////////////////////
449// @ fatfs_ctx_xp      : extended pointer on fatfs context in FAT cluster.
450// @ free_clusters     : new free_clusters value.
451// @ free_cluster_hint : new free_cluster_hint value.
452// @ return 0 if success, return -1 if the FS_INFO sector cannot be updated.
453//////////////////////////////////////////////////////////////////////////////////////////
454static error_t fatfs_free_clusters_update_ioc( xptr_t    fatfs_ctx_xp,
455                                               uint32_t  free_clusters,
456                                               uint32_t  free_cluster_hint )
457{
458    cxy_t         fat_cxy;             // FAT cluster identifier
459    fatfs_ctx_t * fatfs_ctx_ptr;       // local pointer on fatfs context in FAT cluster
460    uint8_t     * fs_info_buffer_ptr;  // local pointer on FS_INFO buffer in FAT cluster
461    xptr_t        fs_info_buffer_xp;   // extended pointer on FS_INFO buffer in FAT cluster
462    uint32_t      fs_info_lba;         // FS_INFO sector lba on IOC device
463
464    // get cluster and local pointer on FAT cluster context
465    fat_cxy       = GET_CXY( fatfs_ctx_xp ); 
466    fatfs_ctx_ptr = GET_PTR( fatfs_ctx_xp ); 
467
468    // get pointers on FS_INFO buffer in FAT cluster
469    fs_info_buffer_ptr = hal_remote_lpt( XPTR( fat_cxy , &fatfs_ctx_ptr->fs_info_buffer ) );
470    fs_info_buffer_xp  = XPTR( fat_cxy , fs_info_buffer_ptr );
471
472    // get lba of FS_INFO sector on IOC device from fatfs context
473    fs_info_lba = hal_remote_l32( XPTR( fat_cxy , &fatfs_ctx_ptr->fs_info_lba ) );
474
475    // update the FS_INFO buffer in FAT cluster
476    fatfs_set_remote_record( FS_FREE_CLUSTERS     , fs_info_buffer_xp , free_clusters );
477    fatfs_set_remote_record( FS_FREE_CLUSTER_HINT , fs_info_buffer_xp , free_cluster_hint );
478   
479    // update the FS_INFO sector on IOC device
480    return dev_ioc_sync_write( fs_info_buffer_xp , fs_info_lba , 1 );
481 
482}  // fatfs_free_clusters_update_ioc()
483
484//////////////////////////////////////////////////////////////////////////////////////////
[625]485// This static function decrements the  "free_clusters" variable, and updates the
[626]486// "free_cluster_hint" variable in the FATFS context in FAT cluster, identified
487// by the <fat_ctx_xp> argument, when a new <cluster> has been allocated from FAT.
[625]488// It scan all slots in the FAT mapper seen as an array of 32 bits words, looking for the
489// first free slot larger than the <cluster> argument, to update "free_cluster_hint".
[626]490// It calls the fatfs_free_clusters_update_ioc() function to synchronously update the
491// FS_INFO sector on the IOC device. It can be called by a thead running in any cluster.
[602]492//
493// WARNING : The free_lock protecting exclusive access to these variables
494//           must be taken by the calling function.
495//////////////////////////////////////////////////////////////////////////////////////////
[626]496// @ fatfs_ctx_xp  : extended pointer on FATFS context in FAT cluster.
497// @ cluster       : recently allocated cluster index in FAT.
498// @ return 0 if success, return -1 if the FS_INFO sector cannot be updated.
[602]499//////////////////////////////////////////////////////////////////////////////////////////
[626]500static error_t fatfs_free_clusters_decrement( xptr_t    fatfs_ctx_xp,
501                                              uint32_t  cluster )
[602]502{
[626]503    error_t       error;
504    cxy_t         fat_cxy;      // FAT cluster identifier
505    fatfs_ctx_t * fat_ctx_ptr;  // local pointer on fatfs context in FAT cluster
[602]506    xptr_t        mapper_xp;    // extended pointer on FAT mapper
507    xptr_t        hint_xp;      // extended pointer on "free_cluster_hint" shared variable
508    xptr_t        numb_xp;      // extended pointer on "free_clusters" shared variable
509    uint32_t      numb;         // "free_clusters" variable current value
[626]510    uint32_t      hint;         // "free_cluster_hint" variable current value
[602]511    uint32_t      page_id;      // page index in FAT mapper
512    uint32_t      slot_id;      // slot index in one page of FAT (1024 slots per page)
513    uint32_t      page_max;     // max number of pages in FAT mapper
514    xptr_t        page_xp;      // extended pointer on current page in FAT mapper
[625]515    xptr_t        base_xp;      // extended pointer on current page base
[602]516    xptr_t        slot_xp;      // extended pointer on current slot in FAT mapper
517
518#if DEBUG_FATFS_FREE_CLUSTERS
519uint32_t   cycle = (uint32_t)hal_get_cycles();
520thread_t * this  = CURRENT_THREAD;
[626]521if( DEBUG_FATFS_FREE_CLUSTERS < cycle )
[602]522printk("\n[%s] thread[%x,%x] enter for allocated cluster %x / cycle %d\n",
523__FUNCTION__, this->process->pid, this->trdid, cluster , cycle );
524#endif
525
[626]526    // get FAT cluster an local pointer on fatfs context in FAT cluster
527    fat_cxy      = GET_CXY( fatfs_ctx_xp );
528    fat_ctx_ptr  = GET_PTR( fatfs_ctx_xp );
529   
530    // build extended pointers on free_clusters, and free_cluster_hint in fatfs context
531    hint_xp = XPTR( fat_cxy , &fat_ctx_ptr->free_cluster_hint );
532    numb_xp = XPTR( fat_cxy , &fat_ctx_ptr->free_clusters );
[602]533
[626]534    // update "free_clusters" value
535    numb = hal_remote_l32( numb_xp ) - 1;
536    hal_remote_s32( numb_xp , numb );
[602]537
[625]538    // get extended pointer on FAT mapper
[626]539    mapper_xp = hal_remote_l64( XPTR( fat_cxy , &fat_ctx_ptr->fat_mapper_xp ) );
[625]540
541    // initialise variables to scan the FAT mapper
542    // and find the first free slot > cluster
[602]543    page_id  = (cluster + 1) >> 10;
544    slot_id  = (cluster + 1) & 0x3FF;
[626]545    page_max = hal_remote_l32( XPTR( fat_cxy, &fat_ctx_ptr->fat_sectors_count ) ) >> 3;
[602]546
547    // scan FAT mapper / loop on pages
548    while ( page_id < page_max )           
549    {
550        // get current page from mapper
551        page_xp = mapper_remote_get_page( mapper_xp , page_id );
552
553        if( page_xp == XPTR_NULL )
554        {
555            printk("\n[ERROR] in %s : cannot access FAT mapper\n", __FUNCTION__ );
556            return -1;
557        }
558
[625]559        // get extended pointer on page
560        base_xp = ppm_page2base( page_xp );
561
[602]562        // scan FAT mapper / loop on slots
563        while ( slot_id < 1024 )
564        {
565            // get extended pointer on current slot
[625]566            slot_xp = base_xp + (slot_id << 2);
[602]567
[625]568            // test slot value
[602]569            if ( hal_remote_l32( slot_xp ) == FREE_CLUSTER )
570            {
[626]571                // update "free_cluster_hint" value
572                hint = (page_id << 10) + slot_id - 1;
573                hal_remote_s32( hint_xp , hint );
[602]574
[626]575                // update FS_INFO sector on IOC device
576                error = fatfs_free_clusters_update_ioc( fatfs_ctx_xp , numb , hint );
577
578                if( error ) 
579                {
580                    printk("\n[ERROR] in %s : cannot update FS_INFO on IOC\n", __FUNCTION__ );
581                    return -1;
582                }
583
[602]584#if DEBUG_FATFS_FREE_CLUSTERS
585cycle = (uint32_t)hal_get_cycles();
586if( DEBUG_FATFS_FREE_CLUSTERS < (uint32_t)hal_get_cycles() )
[626]587printk("\n[%s] thread[%x,%x] updated free cluster info  / hint %x / number %x\n",
588__FUNCTION__, this->process->pid, this->trdid, 
589hal_remote_l32(hint_xp), hal_remote_l32(numb_xp) );
[602]590#endif
591                return 0;
592            }
593
[625]594            // update slot_id
595            slot_id = 0;
[602]596
597        }  // end loop on slots
598
[626]599        // update page_id & slot_id variables
[602]600        page_id++;
601        slot_id = 0;
602
603    }  // end loop on pages
604
605    // return error if no free cluster found
606    printk("\n[ERROR] in %s : No free cluster found\n", __FUNCTION__ );
607    return -1;
608   
609}  // end fatfs_free_clusters_decrement()
610
611//////////////////////////////////////////////////////////////////////////////////////////
[625]612// This static function increments the "free_clusters" variable, and updates the
[626]613// "free_cluster_hint" variables in the FATFS context in FAT cluster, identified
614// by the <fat_ctx_xp> argument, when a cluster is released to FAT.
[625]615// If the released cluster index is smaller than the current (hint) value,
616// it set "free_cluster_hint" <= cluster.
[626]617// It calls the fatfs_free_clusters_update_ioc() function to synchronously update the
618// FS_INFO sector on the IOC device. It can be called by a thead running in any cluster.
[602]619//
620// WARNING : The free_lock protecting exclusive access to these variables
621//           must be taken by the calling function.
622//////////////////////////////////////////////////////////////////////////////////////////
[626]623// @ fatfs_ctx_xp  : extended pointer on FATFS context in FAT cluster.
624// @ cluster       : recently released cluster index in FAT.
625// @ return 0 if success, return -1 if the FS_INFO sector cannot be updated.
[602]626//////////////////////////////////////////////////////////////////////////////////////////
[626]627static error_t fatfs_free_clusters_increment( xptr_t   fatfs_ctx_xp,
628                                              uint32_t cluster )
[602]629{
[626]630    error_t       error;
631    cxy_t         fat_cxy;      // FAT cluster identifier
632    fatfs_ctx_t * fat_ctx_ptr;  // local pointer on fatfs context in FAT cluster
[602]633    xptr_t        hint_xp;      // extended pointer on "free_cluster_hint" shared variable
634    xptr_t        numb_xp;      // extended pointer on "free_clusters" shared variable
635    uint32_t      hint;         // "free_cluster_hint" variable current value
636    uint32_t      numb;         // "free_clusters" variable current value
637
[626]638#if DEBUG_FATFS_FREE_CLUSTERS
639uint32_t   cycle = (uint32_t)hal_get_cycles();
640thread_t * this  = CURRENT_THREAD;
641if( DEBUG_FATFS_FREE_CLUSTERS < cycle )
642printk("\n[%s] thread[%x,%x] enter for released cluster %x / cycle %d\n",
643__FUNCTION__, this->process->pid, this->trdid, cluster , cycle );
644#endif
645
646    // get FAT cluster an local pointer on fatfs context in FAT cluster
647    fat_cxy      = GET_CXY( fatfs_ctx_xp );
648    fat_ctx_ptr  = GET_PTR( fatfs_ctx_xp );
649   
[625]650    // build extended pointers on free_clusters, and free_cluster_hint
[626]651    hint_xp = XPTR( fat_cxy , &fat_ctx_ptr->free_cluster_hint );
652    numb_xp = XPTR( fat_cxy , &fat_ctx_ptr->free_clusters );
[602]653
654    // get current value of free_cluster_hint and free_clusters
655    hint = hal_remote_l32( hint_xp );
656    numb = hal_remote_l32( numb_xp );
657
[626]658    // update "numb" and "hint" variables as required
659    numb++;
660    if ( (cluster - 1) < hint ) hint = cluster - 1;
[602]661
662    // update free_clusters
[626]663    hal_remote_s32( numb_xp , numb );
664    hal_remote_s32( hint_xp , hint );
[602]665
[626]666    // update FS_INFO sector on IOC device
667    error = fatfs_free_clusters_update_ioc( fatfs_ctx_xp , numb , hint );
668
669    if( error ) 
670    {
671        printk("\n[ERROR] in %s : cannot update FS_INFO on IOC\n", __FUNCTION__ );
672        return -1;
673    }
674
[602]675#if DEBUG_FATFS_FREE_CLUSTERS
676thread_t * this = CURRENT_THREAD;
677if( DEBUG_FATFS_FREE_CLUSTERS < (uint32_t)hal_get_cycles() )
[626]678printk("\n[%s] thread[%x,%x] updated free cluster info : hint %x / number %x\n",
[602]679__FUNCTION__, this->process->pid, this->trdid,
680hal_remote_l32( hint_xp ), hal_remote_l32( numb_xp ) );
681#endif
682
[626]683    return 0;
684
[602]685}  // end fatfs_free_clusters_increment()
686
687//////////////////////////////////////////////////////////////////////////////////////////
688// This recursive function is called by the generic function fatfs_release_all_clusters()
689// It release all clusters allocated to a given inode in the FAT mapper.
690// The removal is done in reverse order of the linked list (from last to first).
691// It does NOT update the FS on the IOC device.
692//////////////////////////////////////////////////////////////////////////////////////////
[625]693// @ mapper_cxy : FAT mapper cluster identifier.
694// @ mapper_ptr : local pointer on FAT mapper.
695// @ fatfs_ctx  : local pointer on FATFS context in FAT cluster.
696// @ cluster    : index of cluster to be released from FAT mapper.
[602]697// @ return 0 if success / return -1 if error (cannot access FAT)
698//////////////////////////////////////////////////////////////////////////////////////////
[625]699static error_t fatfs_recursive_release( cxy_t         mapper_cxy,
700                                        mapper_t    * mapper_ptr,
701                                        fatfs_ctx_t * fatfs_ctx,
702                                        uint32_t      cluster )
[602]703{
704    uint32_t next;
705
[625]706    // build extended pointer on FAT mapper
707    xptr_t mapper_xp = XPTR( mapper_cxy , mapper_ptr );
[602]708
[625]709    // get next cluster index from FAT mapper
710    if ( mapper_remote_get_32( mapper_xp,
711                               cluster,
712                               &next ) ) return -1;
713
[602]714#if (DEBUG_FATFS_RELEASE_INODE & 1)
715thread_t * this = CURRENT_THREAD;
716if ( DEBUG_FATFS_RELEASE_INODE < (uint32_t)hal_get_cycles() )
717printk("\n[%s] thread[%x,%x] access FAT for cluster %x / next %x\n",
718__FUNCTION__, this->process->pid, this->trdid, cluster, next );
719#endif
720
721    if ( next < END_OF_CHAIN_CLUSTER_MIN )  // non terminal case
722    {
723        // call fatfs_recursive_release() on next cluster
[625]724        if ( fatfs_recursive_release( mapper_cxy,
725                                      mapper_ptr,
726                                      fatfs_ctx,
727                                      next ) ) return -1;
[602]728    }       
729
730    // update current cluster in FAT mapper
[625]731    if ( mapper_remote_set_32( mapper_xp, 
732                               cluster,
733                               FREE_CLUSTER ) ) return -1;
[602]734
[626]735    // Update free_cluster info in FATFS context and in FS_INFO sector
736    return fatfs_free_clusters_increment( XPTR( mapper_cxy , fatfs_ctx ) , cluster );
[602]737
738}  // end fatfs_recursive_release()
739
740
741//////////////////////////////////////////////////////////////////////////////////////////
[568]742//              FATFS specific extern functions
[238]743//////////////////////////////////////////////////////////////////////////////////////////
[1]744
[626]745///////////////////////////////////
746void fatfs_display_ctx( cxy_t cxy )
[265]747{
[626]748    // get pointer on local FATFS context
749    vfs_ctx_t   * vfs_ctx = &fs_context[FS_TYPE_FATFS];
750        fatfs_ctx_t * ctx     = hal_remote_lpt( XPTR( cxy , &vfs_ctx->extend ) );
751
752    uint32_t fat_sectors       = hal_remote_l32( XPTR( cxy , &ctx->fat_sectors_count ) );
753    uint32_t sector_size       = hal_remote_l32( XPTR( cxy , &ctx->bytes_per_sector ) );
754    uint32_t sec_per_clus      = hal_remote_l32( XPTR( cxy , &ctx->sectors_per_cluster ) );
755    uint32_t fat_lba           = hal_remote_l32( XPTR( cxy , &ctx->fat_begin_lba ) );
756    uint32_t data_lba          = hal_remote_l32( XPTR( cxy , &ctx->cluster_begin_lba ) );
757    uint32_t fsinfo_lba        = hal_remote_l32( XPTR( cxy , &ctx->fs_info_lba ) );
758    uint32_t root_dir_clus     = hal_remote_l32( XPTR( cxy , &ctx->root_dir_cluster ) );
759    uint32_t free_clusters     = hal_remote_l32( XPTR( cxy , &ctx->free_clusters ) );
760    uint32_t free_cluster_hint = hal_remote_l32( XPTR( cxy , &ctx->free_cluster_hint ) );
761    xptr_t   mapper_xp         = hal_remote_l64( XPTR( cxy , &ctx->fat_mapper_xp ) );
762    void   * fs_info_buffer    = hal_remote_lpt( XPTR( cxy , &ctx->fs_info_buffer ) );
763
764    printk("\n*** FAT context in cluster %x\n" 
[602]765           "- fat_sectors       = %d\n"
766           "- sector size       = %d\n"
767           "- cluster size      = %d\n"
[626]768           "- fat_lba           = %x\n"
769           "- data_lba          = %x\n"
770           "- fsinfo_lba        = %x\n"
771           "- root_dir_cluster  = %x\n"
772           "- free_clusters     = %x\n"
773           "- free_cluster_hint = %x\n"
774           "- fat_mapper_ptr    = %x\n"
775           "- fs_info_buffer    = %x\n",
776           cxy,
777           fat_sectors,
778           sector_size,
779           sector_size * sec_per_clus,
780           fat_lba,
781           data_lba,
782           fsinfo_lba,
783           root_dir_clus,
784           free_clusters,
785           free_cluster_hint,
786           GET_PTR( mapper_xp ),
787           fs_info_buffer );
[265]788
[626]789}  // end fatfs_ctx_display()
[602]790
791//////////////////////////////////////////
792void fatfs_display_fat( uint32_t  page_id,
793                        uint32_t  nentries )
794{
795    uint32_t line;
796    uint32_t maxline;
797
[626]798    // compute number of lines to display
[602]799    maxline = nentries >> 3;
800    if( nentries & 0x7 ) maxline++;
801
802    // get pointer on local FATFS context
[626]803    vfs_ctx_t   * vfs_ctx       = &fs_context[FS_TYPE_FATFS];
804    fatfs_ctx_t * loc_fatfs_ctx = (fatfs_ctx_t *)vfs_ctx->extend;
[602]805
806    // get extended pointer on FAT mapper
[626]807    xptr_t fat_mapper_xp  = loc_fatfs_ctx->fat_mapper_xp;
[602]808
[626]809    // get FAT cluster identifier
810    cxy_t  fat_cxy = GET_CXY( fat_mapper_xp );
811
812    // get pointer on FATFS context in FAT cluster
813    fatfs_ctx_t * fat_fatfs_ctx = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) );
814 
815    // get current value of hint and free_clusters
816    uint32_t hint = hal_remote_l32( XPTR( fat_cxy , &fat_fatfs_ctx->free_cluster_hint ) );
817    uint32_t free = hal_remote_l32( XPTR( fat_cxy , &fat_fatfs_ctx->free_clusters ) );
818 
819    // get extended pointer on requested page in FAT mapper
[602]820    xptr_t     page_xp  = mapper_remote_get_page( fat_mapper_xp , page_id );
821
822    // get extended pointer on requested page base
823    xptr_t     base_xp  = ppm_page2base( page_xp );
[626]824    void     * base     = GET_PTR( base_xp );
[602]825
[626]826    printk("\n***** FAT mapper / cxy %x / page_id %d / base %x / free_clusters %x / hint %x\n",
827    fat_cxy, page_id, base, free, hint );
828
[602]829    for( line = 0 ; line < maxline ; line++ )
830    {
831        printk("%x : %X | %X | %X | %X | %X | %X | %X | %X\n", (line<<3),
832        hal_remote_l32( base_xp + ((line<<5)      ) ),
833        hal_remote_l32( base_xp + ((line<<5) + 4  ) ),
834        hal_remote_l32( base_xp + ((line<<5) + 8  ) ),
835        hal_remote_l32( base_xp + ((line<<5) + 12 ) ),
836        hal_remote_l32( base_xp + ((line<<5) + 16 ) ),
837        hal_remote_l32( base_xp + ((line<<5) + 20 ) ),
838        hal_remote_l32( base_xp + ((line<<5) + 24 ) ),
839        hal_remote_l32( base_xp + ((line<<5) + 28 ) ) );
840    }
841
842}  // end fatfs_display_fat()
843
844///////////////////////////////////////////////////////
845error_t fatfs_get_cluster( uint32_t   first_cluster_id,
[406]846                           uint32_t   searched_page_index,
[265]847                           uint32_t * searched_cluster_id )
[238]848{
[602]849    xptr_t     current_page_xp;        // pointer on current page descriptor
850    uint32_t * buffer;                 // pointer on current page (array of uint32_t)
[406]851    uint32_t   current_page_index;     // index of current page in FAT
[625]852    uint32_t   current_slot_index;     // index of slot in current page
[238]853    uint32_t   page_count_in_file;     // index of page in file (index in linked list)
[406]854    uint32_t   next_cluster_id;        // content of current FAT slot
[1]855
[602]856assert( (searched_page_index > 0) ,
857"no FAT access required for first page\n");
[246]858
[438]859#if DEBUG_FATFS_GET_CLUSTER
[602]860uint32_t   cycle = (uint32_t)hal_get_cycles();
861thread_t * this  = CURRENT_THREAD;
[438]862if( DEBUG_FATFS_GET_CLUSTER < cycle )
[625]863printk("\n[%s] thread[%x,%x] enter / first_cluster_id %d / searched_index %d / cycle %d\n",
[602]864__FUNCTION__, this->process->pid, this->trdid, first_cluster_id, searched_page_index, cycle );
[435]865#endif
[265]866
[602]867    // get local pointer on local FATFS context
868    fatfs_ctx_t * ctx = fs_context[FS_TYPE_FATFS].extend;
[1]869
[602]870    // get extended pointer and cluster on FAT mapper
[625]871    xptr_t fat_mapper_xp  = ctx->fat_mapper_xp;
872    cxy_t  fat_mapper_cxy = GET_CXY( fat_mapper_xp );
[602]873
874    // initialize loop variable (1024 slots per page)
875    current_page_index  = first_cluster_id >> 10;
[625]876    current_slot_index  = first_cluster_id & 0x3FF;
[238]877    page_count_in_file  = 0;
[406]878    next_cluster_id     = 0xFFFFFFFF;
[238]879
[625]880    // scan FAT mapper (i.e. traverse FAT linked list)
[406]881    while( page_count_in_file < searched_page_index )
[238]882    {
[625]883        // get pointer on current page descriptor in FAT mapper
884        current_page_xp = mapper_remote_get_page( fat_mapper_xp , current_page_index );
[238]885
[602]886        if( current_page_xp == XPTR_NULL )
887        {
[625]888            printk("\n[ERROR] in %s : cannot get next page from FAT mapper\n", __FUNCTION__);
[602]889            return -1;
890        }
[238]891
892        // get pointer on buffer for current page
[602]893        xptr_t base_xp = ppm_page2base( current_page_xp );
894        buffer = (uint32_t *)GET_PTR( base_xp );
[238]895
896        // get FAT slot content
[625]897        next_cluster_id = hal_remote_l32( XPTR( fat_mapper_cxy,
898                                                &buffer[current_slot_index] ) );
[238]899
[438]900#if (DEBUG_FATFS_GET_CLUSTER & 1)
901if( DEBUG_FATFS_GET_CLUSTER < cycle )
[602]902printk("\n[%s] traverse FAT / current_page_index = %d\n"
[625]903"current_slot_index = %d / next_cluster_id = %d\n",
904__FUNCTION__, current_page_index, current_slot_index , next_cluster_id );
[435]905#endif
[406]906
[238]907        // update loop variables
[625]908        current_page_index = next_cluster_id >> 10;
909        current_slot_index = next_cluster_id & 0x3FF;
[238]910        page_count_in_file++;
911    }
[246]912
[625]913    if( next_cluster_id == 0xFFFFFFFF )
914    {
915        printk("\n[ERROR] in %s : searched_cluster_id not found in FAT\n", __FUNCTION__ );
916        return -1;
917    }
[406]918   
[438]919#if DEBUG_FATFS_GET_CLUSTER
[435]920cycle = (uint32_t)hal_get_cycles();
[438]921if( DEBUG_FATFS_GET_CLUSTER < cycle )
[602]922printk("\n[%s] thread[%x,%x] exit / searched_cluster_id = %d / cycle %d\n",
923__FUNCTION__, this->process->pid, this->trdid, next_cluster_id / cycle );
[435]924#endif
[406]925
926    *searched_cluster_id = next_cluster_id;
[238]927    return 0;
928
929}  // end fatfs_get_cluster()
930
931
932
[1]933///////////////////////////////////////////////////////////////////////////////////////
[602]934// Generic API : the following functions are called by the kernel VFS
[188]935//               and must be defined by all supported file systems.
[1]936///////////////////////////////////////////////////////////////////////////////////////
937
[568]938/////////////////////////////////////
[484]939fatfs_ctx_t * fatfs_ctx_alloc( void )
[1]940{
[23]941    kmem_req_t    req;
[188]942        req.type    = KMEM_FATFS_CTX;
943        req.size    = sizeof(fatfs_ctx_t);
944    req.flags   = AF_KERNEL | AF_ZERO;
[1]945
[188]946        return (fatfs_ctx_t *)kmem_alloc( &req );
947}
[23]948
[188]949//////////////////////////////////////////////
950void fatfs_ctx_init( fatfs_ctx_t * fatfs_ctx )
951{
952    error_t       error;
953    kmem_req_t    req;
954    uint8_t     * buffer;
[626]955    xptr_t        buffer_xp;
[23]956
[602]957#if DEBUG_FATFS_CTX_INIT
[625]958uint32_t   cycle = (uint32_t)hal_get_cycles();
959thread_t * this  = CURRENT_THREAD;
[602]960if( DEBUG_FATFS_CTX_INIT < cycle )
961printk("\n[%s] thread[%x,%x] enter for fatfs_ctx = %x / cycle %d\n",
962__FUNCTION__ , this->process->pid, this->trdid, fatfs_ctx , cycle );
[435]963#endif
[23]964
[602]965// check argument
[625]966assert( (fatfs_ctx != NULL) , "pointer on FATFS context is NULL" );
[23]967
[626]968// check only cluster 0 does FATFS initialization
[625]969assert( (local_cxy == 0) , "only cluster 0 can initialize FATFS");
[602]970
[626]971    // allocate a permanent 512 bytes buffer to store
972    // - temporarily the BOOT sector
973    // - permanently the FS_INFO sector
[50]974        req.type    = KMEM_512_BYTES;
975    req.flags   = AF_KERNEL | AF_ZERO;
976        buffer      = (uint8_t *)kmem_alloc( &req );
[626]977    buffer_xp   = XPTR( local_cxy , buffer );
[188]978
[602]979    if( buffer == NULL ) 
980    {
981        printk("\n[PANIC] in %s : cannot allocate buffer\n", __FUNCTION__ );
982        hal_core_sleep();
983    }
[50]984     
[602]985    // load the BOOT record from device
[626]986    error = dev_ioc_sync_read( buffer_xp , 0 , 1 );
[23]987
[602]988    if ( error )
989    {
990        printk("\n[PANIC] in %s : cannot access boot record\n", __FUNCTION__ );
991        hal_core_sleep();
992    }
[279]993
[602]994#if (DEBUG_FATFS_CTX_INIT & 0x1)
995if( DEBUG_FATFS_CTX_INIT < cycle )
[623]996putb( "boot record", buffer , 256 );
[50]997#endif
998
[602]999    // get sector size from boot record
[626]1000    uint32_t sector_size = fatfs_get_record( BPB_BYTSPERSEC , buffer );
[602]1001    if ( sector_size != 512 )
1002    {
1003        printk("\n[PANIC] in %s : sector size must be 512 bytes\n", __FUNCTION__ );
1004        hal_core_sleep();
1005    }
[50]1006
[602]1007    // get cluster size from boot record
[626]1008    uint32_t nb_sectors = fatfs_get_record( BPB_SECPERCLUS , buffer );
[602]1009    if ( nb_sectors != 8 )
1010    {
1011        printk("\n[PANIC] in %s : cluster size must be 8 sectors\n", __FUNCTION__ );
1012        hal_core_sleep();
1013    }
[50]1014
[602]1015    // get number of FAT copies from boot record
[626]1016    uint32_t nb_fats = fatfs_get_record( BPB_NUMFATS , buffer );
[602]1017    if ( nb_fats != 1 )
1018    {
1019        printk("\n[PANIC] in %s : number of FAT copies must be 1\n", __FUNCTION__ );
1020        hal_core_sleep();
1021    }
[50]1022
[602]1023    // get number of sectors in FAT from boot record
[626]1024    uint32_t fat_sectors = fatfs_get_record( BPB_FAT32_FATSZ32 , buffer );
[602]1025    if ( (fat_sectors & 0xF) != 0 )
1026    {
1027        printk("\n[PANIC] in %s : FAT size not multiple of 16 sectors\n", __FUNCTION__ );
1028        hal_core_sleep();
1029    }
[50]1030
[602]1031    // get root cluster from boot record
[626]1032    uint32_t root_cluster = fatfs_get_record( BPB_FAT32_ROOTCLUS , buffer );
[602]1033    if ( root_cluster != 2 ) 
1034    {
1035        printk("\n[PANIC] in %s : root cluster index must be 2\n", __FUNCTION__ );
1036        hal_core_sleep();
1037    }
[50]1038
[23]1039    // get FAT lba from boot record
[626]1040    uint32_t fat_lba = fatfs_get_record( BPB_RSVDSECCNT , buffer );
[50]1041
[602]1042    // get FS_INFO sector lba from boot record
[626]1043    uint32_t fs_info_lba = fatfs_get_record( BPB_FAT32_FSINFO , buffer );
[602]1044
1045    // load the FS_INFO record from device
[626]1046    error = dev_ioc_sync_read( buffer_xp , fs_info_lba , 1 );
[602]1047
1048    if ( error )
1049    {
1050        printk("\n[PANIC] in %s : cannot access FS_INFO record\n", __FUNCTION__ );
1051        hal_core_sleep();
1052    }
1053
[626]1054    // get free_clusters number from FS_INFO record
1055    uint32_t free_clusters = fatfs_get_record( FS_FREE_CLUSTERS , buffer );
[602]1056    if ( free_clusters >= fat_sectors << 7 )
1057    {
1058        printk("\n[PANIC] in %s : unconsistent free_clusters\n", __FUNCTION__ );
1059        hal_core_sleep();
1060    }
1061
[626]1062    // get free_cluster_hint from FS_INFO record
1063    uint32_t free_cluster_hint = fatfs_get_record( FS_FREE_CLUSTER_HINT , buffer );
1064
[602]1065    if ( free_cluster_hint >= fat_sectors << 7 )
1066    {
1067        printk("\n[PANIC] in %s : unconsistent free_cluster_hint\n", __FUNCTION__ );
1068        hal_core_sleep();
1069    }
1070
[23]1071    // allocate a mapper for the FAT itself
[246]1072    mapper_t * fat_mapper = mapper_create( FS_TYPE_FATFS );
[602]1073    if ( fat_mapper == NULL )
1074    {
1075        printk("\n[PANIC] in %s : no memory for FAT mapper\n", __FUNCTION__ );
1076        hal_core_sleep();
1077    }
[50]1078
[626]1079    // the inode field is NULL for the FAT mapper
[246]1080    fat_mapper->inode = NULL;
1081
[23]1082    // initialize the FATFS context
1083    fatfs_ctx->fat_begin_lba         = fat_lba;
1084    fatfs_ctx->fat_sectors_count     = fat_sectors; 
1085    fatfs_ctx->bytes_per_sector      = sector_size;
[188]1086    fatfs_ctx->sectors_per_cluster   = nb_sectors;
[23]1087    fatfs_ctx->cluster_begin_lba     = fat_lba + fat_sectors;
1088    fatfs_ctx->root_dir_cluster      = 2;
1089    fatfs_ctx->fat_mapper_xp         = XPTR( local_cxy , fat_mapper );
[626]1090    fatfs_ctx->fs_info_lba           = fs_info_lba;
[602]1091    fatfs_ctx->free_clusters         = free_clusters;
1092    fatfs_ctx->free_cluster_hint     = free_cluster_hint;
[626]1093    fatfs_ctx->fs_info_buffer        = buffer;
[23]1094
[602]1095    remote_queuelock_init( XPTR( local_cxy , &fatfs_ctx->free_lock ) , LOCK_FATFS_FREE );
1096
[625]1097#if (DEBUG_FATFS_CTX_INIT & 0x1)
1098if( DEBUG_FATFS_CTX_INIT < cycle )
1099fatfs_ctx_display( fatfs_ctx );
1100#endif
1101
[602]1102#if DEBUG_FATFS_CTX_INIT
[435]1103cycle = (uint32_t)hal_get_cycles();
[602]1104if( DEBUG_FATFS_CTX_INIT < cycle )
1105printk("\n[%s]  thread[%x,%x] exit for fatfs_ctx = %x / cycle %d\n",
1106__FUNCTION__, this->process->pid, this->trdid, fatfs_ctx, cycle );
[435]1107#endif
[279]1108
[23]1109}  // end fatfs_ctx_init()
1110
[188]1111/////////////////////////////////////////////////
1112void fatfs_ctx_destroy( fatfs_ctx_t * fatfs_ctx )
[23]1113{
1114    kmem_req_t    req;
[188]1115    req.type = KMEM_FATFS_CTX;
[23]1116    req.ptr  = fatfs_ctx;
1117    kmem_free( &req );
1118}
1119
[602]1120///////////////////////////////////////////////
1121error_t fatfs_add_dentry( vfs_inode_t  * inode,
1122                          vfs_dentry_t * dentry )
[1]1123{
[401]1124    error_t       error;
[602]1125    uint32_t      length;        // dentry name length
1126    uint32_t      nb_lfn;        // number or required LFN
1127    char          sfn[11];       // buffer for SFN name
1128    uint8_t       checksum;      // name checksum
1129    mapper_t    * mapper;        // loal pointer on parent inode mapper
1130    xptr_t        mapper_xp;     // extended pointer on parent inode mapper
1131    xptr_t        child_xp;      // extended pointer on child inode
1132    cxy_t         child_cxy;     // child inode cluster
1133    vfs_inode_t * child_ptr;     // child inode local pointer
1134    uint32_t      size;          // child inode size
1135    uint32_t      type;          // child inode type
1136    uint32_t      cluster;       // child inode cluster index
[246]1137
[602]1138#if DEBUG_FATFS_ADD_DENTRY
1139char       dir_name[CONFIG_VFS_MAX_NAME_LENGTH];
1140uint32_t   cycle = (uint32_t)hal_get_cycles();
1141thread_t * this  = CURRENT_THREAD;
1142vfs_inode_get_name( XPTR( local_cxy , inode ) , dir_name );
1143if( DEBUG_FATFS_ADD_DENTRY < cycle )
1144printk("\n[%s]  thread[%x,%x] enter / parent <%s> / child <%s> / cycle %d\n",
1145__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle );
1146#endif
[1]1147
[602]1148// check arguments
1149assert( (inode != NULL) , "inode pointer is NULL\n" );
1150assert( (dentry != NULL) , "dentry pointer is NULL\n" );
1151assert( (inode->mapper != NULL ) , "mapper pointer is NULL\n" );
1152 
1153    // get pointers on directory mapper
1154    mapper    = inode->mapper;
1155    mapper_xp = XPTR( local_cxy , mapper );
[1]1156
[602]1157    // get extended pointers on remote child inode
1158    child_xp  = dentry->child_xp;
1159    child_cxy = GET_CXY( child_xp );
1160    child_ptr = GET_PTR( child_xp );
[1]1161
[602]1162    // get relevant infos from child inode
1163    type    =                     hal_remote_l32( XPTR( child_cxy , &child_ptr->type ) );
1164    size    =                     hal_remote_l32( XPTR( child_cxy , &child_ptr->size ) );
1165    cluster = (uint32_t)(intptr_t)hal_remote_lpt( XPTR( child_cxy , &child_ptr->extend ) );
[1]1166
[602]1167    // analyse dentry name
1168    error = fatfs_name_format( dentry->name,
1169                               &length,
1170                               &nb_lfn,
1171                               sfn,
1172                               &checksum );
1173    if ( error )
[246]1174    {
[602]1175        printk("\n[ERROR] in %s : dentry name > 31 bytes\n", __FUNCTION__ );
1176        return -1;
1177    }
1178                               
1179    // Search end of directory with two embedded loops:
1180    // - scan the pages in the mapper
1181    // - scan the entries in each page to find NO_MORE_ENTRY
[1]1182
[602]1183    xptr_t     page_xp;                 // extended pointer on page descriptor
1184    xptr_t     base_xp;                 // extended pointer on page base
1185    uint8_t  * base;                    // local pointer on page base (array of bytes)
1186    uint32_t   page_id = 0;             // page index in mapper
1187    uint32_t   offset  = 0;             // position in page
1188    uint32_t   found   = 0;             // NO_MORE_ENTRY found
[1]1189
[602]1190    // loop on pages in mapper
1191    while ( found == 0 )
[1]1192    {
[602]1193        // get extended pointer on page descriptor in mapper
1194        page_xp  = mapper_remote_get_page( mapper_xp , page_id );
[1]1195
[602]1196        if ( page_xp == XPTR_NULL )
1197        {
1198            printk("\n[ERROR] in %s : cannot extend directory mapper\n", __FUNCTION__ );
1199            return -1;
1200        }
1201       
1202        // get pointer on page base
1203        base_xp = ppm_page2base( page_xp );
1204        base = GET_PTR( base_xp );
[246]1205
[602]1206        // loop on directory entries in this page
1207        while ( (offset < 4096) && (found == 0) )
[265]1208        {
[626]1209            if ( fatfs_get_record( LDIR_ORD, (base + offset) ) == NO_MORE_ENTRY )
[602]1210            {
1211                found = 1;
1212            } 
1213            else
1214            {
1215                offset = offset + 32;
1216            }
1217        }  // end loop on entries
1218
1219        if ( found == 0 )
1220        {
1221            page_id++;
1222            offset = 0;
[265]1223        }
[602]1224    }  // end loop on pages
1225
1226    // Modify the directory mapper: depending on the name length,
1227    // the new child requires to write (3, 4, or 5) directory entries.
1228    // To actually register the new child, we use a 5 steps FSM
1229    // (one state per entry to be written), that is traversed as:
1230    // LFN3 -> LFN2 -> LFN1 -> NORMAL -> NOMORE
1231    // At most two pages are modified:
1232    // - the page containing the NO_MORE_ENTRY is always modified
1233    // - the following page can be modified if the name spread on to pages.
1234
1235    char * name = dentry->name;
1236
1237    uint32_t step;          // FSM state
1238
1239    if      ( nb_lfn == 1 ) step = 3;
1240    else if ( nb_lfn == 2 ) step = 4;
1241    else if ( nb_lfn == 3 ) step = 5;
1242   
1243    uint8_t  * entry;       // pointer on directory entry to be written
1244    uint32_t   i;           // byte index in one 32 bytes directory
1245    uint32_t   c;           // character index in name
1246
1247    while ( step )   
1248    {
1249        // when the new child is split on two pages,
1250        // we need to access a new page in mapper
1251        if ( offset >= 4096 )
[265]1252        {
[602]1253            // copy the modified page to IOC device
[614]1254            fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
[265]1255
[602]1256            // get the next page in FAT mapper
1257            page_xp  = mapper_remote_get_page( mapper_xp , page_id + 1 );
1258
1259            if ( page_xp == XPTR_NULL )
[265]1260            {
[602]1261                printk("\n[ERROR] in %s : cannot extend directory mapper\n", __FUNCTION__ );
1262                return -1;
1263            }
1264       
1265            // get pointer on page base
1266            base_xp = ppm_page2base( page_xp );
1267            base = GET_PTR( base_xp );
1268           
1269            // update offset
1270            offset = 0;
1271        }
[407]1272
[602]1273        // compute directory entry address
1274        entry = base + offset;
1275
1276#if (DEBUG_FATFS_ADD_DENTRY & 1)
1277if( DEBUG_FATFS_ADD_DENTRY < cycle )
1278printk("\n[%s] FSM step = %d / offset = %x / nb_lfn = %d\n",
1279__FUNCTION__, step, offset, nb_lfn );
[435]1280#endif
[602]1281
1282        // write 32 bytes (one directory entry) per iteration
1283        switch ( step )
1284        {
1285            case 5:   // write LFN3 entry
1286            {
1287                c = 26;
1288                // scan the 32 bytes in dir_entry
1289                for ( i = 0 ; i < 32 ; i++ )
1290                {
1291                    if (i == 0)
1292                    {
1293                        if ( nb_lfn == 3) entry[i] = 0x43;
1294                        else              entry[i] = 0x03;
1295                    }
1296                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
1297                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
1298                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
1299                              ( c < length ) )
1300                    {
1301                                          entry[i] = name[c];
1302                                          c++;
1303                    }
1304                    else if (i == 11)     entry[i] = 0x0F;
1305                    else if (i == 13)     entry[i] = checksum;
1306                    else                  entry[i] = 0x00;
1307                }
1308                step--;
1309                break;
[265]1310            }
[602]1311            case 4:   // write LFN2 entry 
[265]1312            {
[602]1313                c = 13;
1314                // scan the 32 bytes in dir_entry
1315                for ( i = 0 ; i < 32 ; i++ )
1316                {
1317                    if (i == 0)
1318                    {
1319                        if ( nb_lfn == 2) entry[i] = 0x42;
1320                        else              entry[i] = 0x02;
1321                    }
1322                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
1323                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
1324                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
1325                              ( c < length ) )
1326                    {
1327                                          entry[i] = name[c];
1328                                          c++;
1329                    }
1330                    else if (i == 11)     entry[i] = 0x0F;
1331                    else if (i == 13)     entry[i] = checksum;
1332                    else                  entry[i] = 0x00;
1333                }
1334                step--;
1335                break;
1336            }
1337            case 3:   // Write LFN1 entry   
1338            {
1339                c = 0;
1340                // scan the 32 bytes in dir_entry
1341                for ( i = 0 ; i < 32 ; i++ )
1342                {
1343                    if (i == 0)
1344                    {
1345                        if ( nb_lfn == 1) entry[i] = 0x41;
1346                        else              entry[i] = 0x01;
1347                    }
1348                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
1349                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
1350                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
1351                              ( c < length ) )
1352                    {
1353                                          entry[i] = name[c];
1354                                          c++;
1355                    }
1356                    else if (i == 11)     entry[i] = 0x0F;
1357                    else if (i == 13)     entry[i] = checksum;
1358                    else                  entry[i] = 0x00;
1359                }
1360                step--;
1361                break;
1362            }
1363            case 2:   // write NORMAL entry     
1364            {
1365                // scan the 32 bytes in dir_entry
1366                for ( i = 0 ; i < 32 ; i++ )
1367                {
1368                    if      ( i < 11 )                              // 8.3 SFN
1369                    {
1370                                          entry[i] = sfn[i];
1371                    }
1372                    else if (i == 11)                               // ATTR
1373                    {
1374                        if (type == INODE_TYPE_DIR)  entry[i] = 0x10;
1375                        else                         entry[i] = 0x20;
1376                    }
1377                    else if (i == 20)     entry[i] = cluster>>16;   // cluster.B2
1378                    else if (i == 21)     entry[i] = cluster>>24;   // cluster.B3
1379                    else if (i == 26)     entry[i] = cluster>>0;    // cluster.B0
1380                    else if (i == 27)     entry[i] = cluster>>8;    // cluster.B1
1381                    else if (i == 28)     entry[i] = size>>0;       // size.B0
1382                    else if (i == 29)     entry[i] = size>>8;       // size.B1
1383                    else if (i == 30)     entry[i] = size>>16;      // size.B2
1384                    else if (i == 31)     entry[i] = size>>24;      // size.B3
1385                    else                  entry[i] = 0x00;
1386                }
[407]1387
[602]1388                // update the "extend" field in dentry descriptor
1389                dentry->extend = (void*)(intptr_t)(((page_id<<12) + offset)>>5);
1390
1391                step--;
1392                break;
[265]1393            }
[602]1394            case 1:   // write NOMORE entry 
1395            {
1396                entry [0] = 0x00;
1397                step--;
1398                break;
1399            }
1400        } // end switch step
[265]1401
[602]1402        offset += 32;
[265]1403
[602]1404    } // exit while     
1405
1406    // copy the modified page to the IOC device
[614]1407    fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
[602]1408
1409#if DEBUG_FATFS_ADD_DENTRY
1410cycle = (uint32_t)hal_get_cycles();
1411if( DEBUG_FATFS_ADD_DENTRY < cycle )
[614]1412printk("\n[%s]  thread[%x,%x] exit / parent <%s> / child <%s> / cycle %d\n",
[602]1413__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle );
[435]1414#endif
[406]1415
[602]1416    return 0;
[265]1417
[602]1418}  // end fatfs_add_dentry()
[246]1419
[602]1420//////////////////////////////////////////////////
1421error_t fatfs_remove_dentry( vfs_inode_t  * inode,
1422                             vfs_dentry_t * dentry )
1423{
1424    xptr_t     mapper_xp;  // extended pointer on mapper
1425    mapper_t * mapper;     // local pointer on mapper
1426    xptr_t     page_xp;    // extended pointer on mapper page descriptor
1427    xptr_t     base_xp;    // extended pointer on mapper page base
1428    uint8_t  * base;       // local pointer on mapper page base
[246]1429
[602]1430#if DEBUG_FATFS_REMOVE_DENTRY
1431char       dir_name[CONFIG_VFS_MAX_NAME_LENGTH];
1432uint32_t   cycle = (uint32_t)hal_get_cycles();
1433thread_t * this  = CURRENT_THREAD;
1434vfs_inode_get_name( XPTR( local_cxy , inode ) , dir_name );
1435if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
[610]1436printk("\n[%s] thread[%x,%x] enter / parent <%s> / child <%s> / cycle %d\n",
[602]1437__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle );
[435]1438#endif
[401]1439
[602]1440// check arguments
1441assert( (inode != NULL) , "inode pointer is NULL\n" );
1442assert( (dentry != NULL) , "dentry pointer is NULL\n" );
1443assert( (inode->type == INODE_TYPE_DIR) , "inode is not a directory\n" );
1444assert( (inode->mapper != NULL ) , "mapper pointer is NULL\n" );
1445
1446    // get pointers on directory mapper
1447    mapper    = inode->mapper;
1448    mapper_xp = XPTR( local_cxy , mapper );
1449
1450    // compute number of LFN entries
1451    uint32_t nb_lfn;
1452    uint32_t name_length = strlen( dentry->name );
1453
1454    if      ( name_length <= 13 ) nb_lfn  = 1;
1455    else if ( name_length <= 26 ) nb_lfn  = 2;
1456    else                          nb_lfn  = 3;
1457
1458    // we must invalidate (2, 3 or 4) 32 bytes entries:
1459    // the NORMAL entry (registered in dentry->extend) and all preceding LFN entries
1460    // At most two pages are modified:
1461    // - the page containing the NORMAL entry is always modified.
1462    // - the preceding page is modified when the name spread on two pages.
1463
1464    // get 32 bytes directory entry index from dentry->extend
1465    uint32_t  dentry_id  = (uint32_t)(intptr_t)dentry->extend; 
1466
1467    // get page index and offset in parent directory mapper
1468    uint32_t  page_id    = dentry_id >> 7;
1469    uint32_t  offset     = (dentry_id & 0x7F)<<5;
1470
[610]1471#if DEBUG_FATFS_REMOVE_DENTRY & 1
1472if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
1473printk("\n[%s] dentry_id %x / page_id %x / offset %x\n",
1474__FUNCTION__, dentry_id, page_id, offset );
1475#endif
1476
[602]1477    // get extended pointer on page descriptor from parent directory mapper
1478    page_xp  = mapper_remote_get_page( mapper_xp , page_id );
1479
1480    if ( page_xp == XPTR_NULL )
[406]1481    {
[602]1482        printk("\n[ERROR] in %s : cannot extend directory mapper\n", __FUNCTION__ );
1483        return -1;
[406]1484    }
[602]1485       
1486    // get pointers on page base
1487    base_xp = ppm_page2base( page_xp );
1488    base    = GET_PTR( base_xp );
1489
1490    // invalidate NORMAL entry in directory cache
1491    base[offset] = 0xE5;
1492
1493    // invalidate LFN entries
1494    while ( nb_lfn )
1495    {
1496        if (offset == 0)  // we must load page (page_id - 1)
1497        {
1498
1499// check page_id
1500assert( (page_id > 0), "page_id and offset cannot be both 0\n" );
1501
1502            // copy the modified page to the IOC device
[614]1503            fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
[602]1504
1505            // get extended pointer on page descriptor from parent directory mapper
1506            page_xp  = mapper_remote_get_page( mapper_xp , page_id );
1507
1508            if ( page_xp == XPTR_NULL )
1509            {
1510                printk("\n[ERROR] in %s : cannot access directory mapper\n", __FUNCTION__ );
1511                return -1;
1512            }
1513       
1514            // get pointers on page base
1515            base_xp = ppm_page2base( page_xp );
1516            base    = GET_PTR( base_xp );
1517
1518            // update offset
1519            offset = 4096;
1520        }
1521
1522        offset = offset - 32;
1523
1524// check for LFN entry
[626]1525assert( (fatfs_get_record( DIR_ATTR, base + offset ) == ATTR_LONG_NAME_MASK ),
[602]1526"this directory entry must be a LFN\n");
1527
1528        // invalidate LFN entry
1529        base[offset] = 0xE5;
1530
1531        nb_lfn--;
1532    }     
1533
1534    // copy the modified page to the IOC device
[614]1535    fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
[602]1536   
1537
1538#if DEBUG_FATFS_REMOVE_DENTRY
1539cycle = (uint32_t)hal_get_cycles();
1540if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
[610]1541printk("\n[%s] thread[%x,%x] exit / parent %s / child %s / cycle %d\n",
[602]1542__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle );
[406]1543#endif
1544
[1]1545    return 0;
1546
[602]1547}  // end fatfs_remove_dentry
[246]1548
[623]1549
1550//////////////////////////////////////////////////////////////////////////////////////////////
1551// This static function scan the pages of a mapper containing a FAT32 directory, identified
1552// by the <mapper> argument, to find the directory entry identified by the <name> argument,
1553// and return a pointer on the directory entry, described as and array of 32 bytes, and the
1554// incex of this entry in the FAT32 mapper, seen as an array of 32 bytes entries.
1555// It is called by the fatfs_new_dentry() and fatfs_update_dentry() functions.
1556// It must be called by a thread running in the cluster containing the mapper.
1557//////////////////////////////////////////////////////////////////////////////////////////////
1558// @ mapper    : [in]  local pointer on directory mapper.
1559// @ name      : [in]  searched directory entry name.
1560// @ entry     : [out] buffer for the pointer on the 32 bytes directory entry (when found).
1561// @ index     : [out] buffer for the directory entry index in mapper.
1562// @ return 0 if found / return 1 if not found / return -1 if mapper access error.
1563//////////////////////////////////////////////////////////////////////////////////////////////
1564error_t fatfs_scan_directory( mapper_t *  mapper,
1565                              char     *  name,
1566                              uint8_t  ** entry,
1567                              uint32_t *  index )
[1]1568{
[610]1569    // Two embedded loops to scan the directory mapper:
[602]1570    // - scan the parent directory mapper pages
[238]1571    // - scan the directory entries in each 4 Kbytes page
[1]1572
[623]1573// check parent_inode and child_inode
1574assert( (mapper != NULL) , "mapper pointer is NULL\n" );
1575assert( (name   != NULL ), "child name is undefined\n" );
1576assert( (entry  != NULL ), "entry buffer undefined\n" );
1577
1578#if DEBUG_FATFS_SCAN_DIRECTORY
[602]1579char       parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
1580uint32_t   cycle = (uint32_t)hal_get_cycles();
1581thread_t * this  = CURRENT_THREAD;
[623]1582vfs_inode_get_name( XPTR( local_cxy , mapper->inode ) , parent_name );
1583if( DEBUG_FATFS_SCAN_DIRECTORY < cycle )
1584printk("\n[%s]  thread[%x,%x] enter to search child <%s> in parent <%s> / cycle %d\n",
[602]1585__FUNCTION__, this->process->pid, this->trdid, name , parent_name , cycle );
[435]1586#endif
[1]1587
[623]1588    char       cname[CONFIG_VFS_MAX_NAME_LENGTH];  // name extracted from each directory entry
[238]1589
1590    char       lfn1[16];         // buffer for one partial cname
1591    char       lfn2[16];         // buffer for one partial cname
1592    char       lfn3[16];         // buffer for one partial cname
[623]1593    xptr_t     mapper_xp;        // extended pointer on mapper descriptor
[602]1594    xptr_t     page_xp;          // extended pointer on page descriptor
1595    xptr_t     base_xp;          // extended pointer on page base
1596    uint8_t  * base;             // local pointer on page base
[614]1597    uint8_t    attr;             // directory entry ATTR field
1598    uint8_t    ord;              // directory entry ORD field
[238]1599    uint32_t   seq;              // sequence index
[602]1600    uint32_t   lfn       = 0;    // LFN entries number
[623]1601    int32_t    found     = 0;    // not yet = 0 / success = 1 / not found = 2 / error = -1
[602]1602    uint32_t   page_id   = 0;    // page index in mapper
1603    uint32_t   offset    = 0;    // byte offset in page
[238]1604
[623]1605    mapper_xp = XPTR( local_cxy , mapper );
1606
1607    // scan the mapper pages
[238]1608    while ( found == 0 )
1609    {
1610        // get one page
[602]1611        page_xp = mapper_remote_get_page( mapper_xp , page_id );
[238]1612
[623]1613        if( page_xp == XPTR_NULL)
1614        {
1615            found = -1;
1616        }
[238]1617
1618        // get page base
[602]1619        base_xp = ppm_page2base( page_xp );
1620        base    = (uint8_t *)GET_PTR( base_xp );
[238]1621
[623]1622#if (DEBUG_FATFS_SCAN_DIRECTORY & 0x1)
1623if( DEBUG_FATFS_SCAN_DIRECTORY < cycle )
[614]1624mapper_display_page( mapper_xp , page_id , 256 );
[265]1625#endif
[238]1626        // scan this page until end of directory, end of page, or name found
1627        while( (offset < 4096) && (found == 0) )
1628        {
[626]1629            attr = fatfs_get_record( DIR_ATTR , base + offset );   
1630            ord  = fatfs_get_record( LDIR_ORD , base + offset );   
[238]1631
1632            if (ord == NO_MORE_ENTRY)                 // no more entry => break
1633            {
[623]1634                found = 2;
[238]1635            }
1636            else if ( ord == FREE_ENTRY )             // free entry => skip
1637            {
1638                offset = offset + 32;
1639            }
[614]1640            else if ( attr == 0x28 )                  // volune_id => skip
1641            {
1642                offset = offset + 32;
1643            }
[238]1644            else if ( attr == ATTR_LONG_NAME_MASK )   // LFN entry => get partial cname
1645            {
1646                seq = ord & 0x3;
1647                lfn = (seq > lfn) ? seq : lfn;   
1648                if      ( seq == 1 ) fatfs_get_name_from_long( base + offset, lfn1 );
1649                else if ( seq == 2 ) fatfs_get_name_from_long( base + offset, lfn2 );
1650                else if ( seq == 3 ) fatfs_get_name_from_long( base + offset, lfn3 );
1651                offset = offset + 32;
1652            }
1653            else                                 // NORMAL entry
1654            {
1655                // build the extracted name
1656                if      ( lfn == 0 )
1657                {
1658                    fatfs_get_name_from_short( base + offset , cname );
1659                }
1660                else if ( lfn == 1 )
1661                {
1662                    strcpy( cname      , lfn1 );
1663                }   
1664                else if ( lfn == 2 ) 
1665                {
1666                    strcpy( cname      , lfn1 );
1667                    strcpy( cname + 13 , lfn2 );
1668                }
1669                else if ( lfn == 3 ) 
1670                {
1671                    strcpy( cname      , lfn1 );
1672                    strcpy( cname + 13 , lfn2 );
1673                    strcpy( cname + 26 , lfn3 );
1674                }
1675
1676                // get dentry arguments if extracted cname == searched name
1677                if ( strcmp( name , cname ) == 0 )
1678                {
[623]1679                    *entry = base + offset;
1680                    *index = ((page_id<<12) + offset)>>5; 
[602]1681                    found     = 1;
[238]1682                }
1683                offset = offset + 32;
1684                lfn    = 0;
1685            }
[602]1686        }  // end loop on directory entries in page
1687
1688        page_id++;
[238]1689        offset = 0;
[602]1690
[238]1691    }  // end loop on pages
1692
[623]1693    if( found == 1 )
1694    {
[238]1695
[623]1696#if DEBUG_FATFS_SCAN_DIRECTORY
1697cycle = (uint32_t)hal_get_cycles();
1698if( DEBUG_FATFS_SCAN_DIRECTORY < cycle )
1699printk("\n[%s]  thread[%x,%x] exit / found child <%s> in <%s>\n",
1700__FUNCTION__, this->process->pid, this->trdid, name, parent_name );
1701#endif
1702        return 0;
1703    }
1704    else if( found == 2 )
[238]1705    {
1706
[623]1707#if DEBUG_FATFS_SCAN_DIRECTORY
[435]1708cycle = (uint32_t)hal_get_cycles();
[623]1709if( DEBUG_FATFS_SCAN_DIRECTORY < cycle )
1710printk("\n[%s]  thread[%x,%x] exit / child <%s> in <%s> not found\n",
1711__FUNCTION__, this->process->pid, this->trdid, name, parent_name );
[435]1712#endif
[623]1713        return 1;
1714    }
1715    else
1716    {
1717        printk("\n[ERROR] in %s : cannot get page %d from mapper\n",
1718        __FUNCTION__, page_id );
[407]1719
[602]1720        return -1;
[238]1721    }
[623]1722}  // end fatfs_scan_directory()
[602]1723
1724
[610]1725
[623]1726/////////////////////////////////////////////////////
1727error_t fatfs_new_dentry( vfs_inode_t * parent_inode,
1728                          char        * name,
1729                          xptr_t        child_inode_xp )
1730{
[625]1731    uint8_t      * entry;            // pointer on FAT32 directory entry (array of 32 bytes)
1732    uint32_t       index;            // index of FAT32 directory entry in mapper
1733    mapper_t     * mapper;           // pointer on directory mapper
1734    uint32_t       cluster;          // directory entry cluster
1735    uint32_t       size;             // directory entry size
1736    bool_t         is_dir;           // directory entry type (file/dir)
1737    xptr_t         root_xp;          // extended pointer on root of parent dentries
1738    xptr_t         iter_xp;          // iterator for this list
1739    cxy_t          child_inode_cxy;  // child inode cluster 
1740    vfs_inode_t  * child_inode_ptr;  // child inode local pointer
1741    xptr_t         dentry_xp;        // extended pointer on searched dentry descriptor
1742    cxy_t          dentry_cxy;       // cluster identifier of dentry (must be local_cxy)
1743    vfs_dentry_t * dentry_ptr;       // local pointer
1744    error_t        error;
[623]1745
[625]1746    char       dir_name[CONFIG_VFS_MAX_NAME_LENGTH];
1747
[623]1748// check arguments
[626]1749assert( (parent_inode   != NULL)       , "parent_inode is NULL\n" );
1750assert( (name           != NULL)       , "name is NULL\n" );
1751assert( (child_inode_xp != XPTR_NULL ) , "child_inode is NULL\n" );
[623]1752
[626]1753    // get child inode cluster and local pointer
1754    child_inode_cxy = GET_CXY( child_inode_xp );
1755    child_inode_ptr = GET_PTR( child_inode_xp );
1756
1757    // build extended pointer on root of list of parent dentries
1758    root_xp = XPTR( child_inode_cxy , &child_inode_ptr->parents );
1759
1760// check child inode has at least one parent
1761assert( (xlist_is_empty( root_xp ) == false ), "child inode must have one parent\n");
1762
[623]1763#if DEBUG_FATFS_GET_DENTRY
1764uint32_t   cycle = (uint32_t)hal_get_cycles();
1765thread_t * this  = CURRENT_THREAD;
[625]1766vfs_inode_get_name( XPTR( local_cxy , parent_inode ) , dir_name );
[623]1767if( DEBUG_FATFS_GET_DENTRY < cycle )
1768printk("\n[%s]  thread[%x,%x] enter for child <%s> in parent <%s> / cycle %d\n",
[625]1769__FUNCTION__, this->process->pid, this->trdid, name , dir_name , cycle );
[623]1770#endif
1771
[625]1772    // get local pointer on parent mapper
[623]1773    mapper = parent_inode->mapper;
[625]1774
1775    // get pointer and index in mapper for searched directory entry
[623]1776    error  = fatfs_scan_directory( mapper, name , &entry , &index );
1777
[626]1778    // return non fatal error if not found
1779    if( error ) return -1;
[623]1780
[625]1781    // get relevant infos from FAT32 directory entry
[626]1782    cluster = (fatfs_get_record( DIR_FST_CLUS_HI , entry ) << 16) |
1783              (fatfs_get_record( DIR_FST_CLUS_LO , entry )      ) ;
1784    is_dir  = (fatfs_get_record( DIR_ATTR        , entry ) & ATTR_DIRECTORY);
1785    size    =  fatfs_get_record( DIR_FILE_SIZE   , entry );
[623]1786
[625]1787    // scan list of parent dentries to search the parent_inode
1788    bool_t found = false;
1789    XLIST_FOREACH( root_xp , iter_xp )
1790    {
1791        // get pointers on dentry
1792        dentry_xp  = XLIST_ELEMENT( iter_xp , vfs_dentry_t , parents );
1793        dentry_cxy = GET_CXY( dentry_xp );
1794        dentry_ptr = GET_PTR( dentry_xp );
[602]1795
[625]1796        // get local pointer on current parent directory inode
1797        vfs_inode_t * current = hal_remote_lpt( XPTR( dentry_cxy , &dentry_ptr->parent ) );
[602]1798
[625]1799        // check if current parent is the searched parent
1800        if( XPTR( dentry_cxy , current ) == XPTR( local_cxy , parent_inode ) )
1801        {
1802            found = true;
1803            break;
1804        }
1805    }
[602]1806
[625]1807    if( found == false )
1808    { 
1809        vfs_inode_get_name( XPTR( local_cxy , parent_inode ) , dir_name );
1810        printk("\n[ERROR] in %s : cannot find <%s> directory in list of parents for <%s>\n",
1811        __FUNCTION__, dir_name, name );
[623]1812        return -1;
1813    }
1814
[625]1815    // update the child inode "type", "size", and "extend" fields
1816    vfs_inode_type_t type = (is_dir) ? INODE_TYPE_DIR : INODE_TYPE_FILE;
1817
1818    hal_remote_s32( XPTR( child_inode_cxy , &child_inode_ptr->type   ) , type );
1819    hal_remote_s32( XPTR( child_inode_cxy , &child_inode_ptr->size   ) , size );
1820    hal_remote_s32( XPTR( child_inode_cxy , &child_inode_ptr->extend ) , cluster );
1821
1822    // update the dentry "extend" field
1823    dentry_ptr->extend = (void *)(intptr_t)index;
1824
1825#if DEBUG_FATFS_GET_DENTRY
1826cycle = (uint32_t)hal_get_cycles();
1827if( DEBUG_FATFS_GET_DENTRY < cycle )
1828printk("\n[%s]  thread[%x,%x] exit / intialised inode & dentry for <%s> in <%s> / cycle %d\n",
1829__FUNCTION__, this->process->pid, this->trdid, name, dir_name, cycle );
1830#endif
1831
1832    return 0;
1833
[623]1834}  // end fatfs_new_dentry()
1835
1836//////////////////////////////////////////////////
1837error_t fatfs_update_dentry( vfs_inode_t  * inode,
1838                             vfs_dentry_t * dentry,
1839                             uint32_t       size )
1840{
1841    uint8_t  * entry;    // pointer on FAT32 directory entry (array of 32 bytes)
1842    uint32_t   index;    // index of FAT32 directory entry in mapper
1843    mapper_t * mapper;   // pointer on directory mapper
1844    error_t    error;
1845
[625]1846    char       dir_name[CONFIG_VFS_MAX_NAME_LENGTH];
1847
[623]1848// check arguments
1849assert( (inode  != NULL) , "inode is NULL\n" );
1850assert( (dentry != NULL) , "dentry is NULL\n" );
1851assert( (size   != 0   ) , "size is 0\n" );
1852
1853#if DEBUG_FATFS_UPDATE_DENTRY
1854uint32_t   cycle = (uint32_t)hal_get_cycles();
1855thread_t * this  = CURRENT_THREAD;
1856vfs_inode_get_name( XPTR( local_cxy , inode ) , dir_name );
1857if( DEBUG_FATFS_UPDATE_DENTRY < cycle )
[625]1858printk("\n[%s]  thread[%x,%x] enter for <%s/%s> / size %d / cycle %d\n",
1859__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, size, cycle );
[623]1860#endif
1861
[625]1862    // get local pointer on mapper
[623]1863    mapper = inode->mapper;
[625]1864
1865    // get pointer and index in mapper for searched directory entry
[623]1866    error  = fatfs_scan_directory( mapper, dentry->name , &entry , &index );
1867
[625]1868    if( error )
[623]1869    { 
[625]1870        vfs_inode_get_name( XPTR( local_cxy , inode ) , dir_name );
1871        printk("\n[ERROR] in %s : cannot find <%s> in parent mapper <%s>\n",
1872        __FUNCTION__, dentry->name, dir_name );
1873        return -1;
1874    }
[623]1875
[625]1876    // set size in FAT32 directory entry
[626]1877    fatfs_set_record( DIR_FILE_SIZE , entry , size );
[602]1878
[625]1879    // get local pointer on modified page base
1880    void * base = (void *)((intptr_t)entry & (~CONFIG_PPM_PAGE_MASK)); 
[602]1881
[625]1882    // get extended pointer on modified page descriptor
1883    xptr_t page_xp = ppm_base2page( XPTR( local_cxy , base ) );
[602]1884
[625]1885    // synchronously update the modified page on device
1886    error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );
[623]1887
[625]1888    if( error )
1889    { 
1890        vfs_inode_get_name( XPTR( local_cxy , inode ) , dir_name );
1891        printk("\n[ERROR] in %s : cannot update parent directory <%s> on device\n",
1892        __FUNCTION__, dir_name );
[623]1893        return -1;
1894    }
1895
[625]1896#if DEBUG_FATFS_UPDATE_DENTRY
1897cycle = (uint32_t)hal_get_cycles();
1898if( DEBUG_FATFS_UPDATE_DENTRY < cycle )
1899printk("\n[%s]  thread[%x,%x] exit / updated size for <%s/%s> / cycle %d\n",
1900__FUNCTION__, this->process->pid, this->trdid, dir_name, dentry->name, cycle );
1901#endif
1902
1903    return 0;
1904
[623]1905}  // end fatfs_update_dentry()
1906
[612]1907///////////////////////////////////////////////////////
1908error_t fatfs_get_user_dir( struct vfs_inode_s * inode,
1909                            struct dirent      * array, 
1910                            uint32_t             max_dirent,
1911                            uint32_t             min_dentry,
1912                            bool_t               detailed,
1913                            uint32_t           * entries,
1914                            bool_t             * done )
1915{
1916    // Two embedded loops to scan the directory mapper:
1917    // - scan the parent directory mapper pages starting always from page 0
1918    // - scan the 32 bytes NORMAL/LFN directory entries in each page
1919    // Only valid dentries are copied : dentry_id >= min_dentry && dirent_id < dirent_max
1920
1921#if DEBUG_FATFS_GET_USER_DIR
1922char       inode_name[CONFIG_VFS_MAX_NAME_LENGTH];
1923uint32_t   cycle = (uint32_t)hal_get_cycles();
1924thread_t * this  = CURRENT_THREAD;
1925vfs_inode_get_name( XPTR( local_cxy , inode ) , inode_name );
1926if( DEBUG_FATFS_GET_USER_DIR < cycle )
1927printk("\n[%s]  thread[%x,%x] enter for inode <%s> / cycle %d\n",
1928__FUNCTION__, this->process->pid, this->trdid, inode_name , cycle );
1929#endif
1930
1931    mapper_t * mapper    = inode->mapper;
1932    xptr_t     mapper_xp = XPTR( local_cxy , mapper );
1933
1934// check mapper pointer
1935assert( (mapper != NULL) , "mapper is NULL\n");
1936   
1937// TODO handle the detailed flag
1938assert( (detailed == false), "detailed argument not supported/n");
1939
[614]1940    char       cname[CONFIG_VFS_MAX_NAME_LENGTH];  // name extracted from each dentry
[612]1941
1942    char       lfn1[16];           // buffer for one partial cname
1943    char       lfn2[16];           // buffer for one partial cname
1944    char       lfn3[16];           // buffer for one partial cname
1945    xptr_t     page_xp;            // extended pointer on page descriptor
1946    xptr_t     base_xp;            // extended pointer on page base
1947    uint8_t  * base;               // local pointer on page base
[614]1948    uint8_t    attr;               // directory entry ATTR field
1949    uint8_t    ord;                // directory entry ORD field
[612]1950    uint32_t   seq;                // sequence index
1951    uint32_t   lfn       = 0;      // LFN entries number
1952    uint32_t   offset    = 0;      // byte offset in page
1953    uint32_t   page_id   = 0;      // page index in mapper
1954    uint32_t   dentry_id = 0;      // valid (i.e. copied) dentry index in mapper
1955    uint32_t   dirent_id = 0;      // slot index in dirent array to initialize
1956    bool_t     end       = false;  // true if end of directory found
1957
1958    // loop on mapper pages
1959    while ( (end == false) && (dirent_id < max_dirent) )
1960    {
1961        // get one page from mapper
1962        page_xp = mapper_remote_get_page( mapper_xp , page_id );
1963
1964        if( page_xp == XPTR_NULL) return -1;
1965
1966        // get page base
1967        base_xp = ppm_page2base( page_xp );
1968        base    = (uint8_t *)GET_PTR( base_xp );
1969
1970#if (DEBUG_FATFS_GET_USER_DIR & 0x1)
1971if( DEBUG_FATFS_GET_USER_DIR < cycle )
[614]1972mapper_display_page( mapper_xp , page_id , 256 );
[612]1973#endif
1974        // loop on NORMAL/LFN (32 bytes) directory entries in this page
1975        while( (end == false) && (offset < 4096) )
1976        {
1977            // compute condition to copy one dentry to dirent array
1978            bool_t valid = (dentry_id >= min_dentry) && (dirent_id <  max_dirent );
1979
[626]1980            attr = fatfs_get_record( DIR_ATTR , base + offset );   
1981            ord  = fatfs_get_record( LDIR_ORD , base + offset );   
[612]1982
1983            if (ord == NO_MORE_ENTRY)                 // no more entry => break
1984            {
1985                end = true;
1986            }
1987            else if ( ord == FREE_ENTRY )             // free entry => skip
1988            {
1989                offset = offset + 32;
1990            }
[614]1991            else if ( attr == 0x28 )                  // volune_id => skip
1992            {
1993                offset = offset + 32;
1994            }
[612]1995            else if ( attr == ATTR_LONG_NAME_MASK )   // LFN entry
1996            {
1997                if( valid )
1998                {
1999                    // get partial cname
2000                    seq = ord & 0x3;
2001                    lfn = (seq > lfn) ? seq : lfn;   
2002                    if      ( seq == 1 ) fatfs_get_name_from_long( base + offset, lfn1 );
2003                    else if ( seq == 2 ) fatfs_get_name_from_long( base + offset, lfn2 );
2004                    else if ( seq == 3 ) fatfs_get_name_from_long( base + offset, lfn3 );
2005                }
2006                offset = offset + 32;
2007            }
2008            else                                     // NORMAL entry
2009            {
2010                // increment dentry_id
2011                dentry_id++;
2012
2013                if( valid )
2014                {
2015                    // build the complete cname
2016                    if      ( lfn == 0 )
2017                    {
2018                        fatfs_get_name_from_short( base + offset , cname );
2019                    }
2020                    else if ( lfn == 1 )
2021                    {
2022                        strcpy( cname      , lfn1 );
2023                    }   
2024                    else if ( lfn == 2 ) 
2025                    {
2026                        strcpy( cname      , lfn1 );
2027                        strcpy( cname + 13 , lfn2 );
2028                    }
2029                    else if ( lfn == 3 ) 
2030                    {
2031                        strcpy( cname      , lfn1 );
2032                        strcpy( cname + 13 , lfn2 );
2033                        strcpy( cname + 26 , lfn3 );
2034                    }
2035                   
2036                    // copy cname into dirent array
2037                    strcpy( array[dirent_id].d_name , cname ); 
2038
2039                    // increment dirent_id
2040                    dirent_id++;
2041                }
2042                offset = offset + 32;
2043                lfn    = 0;
2044            }
2045        }  // end loop on directory entries in page
2046
2047        page_id++;
2048        offset = 0;
2049
2050    }  // end loop on pages
2051
2052    // return result of scan
2053    *done    = end;
2054    *entries = dirent_id;
2055
2056#if DEBUG_FATFS_GET_USER_DIR
2057cycle = (uint32_t)hal_get_cycles();
2058if( DEBUG_FATFS_GET_USER_DIR < cycle )
2059printk("\n[%s]  thread[%x,%x] exit for inode <%s> / %d entries / cycle %d\n",
[614]2060__FUNCTION__, this->process->pid, this->trdid, inode_name, dirent_id, cycle );
[612]2061#endif
2062
2063    return 0;
2064
2065}  // end fatfs_get_user_dir()
2066
[602]2067///////////////////////////////////////////////
2068error_t fatfs_sync_inode( vfs_inode_t * inode )
2069{
2070
2071// check inode pointer and cluster index
2072assert( (inode != NULL)                  , "inode pointer undefined\n" );
2073assert( (inode->mapper != NULL )         , "mapper pointer undefined\n" );
2074assert( (inode->type == INODE_TYPE_FILE) , "inode must be a file\n" );     
2075
2076#if DEBUG_FATFS_SYNC_INODE
2077char       name[CONFIG_VFS_MAX_NAME_LENGTH];
2078uint32_t   cycle = (uint32_t)hal_get_cycles();
2079thread_t * this  = CURRENT_THREAD;
2080vfs_inode_get_name( XPTR( local_cxy , inode ) , name );
2081if( DEBUG_FATFS_SYNC_INODE < cycle )
2082printk("\n[%s] thread[%x,%x] enter for <%s> / cycle %d\n",
2083__FUNCTION__ , this->process->pid, this->trdid, name, cycle );
2084#endif
2085
2086    error_t    error;
2087    mapper_t * mapper;
2088    page_t   * page;
2089    uint32_t   page_id;
2090
2091    // get mapper from inode
2092    mapper = inode->mapper;
2093
2094    // compute max number of pages in mapper from file size
2095    uint32_t size  = inode->size;
2096    uint32_t pages = size >> CONFIG_PPM_PAGE_SHIFT;
2097    if( size & CONFIG_PPM_PAGE_MASK ) pages++; 
2098         
2099    // get pointer on mapper radix tree
2100    grdxt_t * rt = &mapper->rt;
2101
2102    // scan all pages
2103    for( page_id = 0 ; page_id < pages ; page_id++ )
[238]2104    {
[602]2105        // get page descriptor from mapper
2106        page = grdxt_lookup( rt , page_id );
[238]2107
[602]2108        // check all existing pages
2109        if ( page != NULL )
2110        {
2111            if ( page->flags & PG_DIRTY )
2112            {
[238]2113
[602]2114#if (DEBUG_FATFS_SYNC_INODE & 1)
2115if( DEBUG_FATFS_SYNC_INODE < cycle )
2116printk("\n[%s] thread[%x,%x] synchronizes page %d from <%s> mapper to IOC device\n",
2117__FUNCTION__, page_id, name );
2118#endif
2119                // build extended pointer on page descriptor
2120                xptr_t page_xp = XPTR( local_cxy , page );
[238]2121
[602]2122                // move page from mapper to device
[614]2123                error = fatfs_move_page( page_xp , IOC_WRITE );
[602]2124
2125                if ( error )  return -1;
2126
2127                // reset page dirty flag
2128                ppm_page_undo_dirty( page_xp );
2129            }
2130        }
2131    }  // end loop on pages
2132
2133#if DEBUG_FATFS_SYNC_INODE
[435]2134cycle = (uint32_t)hal_get_cycles();
[602]2135if( DEBUG_FATFS_SYNC_INODE < cycle )
2136printk("\n[%s] thread[%x,%x] exit for <%s> / cycle %d\n",
2137__FUNCTION__ , this->process->pid, this->trdid, name, cycle );
[435]2138#endif
[246]2139
[602]2140    return 0;
2141
2142}  // end fatfs_sync_inode()
2143
2144//////////////////////////////
2145error_t fatfs_sync_fat( void )
2146{
2147
2148#if DEBUG_FATFS_SYNC_FAT
2149uint32_t   cycle = (uint32_t)hal_get_cycles();
2150thread_t * this  = CURRENT_THREAD;
2151if( DEBUG_FATFS_SYNC_FAT < cycle )
2152printk("\n[%s] thread[%x,%x] enter / cycle %d\n",
2153__FUNCTION__ , this->process->pid, this->trdid, cycle );
2154#endif
2155
2156    uint32_t   page_id;
2157    error_t    error;
2158
2159    // get FAT mapper pointers an cluster
2160    fatfs_ctx_t * fatfs_ctx  = fs_context[FS_TYPE_FATFS].extend;
2161    xptr_t        mapper_xp  = fatfs_ctx->fat_mapper_xp;
2162    cxy_t         mapper_cxy = GET_CXY( mapper_xp );
2163    mapper_t    * mapper_ptr = GET_PTR( mapper_xp );
2164
2165    // compute max number of 4 Kbytes pages in FAT mapper
2166    // TODO : this could be improved (see fatfs.h) [AG]
2167    uint32_t   pages = fatfs_ctx->fat_sectors_count >> 3;
2168         
2169    // get pointers on remote FAT mapper radix tree
2170    grdxt_t  * rt_ptr = &mapper_ptr->rt;
2171    xptr_t     rt_xp  = XPTR( mapper_cxy , rt_ptr );
2172
2173    // scan all pages
2174    for( page_id = 0 ; page_id < pages ; page_id++ )
2175    {
2176        // get extended pointer on page descriptor from FAT mapper
2177        xptr_t page_xp = grdxt_remote_lookup( rt_xp , page_id );
2178
2179        // check all existing pages
2180        if ( page_xp != XPTR_NULL )
2181        {
2182            page_t * page_ptr = GET_PTR( page_xp );
2183            uint32_t flags    = hal_remote_l32( XPTR( mapper_cxy , &page_ptr->flags ) );
2184
2185            if ( flags & PG_DIRTY )
2186            {
2187
2188#if (DEBUG_FATFS_SYNC_FAT & 1)
2189if( DEBUG_FATFS_SYNC_FAT < cycle )
2190printk("\n[%s] thread[%x,%x] synchronizes page %d from FAT mapper to IOC device\n",
2191__FUNCTION__, page_id );
2192#endif
2193                // move page from mapper to device
[614]2194                error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );
[602]2195
2196                if ( error )  return -1;
2197
2198                // reset page dirty flag
2199                ppm_page_undo_dirty( page_xp );
2200            }
2201        }
2202    }  // end loop on pages
2203
2204#if DEBUG_FATFS_SYNC_FAT
2205cycle = (uint32_t)hal_get_cycles();
2206if( DEBUG_FATFS_SYNC_FAT < cycle )
2207printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
2208__FUNCTION__ , this->process->pid, this->trdid, cycle );
2209#endif
2210
2211    return 0;
2212
2213}  // end fatfs_sync_fat()
2214
2215////////////////////////////////////
2216error_t fatfs_sync_free_info( void )
2217{
[626]2218    error_t       error;
2219    fatfs_ctx_t * fatfs_ctx_ptr;              // local pointer on fatfs context in cluster 0
2220    uint32_t      ctx_free_clusters;          // number of free clusters from fatfs context
2221    uint32_t      ctx_free_cluster_hint;      // free cluster hint from fatfs context
2222    uint32_t      ioc_free_clusters;          // number of free clusters from fatfs context
2223    uint32_t      ioc_free_cluster_hint;      // free cluster hint from fatfs context
2224    uint32_t      fs_info_lba;                // lba of FS_INFO sector on IOC device
2225    uint8_t     * fs_info_buffer;             // local pointer on FS_INFO buffer in cluster 0
2226    xptr_t        fs_info_buffer_xp;          // extended pointer on FS_INFO buffer in cluster 0
2227    uint8_t       tmp_buf[512];               // 512 bytes temporary buffer
2228    xptr_t        tmp_buf_xp;                 // extended pointer on temporary buffer
[602]2229
2230#if DEBUG_FATFS_SYNC_FSINFO
2231uint32_t   cycle = (uint32_t)hal_get_cycles();
2232thread_t * this  = CURRENT_THREAD;
2233if( DEBUG_FATFS_SYNC_FSINFO < cycle )
2234printk("\n[%s] thread[%x,%x] enter / cycle %d\n",
2235__FUNCTION__ , this->process->pid, this->trdid, cycle );
2236#endif
2237
[626]2238    // get pointer on fatfs context in cluster 0
2239    fatfs_ctx_ptr = hal_remote_lpt( XPTR( 0 , &fs_context[FS_TYPE_FATFS].extend ) );
[602]2240
[626]2241    // get "free_clusters" and "free_cluster_hint" from fatfs context in cluster 0
2242    ctx_free_clusters     = hal_remote_l32( XPTR( 0 , &fatfs_ctx_ptr->free_clusters ) );
2243    ctx_free_cluster_hint = hal_remote_l32( XPTR( 0 , &fatfs_ctx_ptr->free_cluster_hint ) );
[602]2244
[626]2245    // get fs_info_lba
2246    fs_info_lba = hal_remote_l32( XPTR( 0 , &fatfs_ctx_ptr->fs_info_lba ) );
2247
2248    // build extended pointer on temporary buffer
2249    tmp_buf_xp = XPTR( local_cxy , tmp_buf );
2250
2251    // copy FS_INFO sector from IOC to local buffer
2252    error = dev_ioc_sync_read( tmp_buf_xp , fs_info_lba , 1 );
2253
[602]2254    if ( error )
2255    {
[626]2256        printk("\n[ERROR] in %s : cannot access FS_INFO on IOC device\n", __FUNCTION__ );
2257        return -1;
[602]2258    }
2259
[626]2260    // get current values of "free_clusters" and "free_cluster_hint" from FS_INFO on IOC
2261    ioc_free_clusters     = fatfs_get_remote_record( FS_FREE_CLUSTERS     , tmp_buf_xp );
2262    ioc_free_cluster_hint = fatfs_get_remote_record( FS_FREE_CLUSTER_HINT , tmp_buf_xp );
[602]2263
[626]2264    // check values
2265    if( (ioc_free_clusters     != ctx_free_clusters) || 
2266        (ioc_free_cluster_hint != ctx_free_cluster_hint) )
[602]2267    {
[626]2268        printk("\n[WARNING] in %s : unconsistent free clusters info\n"
2269        " ioc_free %x / ctx_free %x / ioc_hint %x / ctx_hint %x\n",
2270        __FUNCTION__, ioc_free_clusters, ctx_free_clusters,
2271        ioc_free_cluster_hint, ctx_free_cluster_hint );
2272
2273        // get pointers on FS_INFO buffer in cluster 0
2274        fs_info_buffer    = hal_remote_lpt( XPTR( 0 , &fatfs_ctx_ptr->fs_info_buffer ) );
2275        fs_info_buffer_xp = XPTR( 0 , fs_info_buffer );
2276
2277        // update FS_INFO buffer in cluster 0
2278        fatfs_set_remote_record(FS_FREE_CLUSTERS    ,fs_info_buffer_xp,ctx_free_clusters );
2279        fatfs_set_remote_record(FS_FREE_CLUSTER_HINT,fs_info_buffer_xp,ctx_free_cluster_hint);
2280
2281        // update the FS_INFO sector on IOC device
2282        error = dev_ioc_sync_write( fs_info_buffer_xp , fs_info_lba , 1 );
2283
2284        if ( error )
2285        {
2286            printk("\n[ERROR] in %s : cannot update FS_INFO on IOC device\n", __FUNCTION__ );
2287            return -1;
2288        }
[602]2289    }
2290
2291#if DEBUG_FATFS_SYNC_FSINFO
2292cycle = (uint32_t)hal_get_cycles();
2293if( DEBUG_FATFS_SYNC_FSINFO < cycle )
2294printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
2295__FUNCTION__ , this->process->pid, this->trdid, cycle );
2296#endif
2297
2298    return 0;
2299
[626]2300}  // end fatfs_sync_free_info()
[602]2301
2302//////////////////////////////////////////////////////////
2303error_t fatfs_cluster_alloc( uint32_t * searched_cluster )
2304{
[626]2305    error_t       error;
[625]2306    uint32_t      page_id;        // page index in FAT mapper
[602]2307    uint32_t      slot_id;        // slot index in page (1024 slots per page)
[625]2308    uint32_t      cluster;        // first free cluster index in FAT
[602]2309    uint32_t      free_clusters;  // total number of free clusters
2310    vfs_ctx_t   * vfs_ctx;        // local pointer on VFS context (same in all clusters)
2311    fatfs_ctx_t * loc_fatfs_ctx;  // local pointer on local FATFS context
2312    fatfs_ctx_t * fat_fatfs_ctx;  // local pointer on FATFS context in FAT cluster
2313    xptr_t        mapper_xp;      // extended pointer on FAT mapper
[626]2314    cxy_t         fat_cxy;        // Fat mapper cluster identifier
[602]2315    xptr_t        page_xp;        // extended pointer on current page descriptor in mapper
2316    xptr_t        slot_xp;        // extended pointer on FAT slot defined by hint
2317    xptr_t        lock_xp;        // extended pointer on lock protecting free clusters info
2318    xptr_t        hint_xp;        // extended pointer on free_cluster_hint in FAT cluster
[626]2319    xptr_t        free_xp;        // extended pointer on free_clusters_number in FAT cluster
[602]2320
2321#if DEBUG_FATFS_CLUSTER_ALLOC
2322uint32_t   cycle = (uint32_t)hal_get_cycles();
2323thread_t * this  = CURRENT_THREAD;
2324if( DEBUG_FATFS_CLUSTER_ALLOC < cycle )
2325printk("\n[%s] thread[%x,%x] enter / cycle = %d\n",
2326__FUNCTION__, this->process->pid, this->trdid, cycle );
2327#endif
2328
2329    // get local pointer on VFS context (same in all clusters)
2330    vfs_ctx = &fs_context[FS_TYPE_FATFS];
2331
2332    // get local pointer on local FATFS context
2333    loc_fatfs_ctx = vfs_ctx->extend;
2334
[626]2335    // get extended pointer on FAT mapper
[602]2336    mapper_xp  = loc_fatfs_ctx->fat_mapper_xp;
[626]2337
2338    // get FAT cluster
2339    fat_cxy = GET_CXY( mapper_xp );
[602]2340   
2341    // get local pointer on FATFS context in FAT cluster
[626]2342    fat_fatfs_ctx = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) );
[602]2343
[625]2344    // build relevant extended pointers on free clusters info in mapper cluster
[626]2345    lock_xp = XPTR( fat_cxy , &fat_fatfs_ctx->free_lock );
2346    hint_xp = XPTR( fat_cxy , &fat_fatfs_ctx->free_cluster_hint );
2347    free_xp = XPTR( fat_cxy , &fat_fatfs_ctx->free_clusters );
[602]2348
2349    // take the lock protecting free clusters
2350    remote_queuelock_acquire( lock_xp );
2351
[626]2352    // get hint and free_clusters values from FATFS context in FAT cluster
[625]2353    cluster       = hal_remote_l32( hint_xp ) + 1;
[626]2354    free_clusters = hal_remote_l32( free_xp );
[602]2355       
2356#if (DEBUG_FATFS_CLUSTER_ALLOC & 1)
2357if( DEBUG_FATFS_CLUSTER_ALLOC < cycle )
[625]2358printk("\n[%s] thread[%x,%x] get free info : hint %x / free_clusters %x\n",
2359__FUNCTION__, this->process->pid, this->trdid, (cluster - 1), free_clusters );
[602]2360#endif
2361
2362    // check "free_clusters"
2363    if ( free_clusters == 0 )
2364    {
2365        printk("\n[ERROR] in %s : no more free FATFS clusters\n", __FUNCTION__ );
2366        remote_queuelock_acquire( lock_xp );
2367        return -1;
2368    }
2369    else if ( free_clusters < CONFIG_VFS_FREE_CLUSTERS_MIN )
2370    {
2371        printk("\n[WARNING] in %s : only %n free FATFS clusters\n", 
2372        __FUNCTION__, CONFIG_VFS_FREE_CLUSTERS_MIN );
2373    }
2374
[625]2375    // get page index & slot index for selected cluster
2376    page_id  = cluster >> 10;
2377    slot_id  = cluster & 0x3FF;
2378
[626]2379    // get relevant page descriptor from FAT mapper
[625]2380    page_xp = mapper_remote_get_page( mapper_xp , page_id );
2381
2382    if( page_xp == XPTR_NULL )
2383    {
2384        printk("\n[ERROR] in %s : cannot acces FAT mapper\n", __FUNCTION__ );
2385        return -1;
2386    }
2387
2388    // build extended pointer on selected cluster slot in FAT mapper
2389    slot_xp = ppm_page2base( page_xp ) + (slot_id << 2);
2390         
2391    // check selected cluster actually free
[602]2392    if( hal_remote_l32( slot_xp ) != FREE_CLUSTER )
2393    { 
[625]2394        printk("\n[ERROR] in %s : selected cluster %x not free\n", __FUNCTION__, cluster );
[602]2395        remote_queuelock_acquire( lock_xp );
2396        return -1;
2397    }
2398
[626]2399    // update free cluster info in FATFS context and in FS_INFO sector
2400    error = fatfs_free_clusters_decrement( XPTR( fat_cxy , fat_fatfs_ctx ) , cluster );
[602]2401
[626]2402    if( error )
2403    { 
2404        printk("\n[ERROR] in %s : cannot update free cluster info\n", __FUNCTION__ );
2405        remote_queuelock_acquire( lock_xp );
2406        return -1;
2407    }
[602]2408
[625]2409    // update FAT mapper
2410    hal_remote_s32( slot_xp , END_OF_CHAIN_CLUSTER_MAX );
2411
2412    // synchronously update FAT on device
[626]2413    error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );
[625]2414
[626]2415    if( error )
2416    { 
2417        printk("\n[ERROR] in %s : cannot update FAT on IOC device\n", __FUNCTION__ );
2418        remote_queuelock_acquire( lock_xp );
2419        return -1;
2420    }
2421
2422    // release free clusters busylock
2423    remote_queuelock_release( lock_xp );
2424
[602]2425#if DEBUG_FATFS_CLUSTER_ALLOC
2426cycle = (uint32_t)hal_get_cycles();
2427if( DEBUG_FATFS_CLUSTER_ALLOC < cycle )
[626]2428printk("\n[%s] thread[%x,%x] exit / allocated cluster %x in FAT / cycle %d\n",
[625]2429__FUNCTION__, this->process->pid, this->trdid, cluster, cycle );
[602]2430#endif
2431
[625]2432    *searched_cluster = cluster;
[602]2433    return 0;
2434
2435}  // end fat_cluster_alloc()
2436
2437//////////////////////////////////////////////
2438error_t fatfs_release_inode( xptr_t inode_xp )
2439{
2440    vfs_ctx_t   * vfs_ctx;        // local pointer on VFS context (same in all clusters).
2441    fatfs_ctx_t * loc_fatfs_ctx;  // local pointer on local FATFS context
2442    fatfs_ctx_t * fat_fatfs_ctx;  // local pointer on FATFS context in FAT cluster
2443    xptr_t        mapper_xp;      // extended pointer on FAT mapper
2444    cxy_t         mapper_cxy;     // Fat mapper cluster identifier
[625]2445    mapper_t    * mapper_ptr;     // local pointer on FAT mapper
[602]2446    xptr_t        lock_xp;        // extended pointer on lock protecting free clusters info.
2447    xptr_t        first_xp;       // extended pointer on inode extension
2448    uint32_t      first_cluster;  // first cluster index for released inode
[626]2449    vfs_inode_t * inode_ptr;      // local pointer on target inode
2450    cxy_t         inode_cxy;      // target inode cluster identifier
[602]2451
2452// check inode pointer
2453assert( (inode_xp != XPTR_NULL) , "inode pointer is NULL\n" );
2454
[623]2455    // get inode cluster and local pointer
[602]2456    inode_ptr     = GET_PTR( inode_xp );
2457    inode_cxy     = GET_CXY( inode_xp );
[623]2458
2459    // get first_cluster from inode extension
[602]2460    first_xp      = XPTR( inode_cxy , &inode_ptr->extend );
2461    first_cluster = (uint32_t)(intptr_t)hal_remote_lpt( first_xp );
2462
2463// check first cluster index
2464assert( (first_cluster != 0) , "inode extend is NULL\n" );
2465
2466#if DEBUG_FATFS_RELEASE_INODE
2467char       name[CONFIG_VFS_MAX_NAME_LENGTH];
2468uint32_t   cycle = (uint32_t)hal_get_cycles();
2469thread_t * this  = CURRENT_THREAD;
2470vfs_inode_get_name( inode_xp , name );
2471if( DEBUG_FATFS_RELEASE_INODE < cycle )
2472printk("\n[%s] thread[%x,%x] enter for <%s> / first_cluster %x / cycle %d\n",
2473__FUNCTION__ , this->process->pid, this->trdid, name, first_cluster, cycle );
2474#endif
2475
[623]2476#if (DEBUG_FATFS_RELEASE_INODE & 1)
2477fatfs_display_fat( 0 , 512 );
2478#endif
2479
[602]2480    // get local pointer on VFS context (same in all clusters)
2481    vfs_ctx = &fs_context[FS_TYPE_FATFS];
2482
2483    // get local pointer on local FATFS context
2484    loc_fatfs_ctx = vfs_ctx->extend;
2485
[625]2486    // get pointers and cluster on FAT mapper
[602]2487    mapper_xp  = loc_fatfs_ctx->fat_mapper_xp;
2488    mapper_cxy = GET_CXY( mapper_xp );
[625]2489    mapper_ptr = GET_PTR( mapper_xp );
[602]2490   
2491    // get local pointer on FATFS context in FAT cluster
2492    fat_fatfs_ctx = hal_remote_lpt( XPTR( mapper_cxy , &vfs_ctx->extend ) );
2493
2494    // get extended pointer on free clusters lock in FAT cluster
2495    lock_xp = XPTR( mapper_cxy , &fat_fatfs_ctx->free_lock );
2496
2497    // take lock protecting free clusters
2498    remote_queuelock_acquire( lock_xp );
2499
2500    // call the recursive function to release all clusters from FAT mapper
[625]2501    if ( fatfs_recursive_release( mapper_cxy,
2502                                  mapper_ptr,
2503                                  fat_fatfs_ctx,
2504                                  first_cluster ) )
[602]2505    {
2506        printk("\n[ERROR] in %s : cannot update FAT mapper\n", __FUNCTION__ );
2507        remote_queuelock_release( lock_xp );
2508        return -1;
2509    }
2510
2511    // release lock protecting free cluster
2512    remote_queuelock_release( lock_xp );
2513
2514#if (DEBUG_FATFS_RELEASE_INODE & 1)
2515if( DEBUG_FATFS_RELEASE_INODE < cycle )
2516printk("\n[%s] inode <%s> removed from FAT mapper\n", __FUNCTION__, name );
2517#endif
2518
2519    // update FAT on IOC device (from FAT mapper)
2520    if ( fatfs_sync_fat() )
2521    {
2522        printk("\n[ERROR] in %s : cannot update FAT on device\n", __FUNCTION__ );
2523        return -1;
2524    }
2525
2526#if (DEBUG_FATFS_RELEASE_INODE & 1)
2527if( DEBUG_FATFS_RELEASE_INODE < cycle )
2528printk("\n[%s] inode <%s> removed from FAT on IOC device\n", __FUNCTION__, name );
2529#endif
2530
2531    // update FS-INFO sector on IOC device (from FATFS context)
2532    if ( fatfs_sync_free_info() )
2533    {
2534        printk("\n[ERROR] in %s: cannot update FS_INFO on device\n", __FUNCTION__ );
2535        return -1;
2536    }
2537
2538#if DEBUG_FATFS_RELEASE_INODE
2539cycle = (uint32_t)hal_get_cycles();
2540if( DEBUG_FATFS_RELEASE_INODE < cycle )
2541printk("\n[%s] thread[%x,%x] removed <%s> inode from FATFS / cycle %d\n",
2542__FUNCTION__ , this->process->pid, this->trdid, name, cycle );
2543#endif
2544
2545    return 0;
2546
2547}  // end fatfs_release_inode()
2548
[614]2549////////////////////////////////////////////
2550error_t fatfs_move_page( xptr_t     page_xp,
2551                         cmd_type_t cmd_type )
[602]2552{
2553    error_t       error;
2554    vfs_inode_t * inode_ptr;
2555    mapper_t    * mapper_ptr;     
2556    uint32_t      page_id;     // page index in mapper
2557
2558#if DEBUG_FATFS_MOVE_PAGE
2559uint32_t   cycle = (uint32_t)hal_get_cycles();
2560thread_t * this  = CURRENT_THREAD;
2561char       name[CONFIG_VFS_MAX_NAME_LENGTH];
2562#endif
2563
2564    // get page cluster an local pointer
2565    cxy_t    page_cxy = GET_CXY( page_xp );
2566    page_t * page_ptr = GET_PTR( page_xp );
2567
2568    // get mapper pointer and page index from page descriptor
2569    mapper_ptr = hal_remote_lpt( XPTR( page_cxy , &page_ptr->mapper ) );
2570    page_id    = hal_remote_l32( XPTR( page_cxy , &page_ptr->index ) );
2571
2572    // get pointer on local FATFS context
[611]2573    fatfs_ctx_t * fatfs_ctx = fs_context[FS_TYPE_FATFS].extend;
[602]2574
2575    // get page base address
[626]2576    xptr_t    buffer_xp  = ppm_page2base( page_xp );
2577    uint8_t * buffer_ptr = (uint8_t *)GET_PTR( buffer_xp );
[602]2578 
2579    // get inode pointer from mapper
2580    inode_ptr  = hal_remote_lpt( XPTR( page_cxy , &mapper_ptr->inode ) );
2581
[626]2582#if DEBUG_FATFS_MOVE_PAGE
2583if( DEBUG_FATFS_MOVE_PAGE < cycle )
2584printk("\n[%s] thread[%x,%x] enters : %s / cxy %x / mapper %x / inode %x / page %x\n",
2585__FUNCTION__, this->process->pid, this->trdid,
2586dev_ioc_cmd_str( cmd_type ), page_cxy, mapper_ptr, inode_ptr, buffer_ptr );
2587#endif
2588
2589    //////////////////////////////  FAT mapper
[602]2590    if( inode_ptr == NULL )
2591    {
2592        // get lba from FATFS context and page_id
[625]2593        uint32_t      lba = fatfs_ctx->fat_begin_lba + (page_id << 3);
[602]2594 
2595        // access device
[626]2596        if     (cmd_type == IOC_SYNC_READ ) error = dev_ioc_sync_read ( buffer_xp  , lba , 8 );
2597        else if(cmd_type == IOC_SYNC_WRITE) error = dev_ioc_sync_write( buffer_xp  , lba , 8 );
2598        else if(cmd_type == IOC_READ      ) error = dev_ioc_read      ( buffer_ptr , lba , 8 );
2599        else if(cmd_type == IOC_WRITE     ) error = dev_ioc_write     ( buffer_ptr , lba , 8 );
2600        else                                error = -1;
[602]2601
[626]2602        if( error )
2603        {
2604            printk("\n[ERROR] in %s : cannot access device\n", __FUNCTION__ );
2605            return -1;
2606        }
[602]2607
2608#if DEBUG_FATFS_MOVE_PAGE
2609if( DEBUG_FATFS_MOVE_PAGE < cycle )
2610{
[614]2611    if ( (cmd_type == IOC_READ) || (cmd_type == IOC_SYNC_READ) )
[626]2612        printk("\n[%s] thread[%x,%x] load FAT mapper page %d from IOC / cycle %d\n",
2613        __FUNCTION__, this->process->pid, this->trdid, page_id, cycle );
[602]2614    else
[626]2615        printk("\n[%s] thread[%x,%x] sync FAT mapper page %d to IOC / cycle %d\n",
[602]2616        __FUNCTION__, this->process->pid, this->trdid, page_id, cycle );
2617}
2618#endif
2619
2620    }
[626]2621    /////////////////////////  inode mapper
[602]2622    else                       
2623    {
2624
2625#if DEBUG_FATFS_MOVE_PAGE
2626vfs_inode_get_name( XPTR( page_cxy , inode_ptr ) , name );
2627#endif
2628
2629        uint32_t  searched_cluster;
2630        uint32_t  first_cluster;
2631
2632        // get first_cluster from inode extension
2633        void * extend = hal_remote_lpt( XPTR( page_cxy , &inode_ptr->extend ) );
2634        first_cluster = (uint32_t)(intptr_t)extend;
2635
2636        // compute searched_cluster
2637        if( page_id == 0 )            // no need to access FAT mapper
2638        {
2639            // searched cluster is first cluster
2640            searched_cluster = first_cluster;
2641        }
2642        else                        // FAT mapper access required
2643        {
2644            // access FAT mapper to get searched cluster
2645            error = fatfs_get_cluster( first_cluster,
2646                                       page_id,
2647                                       &searched_cluster );
[625]2648            if( error )
2649            {
2650                printk("\n[ERROR] in %s : cannot access FAT mapper\n", __FUNCTION__ );
2651                return -1;
2652            }
[602]2653        }
2654
[626]2655        // get lba for searched_cluster
[602]2656        uint32_t lba = fatfs_lba_from_cluster( fatfs_ctx , searched_cluster );
2657
[626]2658        // access device
2659        if     (cmd_type == IOC_SYNC_READ ) error = dev_ioc_sync_read ( buffer_xp  , lba , 8 );
2660        else if(cmd_type == IOC_SYNC_WRITE) error = dev_ioc_sync_write( buffer_xp  , lba , 8 );
2661        else if(cmd_type == IOC_READ      ) error = dev_ioc_read      ( buffer_ptr , lba , 8 );
2662        else if(cmd_type == IOC_WRITE     ) error = dev_ioc_write     ( buffer_ptr , lba , 8 );
2663        else                                error = -1;
2664
2665        if( error )
2666        {
2667            printk("\n[ERROR] in %s : cannot access device\n", __FUNCTION__ );
2668            return -1;
2669        }
2670
[625]2671#if DEBUG_FATFS_MOVE_PAGE
2672if( DEBUG_FATFS_MOVE_PAGE < cycle )
2673{
2674    if ( (cmd_type == IOC_READ) || (cmd_type == IOC_SYNC_READ) )
2675    printk("\n[%s] thread[%x,%x] load page %d of <%s> / cluster_id %x / cycle %d\n",
2676    __FUNCTION__, this->process->pid, this->trdid, page_id, name, searched_cluster, cycle );
2677    else
2678    printk("\n[%s] thread[%x,%x] sync page %d of <%s> / cluster_id %x / cycle %d\n",
2679    __FUNCTION__, this->process->pid, this->trdid, page_id, name, searched_cluster, cycle );
2680}
2681#endif
2682
[602]2683    }
2684
2685    return 0;
2686
2687}  // end fatfs_move_page()
2688
2689
Note: See TracBrowser for help on using the repository browser.