source: trunk/kernel/fs/fatfs.c

Last change on this file was 683, checked in by alain, 3 years ago

All modifications required to support the <tcp_chat> application
including error recovery in case of packet loss.A

File size: 124.4 KB
Line 
1/*
2 * fatfs.c - FATFS file system API implementation.
3 *
4 * Author    Alain Greiner (2016,2017,2018,2019,2020)
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
25#include <hal_kernel_types.h>
26#include <hal_special.h>
27#include <printk.h>
28#include <thread.h>
29#include <kmem.h>
30#include <ppm.h>
31#include <vfs.h>
32#include <string.h>
33#include <rpc.h>
34#include <mapper.h>
35#include <cluster.h>
36#include <dev_ioc.h>
37#include <fatfs.h>
38
39#define LITTLE_ENDIAN  1
40
41//////////////////////////////////////////////////////////////////////////////////////////
42//          Extern  variables         
43//////////////////////////////////////////////////////////////////////////////////////////
44
45extern vfs_ctx_t     fs_context[FS_TYPES_NR];   // allocated in kernel_init.c file
46
47//////////////////////////////////////////////////////////////////////////////////////////
48//              FATFS specific static functions
49//////////////////////////////////////////////////////////////////////////////////////////
50
51//////////////////////////////////////////////////////////////////////////////////////////
52// These functions return the "offset" and "length" values of an
53// [offset,length] constant defined in the fatfs.h file.
54//////////////////////////////////////////////////////////////////////////////////////////
55
56static inline int get_length( int offset __attribute__((unused)), int length ) { return length; }
57
58static inline int get_offset( int offset, int length __attribute__((unused)) ) { return offset; }
59
60//////////////////////////////////////////////////////////////////////////////////////////
61// This static function returns the LBA of the first sector of a FAT cluster.
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 )
70{
71    return (ctx->cluster_begin_lba + ((cluster - 2) << 3));
72}
73
74//////////////////////////////////////////////////////////////////////////////////////////
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.
78//////////////////////////////////////////////////////////////////////////////////////////
79// @ offset        : first byte in array.
80// @ nbytes        : record length in bytes (1/2/4).
81// @ buffer        : local pointer on byte array.
82// @ return the integer value in a 32 bits word.
83//////////////////////////////////////////////////////////////////////////////////////////
84static uint32_t fatfs_get_record( uint32_t    offset,
85                                  uint32_t    nbytes,
86                                  uint8_t   * buffer )
87{
88    uint32_t i;
89    uint32_t res = 0;
90
91    if ( LITTLE_ENDIAN )
92    {
93        for( i = nbytes ; i > 0 ; i-- ) res = (res<<8) | buffer[offset+i-1];
94    }
95    else 
96    {
97        for( i = 0 ; i < nbytes ; i++ ) res = (res<<8) | buffer[offset+i];
98    }
99    return res;
100
101}  // end fatfs_get_record()
102
103//////////////////////////////////////////////////////////////////////////////////////////
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.
107//////////////////////////////////////////////////////////////////////////////////////////
108// @ offset        : first byte in array.
109// @ nbytes        : record length in bytes (1/2/4).
110// @ buffer_xp     : extended pointer on byte array.
111// @ return the integer value in a 32 bits word.
112//////////////////////////////////////////////////////////////////////////////////////////
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
140//////////////////////////////////////////////////////////////////////////////////////////
141// This function writes one, two, or four bytes from a 32 bits integer to a local
142// array of bytes, taking into account the global LITTLE_ENDIAN parameter:
143// if LITTLE_ENDIAN is true, the most significant byte has the highest address.
144//////////////////////////////////////////////////////////////////////////////////////////
145// @ offset        : first byte in array.
146// @ nbytes        : record length in bytes (1/2/4).
147// @ buffer        : local pointer on byte array.
148// @ value         : 32 bits integer value.
149//////////////////////////////////////////////////////////////////////////////////////////
150static void fatfs_set_record( uint32_t    offset,
151                              uint32_t    nbytes,
152                              uint8_t   * buffer,
153                              uint32_t    value )
154{
155    uint32_t i;
156
157    if ( LITTLE_ENDIAN )
158    {
159        for( i = nbytes ; i > 0 ; i-- ) buffer[offset+i-1] = (uint8_t)(value>>((i-1)<<3));
160    }
161    else
162    {
163        for( i = 0 ; i < nbytes ; i++ ) buffer[offset+i] = (uint8_t)(value>>((nbytes-1-i)<<3));
164    }
165
166}  // end fatfs_set_record()
167
168*/
169
170//////////////////////////////////////////////////////////////////////////////////////////
171// This function writes one, two, or four bytes from a 32 bits integer to a remote
172// array of bytes, taking into account the global LITTLE_ENDIAN parameter:
173// if LITTLE_ENDIAN is true, the most significant byte has the highest address.
174//////////////////////////////////////////////////////////////////////////////////////////
175// @ offset        : first byte in array.
176// @ nbytes        : record length in bytes (1/2/4).
177// @ buffer_xp     : extended pointer on byte array.
178// @ value         : 32 bits integer value.
179//////////////////////////////////////////////////////////////////////////////////////////
180static void fatfs_set_remote_record( uint32_t    offset,
181                                     uint32_t    nbytes,
182                                     xptr_t      buffer_xp,
183                                     uint32_t    value )
184{
185    uint32_t i;
186
187    if ( LITTLE_ENDIAN )
188    {
189        for( i = nbytes ; i > 0 ; i-- )
190        {
191            hal_remote_sb( (buffer_xp + offset + i-1 ) , (uint8_t)(value>>((i-1)<<3)) );
192        }
193    }
194    else
195    {
196        for( i = 0 ; i < nbytes ; i++ )
197        {
198            hal_remote_sb( (buffer_xp + offset + i) , (uint8_t)(value>>((nbytes-1-i)<<3)) );
199        }
200    }
201
202}  // end fatfs_set_record()
203
204//////////////////////////////////////////////////////////////////////////////////////////
205// This static function retun in the <name> buffer a short name stored in
206// a SFN FATFS directory entry.
207/////////////////////////i////////////////////////////////////////////////////////////////
208// @ buffer   : pointer on buffer containing the directory entry.
209// @ name     : [out] buffer allocated by the caller.
210//////////////////////////////////////////////////////////////////////////////////////////
211static void fatfs_get_name_from_short( uint8_t * buffer,
212                                       char    * name )
213{
214    uint32_t i;
215    uint32_t j = 0;
216
217    // get name
218    for ( i = 0; i < 8 && buffer[i] != ' '; i++ )
219    {
220        name[j] = to_lower( buffer[i] );
221        j++;
222    }
223
224    // get extension
225    for ( i = 8; i < 8 + 3 && buffer[i] != ' '; i++ )
226    {
227        // we entered the loop so there is an extension. add the dot
228        if ( i == 8 )
229        {
230            name[j] = '.';
231            j++;
232        }
233
234        name[j] = to_lower( buffer[i] );
235        j++;
236    }
237
238    name[j] = '\0';
239
240}  // fatfs_get_name_from_short()
241
242//////////////////////////////////////////////////////////////////////////////////////////
243// This static function retun in the <name> buffer a partial name stored in
244// a LFN FATFS directory entry.
245/////////////////////////i////////////////////////////////////////////////////////////////
246// @ buffer   : pointer on buffer containing the directory entry.
247// @ name     : [out] buffer allocated by the caller.
248//////////////////////////////////////////////////////////////////////////////////////////
249static void fatfs_get_name_from_long( uint8_t * buffer,
250                                      char    * name )
251{
252    uint32_t   name_offset   = 0;
253    uint32_t   buffer_offset = get_length(LDIR_ORD);
254    uint32_t   l_name_1      = get_length(LDIR_NAME_1);
255    uint32_t   l_name_2      = get_length(LDIR_NAME_2);
256    uint32_t   l_name_3      = get_length(LDIR_NAME_3);
257    uint32_t   l_attr        = get_length(LDIR_ATTR);
258    uint32_t   l_type        = get_length(LDIR_TYPE);
259    uint32_t   l_chksum      = get_length(LDIR_CHKSUM);
260    uint32_t   l_rsvd        = get_length(LDIR_RSVD);
261
262    uint32_t   j             = 0;
263    uint32_t   eof           = 0;
264
265    while ( (buffer_offset != DIR_ENTRY_SIZE)  && (!eof) )
266    {
267        while (j != l_name_1 && !eof )
268        {
269            if ( (buffer[buffer_offset] == 0x00) || 
270                 (buffer[buffer_offset] == 0xFF) )
271            {
272                eof = 1;
273                continue;
274            }
275            name[name_offset] = buffer[buffer_offset];
276            buffer_offset += 2;
277            j += 2;
278            name_offset++;
279        }
280
281        buffer_offset += (l_attr + l_type + l_chksum);
282        j = 0;
283
284        while (j != l_name_2 && !eof )
285        {
286            if ( (buffer[buffer_offset] == 0x00) || 
287                 (buffer[buffer_offset] == 0xFF) )
288            {
289                eof = 1;
290                continue;
291            }
292            name[name_offset] = buffer[buffer_offset];
293            buffer_offset += 2;
294            j += 2;
295            name_offset++;
296        }
297
298        buffer_offset += l_rsvd;
299        j = 0;
300
301        while (j != l_name_3 && !eof )
302        {
303            if ( (buffer[buffer_offset] == 0x00) || 
304                 (buffer[buffer_offset] == 0xFF) )
305            {
306                eof = 1;
307                continue;
308            }
309            name[name_offset] = buffer[buffer_offset];
310            buffer_offset += 2;
311            j += 2;
312            name_offset++;
313        }
314    }
315    name[name_offset] = 0;
316
317} // end fatfs_get_name_from_long()
318
319//////////////////////////////////////////////////////////////////////////////////////////
320// This static function analyse the <name> input argument, and returns in other
321// output arguments various informations required to store the name in FATFS directory.
322// The <name> length cannot be larger than 31 characters :
323// - Short name (less than 13 characters) require 1 LFN entry.
324// - Medium names (from 14 to 26 characters require 2 LFN entries.
325// - Large names (up to 31 characters) require 3 LFN entries.
326//////////////////////////////////////////////////////////////////////////////////////////
327// @ name     : [in]  complete directory entry name.
328// @ length   : [out] total number of characters in name.
329// @ nb_lfn   : [out] number of LFN entries required to store the name.
330// @ sfn      : [out] a legal SFN name extracted from name / upper case and 8-3 format.
331// @ checksum : [out] checksum to be stored in SFN.
332// @ returns 0 on success / returns 1 if the name length is larger than 31 characters.
333//////////////////////////////////////////////////////////////////////////////////////////
334static error_t fatfs_name_format( const char  * name,
335                                  uint32_t    * length,
336                                  uint32_t    * nb_lfn,
337                                  char        * sfn,
338                                  uint8_t     * checksum )
339{
340    // compute name length
341    uint32_t name_length = strlen( name );
342    *length = name_length;
343
344    uint32_t suffix_length = 0;
345    uint32_t prefix_length = 0;
346    uint32_t dot_found     = 0;
347    uint32_t i;
348
349    // compute prefix and suffix length
350    // only the last '.' is taken into account
351    for ( i=0 ; i<name_length ; i++ )
352    {
353        if (name[i] == '.' )
354        {
355            if ( dot_found ) 
356            {
357                prefix_length += suffix_length + 1;
358                suffix_length =  0;
359            }
360            else
361            {
362                dot_found = 1;
363            }
364        }
365        else
366        { 
367            if ( dot_found)  suffix_length++;
368            else             prefix_length++;
369        }
370    } 
371
372    // build SFN prefix (8bits)
373    if (prefix_length <= 8)
374    {
375        for( i=0 ; i<8 ; i++)
376        {
377            if ( i<prefix_length ) sfn[i] = to_upper( name[i] );
378            else                   sfn[i] = 0x20;
379        }
380    }
381    else
382    {
383        for( i=0 ; i<6 ; i++)
384        {
385            sfn[i] = to_upper( name[i] );
386        }
387        sfn[6] = 0x7E;
388        sfn[7] = 0x31;
389    }
390
391    // build SFN suffix (3 bits)
392    if ( suffix_length == 0 )
393    {
394        sfn[8]  = 0x20;
395        sfn[9]  = 0x20;
396        sfn[10] = 0x20;
397    }
398    else if ( suffix_length == 1 )
399    {
400        sfn[8]  = to_upper( name[name_length-1] );
401        sfn[9]  = 0x20;
402        sfn[10] = 0x20;
403    }
404    else if ( suffix_length == 2 )
405    {
406        sfn[8]  = to_upper( name[name_length-2] );
407        sfn[9]  = to_upper( name[name_length-1] );
408        sfn[10] = 0x20;
409    }
410    else
411    {
412        sfn[8]  = to_upper( name[name_length-suffix_length] );
413        sfn[9]  = to_upper( name[name_length-suffix_length+1] );
414        sfn[10] = to_upper( name[name_length-suffix_length+2] );
415    }
416
417    // compute 8 bits checksum
418    uint8_t sum = 0;
419    for ( i=0 ; i<11 ; i++ )
420    {
421        sum = (((sum & 0x01)<<7) | ((sum & 0xFE)>>1)) + sfn[i];
422    }
423    *checksum = sum;
424
425    // set nb_lfn and length values
426    if      ( name_length <= 13 )
427    {
428        *nb_lfn  = 1;
429        return 0;
430    }
431    else if ( name_length <= 26 )
432    {
433        *nb_lfn  = 2;
434        return 0;
435    }
436    else if ( name_length <= 31 )
437    {
438        *nb_lfn  = 3;
439        return 0;
440    }
441    else
442    {
443        return 1;
444    }
445}   // end fatfs_name_format() 
446
447//////////////////////////////////////////////////////////////////////////////////////////
448// This static function synchronously updates the FAT on IOC device.
449// It scan the FAT mapper to copy on IOC device all dirty pages in the interval
450// defined by the <page_min> & <page_max> arguments.
451// It can be called by a thread running in any cluster.
452// WARNING : We don't take the lock protecting the FAT mapper, because the FAT lock
453// (in FATFS context) must be taken by the calling function.
454//////////////////////////////////////////////////////////////////////////////////////////
455// @ fatfs_ctx_xp  : extended pointer on FATFS context in FAT cluster.
456// @ page_min      : first page to be checked.
457// @ page_max      : last page to be checked
458// @ return 0 if success, return -1 if the FS_INFO sector cannot be updated.
459//////////////////////////////////////////////////////////////////////////////////////////
460static error_t fatfs_update_ioc_fat( xptr_t   fatfs_ctx_xp,
461                                     uint32_t page_min,
462                                     uint32_t page_max )
463{
464
465#if DEBUG_FATFS_UPDATE_IOC
466uint32_t   cycle = (uint32_t)hal_get_cycles();
467thread_t * this  = CURRENT_THREAD;
468if( DEBUG_FATFS_UPDATE_IOC < cycle )
469printk("\n[%s] thread[%x,%x] enter / page_min %d / page_max %d / cycle %d\n",
470__FUNCTION__ , this->process->pid, this->trdid, page_min, page_max, cycle );
471#endif
472
473    error_t       error;
474    cxy_t         fat_cxy;         // FAT cluster identifier
475    fatfs_ctx_t * fatfs_ctx;       // local pointer on FATFS context in FAT cluster
476    mapper_t    * fat_mapper_ptr;  // local pointer on FAT mapper
477    uint32_t      page_id;         // current page index in FAT mapper
478    xptr_t        rt_xp;           // extended pointer on FAT mapper radix tree
479    xptr_t        page_xp;         // extended pointer on current page in FAT mapper
480    page_t      * page_ptr;        // local pointer on current page
481    uint32_t      flags;           // current page flags
482
483    // get pointer and cluster on FATFS context in FAT cluster
484    fat_cxy   = GET_CXY( fatfs_ctx_xp );
485    fatfs_ctx = GET_PTR( fatfs_ctx_xp );
486 
487    // get FAT mapper pointer from FATFS context
488    fat_mapper_ptr  = hal_remote_lpt( XPTR( fat_cxy , &fatfs_ctx->fat_mapper ) );
489
490    // build extended pointer on FAT mapper radix tree
491    rt_xp   = XPTR( fat_cxy , &fat_mapper_ptr->rt );
492
493    // scan all pages in [min,max] interval
494    for( page_id = page_min ; page_id <= page_max ; page_id++ )
495    {
496        // get extended pointer on page descriptor from FAT mapper
497        page_xp = grdxt_remote_lookup( rt_xp , page_id );
498
499        // check only existing pages
500        if ( page_xp != XPTR_NULL )
501        {
502            page_ptr = GET_PTR( page_xp );
503            flags    = hal_remote_l32( XPTR( fat_cxy , &page_ptr->flags ) );
504
505            // copy only dirty pages
506            if ( flags & PG_DIRTY )
507            {
508
509#if (DEBUG_FATFS_UPDATE_IOC & 1)
510if( DEBUG_FATFS_UPDATE_IOC < cycle )
511printk("\n[%s] thread[%x,%x] copy page %d from FAT mapper to IOC device\n",
512__FUNCTION__, page_id );
513#endif
514                // move page from mapper to device
515                error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );
516
517                if ( error )  return -1;
518
519                // reset page dirty flag
520                ppm_page_undo_dirty( page_xp );
521            }
522        }
523    }  // end loop on pages
524
525#if DEBUG_FATFS_UPDATE_IOC
526cycle = (uint32_t)hal_get_cycles();
527if( DEBUG_FATFS_UPDATE_IOC < cycle )
528printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
529__FUNCTION__ , this->process->pid, this->trdid, cycle );
530#endif
531
532    return 0;
533
534}  // end fatfs_update_ioc_fat()
535
536//////////////////////////////////////////////////////////////////////////////////////////
537// This static function synchronously updates the FS_INFO sector on IOC device,
538// from values contained in the FATFS context in FAT cluster.
539// It uses and updates the FS_INFO buffer allocated in the FAT cluster.
540// It can be called by a thread running in any cluster.
541//////////////////////////////////////////////////////////////////////////////////////////
542// @ fatfs_ctx_xp  : extended pointer on FATFS context in FAT cluster.
543// @ return 0 if success, return -1 if the FS_INFO sector cannot be updated.
544//////////////////////////////////////////////////////////////////////////////////////////
545static error_t fatfs_update_ioc_fsinfo( xptr_t fatfs_ctx_xp )
546{
547    cxy_t         fat_cxy;             // FAT cluster identifier
548    fatfs_ctx_t * fatfs_ctx_ptr;       // local pointer on fatfs context in FAT cluster
549    uint32_t      free_clusters;       // current vale of "free_clusters" in fatfs context
550    uint32_t      free_cluster_hint;   // current vale of "free_cluster_hint" in fatfs context
551    uint8_t     * fs_info_buffer_ptr;  // local pointer on FS_INFO buffer in FAT cluster
552    xptr_t        fs_info_buffer_xp;   // extended pointer on FS_INFO buffer in FAT cluster
553    uint32_t      fs_info_lba;         // FS_INFO sector lba on IOC device
554
555    // get cluster and local pointer on FAT cluster context
556    fat_cxy       = GET_CXY( fatfs_ctx_xp ); 
557    fatfs_ctx_ptr = GET_PTR( fatfs_ctx_xp ); 
558
559    // force FATFS context update
560    hal_fence();
561
562    // get relevant info from fatfs context in FAT cluster
563    fs_info_lba        = hal_remote_l32( XPTR( fat_cxy , &fatfs_ctx_ptr->fs_info_lba ) );
564    free_clusters      = hal_remote_l32( XPTR( fat_cxy , &fatfs_ctx_ptr->free_clusters ) );
565    free_cluster_hint  = hal_remote_l32( XPTR( fat_cxy , &fatfs_ctx_ptr->free_cluster_hint ) );
566    fs_info_buffer_ptr = hal_remote_lpt( XPTR( fat_cxy , &fatfs_ctx_ptr->fs_info_buffer ) );
567
568    // build extended pointer on FS_INFO buffer in FAT cluster
569    fs_info_buffer_xp  = XPTR( fat_cxy , fs_info_buffer_ptr );
570   
571    // update the FS_INFO buffer in FAT cluster
572    fatfs_set_remote_record( FS_FREE_CLUSTERS     , fs_info_buffer_xp , free_clusters );
573    fatfs_set_remote_record( FS_FREE_CLUSTER_HINT , fs_info_buffer_xp , free_cluster_hint );
574   
575    // update the FS_INFO sector on IOC device
576    return dev_ioc_sync_write( fs_info_buffer_xp , fs_info_lba , 1 );
577 
578}  // end fatfs_update_ioc_fsinfo()
579
580//////////////////////////////////////////////////////////////////////////////////////////
581// This static function decrements the  "free_clusters" variable, and updates the
582// "free_cluster_hint" variable in the FATFS context in FAT cluster,  when a new
583// <cluster_id> has been allocated from the FAT.
584// It synchronously updates the FS_INFO sector on the IOC device.
585// The FATFS context in FAT cluster is identified by the <fat_ctx_xp> argument.
586// It can be called by a thead running in any cluster.
587// It scan all slots in the FAT mapper seen as an array of 32 bits words, looking for the
588// first free slot larger than the <cluster_id>.
589// The lock protecting exclusive access to the FAT must be taken by the calling function.
590//////////////////////////////////////////////////////////////////////////////////////////
591// @ fatfs_ctx_xp  : extended pointer on FATFS context in FAT cluster.
592// @ cluster_id    : recently allocated cluster index in FAT.
593// @ return 0 if success, return -1 if the FS_INFO sector cannot be updated.
594//////////////////////////////////////////////////////////////////////////////////////////
595static error_t fatfs_free_clusters_decrement( xptr_t    fatfs_ctx_xp,
596                                              uint32_t  cluster_id )
597{
598    error_t       error;
599    cxy_t         fat_cxy;         // FAT cluster identifier
600    fatfs_ctx_t * fat_ctx_ptr;     // local pointer on fatfs context in FAT cluster
601    mapper_t    * fat_mapper_ptr;  // local pointer on FAT mapper
602    xptr_t        fat_mapper_xp;   // extended pointer on FAT mapper
603    xptr_t        hint_xp;         // extended pointer on "free_cluster_hint" shared variable
604    xptr_t        numb_xp;         // extended pointer on "free_clusters" shared variable
605    uint32_t      page_id;         // page index in FAT mapper
606    uint32_t      slot_id;         // slot index in one page of FAT (1024 slots per page)
607    uint32_t      page_max;        // max number of pages in FAT mapper
608    xptr_t        page_xp;         // extended pointer on current page in FAT mapper
609    xptr_t        base_xp;         // extended pointer on current page base
610    xptr_t        slot_xp;         // extended pointer on current slot in FAT mapper
611    uint32_t      found;           // free slot found when non zero
612
613#if DEBUG_FATFS_FREE_CLUSTERS
614uint32_t   cycle = (uint32_t)hal_get_cycles();
615thread_t * this  = CURRENT_THREAD;
616if( DEBUG_FATFS_FREE_CLUSTERS < cycle )
617printk("\n[%s] thread[%x,%x] enter for allocated cluster_id %x / cycle %d\n",
618__FUNCTION__, this->process->pid, this->trdid, cluster_id , cycle );
619#endif
620
621    // get FAT cluster an local pointer on fatfs context in FAT cluster
622    fat_cxy      = GET_CXY( fatfs_ctx_xp );
623    fat_ctx_ptr  = GET_PTR( fatfs_ctx_xp );
624   
625    // build extended pointers on free_clusters, and free_cluster_hint in fatfs context
626    hint_xp = XPTR( fat_cxy , &fat_ctx_ptr->free_cluster_hint );
627    numb_xp = XPTR( fat_cxy , &fat_ctx_ptr->free_clusters );
628
629    // get pointers on FAT mapper from FATFS context
630    fat_mapper_ptr = hal_remote_lpt( XPTR( fat_cxy , &fat_ctx_ptr->fat_mapper ) );
631    fat_mapper_xp  = XPTR( fat_cxy , fat_mapper_ptr );
632
633    // initialise the loop variables to scan the FAT mapper
634    page_id  = (cluster_id + 1) >> 10;
635    slot_id  = (cluster_id + 1) & 0x3FF;
636    page_max = hal_remote_l32( XPTR( fat_cxy, &fat_ctx_ptr->fat_sectors_count ) ) >> 3;
637    found    = 0;
638
639    // scan FAT mapper : first loop on pages
640    while ( (page_id < page_max) && (found == 0) )           
641    {
642        // get current page from mapper
643        page_xp = mapper_get_fat_page( fat_mapper_xp , page_id );
644
645        if( page_xp == XPTR_NULL )
646        {
647            printk("\n[ERROR] in %s : cannot access FAT mapper\n", __FUNCTION__ );
648            return -1;
649        }
650
651        // get extended pointer on page
652        base_xp = ppm_page2base( page_xp );
653
654        // scan the FAT mapper : second loop on slots
655        while ( (slot_id < 1024) && (found == 0) )
656        {
657            // get extended pointer on current slot
658            slot_xp = base_xp + (slot_id << 2);
659
660            // test slot value
661            if ( hal_remote_l32( slot_xp ) == FREE_CLUSTER )
662            {
663                // exit both loops
664                found = 1;
665            }
666            else
667            {
668                // update slot_id if not found
669                slot_id++;
670            }
671        }  // end loop on slots
672
673        // update page_id & slot_id variables if not found
674        if( found == 0 )
675        {
676            page_id++;
677            slot_id = 0;
678        }
679    }  // end loop on pages
680
681    if( found )  // free cluster found
682    {
683        // update "free_clusters" and "free_cluster_hint" value in FATFS context
684        hal_remote_atomic_add( numb_xp , -1 );
685        hal_remote_s32( hint_xp , (page_id << 10) + slot_id - 1 );
686
687        // update FS_INFO sector on IOC device
688        error = fatfs_update_ioc_fsinfo( fatfs_ctx_xp );
689
690        if( error ) 
691        {
692            printk("\n[ERROR] in %s : cannot update FS_INFO on IOC\n", __FUNCTION__ );
693            return -1;
694        }
695
696#if DEBUG_FATFS_FREE_CLUSTERS
697if( DEBUG_FATFS_FREE_CLUSTERS < cycle )
698printk("\n[%s] thread[%x,%x] exit / hint %x / free %x\n",
699__FUNCTION__, this->process->pid, this->trdid, 
700hal_remote_l32(hint_xp), hal_remote_l32(numb_xp) );
701#endif
702        return 0;
703    }
704    else  // free cluster not found
705    {
706        printk("\n[ERROR] in %s : No free cluster_id found\n", __FUNCTION__ );
707        return -1;
708    }
709   
710}  // end fatfs_free_clusters_decrement()
711
712//////////////////////////////////////////////////////////////////////////////////////////
713// This static function increments the "free_clusters" variable, and updates the
714// "free_cluster_hint" variables in the FATFS context in FAT cluster, identified
715// by the <fat_ctx_xp> argument, when a FATFS <cluster_id> has been released.
716// If the released cluster index is smaller than the current (hint) value,
717// it set "free_cluster_hint" <= cluster.
718// It does NOT update the  FS_INFO sector on the IOC device, as this is done by the
719// calling fatfs_release_inode() function.
720// It can be called by a thead running in any cluster.
721// The lock protecting exclusive access to the FAT must be taken by the calling function.
722//////////////////////////////////////////////////////////////////////////////////////////
723// @ fatfs_ctx_xp  : extended pointer on FATFS context in FAT cluster.
724// @ cluster_id    : recently released cluster index in FAT.
725// @ return 0 if success, return -1 if the FS_INFO sector cannot be updated.
726//////////////////////////////////////////////////////////////////////////////////////////
727static error_t fatfs_free_clusters_increment( xptr_t   fatfs_ctx_xp,
728                                              uint32_t cluster_id )
729{
730    cxy_t         fat_cxy;      // FAT cluster identifier
731    fatfs_ctx_t * fat_ctx_ptr;  // local pointer on fatfs context in FAT cluster
732    xptr_t        hint_xp;      // extended pointer on "free_cluster_hint" shared variable
733    xptr_t        numb_xp;      // extended pointer on "free_clusters" shared variable
734    uint32_t      hint;         // "free_cluster_hint" variable current value
735    uint32_t      numb;         // "free_clusters" variable current value
736
737#if DEBUG_FATFS_FREE_CLUSTERS
738uint32_t   cycle = (uint32_t)hal_get_cycles();
739thread_t * this  = CURRENT_THREAD;
740if( DEBUG_FATFS_FREE_CLUSTERS < cycle )
741printk("\n[%s] thread[%x,%x] enter for released cluster_id %x / cycle %d\n",
742__FUNCTION__, this->process->pid, this->trdid, cluster_id , cycle );
743#endif
744
745    // get FAT cluster an local pointer on fatfs context in FAT cluster
746    fat_cxy      = GET_CXY( fatfs_ctx_xp );
747    fat_ctx_ptr  = GET_PTR( fatfs_ctx_xp );
748   
749    // build extended pointers on free_clusters, and free_cluster_hint
750    hint_xp = XPTR( fat_cxy , &fat_ctx_ptr->free_cluster_hint );
751    numb_xp = XPTR( fat_cxy , &fat_ctx_ptr->free_clusters );
752
753    // get current value of free_cluster_hint and free_clusters
754    hint = hal_remote_l32( hint_xp );
755    numb = hal_remote_l32( numb_xp );
756
757    // update "numb" and "hint" variables as required
758    numb++;
759    if ( (cluster_id - 1) < hint ) hint = cluster_id - 1;
760
761    // update free_clusters
762    hal_remote_s32( numb_xp , numb );
763    hal_remote_s32( hint_xp , hint );
764
765#if DEBUG_FATFS_FREE_CLUSTERS
766if( DEBUG_FATFS_FREE_CLUSTERS < cycle )
767printk("\n[%s] thread[%x,%x] exit / hint %x / free %x\n",
768__FUNCTION__, this->process->pid, this->trdid,
769hal_remote_l32( hint_xp ), hal_remote_l32( numb_xp ) );
770#endif
771
772    return 0;
773
774}  // end fatfs_free_clusters_increment()
775
776//////////////////////////////////////////////////////////////////////////////////////////
777// This recursive function is called by the generic function fatfs_release_inode().
778// It release all FATFS clusters allocated to a given inode to the FAT mapper.
779// It can be called by a thread running in any cluster, as it use remote pointers
780// to access both the FAT mapper and the FATFS context in the FAT cluster, defined
781// by the <fat_mapper_xp> and <fatfs_ctx_xp> arguments.
782// The removal is done in reverse order of the linked list (from last cluster to first).
783// It returns in the <dirty_page_min> and <dirty_page_max> buffers the indexes of the
784// modified pages in the FAT mapper.
785// It updates the FAT mapper and the free_cluster info in the FATFS context, but does NOT
786// update the FAT and the FS_INFO on IOC device.
787//////////////////////////////////////////////////////////////////////////////////////////
788// @ fat_mapper_xp  : [in]  extended pointer on FAT mapper.
789// @ fatfs_ctx_xp   : [in]  extended pointer on FATFS context in FAT cluster.
790// @ cluster        : [in]  index of cluster to be released from FAT mapper.
791// @ dirty_page_min : [out] pointer on buffer for min dirty page index.
792// @ dirty_page_max : [out] pointer on buffer for max dirty page index.
793// @ return 0 if success / return -1 if error.
794//////////////////////////////////////////////////////////////////////////////////////////
795static error_t fatfs_recursive_release( xptr_t      fat_mapper_xp,
796                                        xptr_t      fatfs_ctx_xp,
797                                        uint32_t    cluster,
798                                        uint32_t  * dirty_page_min,
799                                        uint32_t  * dirty_page_max )
800{
801    uint32_t next;       // next cluster index
802    uint32_t page_id;    // page index in FAT mapper
803    uint32_t word_id;    // word index in page
804
805    // get page index and word index from cluster
806    page_id = cluster >> 10;
807    word_id = cluster & 0x3FF;
808
809    // get next cluster index from FAT mapper
810    if ( mapper_remote_get_32( fat_mapper_xp,
811                               page_id,
812                               word_id,
813                               &next ) ) return -1;
814
815#if (DEBUG_FATFS_RELEASE_INODE & 1)
816thread_t * this = CURRENT_THREAD;
817if ( DEBUG_FATFS_RELEASE_INODE < (uint32_t)hal_get_cycles() )
818printk("\n[%s] thread[%x,%x] access FAT for cluster %x / next %x\n",
819__FUNCTION__, this->process->pid, this->trdid, cluster, next );
820#endif
821
822    if ( next < END_OF_CHAIN_CLUSTER_MIN )  // non terminal case
823    {
824        // call fatfs_recursive_release() on next cluster
825        if ( fatfs_recursive_release( fat_mapper_xp,
826                                      fatfs_ctx_xp,
827                                      next,
828                                      dirty_page_min,
829                                      dirty_page_max ) ) return -1;
830    }       
831
832    // update FAT mapper
833    if ( mapper_remote_set_32( fat_mapper_xp,
834                               page_id, 
835                               word_id,
836                               FREE_CLUSTER ) ) return -1;
837
838    // update dirty_page_min / dirty_page_max buffers
839    if( page_id < *dirty_page_min ) *dirty_page_min = page_id;
840    if( page_id > *dirty_page_max ) *dirty_page_max = page_id;
841
842    // Update free_cluster info in FATFS context
843    return fatfs_free_clusters_increment( fatfs_ctx_xp , cluster );
844
845}  // end fatfs_recursive_release()
846
847
848//////////////////////////////////////////////////////////////////////////////////////////
849// This static function access the FAT mapper to allocate a new cluster in the FATFS,
850// and returns in <searched_cluster_id> the FATFS cluster index of a free cluster.
851// It updates the FAT mapper (handling miss from IOC device if required) :
852// - if the <last_cluster_id> is zero, the new cluster is the first allocated cluster,
853//   and the <searched_cluster_id> FAT slot is set to END_OF_CHAIN_CLUSTER.
854// - if the <last_cluster_id> argument is not zero, the new cluster is not the first,
855//   the <last_cluster_id> FAT slot is set to <searched_cluster_id>,
856//   the <searched_cluster_id> FAT slot is set to END_OF_CHAIN_CLUSTER.
857// This function also updates the  two "free_cluster_hint" and "free_clusters" variables
858// stored in the FATFS context. It takes the rwlock stored in the FATFS context in the
859// FAT cluster to get exclusive access to the FAT.
860// This function synchronously updates the FAT region on IOC device.
861// It can be called by a thread running in any cluster as it uses remote accesses.
862//////////////////////////////////////////////////////////////////////////////////////////
863// @ last_cluster_id      : [in]  previous last cluster index.
864// @ searched_cluster_id  : [out] allocated cluster index.
865// @ return 0 if success / return -1 if no more free clusters on IOC device.
866//////////////////////////////////////////////////////////////////////////////////////////
867static error_t fatfs_cluster_alloc( uint32_t   last_cluster_id,
868                                    uint32_t * searched_cluster_id )
869{
870    error_t       error;
871    uint32_t      free_clusters;   // total number of free clusters
872    uint32_t      hint;            // hint + 1 is the first free cluster
873    uint32_t      new_cluster_id;  // allocated cluster index in FAT
874    uint32_t      new_page_id;     // allocated cluster page index in FAT mapper
875    uint32_t      new_slot_id;     // allocated cluster slot index in page (1024 slots per page)
876    xptr_t        new_page_xp;     // extended pointer on FAT page for allocated cluster
877    xptr_t        new_slot_xp;     // extended pointer on allocated cluster slot in FAT
878    uint32_t      last_page_id;    // last cluster page index in FAT mapper
879    uint32_t      last_slot_id;    // last cluster slot index in page (1024 slots per page)
880    xptr_t        last_slot_xp;    // extended pointer on last cluster slot in FAT
881    xptr_t        last_page_xp;    // extended pointer on FAT page for last cluster
882    vfs_ctx_t   * vfs_ctx;         // local pointer on VFS context (same in all clusters)
883    fatfs_ctx_t * loc_fatfs_ctx;   // local pointer on local FATFS context
884    fatfs_ctx_t * fat_fatfs_ctx;   // local pointer on FATFS context in FAT cluster
885    mapper_t    * fat_mapper_ptr;  // local pointer on FAT mapper
886    xptr_t        fat_mapper_xp;   // extended pointer on FAT mapper
887    cxy_t         fat_cxy;         // FAT mapper cluster identifier
888    xptr_t        lock_xp;         // extended pointer on lock protecting free clusters info
889    xptr_t        hint_xp;         // extended pointer on free_cluster_hint in FAT cluster
890    xptr_t        free_xp;         // extended pointer on free_clusters_number in FAT cluster
891   
892#if DEBUG_FATFS_CLUSTER_ALLOC
893uint32_t   cycle = (uint32_t)hal_get_cycles();
894thread_t * this  = CURRENT_THREAD;
895if( DEBUG_FATFS_CLUSTER_ALLOC < cycle )
896printk("\n[%s] thread[%x,%x] enter / lats_cluster_id %x / cycle = %d\n",
897__FUNCTION__, this->process->pid, this->trdid, last_cluster_id, cycle );
898#endif
899
900    // get local pointer on VFS context (same in all clusters)
901    vfs_ctx = &fs_context[FS_TYPE_FATFS];
902
903    // get local pointer on local FATFS context
904    loc_fatfs_ctx = vfs_ctx->extend;
905
906    // get FAT cluster
907    fat_cxy = CONFIG_VFS_ROOT_CXY;
908
909    // get pointers on FAT mapper
910    fat_mapper_ptr = loc_fatfs_ctx->fat_mapper;
911    fat_mapper_xp  = XPTR( fat_cxy , fat_mapper_ptr );
912
913    // get local pointer on FATFS context in FAT cluster
914    fat_fatfs_ctx = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) );
915
916    // build relevant extended pointers on free clusters info in FAT cluster
917    lock_xp = XPTR( fat_cxy , &fat_fatfs_ctx->lock );
918    hint_xp = XPTR( fat_cxy , &fat_fatfs_ctx->free_cluster_hint );
919    free_xp = XPTR( fat_cxy , &fat_fatfs_ctx->free_clusters );
920
921    // take the FAT lock in write mode
922    remote_rwlock_wr_acquire( lock_xp );
923
924    // get hint and free_clusters values from FATFS context in FAT cluster
925    hint          = hal_remote_l32( hint_xp );
926    free_clusters = hal_remote_l32( free_xp );
927       
928#if (DEBUG_FATFS_CLUSTER_ALLOC & 1)
929if( DEBUG_FATFS_CLUSTER_ALLOC < cycle )
930printk("\n[%s] thread[%x,%x] get free info : hint %x / free_clusters %x\n",
931__FUNCTION__, this->process->pid, this->trdid, hint, free_clusters );
932#endif
933
934    // check "free_clusters"
935    if ( free_clusters == 0 )
936    {
937        printk("\n[ERROR] in %s : no more free FATFS clusters\n", __FUNCTION__ );
938        remote_rwlock_wr_release( lock_xp );
939        return -1;
940    }
941    else if ( free_clusters < CONFIG_VFS_FREE_CLUSTERS_MIN )
942    {
943        printk("\n[WARNING] in %s : only %d free FATFS clusters\n", 
944        __FUNCTION__, CONFIG_VFS_FREE_CLUSTERS_MIN );
945    }
946
947    // get new cluster, page & slot indexes in FAT
948    new_cluster_id = hint + 1; 
949    new_page_id    = new_cluster_id >> 10;
950    new_slot_id    = new_cluster_id & 0x3FF;
951
952    // get relevant FAT page descriptor from FAT mapper
953    new_page_xp = mapper_get_fat_page( fat_mapper_xp , new_page_id );
954
955    if( new_page_xp == XPTR_NULL )
956    {
957        printk("\n[ERROR] in %s : cannot acces FAT mapper\n", __FUNCTION__ );
958        remote_rwlock_wr_release( lock_xp );
959        return -1;
960    }
961
962    // build extended pointer on new cluster slot in FAT mapper
963    new_slot_xp = ppm_page2base( new_page_xp ) + (new_slot_id << 2);
964         
965    // check selected cluster actually free
966    if( hal_remote_l32( new_slot_xp ) != FREE_CLUSTER )
967    { 
968        printk("\n[ERROR] in %s : selected cluster_id %x not free\n",
969        __FUNCTION__, new_cluster_id );
970        remote_rwlock_wr_release( lock_xp );
971        return -1;
972    }
973
974    // update new_cluster slot in FAT mapper
975    hal_remote_s32( new_slot_xp  , END_OF_CHAIN_CLUSTER_MIN );
976
977    // handle last_cluster_id argument if non zero
978    if( last_cluster_id )
979    {
980        // get last cluster page & slot indexes in FAT
981        last_page_id    = last_cluster_id >> 10;
982        last_slot_id    = last_cluster_id & 0x3FF;
983
984        // get relevant FAT page descriptor from FAT mapper
985        last_page_xp = mapper_get_fat_page( fat_mapper_xp , last_page_id );
986
987        if( last_page_xp == XPTR_NULL )
988        {
989            printk("\n[ERROR] in %s : cannot acces FAT mapper\n", __FUNCTION__ );
990            remote_rwlock_wr_release( lock_xp );
991            return -1;
992        }
993
994        // build extended pointer on new cluster slot in FAT mapper
995        last_slot_xp = ppm_page2base( last_page_xp ) + (last_slot_id << 2);
996         
997        // check last cluster actually end of chain
998        if( hal_remote_l32( last_slot_xp ) != END_OF_CHAIN_CLUSTER_MIN )
999        { 
1000            printk("\n[ERROR] in %s : last_cluster_id %x not END_OF_CHAIN\n",
1001            __FUNCTION__, last_cluster_id );
1002            remote_rwlock_wr_release( lock_xp );
1003            return -1;
1004        }
1005
1006        // update last_cluster slot in FAT mapper
1007        hal_remote_s32( last_slot_xp , new_cluster_id );
1008    }
1009    else
1010    {
1011        last_page_xp = XPTR_NULL;
1012    }
1013
1014    // update the FAT new_page on device
1015    error = fatfs_move_page( new_page_xp , IOC_SYNC_WRITE );
1016
1017    if( error )
1018    { 
1019        printk("\n[ERROR] in %s : cannot update FAT on IOC device\n", __FUNCTION__ );
1020        remote_rwlock_wr_release( lock_xp );
1021        return -1;
1022    }
1023
1024    // update the FAT last_page on device when required
1025    if( (last_page_xp != XPTR_NULL) && (last_page_xp != new_page_xp) )
1026    {
1027        error = fatfs_move_page( last_page_xp , IOC_SYNC_WRITE );
1028
1029        if( error )
1030        { 
1031            printk("\n[ERROR] in %s : cannot update FAT on IOC device\n", __FUNCTION__ );
1032            remote_rwlock_wr_release( lock_xp );
1033            return -1;
1034        }
1035    }
1036
1037    // update free cluster info in FATFS context and in FS_INFO sector
1038    error = fatfs_free_clusters_decrement( XPTR( fat_cxy , fat_fatfs_ctx ) , new_cluster_id );
1039
1040    if( error )
1041    { 
1042        printk("\n[ERROR] in %s : cannot update free cluster info\n", __FUNCTION__ );
1043        remote_rwlock_wr_release( lock_xp );
1044        return -1;
1045    }
1046
1047    // release FAT lock
1048    remote_rwlock_wr_release( lock_xp );
1049
1050#if DEBUG_FATFS_CLUSTER_ALLOC
1051cycle = (uint32_t)hal_get_cycles();
1052if( DEBUG_FATFS_CLUSTER_ALLOC < cycle )
1053printk("\n[%s] thread[%x,%x] exit / allocated cluster_id %x in FAT / cycle %d\n",
1054__FUNCTION__, this->process->pid, this->trdid, new_cluster_id, cycle );
1055#endif
1056
1057    *searched_cluster_id = new_cluster_id;
1058    return 0;
1059
1060}  // end fatfs_cluster_alloc()
1061
1062
1063//////////////////////////////////////////////////////////////////////////////////////////
1064// This static function access the FAT (File Allocation Table), stored in the FAT mapper,
1065// and returns in <searched_cluster_id> the FATFS cluster_id for a given page of a given
1066// inode, identified by the <searched_page_id> argument that is the page index in file
1067// (i.e. the page index in file mapper). The entry point in the FAT is defined by the
1068// <first_cluster_id> argument, that is the cluster_id of an already allocated cluster.
1069// It can be the cluster_id of the first page of the file (always registered in the
1070// fatfs_inode extension), or any page of the file whose <first_page_id> argument
1071// is smaller than the searched <first_page_id> argument.
1072// This function can be called by a thread running in any cluster.
1073// The FAT mapper being a WRITE-THROUGH cache, this function updates the FAT mapper
1074// from informations stored on IOC device in case of miss when scanning the FAT mapper.
1075// The searched inode mapper being a WRITE-BACK cache, this function allocates a new
1076// cluster_id when the searched page exist in the inode mapper, and there is no FATFS
1077// cluster allocated yet for this page. It updates the FAT, but it does NOT copy the
1078// mapper page content to the File System.
1079//////////////////////////////////////////////////////////////////////////////////////////
1080// @ first_page_id       : [in]  index in file mapper for an existing page.
1081// @ first_cluster_id    : [in]  cluster_id for this existing page.
1082// @ searched_page_id    : [in]  index in file mapper for the searched page.
1083// @ searched_cluster_id : [out] cluster_id for the searched page.
1084// @ return 0 if success / return -1 if a FAT mapper miss cannot be solved,
1085//                         or if a missing cluster_id cannot be allocated.
1086//////////////////////////////////////////////////////////////////////////////////////////
1087static error_t fatfs_get_cluster( uint32_t   first_page_id,
1088                                  uint32_t   first_cluster_id,
1089                                  uint32_t   searched_page_id,
1090                                  uint32_t * searched_cluster_id )
1091{
1092    uint32_t   current_page_id;        // index of page in file mapper
1093    uint32_t   current_cluster_id;     // index of cluster in FATFS
1094    xptr_t     lock_xp;                // extended pointer on FAT lock
1095    xptr_t     fat_mapper_xp;          // extended pointer on FAT mapper
1096    mapper_t * fat_mapper_ptr;         // local pointer on FAT mapper
1097    cxy_t      fat_cxy;                // FAT cluster
1098    error_t    error;
1099
1100assert( __FUNCTION__, (searched_page_id > first_page_id) ,
1101"searched_page_id must be larger than first_page_id\n");
1102
1103#if DEBUG_FATFS_GET_CLUSTER
1104uint32_t   cycle = (uint32_t)hal_get_cycles();
1105thread_t * this  = CURRENT_THREAD;
1106if( DEBUG_FATFS_GET_CLUSTER < cycle )
1107printk("\n[%s] thread[%x,%x] enter / frst_pid %x / frst_cid %x / srch_pid %d / cycle %d\n",
1108__FUNCTION__, this->process->pid, this->trdid,
1109first_page_id, first_cluster_id, searched_page_id, cycle );
1110#endif
1111
1112    // get local pointer on VFS context (same in all clusters)
1113    vfs_ctx_t * vfs_ctx = &fs_context[FS_TYPE_FATFS];
1114
1115    // get local pointer on local FATFS context
1116    fatfs_ctx_t * loc_fatfs_ctx = vfs_ctx->extend;
1117
1118    // get FAT cluster
1119    fat_cxy = CONFIG_VFS_ROOT_CXY;
1120   
1121    // get pointers on FAT mapper
1122    fat_mapper_ptr = loc_fatfs_ctx->fat_mapper;
1123    fat_mapper_xp  = XPTR( fat_cxy , fat_mapper_ptr );
1124
1125    // get local pointer on FATFS context in FAT cluster
1126    fatfs_ctx_t * fat_fatfs_ctx = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) );
1127
1128    // build extended pointer on FAT lock in FAT cluster
1129    lock_xp = XPTR( fat_cxy , &fat_fatfs_ctx->lock );
1130
1131    // take FAT lock in read mode
1132    remote_rwlock_rd_acquire( lock_xp );
1133
1134    // initialize loop variables
1135    current_page_id    = first_page_id;
1136    current_cluster_id = first_cluster_id;
1137
1138    // scan FAT mapper (i.e. traverse FAT linked list)
1139    // starting from first_page_id until searched_page_id
1140    // each iteration in this loop can change both
1141    // the FAT page index and the slot index in FAT
1142    while( current_page_id < searched_page_id )
1143    {
1144        // FAT mapper page and slot indexes (1024 slots per FAT page)
1145        uint32_t fat_page_index   = current_cluster_id >> 10;
1146        uint32_t fat_slot_index   = current_cluster_id & 0x3FF;
1147
1148        // get pointer on current page descriptor in FAT mapper
1149        xptr_t current_page_xp = mapper_get_fat_page( fat_mapper_xp , fat_page_index );
1150
1151        if( current_page_xp == XPTR_NULL )
1152        {
1153            printk("\n[ERROR] in %s : cannot get page %d from FAT mapper\n",
1154            __FUNCTION__ , fat_page_index );
1155            remote_rwlock_rd_release( lock_xp );
1156            return -1;
1157        }
1158
1159        // get pointer on buffer containing the FAT mapper page
1160        xptr_t     base_xp = ppm_page2base( current_page_xp );
1161        uint32_t * buffer  = (uint32_t *)GET_PTR( base_xp );
1162
1163        // get next_cluster_id from FAT slot 
1164        uint32_t next_cluster_id = hal_remote_l32( XPTR( fat_cxy, &buffer[fat_slot_index] ) );
1165
1166        // allocate a new FAT cluster when END_OF_CHAIN found
1167        if( next_cluster_id >= END_OF_CHAIN_CLUSTER_MIN ) 
1168        {
1169            // release the FAT lock in read mode,
1170            remote_rwlock_rd_release( lock_xp );
1171
1172            // allocate a new cluster_id (and update both FAT mapper and FAT on device).
1173            error = fatfs_cluster_alloc( current_cluster_id,
1174                                         &next_cluster_id );
1175            if( error )
1176            {
1177                printk("\n[ERROR] in %s : cannot allocate cluster on FAT32 for page %d\n",
1178                __FUNCTION__ , current_page_id );
1179                remote_rwlock_wr_release( lock_xp );
1180                return -1;
1181            }
1182
1183#if (DEBUG_FATFS_GET_CLUSTER & 1)
1184if( DEBUG_FATFS_GET_CLUSTER < cycle )
1185printk("\n[%s] allocated a new cluster_id %x in FATFS\n",
1186__FUNCTION__, next_cluster_id );
1187#endif
1188            // take the FAT lock in read mode,
1189            remote_rwlock_rd_acquire( lock_xp );
1190        }
1191
1192#if (DEBUG_FATFS_GET_CLUSTER & 1)
1193if( DEBUG_FATFS_GET_CLUSTER < cycle )
1194printk("\n[%s] traverse FAT / current_cluster_id %x / next_cluster_id %x\n",
1195__FUNCTION__, current_cluster_id , next_cluster_id );
1196#endif
1197
1198        // update loop variables
1199        current_cluster_id = next_cluster_id;
1200        current_page_id++;
1201    }
1202   
1203    // release FAT lock
1204    remote_rwlock_rd_release( lock_xp );
1205
1206#if DEBUG_FATFS_GET_CLUSTER
1207if( DEBUG_FATFS_GET_CLUSTER < cycle )
1208printk("\n[%s] thread[%x,%x] exit / frst_pid %d / frst_cid %x / srch_pid %d / srch_cid %x\n",
1209__FUNCTION__, this->process->pid, this->trdid,
1210first_page_id, first_cluster_id, searched_page_id, current_cluster_id );
1211#endif
1212
1213    *searched_cluster_id = current_cluster_id;
1214    return 0;
1215
1216}  // end fatfs_get_cluster()
1217
1218//////////////////////////////////////////////////////////////////////////////////////////////
1219// This static function scan the pages of a directory mapper, identified by the <mapper_xp>
1220// argument, to find the directory entry identified by the <name> argument, and returns
1221// a pointer on the directory entry, described as an array of 32 bytes, and the index of
1222// this entry in the FAT32 mapper, seen as an array of 32 bytes entries.
1223// It makes a local copy of each directory entry to reduce the number of remote accesses.
1224// It is called by the fatfs_new_dentry_from_mapper() function.
1225// It can be called by a thread running in any cluster.
1226//////////////////////////////////////////////////////////////////////////////////////////////
1227// @ mapper_xp : [in]  extended pointer on directory mapper.
1228// @ name      : [in]  searched directory entry name.
1229// @ entry     : [out] buffer for the pointer on the 32 bytes directory entry (when found).
1230// @ index     : [out] buffer for the directory entry index in mapper.
1231// @ return 0 if found / return 1 if not found / return -1 if mapper access error.
1232//////////////////////////////////////////////////////////////////////////////////////////////
1233static error_t fatfs_scan_directory( xptr_t      mapper_xp,
1234                                     char     *  name,
1235                                     uint8_t  ** entry,
1236                                     uint32_t *  index )
1237{
1238    uint8_t  buf[32];   // local buffer for one FAT32 directory entry
1239
1240// check arguments
1241assert( __FUNCTION__, (mapper_xp != XPTR_NULL) , "mapper pointer is NULL\n" );
1242assert( __FUNCTION__, (name      != NULL     ) , "child name is undefined\n" );
1243
1244#if DEBUG_FATFS_SCAN_DIRECTORY
1245char          parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
1246uint32_t      cycle      = (uint32_t)hal_get_cycles();
1247thread_t    * this       = CURRENT_THREAD;
1248mapper_t    * mapper_ptr = GET_PTR( mapper_xp );
1249cxy_t         mapper_cxy = GET_CXY( mapper_xp );
1250vfs_inode_t * inode_ptr  = hal_remote_lpt( XPTR( mapper_cxy , &mapper_ptr->inode ) );
1251vfs_inode_get_name( XPTR( mapper_cxy , inode_ptr ) , parent_name );
1252if( DEBUG_FATFS_SCAN_DIRECTORY < cycle )
1253printk("\n[%s]  thread[%x,%x] enter to search child <%s> in parent <%s> / cycle %d\n",
1254__FUNCTION__, this->process->pid, this->trdid, name , parent_name , cycle );
1255#endif
1256
1257    char       cname[CONFIG_VFS_MAX_NAME_LENGTH];  // name extracted from directory entry
1258
1259    char       lfn1[16];         // buffer for one partial cname
1260    char       lfn2[16];         // buffer for one partial cname
1261    char       lfn3[16];         // buffer for one partial cname
1262    xptr_t     page_xp;          // extended pointer on one page descriptor
1263    xptr_t     base_xp;          // extended pointer on one page base
1264    uint8_t    attr;             // directory entry ATTR field
1265    uint8_t    ord;              // directory entry ORD field
1266    uint32_t   seq;              // sequence index
1267    uint32_t   lfn       = 0;    // LFN entries number
1268    int32_t    found     = 0;    // not yet = 0 / success = 1 / not found = 2 / error = -1
1269    uint32_t   page_id   = 0;    // page index in mapper
1270    uint32_t   offset    = 0;    // byte offset in page
1271
1272    // Two embedded loops to scan the directory mapper:
1273    // - scan the parent directory mapper pages
1274    // - scan the directory entries in each 4 Kbytes page
1275
1276    // scan the mapper pages
1277    while ( found == 0 )
1278    {
1279        // get one page
1280        page_xp = mapper_get_page( mapper_xp , page_id );
1281
1282        if( page_xp == XPTR_NULL)
1283        {
1284            found = -1;
1285        }
1286
1287        // get page base
1288        base_xp = ppm_page2base( page_xp );
1289
1290#if (DEBUG_FATFS_SCAN_DIRECTORY & 1)
1291if( DEBUG_FATFS_SCAN_DIRECTORY < cycle )
1292mapper_display_page( mapper_xp , page_xp , 256 );
1293#endif
1294        // scan this page until end of directory, end of page, or name found
1295        while( (offset < 4096) && (found == 0) )
1296        {
1297            // makes a local copy of current directory entry  (32 bytes)
1298            hal_remote_memcpy( XPTR( local_cxy , buf ) , base_xp + offset , 32 );
1299
1300            // get attr and ord from local buffer
1301            attr = fatfs_get_record( DIR_ATTR , buf );   
1302            ord  = fatfs_get_record( LDIR_ORD , buf );   
1303
1304            if (ord == NO_MORE_ENTRY)                 // no more entry => break
1305            {
1306                found = 2;
1307            }
1308            else if ( ord == FREE_ENTRY )             // free entry => skip
1309            {
1310                offset = offset + 32;
1311            }
1312            else if ( attr == 0x28 )                  // volune_id => skip
1313            {
1314                offset = offset + 32;
1315            }
1316            else if ( attr == ATTR_LONG_NAME_MASK )   // LFN entry => get partial cname
1317            {
1318                seq = ord & 0x3;
1319                lfn = (seq > lfn) ? seq : lfn;   
1320                if      ( seq == 1 ) fatfs_get_name_from_long( buf , lfn1 );
1321                else if ( seq == 2 ) fatfs_get_name_from_long( buf , lfn2 );
1322                else if ( seq == 3 ) fatfs_get_name_from_long( buf , lfn3 );
1323                offset = offset + 32;
1324            }
1325            else                                 // NORMAL entry
1326            {
1327                // build the extracted name
1328                if      ( lfn == 0 )
1329                {
1330                    fatfs_get_name_from_short( buf , cname );
1331                }
1332                else if ( lfn == 1 )
1333                {
1334                    strcpy( cname      , lfn1 );
1335                }   
1336                else if ( lfn == 2 ) 
1337                {
1338                    strcpy( cname      , lfn1 );
1339                    strcpy( cname + 13 , lfn2 );
1340                }
1341                else if ( lfn == 3 ) 
1342                {
1343                    strcpy( cname      , lfn1 );
1344                    strcpy( cname + 13 , lfn2 );
1345                    strcpy( cname + 26 , lfn3 );
1346                }
1347
1348                // get dentry arguments if extracted cname == searched name
1349                if ( strcmp( name , cname ) == 0 )
1350                {
1351                    uint8_t * base = GET_PTR( base_xp );
1352                    *entry = base + offset;
1353                    *index = ( (page_id << 12) + offset ) >> 5; 
1354                    found  = 1;
1355                }
1356                offset = offset + 32;
1357                lfn    = 0;
1358            }
1359
1360        }  // end loop on directory entries in page
1361
1362        page_id++;
1363        offset = 0;
1364
1365    }  // end loop on pages
1366
1367    if( found == 1 )
1368    {
1369
1370#if DEBUG_FATFS_SCAN_DIRECTORY
1371if( DEBUG_FATFS_SCAN_DIRECTORY < cycle )
1372printk("\n[%s]  thread[%x,%x] exit / found child <%s> in <%s>\n",
1373__FUNCTION__, this->process->pid, this->trdid, name, parent_name );
1374#endif
1375        return 0;
1376    }
1377    else if( found == 2 )
1378    {
1379
1380#if DEBUG_FATFS_SCAN_DIRECTORY
1381if( DEBUG_FATFS_SCAN_DIRECTORY < cycle )
1382printk("\n[%s]  thread[%x,%x] exit / child <%s> in <%s> not found\n",
1383__FUNCTION__, this->process->pid, this->trdid, name, parent_name );
1384#endif
1385        return 1;
1386    }
1387    else
1388    {
1389        printk("\n[ERROR] in %s : cannot get page %d from mapper\n",
1390        __FUNCTION__, page_id );
1391
1392        return -1;
1393    }
1394}  // end fatfs_scan_directory()
1395
1396
1397//////////////////////////////////////////////////////////////////////////////////////////
1398//              FATFS debug functions
1399//////////////////////////////////////////////////////////////////////////////////////////
1400
1401///////////////////////////////////
1402void fatfs_display_ctx( cxy_t cxy )
1403{
1404    // get pointer on local FATFS context
1405    vfs_ctx_t   * vfs_ctx = &fs_context[FS_TYPE_FATFS];
1406        fatfs_ctx_t * ctx     = hal_remote_lpt( XPTR( cxy , &vfs_ctx->extend ) );
1407
1408    uint32_t fat_sectors       = hal_remote_l32( XPTR( cxy , &ctx->fat_sectors_count ) );
1409    uint32_t sector_size       = hal_remote_l32( XPTR( cxy , &ctx->bytes_per_sector ) );
1410    uint32_t sec_per_clus      = hal_remote_l32( XPTR( cxy , &ctx->sectors_per_cluster ) );
1411    uint32_t fat_lba           = hal_remote_l32( XPTR( cxy , &ctx->fat_begin_lba ) );
1412    uint32_t data_lba          = hal_remote_l32( XPTR( cxy , &ctx->cluster_begin_lba ) );
1413    uint32_t fsinfo_lba        = hal_remote_l32( XPTR( cxy , &ctx->fs_info_lba ) );
1414    uint32_t root_dir_clus     = hal_remote_l32( XPTR( cxy , &ctx->root_dir_cluster ) );
1415    uint32_t free_clusters     = hal_remote_l32( XPTR( cxy , &ctx->free_clusters ) );
1416    uint32_t free_cluster_hint = hal_remote_l32( XPTR( cxy , &ctx->free_cluster_hint ) );
1417    void   * fat_mapper        = hal_remote_lpt( XPTR( cxy , &ctx->fat_mapper ) );
1418    void   * fs_info_buffer    = hal_remote_lpt( XPTR( cxy , &ctx->fs_info_buffer ) );
1419
1420    printk("\n*** FAT context in cluster %x\n" 
1421           "- fat_sectors       = %d\n"
1422           "- sector size       = %d\n"
1423           "- cluster size      = %d\n"
1424           "- fat_lba           = %x\n"
1425           "- data_lba          = %x\n"
1426           "- fsinfo_lba        = %x\n"
1427           "- root_dir_cluster  = %x\n"
1428           "- free_clusters     = %x\n"
1429           "- free_cluster_hint = %x\n"
1430           "- fat_mapper        = %x\n"
1431           "- fs_info_buffer    = %x\n",
1432           cxy,
1433           fat_sectors,
1434           sector_size,
1435           sector_size * sec_per_clus,
1436           fat_lba,
1437           data_lba,
1438           fsinfo_lba,
1439           root_dir_clus,
1440           free_clusters,
1441           free_cluster_hint,
1442           fat_mapper,
1443           fs_info_buffer );
1444
1445}  // end fatfs_display_ctx()
1446
1447//////////////////////////////////////////
1448void fatfs_display_fat( uint32_t  min_slot,
1449                        uint32_t  nb_slots )
1450{
1451    // one FAT mapper page contains 1024 slots = 128 lines of 8 slots
1452
1453    uint32_t   page_id;
1454    uint32_t   line;
1455    cxy_t      fat_cxy;         // FAT cluster
1456    mapper_t * mapper;          // local pointer on FAT mapper
1457    xptr_t     mapper_xp;       // extended pointer on fat_mapper
1458    uint32_t   min_cluster_id;  // index of min slot to be displayed
1459    uint32_t   min_page_id;     // index of min page to be displayed
1460    uint32_t   min_line_id;     // index of min line in min page ( < 128 )
1461    uint32_t   max_cluster_id;  // index of max slot to be displayed
1462    uint32_t   max_page_id;     // index of max page to be displayed
1463    uint32_t   max_line_id;     // index of max line in max page ( < 128 )
1464
1465    // compute min values
1466    min_cluster_id = min_slot & 0xFFFFFFF8;
1467    min_line_id    = (min_cluster_id & 0x3FF) >> 3;
1468    min_page_id    = min_cluster_id >> 10;
1469
1470    // compute max values
1471    max_cluster_id = min_slot + nb_slots - 1;
1472    max_line_id    = (max_cluster_id & 0x3FF) >> 3;
1473    max_page_id    = max_cluster_id >> 10;
1474
1475    // get pointer on local FATFS context
1476    vfs_ctx_t   * vfs_ctx       = &fs_context[FS_TYPE_FATFS];
1477    fatfs_ctx_t * loc_fatfs_ctx = (fatfs_ctx_t *)vfs_ctx->extend;
1478
1479    // get FAT cluster
1480    fat_cxy = CONFIG_VFS_ROOT_CXY;
1481
1482    // get pointers on FAT mapper (in FAT cluster)
1483    mapper    = loc_fatfs_ctx->fat_mapper;
1484    mapper_xp = XPTR( fat_cxy , mapper );
1485
1486    // get pointer on FATFS context in FAT cluster
1487    fatfs_ctx_t * fat_fatfs_ctx = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) );
1488 
1489    // get current value of hint and free_clusters
1490    uint32_t hint = hal_remote_l32( XPTR( fat_cxy , &fat_fatfs_ctx->free_cluster_hint ) );
1491    uint32_t free = hal_remote_l32( XPTR( fat_cxy , &fat_fatfs_ctx->free_clusters ) );
1492
1493    printk("\n***** FAT mapper / cxy %x / free_clusters %x / hint %x\n", fat_cxy, free, hint );
1494
1495    // scan all pages as required by min_page_id and max_page_id
1496    for( page_id = min_page_id ; page_id <= max_page_id ; page_id++ )
1497    {
1498        // get extended pointer on requested page descriptor in FAT mapper
1499        xptr_t page_xp = mapper_get_fat_page( mapper_xp , page_id );
1500
1501        // get extended pointer on requested page base
1502        xptr_t     base_xp  = ppm_page2base( page_xp );
1503
1504        // compute min_line & max_line in current page
1505        uint32_t min_line = (page_id == min_page_id) ? min_line_id : 0;
1506        uint32_t max_line = (page_id == max_page_id) ? max_line_id : 127;
1507
1508        // loop on lines in current page
1509        for( line = min_line ; line <= max_line ; line++ )
1510        {
1511            printk("%x : %X | %X | %X | %X | %X | %X | %X | %X\n",
1512            (page_id << 10) + (line <<3 ),
1513            hal_remote_l32( base_xp + ((line<<5)      ) ),
1514            hal_remote_l32( base_xp + ((line<<5) + 4  ) ),
1515            hal_remote_l32( base_xp + ((line<<5) + 8  ) ),
1516            hal_remote_l32( base_xp + ((line<<5) + 12 ) ),
1517            hal_remote_l32( base_xp + ((line<<5) + 16 ) ),
1518            hal_remote_l32( base_xp + ((line<<5) + 20 ) ),
1519            hal_remote_l32( base_xp + ((line<<5) + 24 ) ),
1520            hal_remote_l32( base_xp + ((line<<5) + 28 ) ) );
1521        }
1522    }
1523}  // end fatfs_display_fat()
1524
1525/////////////////////////////////////
1526error_t fatfs_check_free_info( void )
1527{
1528    error_t       error;
1529    fatfs_ctx_t * fatfs_ctx_ptr;              // local pointer on fatfs context in cluster 0
1530    uint32_t      ctx_free_clusters;          // number of free clusters from fatfs context
1531    uint32_t      ctx_free_cluster_hint;      // free cluster hint from fatfs context
1532    uint32_t      ioc_free_clusters;          // number of free clusters from fatfs context
1533    uint32_t      ioc_free_cluster_hint;      // free cluster hint from fatfs context
1534    uint32_t      fs_info_lba;                // lba of FS_INFO sector on IOC device
1535    uint8_t     * fs_info_buffer;             // local pointer on FS_INFO buffer in cluster 0
1536    xptr_t        fs_info_buffer_xp;          // extended pointer on FS_INFO buffer in cluster 0
1537    uint8_t       tmp_buf[512];               // 512 bytes temporary buffer
1538    xptr_t        tmp_buf_xp;                 // extended pointer on temporary buffer
1539
1540#if DEBUG_FATFS_SYNC_FSINFO
1541uint32_t   cycle = (uint32_t)hal_get_cycles();
1542thread_t * this  = CURRENT_THREAD;
1543if( DEBUG_FATFS_SYNC_FSINFO < cycle )
1544printk("\n[%s] thread[%x,%x] enter / cycle %d\n",
1545__FUNCTION__ , this->process->pid, this->trdid, cycle );
1546#endif
1547
1548    // get pointer on fatfs context in cluster 0
1549    fatfs_ctx_ptr = hal_remote_lpt( XPTR( 0 , &fs_context[FS_TYPE_FATFS].extend ) );
1550
1551    // get "free_clusters" and "free_cluster_hint" from fatfs context in cluster 0
1552    ctx_free_clusters     = hal_remote_l32( XPTR( 0 , &fatfs_ctx_ptr->free_clusters ) );
1553    ctx_free_cluster_hint = hal_remote_l32( XPTR( 0 , &fatfs_ctx_ptr->free_cluster_hint ) );
1554
1555    // get fs_info_lba
1556    fs_info_lba = hal_remote_l32( XPTR( 0 , &fatfs_ctx_ptr->fs_info_lba ) );
1557
1558    // build extended pointer on temporary buffer
1559    tmp_buf_xp = XPTR( local_cxy , tmp_buf );
1560
1561    // copy FS_INFO sector from IOC to local buffer
1562    error = dev_ioc_sync_read( tmp_buf_xp , fs_info_lba , 1 );
1563
1564    if ( error )
1565    {
1566        printk("\n[ERROR] in %s : cannot access FS_INFO on IOC device\n", __FUNCTION__ );
1567        return -1;
1568    }
1569
1570    // get current values of "free_clusters" and "free_cluster_hint" from FS_INFO on IOC
1571    ioc_free_clusters     = fatfs_get_remote_record( FS_FREE_CLUSTERS     , tmp_buf_xp );
1572    ioc_free_cluster_hint = fatfs_get_remote_record( FS_FREE_CLUSTER_HINT , tmp_buf_xp );
1573
1574#if DEBUG_FATFS_SYNC_FSINFO
1575if( DEBUG_FATFS_SYNC_FSINFO < cycle )
1576printk("\n[%s] thread[%x,%x] / ctx_free %x / ioc_free %x / ctx_hint %x / ioc_hint %x\n",
1577__FUNCTION__ , this->process->pid, this->trdid, 
1578ctx_free_clusters, ioc_free_clusters, ctx_free_cluster_hint, ioc_free_cluster_hint );
1579#endif
1580
1581    // check values
1582    if( (ioc_free_clusters     != ctx_free_clusters) || 
1583        (ioc_free_cluster_hint != ctx_free_cluster_hint) )
1584    {
1585        printk("\n[WARNING] in %s : unconsistent free clusters info\n"
1586        " ioc_free %x / ctx_free %x / ioc_hint %x / ctx_hint %x\n",
1587        __FUNCTION__, ioc_free_clusters, ctx_free_clusters,
1588        ioc_free_cluster_hint, ctx_free_cluster_hint );
1589
1590        // get pointers on FS_INFO buffer in cluster 0
1591        fs_info_buffer    = hal_remote_lpt( XPTR( 0 , &fatfs_ctx_ptr->fs_info_buffer ) );
1592        fs_info_buffer_xp = XPTR( 0 , fs_info_buffer );
1593
1594        // update FS_INFO buffer in cluster 0
1595        fatfs_set_remote_record(FS_FREE_CLUSTERS    ,fs_info_buffer_xp,ctx_free_clusters );
1596        fatfs_set_remote_record(FS_FREE_CLUSTER_HINT,fs_info_buffer_xp,ctx_free_cluster_hint);
1597
1598        // update the FS_INFO sector on IOC device
1599        error = dev_ioc_sync_write( fs_info_buffer_xp , fs_info_lba , 1 );
1600
1601        if ( error )
1602        {
1603            printk("\n[ERROR] in %s : cannot update FS_INFO on IOC device\n", __FUNCTION__ );
1604            return -1;
1605        }
1606    }
1607
1608#if DEBUG_FATFS_SYNC_FSINFO
1609cycle = (uint32_t)hal_get_cycles();
1610if( DEBUG_FATFS_SYNC_FSINFO < cycle )
1611printk("\n[%s] thread[%x,%x] exit / cycle %d\n",
1612__FUNCTION__ , this->process->pid, this->trdid, cycle );
1613#endif
1614
1615    return 0;
1616
1617}  // end fatfs_check_free_info()
1618
1619
1620
1621
1622
1623///////////////////////////////////////////////////////////////////////////////////////
1624// Generic API : the following functions are called by the kernel VFS
1625//               and must be defined by all supported file systems.
1626///////////////////////////////////////////////////////////////////////////////////////
1627
1628
1629/////////////////////////////////////
1630xptr_t  fatfs_ctx_alloc( cxy_t  cxy )
1631{
1632    // allocate memory from remote cluster
1633    void * ptr = kmem_remote_alloc( cxy,
1634                                    bits_log2(sizeof(fatfs_ctx_t)),
1635                                    AF_ZERO );
1636
1637    if( ptr == NULL ) return XPTR_NULL;
1638    else              return XPTR( cxy , ptr );
1639
1640}  //end faffs_ctx_alloc()
1641
1642///////////////////////////////////////////////
1643error_t  fatfs_ctx_init( xptr_t  fatfs_ctx_xp )
1644{
1645    error_t       error;
1646    cxy_t         cxy;             // FATFS context cluster identifier
1647    fatfs_ctx_t * fatfs_ctx_ptr;   // local pointer on FATFS context
1648    uint8_t     * buffer;          // local pointer on 512 bytes buffer
1649    xptr_t        buffer_xp;       // extended pointer on 512 bytes buffer
1650    xptr_t        fat_mapper_xp;   // extended pointer on FAT mapper
1651    mapper_t    * fat_mapper;      // local pointer on FAT mapper
1652
1653    // get FATFS context cluster and local pointer
1654    cxy           = GET_CXY( fatfs_ctx_xp );       
1655    fatfs_ctx_ptr = GET_PTR( fatfs_ctx_xp );
1656   
1657#if DEBUG_FATFS_CTX_INIT
1658uint32_t   cycle = (uint32_t)hal_get_cycles();
1659thread_t * this  = CURRENT_THREAD;
1660if( DEBUG_FATFS_CTX_INIT < cycle )
1661printk("\n[%s] thread[%x,%x] enter for fatfs_ctx (%x,%x) / cycle %d\n",
1662__FUNCTION__ , this->process->pid, this->trdid, cxy, fatfs_ctx_ptr , cycle );
1663#endif
1664
1665    // allocate a 512 bytes buffer in remote cluster, used to store
1666    // temporarily the BOOT sector, and permanently the FS_INFO sector
1667        buffer = kmem_remote_alloc( cxy, 
1668                                9,
1669                                AF_ZERO );
1670    if( buffer == NULL ) 
1671    {
1672        printk("\n[PANIC] in %s : cannot allocate buffer in cluster %x\n",
1673        __FUNCTION__ , cxy );
1674        return -1;
1675    }
1676     
1677    // build extended pointer on buffer
1678    buffer_xp = XPTR( cxy , buffer );
1679
1680    // load the BOOT record from device
1681    error = dev_ioc_sync_read( buffer_xp , 0 , 1 );
1682
1683    if ( error )
1684    {
1685        printk("\n[PANIC] in %s : cannot access boot record\n", __FUNCTION__ );
1686        return -1;
1687    }
1688
1689#if (DEBUG_FATFS_CTX_INIT & 0x1)
1690uint8_t bs[256];
1691hal_remote_memcpy( XPTR( local_cxy , bs ) , buffer_xp , 256 );
1692if( DEBUG_FATFS_CTX_INIT < cycle )
1693putb( "boot record", bs , 256 );
1694#endif
1695
1696    // get sector size from boot record
1697    uint32_t sector_size = fatfs_get_remote_record( BPB_BYTSPERSEC , buffer_xp );
1698
1699    if ( sector_size != 512 )
1700    {
1701        printk("\n[PANIC] in %s : sector size must be 512 bytes\n", __FUNCTION__ );
1702        return -1;
1703    }
1704
1705    // get cluster size from boot record
1706    uint32_t nb_sectors = fatfs_get_remote_record( BPB_SECPERCLUS , buffer_xp );
1707
1708    if ( nb_sectors != 8 )
1709    {
1710        printk("\n[PANIC] in %s : cluster size must be 8 sectors\n", __FUNCTION__ );
1711        return -1;
1712    }
1713
1714    // get number of FAT copies from boot record
1715    uint32_t nb_fats = fatfs_get_remote_record( BPB_NUMFATS , buffer_xp );
1716
1717    if ( nb_fats != 1 )
1718    {
1719        printk("\n[PANIC] in %s : number of FAT copies must be 1\n", __FUNCTION__ );
1720        return -1;
1721    }
1722
1723    // get number of sectors in FAT from boot record
1724    uint32_t fat_sectors = fatfs_get_remote_record( BPB_FAT32_FATSZ32 , buffer_xp );
1725
1726    if ( (fat_sectors & 0xF) != 0 )
1727    {
1728        printk("\n[PANIC] in %s : FAT size not multiple of 16 sectors\n", __FUNCTION__ );
1729        return -1;
1730    }
1731
1732    // get root cluster from boot record
1733    uint32_t root_cluster = fatfs_get_remote_record( BPB_FAT32_ROOTCLUS , buffer_xp );
1734
1735    if ( root_cluster != 2 ) 
1736    {
1737        printk("\n[PANIC] in %s : root cluster index must be 2\n", __FUNCTION__ );
1738        return -1;
1739    }
1740
1741    // get FAT lba from boot record
1742    uint32_t fat_lba = fatfs_get_remote_record( BPB_RSVDSECCNT , buffer_xp );
1743
1744    // get FS_INFO sector lba from boot record
1745    uint32_t fs_info_lba = fatfs_get_remote_record( BPB_FAT32_FSINFO , buffer_xp );
1746
1747    // load the FS_INFO record from device
1748    error = dev_ioc_sync_read( buffer_xp , fs_info_lba , 1 );
1749
1750    if ( error )
1751    {
1752        printk("\n[PANIC] in %s : cannot access FS_INFO record\n", __FUNCTION__ );
1753        return -1;
1754    }
1755
1756    // get free_clusters number from FS_INFO record
1757    uint32_t free_clusters = fatfs_get_remote_record( FS_FREE_CLUSTERS , buffer_xp );
1758
1759    if ( free_clusters >= fat_sectors << 7 )
1760    {
1761        printk("\n[PANIC] in %s : unconsistent free_clusters\n", __FUNCTION__ );
1762        return -1;
1763    }
1764
1765    // get free_cluster_hint from FS_INFO record
1766    uint32_t free_hint = fatfs_get_remote_record( FS_FREE_CLUSTER_HINT , buffer_xp );
1767
1768    if ( free_hint >= fat_sectors << 7 )
1769    {
1770        printk("\n[PANIC] in %s : unconsistent free_cluster_hint\n", __FUNCTION__ );
1771        return -1;
1772    }
1773
1774    // allocate a mapper for the FAT in remote cluster
1775    fat_mapper_xp  = mapper_create( cxy , FS_TYPE_FATFS );
1776
1777    // get local pointer on FAT mapper
1778    fat_mapper = GET_PTR( fat_mapper_xp );
1779
1780    if ( fat_mapper == NULL )
1781    {
1782        printk("\n[PANIC] in %s : no memory for FAT mapper in cluster %x\n",
1783        __FUNCTION__ , cxy );
1784        return -1;
1785    }
1786
1787#if (DEBUG_FATFS_CTX_INIT & 0x1)
1788if( DEBUG_FATFS_CTX_INIT < cycle )
1789printk("\n[%s] sector_size %d / nb_sectors %d / fat_sectors %x / hint %x\n",
1790__FUNCTION__, sector_size, nb_sectors, fat_sectors, free_hint );
1791#endif
1792
1793    // the inode field is NULL for the FAT mapper
1794    hal_remote_spt( XPTR( cxy , &fat_mapper->inode ) , NULL );
1795
1796    // initialize the FATFS context
1797    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->fat_begin_lba       ), fat_lba );
1798    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->fat_sectors_count   ), fat_sectors );
1799    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->bytes_per_sector    ), sector_size );
1800    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->sectors_per_cluster ), nb_sectors );
1801    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->cluster_begin_lba   ), fat_lba + fat_sectors );
1802    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->root_dir_cluster    ), 2 );
1803    hal_remote_spt( XPTR( cxy , &fatfs_ctx_ptr->fat_mapper          ), fat_mapper );
1804    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->fs_info_lba         ), fs_info_lba );
1805    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->free_clusters       ), free_clusters );
1806    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->free_cluster_hint   ), free_hint );
1807    hal_remote_spt( XPTR( cxy , &fatfs_ctx_ptr->fs_info_buffer      ), buffer );
1808
1809    // initialize FATFS lock
1810    remote_rwlock_init( XPTR( cxy , &fatfs_ctx_ptr->lock ) , LOCK_FATFS_FAT );
1811
1812#if DEBUG_FATFS_CTX_INIT
1813if( DEBUG_FATFS_CTX_INIT < cycle )
1814printk("\n[%s]  thread[%x,%x] exit for fatfs_ctx (%x,%x)\n",
1815__FUNCTION__, this->process->pid, this->trdid, cxy, fatfs_ctx_ptr );
1816#endif
1817
1818        return 0;
1819
1820}  // end fatfs_ctx_init()
1821
1822//////////////////////////////////////////////
1823void fatfs_ctx_destroy( xptr_t  fatfs_ctx_xp )
1824{
1825    mapper_t   * fat_mapper;
1826    uint8_t    * fs_info_buffer;
1827
1828    // get FATFS context cluster and local pointer
1829    fatfs_ctx_t * fatfs_ctx_ptr = GET_PTR( fatfs_ctx_xp );
1830    cxy_t         fatfs_ctx_cxy = GET_CXY( fatfs_ctx_xp );
1831
1832    // get pointer on FAT mapper
1833    fat_mapper = hal_remote_lpt( XPTR( fatfs_ctx_cxy , &fatfs_ctx_ptr->fat_mapper ) );
1834
1835    // release FAT mapper
1836    mapper_destroy( XPTR( fatfs_ctx_cxy , fat_mapper ) );
1837
1838    // get pointer on FS_INFO buffer
1839    fs_info_buffer = hal_remote_lpt( XPTR( fatfs_ctx_cxy , &fatfs_ctx_ptr->fs_info_buffer ) );
1840
1841    // release FS_INFO buffer (512 bytes)
1842    kmem_remote_free( fatfs_ctx_cxy,
1843                      fs_info_buffer,
1844                      9 );               
1845
1846    // release FATFS context descriptor
1847    kmem_remote_free( fatfs_ctx_cxy,
1848                      fatfs_ctx_ptr,
1849                      bits_log2(sizeof(fatfs_ctx_t)) );
1850
1851}  // end fatfs_ctx_destroy()
1852
1853/////////////////////////////////////////////////////////
1854error_t fatfs_add_dentry( xptr_t         parent_inode_xp,
1855                          vfs_dentry_t * dentry_ptr )
1856{
1857    error_t       error;
1858    vfs_inode_t * parent_inode_ptr;   // parent inode local pointer
1859    cxy_t         parent_cxy;         // pparent inode cluster
1860    xptr_t        child_inode_xp;     // extended pointer on child inode
1861    cxy_t         child_cxy;          // child inode cluster
1862    vfs_inode_t * child_inode_ptr;    // child inode local pointer
1863    uint32_t      length;             // dentry name length
1864    uint32_t      nb_lfn;             // number or required LFN
1865    char          sfn[11];            // buffer for SFN name
1866    uint8_t       checksum;           // name checksum
1867    mapper_t    * mapper_ptr;         // local pointer on parent directory mapper
1868    xptr_t        mapper_xp;          // extended pointer on parent directory mapper
1869    uint32_t      size;               // child inode size
1870    uint32_t      type;               // child inode type
1871    void        * extend;             // child inode extension
1872    uint32_t      cluster_id;         // child inode first cluster_id in FATFS
1873
1874    char          child_name[CONFIG_VFS_MAX_NAME_LENGTH];
1875
1876    uint8_t       buf[32];            // local buffer for one FAT32 directory entry
1877 
1878// check arguments
1879assert( __FUNCTION__, (parent_inode_xp != XPTR_NULL) , "parent_inode_xp argument is NULL\n" );
1880assert( __FUNCTION__, (dentry_ptr      != NULL)      , "dentry_ptr argument is NULL\n" );
1881
1882    // get directory inode cluster and local pointer
1883    parent_cxy       = GET_CXY( parent_inode_xp );
1884    parent_inode_ptr = GET_PTR( parent_inode_xp );
1885
1886    // get extended pointers on child inode
1887    child_inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
1888    child_cxy       = GET_CXY( child_inode_xp );
1889    child_inode_ptr = GET_PTR( child_inode_xp );
1890
1891    // get a local copy of the child name
1892    vfs_inode_get_name( child_inode_xp  , child_name );
1893
1894#if DEBUG_FATFS_ADD_DENTRY
1895uint32_t   cycle = (uint32_t)hal_get_cycles();
1896thread_t * this  = CURRENT_THREAD;
1897char       parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
1898vfs_inode_get_name( parent_inode_xp , parent_name );
1899if( DEBUG_FATFS_ADD_DENTRY < cycle )
1900printk("\n[%s]  thread[%x,%x] enter for <%s> in <%s> directory / cycle %d\n",
1901__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cycle );
1902#endif
1903
1904    // get pointers on parent directory mapper
1905    mapper_ptr = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) );
1906    mapper_xp  = XPTR( parent_cxy , mapper_ptr );
1907
1908#if (DEBUG_FATFS_ADD_DENTRY & 1)
1909mapper_display_page( mapper_xp , 0 , 512 );
1910#endif
1911
1912    // get relevant infos from child inode
1913    type       = hal_remote_l32( XPTR( child_cxy , &child_inode_ptr->type ) );
1914    size       = hal_remote_l32( XPTR( child_cxy , &child_inode_ptr->size ) );
1915    extend     = hal_remote_lpt( XPTR( child_cxy , &child_inode_ptr->extend ) );
1916    cluster_id = (uint32_t)(intptr_t)extend;
1917
1918    // analyse child name
1919    error = fatfs_name_format( child_name,
1920                               &length,
1921                               &nb_lfn,
1922                               sfn,
1923                               &checksum );
1924    if ( error )
1925    {
1926        printk("\n[ERROR] in %s : dentry name > 31 bytes\n", __FUNCTION__ );
1927        return -1;
1928    }
1929                               
1930    // Search end of directory with two embedded loops:
1931    // - scan the pages in the mapper
1932    // - scan the entries in each page to find NO_MORE_ENTRY
1933
1934    xptr_t     page_xp;                 // extended pointer on page descriptor
1935    xptr_t     base_xp;                 // extended pointer on page base
1936
1937    // initialise loop variables
1938    uint32_t   page_id = 0;             // page index in mapper
1939    uint32_t   offset  = 0;             // position in page
1940    uint32_t   found   = 0;             // NO_MORE_ENTRY found
1941
1942    // loop on pages in mapper
1943    while ( found == 0 )
1944    {
1945        // get extended pointer on page descriptor in mapper
1946        page_xp  = mapper_get_page( mapper_xp , page_id );
1947
1948        if ( page_xp == XPTR_NULL )
1949        {
1950            printk("\n[ERROR] in %s : cannot extend directory mapper\n",
1951            __FUNCTION__ );
1952            return -1;
1953        }
1954       
1955        // get pointer on page base
1956        base_xp = ppm_page2base( page_xp );
1957
1958        // loop on directory entries in this page
1959        while ( (offset < 4096) && (found == 0) )
1960        {
1961            if ( fatfs_get_remote_record( LDIR_ORD, (base_xp + offset) ) == NO_MORE_ENTRY )
1962            {
1963                found = 1;
1964            } 
1965            else
1966            {
1967                offset = offset + 32;
1968            }
1969        }  // end loop on entries
1970
1971        if ( found == 0 )
1972        {
1973            page_id++;
1974            offset = 0;
1975        }
1976    }  // end loop on pages
1977
1978#if (DEBUG_FATFS_ADD_DENTRY & 1)
1979if( DEBUG_FATFS_ADD_DENTRY < cycle )
1980printk("\n[%s]  thread[%x,%x] found NO_MORE_ENTRY : page_id %d / offset %d\n",
1981__FUNCTION__, this->process->pid, this->trdid, page_id, offset );
1982#endif
1983
1984    // Modify the directory mapper: depending on the name length,
1985    // the new child requires to write (3, 4, or 5) directory entries.
1986    // We build one complete directory entry in a local buffer
1987    // before copying it the remote mapper. We use use a 5 steps FSM
1988    // (one state per entry to be written), that is traversed as :
1989    // LFN3 -> LFN2 -> LFN1 -> NORMAL -> NOMORE
1990    // At most two pages are modified:
1991    // - the page containing the NO_MORE_ENTRY is always modified
1992    // - the following page can be modified if the name spread on two pages.
1993
1994    uint32_t step;          // FSM state
1995
1996    if      ( nb_lfn == 1 ) step = 3;
1997    else if ( nb_lfn == 2 ) step = 4;
1998    else if ( nb_lfn == 3 ) step = 5;
1999   
2000    uint32_t   i;           // byte index in one 32 bytes directory
2001    uint32_t   c;           // character index in name
2002
2003    while ( step )   
2004    {
2005        // this block is only executed when the new name spread
2006        // on two pages, and we need to access a new page in mapper
2007        if ( offset >= 4096 )
2008        {
2009            // copy the modified page to IOC device
2010            error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
2011
2012            if ( error )
2013            {
2014                printk("\n[ERROR] in %s : cannot update directory on device\n",
2015                __FUNCTION__ );
2016                return -1;
2017            }
2018
2019            // get the next page in directory mapper
2020            page_xp  = mapper_get_page( mapper_xp , page_id + 1 );
2021
2022            if ( page_xp == XPTR_NULL )
2023            {
2024                printk("\n[ERROR] in %s : cannot extend directory mapper\n",
2025                __FUNCTION__ );
2026                return -1;
2027            }
2028       
2029            // get pointer on page base
2030            base_xp = ppm_page2base( page_xp );
2031           
2032            // update offset
2033            offset = 0;
2034        }
2035
2036#if (DEBUG_FATFS_ADD_DENTRY & 1)
2037cycle = (uint32_t)hal_get_cycles();
2038if( DEBUG_FATFS_ADD_DENTRY < cycle )
2039printk("\n[%s] FSM step = %d / offset = %x / nb_lfn = %d / cycle %d\n",
2040__FUNCTION__, step, offset, nb_lfn, cycle );
2041#endif
2042
2043        // write one FATFS directory entry (32 bytes) per iteration
2044        switch ( step )
2045        {
2046            case 5:   // write LFN3 entry
2047            {
2048                c = 26;
2049
2050                // write 32 bytes in local buf
2051                for ( i = 0 ; i < 32 ; i++ )
2052                {
2053                    if (i == 0)
2054                    {
2055                        if ( nb_lfn == 3) buf[i] = 0x43;
2056                        else              buf[i] = 0x03;
2057                    }
2058                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
2059                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
2060                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
2061                              ( c < length ) )
2062                    {
2063                                          buf[i] = child_name[c];
2064                                          c++;
2065                    }
2066                    else if (i == 11)     buf[i] = 0x0F;
2067                    else if (i == 13)     buf[i] = checksum;
2068                    else                  buf[i] = 0x00;
2069                }
2070
2071                // copy 32 bytes from local buffer to remote mapper
2072                hal_remote_memcpy( base_xp + offset , XPTR( local_cxy  , buf ) , 32 );
2073
2074                step--;
2075                break;
2076            }
2077            case 4:   // write LFN2 entry 
2078            {
2079                c = 13;
2080
2081                // write 32 bytes in local buf
2082                for ( i = 0 ; i < 32 ; i++ )
2083                {
2084                    if (i == 0)
2085                    {
2086                        if ( nb_lfn == 2) buf[i] = 0x42;
2087                        else              buf[i] = 0x02;
2088                    }
2089                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
2090                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
2091                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
2092                              ( c < length ) )
2093                    {
2094                                          buf[i] = child_name[c];
2095                                          c++;
2096                    }
2097                    else if (i == 11)     buf[i] = 0x0F;
2098                    else if (i == 13)     buf[i] = checksum;
2099                    else                  buf[i] = 0x00;
2100                }
2101
2102                // copy 32 bytes from local buffer to remote mapper
2103                hal_remote_memcpy( base_xp + offset , XPTR( local_cxy  , buf ) , 32 );
2104
2105                step--;
2106                break;
2107            }
2108            case 3:   // Write LFN1 entry   
2109            {
2110                c = 0;
2111
2112                // write 32 bytes in local buf
2113                for ( i = 0 ; i < 32 ; i++ )
2114                {
2115                    if (i == 0)
2116                    {
2117                        if ( nb_lfn == 1) buf[i] = 0x41;
2118                        else              buf[i] = 0x01;
2119                    }
2120                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
2121                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
2122                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
2123                              ( c < length ) )
2124                    {
2125                                          buf[i] = child_name[c];
2126                                          c++;
2127                    }
2128                    else if (i == 11)     buf[i] = 0x0F;
2129                    else if (i == 13)     buf[i] = checksum;
2130                    else                  buf[i] = 0x00;
2131                }
2132
2133                // copy 32 bytes from local buffer to remote mapper
2134                hal_remote_memcpy( base_xp + offset , XPTR( local_cxy  , buf ) , 32 );
2135
2136                step--;
2137                break;
2138            }
2139            case 2:   // write NORMAL entry     
2140            {
2141                // write 32 bytes in local buf
2142                for ( i = 0 ; i < 32 ; i++ )
2143                {
2144                    if      ( i < 11 )                               // 8.3 SFN
2145                    {
2146                                          buf[i] = sfn[i];
2147                    }
2148                    else if (i == 11)                                // ATTR
2149                    {
2150                        if (type == FILE_TYPE_DIR)  buf[i] = 0x10;
2151                        else                         buf[i] = 0x20;
2152                    }
2153                    else if (i == 20)     buf[i] = cluster_id>>16;   // cluster.B2
2154                    else if (i == 21)     buf[i] = cluster_id>>24;   // cluster.B3
2155                    else if (i == 26)     buf[i] = cluster_id>>0;    // cluster.B0
2156                    else if (i == 27)     buf[i] = cluster_id>>8;    // cluster.B1
2157                    else if (i == 28)     buf[i] = size>>0;          // size.B0
2158                    else if (i == 29)     buf[i] = size>>8;          // size.B1
2159                    else if (i == 30)     buf[i] = size>>16;         // size.B2
2160                    else if (i == 31)     buf[i] = size>>24;         // size.B3
2161                    else                  buf[i] = 0x00;
2162                }
2163
2164                // copy 32 bytes from local buffer to remote mapper
2165                hal_remote_memcpy( base_xp + offset , XPTR( local_cxy  , buf ) , 32 );
2166
2167                // set the dentry "extend" field
2168                hal_remote_spt( XPTR( parent_cxy , &dentry_ptr->extend ),
2169                                (void *)(intptr_t)(((page_id << 12) + offset) >> 5 ) );
2170                step--;
2171                break;
2172            }
2173            case 1:   // write NOMORE entry 
2174            {
2175                hal_remote_s32( base_xp + offset , 0 );
2176
2177                step--;
2178                break;
2179            }
2180
2181        } // end switch step
2182
2183        offset += 32;
2184
2185    } // end while     
2186
2187    // copy the modified page to the IOC device
2188    error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
2189
2190    if ( error )
2191    {
2192        printk("\n[ERROR] in %s : cannot update directory on device\n",
2193        __FUNCTION__ );
2194        return -1;
2195    }
2196
2197#if DEBUG_FATFS_ADD_DENTRY
2198cycle = (uint32_t)hal_get_cycles();
2199if( DEBUG_FATFS_ADD_DENTRY < cycle )
2200printk("\n[%s]  thread[%x,%x] exit for <%s> in <%s> directory\n",
2201__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name );
2202#endif
2203
2204#if (DEBUG_FATFS_ADD_DENTRY & 1)
2205mapper_display_page( mapper_xp , 0 , 512 );
2206#endif
2207
2208    return 0;
2209
2210}  // end fatfs_add_dentry()
2211
2212////////////////////////////////////////////////////////////
2213error_t fatfs_remove_dentry( xptr_t         parent_inode_xp,
2214                             vfs_dentry_t * dentry_ptr )
2215{
2216    error_t       error;
2217    vfs_inode_t * parent_inode_ptr;   // parent inode local pointer
2218    cxy_t         parent_cxy;         // pparent inode cluster
2219    xptr_t        child_inode_xp;     // extended pointer on child inode
2220    cxy_t         child_cxy;          // child inode cluster
2221    vfs_inode_t * child_inode_ptr;    // child inode local pointer
2222    xptr_t        mapper_xp;          // extended pointer on mapper
2223    mapper_t    * mapper_ptr;         // local pointer on mapper
2224    xptr_t        page_xp;            // extended pointer on mapper page descriptor
2225    xptr_t        base_xp;            // extended pointer on mapper page base
2226    uint32_t      dentry_id;          // FAT32 directory entry index
2227    uint32_t      page_id;            // page index in directory mapper
2228    uint32_t      offset;             // offset in this mapper page
2229
2230    char          child_name[CONFIG_VFS_MAX_NAME_LENGTH];
2231
2232// check arguments
2233assert( __FUNCTION__, (parent_inode_xp != XPTR_NULL) , "parent_inode_xp argument is NULL\n" );
2234assert( __FUNCTION__, (dentry_ptr      != NULL)      , "dentry_ptr argument is NULL\n" );
2235
2236    // get directory inode cluster and local pointer
2237    parent_cxy       = GET_CXY( parent_inode_xp );
2238    parent_inode_ptr = GET_PTR( parent_inode_xp );
2239
2240    // get extended pointers on child inode
2241    child_inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
2242    child_cxy       = GET_CXY( child_inode_xp );
2243    child_inode_ptr = GET_PTR( child_inode_xp );
2244
2245    // get a local copy of the child name
2246    vfs_inode_get_name( child_inode_xp  , child_name );
2247
2248#if DEBUG_FATFS_REMOVE_DENTRY
2249uint32_t   cycle = (uint32_t)hal_get_cycles();
2250thread_t * this  = CURRENT_THREAD;
2251char       parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
2252vfs_inode_get_name( parent_inode_xp , parent_name );
2253if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
2254printk("\n[%s]  thread[%x,%x] enter for <%s> in <%s> directory / cycle %d\n",
2255__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cycle );
2256#endif
2257
2258    // get pointers on directory mapper
2259    mapper_ptr = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) );
2260    mapper_xp  = XPTR( parent_cxy , mapper_ptr );
2261
2262    // compute number of LFN entries
2263    uint32_t nb_lfn;
2264    uint32_t name_length = strlen( child_name );
2265
2266    if      ( name_length <= 13 ) nb_lfn  = 1;
2267    else if ( name_length <= 26 ) nb_lfn  = 2;
2268    else                          nb_lfn  = 3;
2269
2270    // We must invalidate (2,3,4) 32 bytes entries:
2271    // - the NORMAL entry, registered in dentry->extend.
2272    // - the (1,2,3) preceding LFN entries.
2273    // At most two pages are modified:
2274    // - the page containing the NORMAL entry is always modified.
2275    // - the preceding page is modified when the name spread on two pages.
2276
2277    // get NORMAL entry index from dentry extension
2278    dentry_id  = (uint32_t)(intptr_t)hal_remote_lpt( XPTR( parent_cxy , &dentry_ptr->extend ) ); 
2279
2280    // get page index and offset in parent directory mapper
2281    page_id  = dentry_id >> 7;
2282    offset   = (dentry_id & 0x7F)<<5;
2283
2284#if DEBUG_FATFS_REMOVE_DENTRY & 1
2285if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
2286printk("\n[%s] dentry_id %x / page_id %x / offset %x\n",
2287__FUNCTION__, dentry_id, page_id, offset );
2288#endif
2289
2290    // get extended pointer on page descriptor
2291    page_xp  = mapper_get_page( mapper_xp , page_id );
2292
2293    if ( page_xp == XPTR_NULL )
2294    {
2295        printk("\n[ERROR] in %s : cannot access directory mapper\n", __FUNCTION__ );
2296        return -1;
2297    }
2298       
2299    // get extended pointer on page base
2300    base_xp = ppm_page2base( page_xp );
2301
2302    // invalidate NORMAL entry in directory cache
2303    hal_remote_sb( base_xp + offset , 0xE5 );
2304
2305    // invalidate LFN entries
2306    while ( nb_lfn )
2307    {
2308        // this block is only executed when the removed name
2309        // spread on two mapper pages
2310        if (offset == 0)  // we must load page (page_id - 1)
2311        {
2312            // copy the modified page to the IOC device
2313            error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
2314
2315            if ( error )
2316            {
2317                printk("\n[ERROR] in %s : cannot update directory on device\n",
2318                __FUNCTION__ );
2319                return -1;
2320            }
2321
2322            // get extended pointer on page descriptor
2323            page_xp  = mapper_get_page( mapper_xp , page_id );
2324
2325            if ( page_xp == XPTR_NULL )
2326            {
2327                printk("\n[ERROR] in %s : cannot access directory mapper\n", __FUNCTION__ );
2328                return -1;
2329            }
2330       
2331            // get extended pointer on page base
2332            base_xp = ppm_page2base( page_xp );
2333
2334            // update offset
2335            offset = 4096;
2336        }
2337
2338        offset = offset - 32;
2339
2340// check for LFN entry
2341assert( __FUNCTION__, (fatfs_get_remote_record( DIR_ATTR, base_xp + offset ) == ATTR_LONG_NAME_MASK ),
2342"this directory entry must be a LFN\n");
2343
2344        // invalidate LFN entry
2345        hal_remote_sb( base_xp + offset , 0xE5 );
2346
2347        nb_lfn--;
2348    }     
2349
2350    // copy the modified page to the IOC device
2351    error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
2352   
2353    if ( error )
2354    {
2355        printk("\n[ERROR] in %s : cannot update directory on device\n",
2356        __FUNCTION__ );
2357        return -1;
2358    }
2359
2360#if DEBUG_FATFS_REMOVE_DENTRY
2361cycle = (uint32_t)hal_get_cycles();
2362if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
2363printk("\n[%s] thread[%x,%x] exit for <%s> in <%s> directory\n",
2364__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name );
2365#endif
2366
2367    return 0;
2368
2369}  // end fatfs_remove_dentry
2370
2371/////////////////////////////////////////////////////////////////////
2372error_t fatfs_new_dentry_from_mapper( xptr_t         parent_inode_xp,
2373                                      vfs_dentry_t * dentry_ptr )
2374{
2375    uint32_t       cluster_id;        // directory entry first FATFS cluster
2376    uint32_t       size;              // directory entry size
2377    bool_t         is_dir;            // directory entry type (file/dir)
2378    cxy_t          parent_cxy;        // parent inode cluster 
2379    vfs_inode_t  * parent_inode_ptr;  // parent inode local pointer
2380    mapper_t     * parent_mapper;     // pointer on parent directory mapper
2381    xptr_t         child_inode_xp;    // extended pointer on child inode
2382    cxy_t          child_cxy;         // child inode cluster 
2383    vfs_inode_t  * child_inode_ptr;   // child inode local pointer
2384    error_t        error;
2385
2386    uint8_t        buf[32];           // FAT32 directory entry local copy
2387
2388    char           parent_name[CONFIG_VFS_MAX_NAME_LENGTH];     // local parent name copy
2389    char           child_name[CONFIG_VFS_MAX_NAME_LENGTH];      // local child name copy
2390
2391// check arguments
2392assert( __FUNCTION__, (parent_inode_xp != XPTR_NULL)  , "parent_inode_xp is NULL\n" );
2393assert( __FUNCTION__, (dentry_ptr      != NULL ) , "dentry_ptr is NULL\n" );
2394
2395    // get parent inode cluster and local pointer
2396    parent_cxy       = GET_CXY( parent_inode_xp );
2397    parent_inode_ptr = GET_PTR( parent_inode_xp );
2398
2399    // get child inode cluster and pointers
2400    child_inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
2401    child_cxy       = GET_CXY( child_inode_xp );
2402    child_inode_ptr = GET_PTR( child_inode_xp );
2403
2404    // get child and parent names
2405    vfs_inode_get_name( parent_inode_xp , parent_name );
2406    vfs_inode_get_name( child_inode_xp  , child_name );
2407
2408#if DEBUG_FATFS_NEW_DENTRY_FROM
2409uint32_t   cycle = (uint32_t)hal_get_cycles();
2410thread_t * this  = CURRENT_THREAD;
2411if( DEBUG_FATFS_NEW_DENTRY_FROM < cycle )
2412printk("\n[%s]  thread[%x,%x] enter for child <%s> in parent <%s> / cycle %d\n",
2413__FUNCTION__, this->process->pid, this->trdid, child_name , parent_name , cycle );
2414#endif
2415
2416    // get local pointer on parent mapper
2417    parent_mapper = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) );
2418
2419    // try to get pointer and index of directory entry in mapper
2420    uint8_t      * entry = NULL;
2421    uint32_t       index = 0;
2422
2423    error  = fatfs_scan_directory( XPTR( parent_cxy , parent_mapper ),
2424                                   child_name, 
2425                                   &entry,
2426                                   &index );
2427   
2428    // an error can be non fatal, for a new (created) entry
2429    if( error )  return -1;
2430
2431    // get local copy of found directory entry
2432    hal_remote_memcpy( XPTR( local_cxy  , buf ),
2433                       XPTR( parent_cxy , entry ), 32 );
2434
2435    // get relevant infos from directory entry
2436    cluster_id = (fatfs_get_record( DIR_FST_CLUS_HI , buf ) << 16) |
2437                 (fatfs_get_record( DIR_FST_CLUS_LO , buf )      ) ;
2438    is_dir     = (fatfs_get_record( DIR_ATTR        , buf ) & ATTR_DIRECTORY );
2439    size       =  fatfs_get_record( DIR_FILE_SIZE   , buf );
2440
2441    // update the child inode "type", "size", and "extend" fields
2442    vfs_file_type_t type = (is_dir) ? FILE_TYPE_DIR : FILE_TYPE_REG;
2443
2444    hal_remote_s32( XPTR( child_cxy , &child_inode_ptr->type   ) , type );
2445    hal_remote_s32( XPTR( child_cxy , &child_inode_ptr->size   ) , size );
2446    hal_remote_s32( XPTR( child_cxy , &child_inode_ptr->extend ) , cluster_id );
2447
2448    // update the dentry "extend" field
2449    hal_remote_spt( XPTR( parent_cxy , &dentry_ptr->extend ) , (void *)(intptr_t)index );
2450
2451#if DEBUG_FATFS_NEW_DENTRY_FROM
2452cycle = (uint32_t)hal_get_cycles();
2453if( DEBUG_FATFS_NEW_DENTRY_FROM < cycle )
2454printk("\n[%s]  thread[%x,%x] exit for <%s> in <%s> / cluster_id %x / size %d\n",
2455__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cluster_id, size );
2456#endif
2457
2458#if (DEBUG_FATFS_NEW_DENTRY_FROM & 1)
2459if( DEBUG_FATFS_NEW_DENTRY_FROM < cycle )
2460fatfs_display_fat( cluster_id , 32 );
2461#endif
2462
2463    return 0;
2464
2465}  // end fatfs_new_dentry_from_mapper()
2466
2467///////////////////////////////////////////////////////////////////
2468error_t fatfs_new_dentry_to_mapper( xptr_t         parent_inode_xp,
2469                                    vfs_dentry_t * dentry_ptr )
2470{
2471    uint32_t       cluster_id;        // directory entry cluster
2472    cxy_t          parent_cxy;        // parent inode cluster identifier 
2473    vfs_inode_t  * parent_inode_ptr;  // child inode local pointer
2474    xptr_t         child_inode_xp;    // extended pointer on child inode
2475    cxy_t          child_cxy;         // child inode cluster identifier 
2476    vfs_inode_t  * child_inode_ptr;   // child inode local pointer
2477    error_t        error;
2478
2479    char           parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
2480    char           child_name[CONFIG_VFS_MAX_NAME_LENGTH];
2481
2482// check arguments
2483assert( __FUNCTION__, (parent_inode_xp != XPTR_NULL) , "parent_inode_xp argument is NULL\n" );
2484assert( __FUNCTION__, (dentry_ptr      != NULL)      , "dentry_ptr argument NULL\n" );
2485
2486    // get child inode cluster and local pointer
2487    parent_cxy       = GET_CXY( parent_inode_xp );
2488    parent_inode_ptr = GET_PTR( parent_inode_xp );
2489
2490    // get child inode cluster and pointers
2491    child_inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
2492    child_cxy       = GET_CXY( child_inode_xp );
2493    child_inode_ptr = GET_PTR( child_inode_xp );
2494
2495    // get child and parent names
2496    vfs_inode_get_name( parent_inode_xp , parent_name );
2497    vfs_inode_get_name( child_inode_xp  , child_name );
2498
2499#if DEBUG_FATFS_NEW_DENTRY_TO_MAP
2500uint32_t   cycle = (uint32_t)hal_get_cycles();
2501thread_t * this  = CURRENT_THREAD;
2502if( DEBUG_FATFS_NEW_DENTRY_TO_MAP < cycle )
2503printk("\n[%s]  thread[%x,%x] enter for child <%s> in parent <%s> / cycle %d\n",
2504__FUNCTION__, this->process->pid, this->trdid, child_name , parent_name , cycle );
2505#endif
2506
2507    // 1. allocate one FATFS cluster (update FAT and FSINFO)
2508    error = fatfs_cluster_alloc( 0 , &cluster_id );
2509
2510    if( error )
2511    {
2512        printk("\n[ERROR] in %s : cannot find a free cluster_id\n",
2513        __FUNCTION__ );
2514        return -1;
2515    }
2516       
2517    // 2. register cluster_id in inode descriptor
2518    hal_remote_spt( XPTR( child_cxy , &child_inode_ptr->extend ),
2519                    (void*)(intptr_t)cluster_id );
2520   
2521    // 3. introduce dentry in the directory mapper
2522    error = fatfs_add_dentry( parent_inode_xp , dentry_ptr );
2523       
2524    if( error )
2525    {
2526        printk("\n[ERROR] in %s : cannot update parent directory mapper\n",
2527        __FUNCTION__ );
2528        // TODO release cluster_id [AG]
2529        return -1;
2530    }
2531
2532#if DEBUG_FATFS_NEW_DENTRY_TO_MAP
2533if( DEBUG_FATFS_NEW_DENTRY_TO_MAP < cycle )
2534printk("\n[%s]  thread[%x,%x] exit for <%s> in <%s> / cluster_id %x\n",
2535__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cluster_id );
2536#endif
2537
2538    return 0;
2539
2540}  // end fatfs_new_dentry_to mapper()
2541
2542
2543////////////////////////////////////////////////////////////
2544error_t fatfs_update_dentry( xptr_t         parent_inode_xp,
2545                             vfs_dentry_t * dentry_ptr )
2546{
2547    cxy_t         parent_cxy;        // parent directory cluster identifier
2548    vfs_inode_t * parent_inode_ptr;  // extended pointer on parent directory inode
2549    mapper_t    * parent_mapper_ptr; // local pointer on parent directory mapper
2550    xptr_t        parent_mapper_xp;  // extended pointer on parent directory mapper
2551    xptr_t        child_inode_xp;    // extended pointer on child inode
2552    cxy_t         child_cxy;         // child inode cluster identifier
2553    vfs_inode_t * child_inode_ptr;   // extended pointer on child inode
2554
2555    uint32_t      current_size;      // current size in directory mapper
2556    uint32_t      new_size;          // new size (from child inode)
2557   
2558    uint32_t      entry_id;          // directory entry index in parent directory mapper
2559    uint32_t      page_id;           // page_index in parent directory mapper
2560    uint32_t      offset;            // directory entry offset in page
2561    xptr_t        page_xp;           // extended pointer on page descriptor
2562    xptr_t        base_xp;           // extended pointer on page base
2563    xptr_t        entry_xp;          // extended pointer on directory entry
2564
2565    error_t       error;
2566
2567    char       parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
2568    char       child_name[CONFIG_VFS_MAX_NAME_LENGTH];
2569
2570// check arguments
2571assert( __FUNCTION__, (parent_inode_xp  != XPTR_NULL) , "parent_inode_xp argument is NULL\n" );
2572assert( __FUNCTION__, (dentry_ptr       != NULL)      , "dentry_ptr argument is NULL\n" );
2573
2574    // get parent directory cluster ans local pointer
2575    parent_inode_ptr = GET_PTR( parent_inode_xp );
2576    parent_cxy       = GET_CXY( parent_inode_xp );
2577
2578    // get extended pointer on child inode
2579    child_inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
2580
2581    // get child and parent names
2582    vfs_inode_get_name( parent_inode_xp , parent_name );
2583    vfs_inode_get_name( child_inode_xp  , child_name );
2584
2585#if DEBUG_FATFS_UPDATE_DENTRY
2586uint32_t   cycle = (uint32_t)hal_get_cycles();
2587thread_t * this  = CURRENT_THREAD;
2588if( DEBUG_FATFS_UPDATE_DENTRY < cycle )
2589printk("\n[%s]  thread[%x,%x] enter for <%s> in <%s> / new_size %d / cycle %d\n",
2590__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, new_size, cycle );
2591#endif
2592
2593    // get child inode cluster and local pointer
2594    child_cxy       = GET_CXY( child_inode_xp );
2595    child_inode_ptr = GET_PTR( child_inode_xp );
2596 
2597    // get size from child inode
2598    new_size = hal_remote_l32( XPTR( child_cxy , &child_inode_ptr->size ) );
2599 
2600    // get local and extended pointers on parent directory mapper
2601    parent_mapper_ptr = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) );
2602    parent_mapper_xp  = XPTR( parent_cxy , parent_mapper_ptr );
2603
2604    // get directory entry index from dentry extension
2605    entry_id = (intptr_t)hal_remote_lpt( XPTR( parent_cxy , &dentry_ptr->extend ) );
2606
2607    // get page index and offset in parent directory mapper
2608    page_id  = entry_id >> 7;
2609    offset   = (entry_id & 0x7F) << 5;
2610
2611    // get extended pointers on page descriptor and page base
2612    page_xp = mapper_get_page( parent_mapper_xp , page_id );     
2613    base_xp = ppm_page2base( page_xp ); 
2614
2615    // build extended pointer on directory entry
2616    entry_xp = base_xp + offset;
2617
2618    // get current size from directory mapper
2619    current_size = fatfs_get_remote_record( DIR_FILE_SIZE , entry_xp );
2620
2621    // update dentry in mapper & device only if required
2622    if( new_size != current_size )
2623    {
2624        // set size field in FAT32 directory entry
2625        fatfs_set_remote_record( DIR_FILE_SIZE , entry_xp , new_size );
2626
2627        // synchronously update the modified mapper page on device
2628        error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );
2629
2630        if( error )
2631        { 
2632            printk("\n[ERROR] in %s : cannot update parent directory <%s> on device\n",
2633            __FUNCTION__, parent_name );
2634            return -1;
2635        }
2636    }
2637
2638#if DEBUG_FATFS_UPDATE_DENTRY
2639cycle = (uint32_t)hal_get_cycles();
2640if( DEBUG_FATFS_UPDATE_DENTRY < cycle )
2641printk("\n[%s]  thread[%x,%x] exit for <%s> in <%s> directory / size %d / cycle %d\n",
2642__FUNCTION__, this->process->pid, this->trdid, parent_name, child->name, new_size, cycle );
2643#endif
2644
2645    return 0;
2646
2647}  // end fatfs_update_dentry()
2648
2649///////////////////////////////////////////////////////
2650error_t fatfs_get_user_dir( struct vfs_inode_s * inode,
2651                            struct dirent      * array, 
2652                            uint32_t             max_dirent,
2653                            uint32_t             min_dentry,
2654                            bool_t               detailed,
2655                            uint32_t           * entries,
2656                            bool_t             * done )
2657{
2658    // Two embedded loops to scan the directory mapper:
2659    // - scan the parent directory mapper pages starting always from page 0
2660    // - scan the 32 bytes NORMAL/LFN directory entries in each page
2661    // Only valid dentries are copied : dentry_id >= min_dentry && dirent_id < dirent_max
2662
2663#if DEBUG_FATFS_GET_USER_DIR
2664char       inode_name[CONFIG_VFS_MAX_NAME_LENGTH];
2665uint32_t   cycle = (uint32_t)hal_get_cycles();
2666thread_t * this  = CURRENT_THREAD;
2667vfs_inode_get_name( XPTR( local_cxy , inode ) , inode_name );
2668if( DEBUG_FATFS_GET_USER_DIR < cycle )
2669printk("\n[%s]  thread[%x,%x] enter for inode <%s> / cycle %d\n",
2670__FUNCTION__, this->process->pid, this->trdid, inode_name , cycle );
2671#endif
2672
2673    mapper_t * mapper    = inode->mapper;
2674    xptr_t     mapper_xp = XPTR( local_cxy , mapper );
2675
2676// check mapper pointer
2677assert( __FUNCTION__, (mapper != NULL) , "mapper is NULL\n");
2678   
2679// TODO handle the detailed flag
2680assert( __FUNCTION__, (detailed == false), "detailed argument not supported/n");
2681
2682    char       cname[CONFIG_VFS_MAX_NAME_LENGTH];  // name extracted from each dentry
2683
2684    char       lfn1[16];           // buffer for one partial cname
2685    char       lfn2[16];           // buffer for one partial cname
2686    char       lfn3[16];           // buffer for one partial cname
2687    xptr_t     page_xp;            // extended pointer on page descriptor
2688    xptr_t     base_xp;            // extended pointer on page base
2689    uint8_t  * base;               // local pointer on page base
2690    uint8_t    attr;               // directory entry ATTR field
2691    uint8_t    ord;                // directory entry ORD field
2692    uint32_t   seq;                // sequence index
2693    uint32_t   lfn       = 0;      // LFN entries number
2694    uint32_t   offset    = 0;      // byte offset in page
2695    uint32_t   page_id   = 0;      // page index in mapper
2696    uint32_t   dentry_id = 0;      // valid (i.e. copied) dentry index in mapper
2697    uint32_t   dirent_id = 0;      // slot index in dirent array to initialize
2698    bool_t     end       = false;  // true if end of directory found
2699
2700    // loop on mapper pages
2701    while ( (end == false) && (dirent_id < max_dirent) )
2702    {
2703        // get one page from mapper
2704        page_xp = mapper_get_page( mapper_xp , page_id );
2705
2706        if( page_xp == XPTR_NULL) return -1;
2707
2708        // get page base
2709        base_xp = ppm_page2base( page_xp );
2710        base    = (uint8_t *)GET_PTR( base_xp );
2711
2712#if (DEBUG_FATFS_GET_USER_DIR & 0x1)
2713if( DEBUG_FATFS_GET_USER_DIR < cycle )
2714mapper_display_page( mapper_xp , page_id , 256 );
2715#endif
2716        // loop on NORMAL/LFN (32 bytes) directory entries in this page
2717        while( (end == false) && (offset < 4096) )
2718        {
2719            // compute condition to copy one dentry to dirent array
2720            bool_t valid = (dentry_id >= min_dentry) && (dirent_id <  max_dirent );
2721
2722            attr = fatfs_get_record( DIR_ATTR , base + offset );   
2723            ord  = fatfs_get_record( LDIR_ORD , base + offset );   
2724
2725            if (ord == NO_MORE_ENTRY)                 // no more entry => break
2726            {
2727                end = true;
2728            }
2729            else if ( ord == FREE_ENTRY )             // free entry => skip
2730            {
2731                offset = offset + 32;
2732            }
2733            else if ( attr == 0x28 )                  // volune_id => skip
2734            {
2735                offset = offset + 32;
2736            }
2737            else if ( attr == ATTR_LONG_NAME_MASK )   // LFN entry
2738            {
2739                if( valid )
2740                {
2741                    // get partial cname
2742                    seq = ord & 0x3;
2743                    lfn = (seq > lfn) ? seq : lfn;   
2744                    if      ( seq == 1 ) fatfs_get_name_from_long( base + offset, lfn1 );
2745                    else if ( seq == 2 ) fatfs_get_name_from_long( base + offset, lfn2 );
2746                    else if ( seq == 3 ) fatfs_get_name_from_long( base + offset, lfn3 );
2747                }
2748                offset = offset + 32;
2749            }
2750            else                                     // NORMAL entry
2751            {
2752                // increment dentry_id
2753                dentry_id++;
2754
2755                if( valid )
2756                {
2757                    // build the complete cname
2758                    if      ( lfn == 0 )
2759                    {
2760                        fatfs_get_name_from_short( base + offset , cname );
2761                    }
2762                    else if ( lfn == 1 )
2763                    {
2764                        strcpy( cname      , lfn1 );
2765                    }   
2766                    else if ( lfn == 2 ) 
2767                    {
2768                        strcpy( cname      , lfn1 );
2769                        strcpy( cname + 13 , lfn2 );
2770                    }
2771                    else if ( lfn == 3 ) 
2772                    {
2773                        strcpy( cname      , lfn1 );
2774                        strcpy( cname + 13 , lfn2 );
2775                        strcpy( cname + 26 , lfn3 );
2776                    }
2777                   
2778                    // copy cname into dirent array
2779                    strcpy( array[dirent_id].d_name , cname ); 
2780
2781                    // increment dirent_id
2782                    dirent_id++;
2783                }
2784                offset = offset + 32;
2785                lfn    = 0;
2786            }
2787        }  // end loop on directory entries in page
2788
2789        page_id++;
2790        offset = 0;
2791
2792    }  // end loop on pages
2793
2794    // return result of scan
2795    *done    = end;
2796    *entries = dirent_id;
2797
2798#if DEBUG_FATFS_GET_USER_DIR
2799cycle = (uint32_t)hal_get_cycles();
2800if( DEBUG_FATFS_GET_USER_DIR < cycle )
2801printk("\n[%s]  thread[%x,%x] exit for inode <%s> / %d entries / cycle %d\n",
2802__FUNCTION__, this->process->pid, this->trdid, inode_name, dirent_id, cycle );
2803#endif
2804
2805    return 0;
2806
2807}  // end fatfs_get_user_dir()
2808
2809///////////////////////////////////////////
2810error_t fatfs_sync_inode( xptr_t inode_xp )
2811{
2812    cxy_t         inode_cxy;      // remote inode cluster
2813    vfs_inode_t * inode_ptr;      // remote inode local pointer
2814    mapper_t    * mapper;         // remote inode mapper local pointer
2815    uint32_t      size;           // remote inode size in bytes
2816    uint32_t      type;           // remote inode type
2817    xptr_t        rt_xp;          // extended pointer on mapper radix tree
2818    uint32_t      npages;         // number of pages in mapper
2819    uint32_t      page_id;        // current page index in mapper
2820    xptr_t        page_xp;        // extended pointer on current page
2821    page_t      * page_ptr;       // local pointer on current page
2822    uint32_t      flags;          // current page flags
2823    error_t       error;
2824
2825// check inode pointer and cluster index
2826assert( __FUNCTION__, (inode_xp != XPTR_NULL)          , "inode pointer undefined\n" );
2827
2828#if DEBUG_FATFS_SYNC_INODE
2829char       name[CONFIG_VFS_MAX_NAME_LENGTH];
2830uint32_t   cycle = (uint32_t)hal_get_cycles();
2831thread_t * this  = CURRENT_THREAD;
2832vfs_inode_get_name( inode_xp , name );
2833if( DEBUG_FATFS_SYNC_INODE < cycle )
2834printk("\n[%s] thread[%x,%x] enter for <%s> / cycle %d\n",
2835__FUNCTION__ , this->process->pid, this->trdid, name, cycle );
2836#endif
2837
2838    // get inode cluster and local pointer
2839    inode_cxy = GET_CXY( inode_xp );
2840    inode_ptr = GET_PTR( inode_xp );
2841
2842    //get inode mapper pointer
2843    mapper    = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );
2844
2845assert( __FUNCTION__, (mapper != NULL) , "mapper pointer is NULL\n" );     
2846
2847    // get inode type and size
2848    size = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->size ) );
2849    type = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type ) );
2850
2851assert( __FUNCTION__, (type == FILE_TYPE_REG) , "inode is not a file\n" );     
2852
2853    // compute number of pages
2854    npages = size >> CONFIG_PPM_PAGE_ORDER;
2855    if( size & CONFIG_PPM_PAGE_MASK ) npages++; 
2856         
2857    // build pointers on mapper radix tree
2858    rt_xp = XPTR( inode_cxy , &mapper->rt );
2859
2860    // scan all pages
2861    for( page_id = 0 ; page_id < npages ; page_id++ )
2862    {
2863        // get page descriptor from mapper
2864        page_xp = grdxt_remote_lookup( rt_xp , page_id );
2865
2866        // check all existing pages
2867        if ( page_xp != XPTR_NULL )
2868        {
2869            // get page cluster and local pointer
2870            page_ptr = GET_PTR( page_xp );
2871
2872            // get page flags
2873            flags = hal_remote_l32( XPTR( inode_cxy , &page_ptr->flags ) );
2874   
2875            if ( flags & PG_DIRTY )
2876            {
2877
2878#if (DEBUG_FATFS_SYNC_INODE & 1)
2879if( DEBUG_FATFS_SYNC_INODE < cycle )
2880printk("\n[%s] thread[%x,%x] synchronizes page %d of <%s> mapper to IOC device\n",
2881__FUNCTION__, page_id, name );
2882#endif
2883                // move page from mapper to device
2884                error = fatfs_move_page( page_xp , IOC_WRITE );
2885
2886                if ( error )  return -1;
2887
2888                // reset page dirty flag
2889                ppm_page_undo_dirty( page_xp );
2890            }
2891        }
2892    }  // end loop on pages
2893
2894#if DEBUG_FATFS_SYNC_INODE
2895cycle = (uint32_t)hal_get_cycles();
2896if( DEBUG_FATFS_SYNC_INODE < cycle )
2897printk("\n[%s] thread[%x,%x] exit for <%s>\n",
2898__FUNCTION__ , this->process->pid, this->trdid, name );
2899#endif
2900
2901    return 0;
2902
2903}  // end fatfs_sync_inode()
2904
2905//////////////////////////////
2906error_t fatfs_sync_fat( void )
2907{
2908
2909    fatfs_ctx_t * fatfs_ctx; 
2910    cxy_t         fat_cxy;
2911    mapper_t    * mapper_ptr;
2912    xptr_t        mapper_xp;
2913    uint32_t      start_page_id;
2914    uint32_t      found_page_id;
2915    page_t      * page_ptr;
2916    xptr_t        page_xp;
2917    uint32_t      flags;
2918    error_t       error;
2919
2920#if DEBUG_FATFS_SYNC_FAT
2921uint32_t   cycle = (uint32_t)hal_get_cycles();
2922thread_t * this  = CURRENT_THREAD;
2923if( DEBUG_FATFS_SYNC_FAT < cycle )
2924printk("\n[%s] thread[%x,%x] enter / cycle %d\n",
2925__FUNCTION__ , this->process->pid, this->trdid, cycle );
2926#endif
2927   
2928    // get FAT cluster
2929    fat_cxy = CONFIG_VFS_ROOT_CXY;
2930   
2931    // get FAT mapper pointers 
2932    fatfs_ctx  = fs_context[FS_TYPE_FATFS].extend;
2933    mapper_ptr = fatfs_ctx->fat_mapper;
2934    mapper_xp  = XPTR( fat_cxy , mapper_ptr );
2935
2936    // get pointers on remote FAT mapper radix tree
2937    grdxt_t  * rt_ptr = &mapper_ptr->rt;
2938    xptr_t     rt_xp  = XPTR( fat_cxy , rt_ptr );
2939
2940    // initialise page_id
2941    start_page_id = 0;
2942
2943    // scan FAT mapper
2944    while( 1 )
2945    {
2946        // get one page
2947        page_xp = grdxt_remote_get_first( rt_xp , start_page_id , &found_page_id );
2948
2949        // exit loop when no more page found
2950        if ( page_xp != XPTR_NULL ) break;
2951
2952        // get page flags
2953        page_ptr = GET_PTR( page_xp );
2954        flags    = hal_remote_l32( XPTR( fat_cxy , &page_ptr->flags ) );
2955
2956        if ( flags & PG_DIRTY )
2957        {
2958
2959#if (DEBUG_FATFS_SYNC_FAT & 1)
2960if( DEBUG_FATFS_SYNC_FAT < cycle )
2961printk("\n[%s] thread[%x,%x] synchronizes page %d from FAT mapper to IOC device\n",
2962__FUNCTION__, page_id );
2963#endif
2964            // move page from mapper to device
2965            error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );
2966
2967            if ( error )  return -1;
2968
2969            // reset page dirty flag
2970            ppm_page_undo_dirty( page_xp );
2971        }
2972
2973        // update loop variable
2974        start_page_id = found_page_id + 1;
2975
2976    }  // end loop on pages
2977
2978#if DEBUG_FATFS_SYNC_FAT
2979cycle = (uint32_t)hal_get_cycles();
2980if( DEBUG_FATFS_SYNC_FAT < cycle )
2981printk("\n[%s] thread[%x,%x] exit\n",
2982__FUNCTION__ , this->process->pid, this->trdid );
2983#endif
2984
2985    return 0;
2986
2987}  // end fatfs_sync_fat()
2988
2989//////////////////////////////////////////////
2990error_t fatfs_release_inode( xptr_t inode_xp )
2991{
2992    vfs_ctx_t   * vfs_ctx;           // local pointer on VFS context (same in all clusters)
2993    cxy_t         fat_cxy;           // FAT cluster identifier
2994    fatfs_ctx_t * fatfs_ctx_ptr;     // local pointer on FATFS context in FAT cluster
2995    xptr_t        fatfs_ctx_xp;      // extended pointer on FATFS-context in FAT cluster
2996    mapper_t    * fat_mapper_ptr;    // local pointer on FAT mapper
2997    xptr_t        fat_mapper_xp;     // extended pointer on FAT mapper
2998    xptr_t        lock_xp;           // extended pointer on lock protecting FAT.
2999    xptr_t        first_xp;          // extended pointer on inode extension
3000    uint32_t      first_cluster_id;  // first cluster index for released inode
3001    vfs_inode_t * inode_ptr;         // local pointer on target inode
3002    cxy_t         inode_cxy;         // target inode cluster identifier
3003    error_t       error;
3004
3005// check inode pointer
3006assert( __FUNCTION__, (inode_xp != XPTR_NULL) , "inode pointer is NULL\n" );
3007
3008    // get inode cluster and local pointer
3009    inode_ptr     = GET_PTR( inode_xp );
3010    inode_cxy     = GET_CXY( inode_xp );
3011
3012    // get first_cluster_id from inode extension
3013    first_xp         = XPTR( inode_cxy , &inode_ptr->extend );
3014    first_cluster_id = (uint32_t)(intptr_t)hal_remote_lpt( first_xp );
3015
3016// check first cluster index
3017assert( __FUNCTION__, (first_cluster_id != 0) , "inode extend is NULL\n" );
3018
3019#if DEBUG_FATFS_RELEASE_INODE
3020char       name[CONFIG_VFS_MAX_NAME_LENGTH];
3021uint32_t   cycle = (uint32_t)hal_get_cycles();
3022thread_t * this  = CURRENT_THREAD;
3023vfs_inode_get_name( inode_xp , name );
3024if( DEBUG_FATFS_RELEASE_INODE < cycle )
3025printk("\n[%s] thread[%x,%x] enter for <%s> / first_cluster_id %x / cycle %d\n",
3026__FUNCTION__ , this->process->pid, this->trdid, name, first_cluster_id, cycle );
3027#endif
3028
3029    // get local pointer on VFS context (same in all clusters)
3030    vfs_ctx = &fs_context[FS_TYPE_FATFS];
3031
3032    // get FAT cluster
3033    fat_cxy = CONFIG_VFS_ROOT_CXY;
3034
3035    // get pointers on FATFS context in FAT cluster
3036    fatfs_ctx_ptr = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) );
3037    fatfs_ctx_xp  = XPTR( fat_cxy , fatfs_ctx_ptr ); 
3038
3039    // get FAT mapper pointers
3040    fat_mapper_ptr = hal_remote_lpt( XPTR( fat_cxy , &fatfs_ctx_ptr->fat_mapper ) );
3041        fat_mapper_xp  = XPTR( fat_cxy , fat_mapper_ptr );
3042   
3043    // build extended pointer on FAT lock in FAT cluster
3044    lock_xp = XPTR( fat_cxy , &fatfs_ctx_ptr->lock );
3045
3046    // take FAT lock in write mode
3047    remote_rwlock_wr_acquire( lock_xp );
3048
3049#if (DEBUG_FATFS_RELEASE_INODE & 0x11 == 0x11)
3050mapper_display_page( fat_mapper_xp , 0 , 4096 );
3051#endif
3052
3053    // call the recursive function to release all clusters from FAT mapper
3054    uint32_t dirty_page_min = 0xFFFFFFFF;
3055    uint32_t dirty_page_max = 0;
3056
3057    if ( fatfs_recursive_release( fat_mapper_xp,
3058                                  fatfs_ctx_xp,
3059                                  first_cluster_id,
3060                                  &dirty_page_min,
3061                                  &dirty_page_max ) )
3062    {
3063        printk("\n[ERROR] in %s : cannot update FAT mapper\n", __FUNCTION__ );
3064        remote_rwlock_wr_release( lock_xp );
3065        return -1;
3066    }
3067
3068#if (DEBUG_FATFS_RELEASE_INODE & 1)
3069if( DEBUG_FATFS_RELEASE_INODE < cycle )
3070printk("\n[%s] inode <%s> removed from FAT mapper\n", __FUNCTION__, name );
3071#endif
3072
3073#if (DEBUG_FATFS_RELEASE_INODE & 0x11 == 0x11)
3074mapper_display_page( fat_mapper_xp , 0 , 4096 );
3075#endif
3076
3077    // update FAT on IOC device (from FAT mapper)
3078    error = fatfs_update_ioc_fat( fatfs_ctx_xp,
3079                                  dirty_page_min,
3080                                  dirty_page_max );
3081
3082    if( error )
3083    {
3084        printk("\n[ERROR] in %s : cannot update FAT on IOC device\n", __FUNCTION__ );
3085        remote_rwlock_wr_release( lock_xp );
3086        return -1;
3087    }
3088
3089#if (DEBUG_FATFS_RELEASE_INODE & 1)
3090if( DEBUG_FATFS_RELEASE_INODE < cycle )
3091printk("\n[%s] inode <%s> removed from FAT on IOC device\n", __FUNCTION__, name );
3092#endif
3093
3094    // update FS-INFO on IOC device (from FATFS context)
3095    error = fatfs_update_ioc_fsinfo( fatfs_ctx_xp );
3096
3097    if( error )
3098    {
3099        printk("\n[ERROR] in %s: cannot update FSINFO on IOC device\n", __FUNCTION__ );
3100        remote_rwlock_wr_release( lock_xp );
3101        return -1;
3102    }
3103
3104#if (DEBUG_FATFS_RELEASE_INODE & 1)
3105if( DEBUG_FATFS_RELEASE_INODE < cycle )
3106printk("\n[%s] updated FS_INFO on IOC device for >%s>\n", __FUNCTION__, name );
3107#endif
3108
3109    // release FAT lock
3110    remote_rwlock_wr_release( lock_xp );
3111
3112#if DEBUG_FATFS_RELEASE_INODE
3113cycle = (uint32_t)hal_get_cycles();
3114if( DEBUG_FATFS_RELEASE_INODE < cycle )
3115printk("\n[%s] thread[%x,%x] removed <%s> inode from FATFS / cycle %d\n",
3116__FUNCTION__ , this->process->pid, this->trdid, name, cycle );
3117#endif
3118
3119    return 0;
3120
3121}  // end fatfs_release_inode()
3122
3123/////////////////////////////////////////////////
3124error_t fatfs_move_page( xptr_t          page_xp,
3125                         ioc_cmd_type_t  cmd_type )
3126{
3127    error_t       error = 0;
3128
3129    vfs_inode_t * inode_ptr;
3130    mapper_t    * mapper_ptr;     
3131    uint32_t      page_id;     // page index in mapper
3132
3133#if DEBUG_FATFS_MOVE_PAGE
3134uint32_t   cycle = (uint32_t)hal_get_cycles();
3135thread_t * this  = CURRENT_THREAD;
3136char       name[CONFIG_VFS_MAX_NAME_LENGTH];
3137#endif
3138
3139    // get page cluster and local pointer
3140    cxy_t    page_cxy = GET_CXY( page_xp );
3141    page_t * page_ptr = GET_PTR( page_xp );
3142
3143    // get mapper pointer and page index from page descriptor
3144    mapper_ptr = hal_remote_lpt( XPTR( page_cxy , &page_ptr->mapper ) );
3145    page_id    = hal_remote_l32( XPTR( page_cxy , &page_ptr->index ) );
3146
3147    // get pointer on local FATFS context
3148    fatfs_ctx_t * fatfs_ctx = fs_context[FS_TYPE_FATFS].extend;
3149
3150    // get page base address
3151    xptr_t    buffer_xp  = ppm_page2base( page_xp );
3152 
3153    // get inode pointer from mapper
3154    inode_ptr  = hal_remote_lpt( XPTR( page_cxy , &mapper_ptr->inode ) );
3155
3156    //////////////////////////////  FAT mapper  /////////////////////////////////////////
3157    if( inode_ptr == NULL )
3158    {
3159
3160#if DEBUG_FATFS_MOVE_PAGE
3161if( DEBUG_FATFS_MOVE_PAGE < cycle )
3162printk("\n[%s] thread[%x,%x] enters %s for  page %d in FAT mapper / cycle %d\n",
3163__FUNCTION__, this->process->pid, this->trdid, dev_ioc_cmd_str(cmd_type), page_id, cycle );
3164#endif
3165        // get lba from FATFS context and page_id
3166        uint32_t      lba = fatfs_ctx->fat_begin_lba + (page_id << 3);
3167 
3168        // access IOC device
3169        if     (cmd_type == IOC_SYNC_WRITE) error = dev_ioc_sync_write( buffer_xp, lba, 8 );
3170        else if(cmd_type == IOC_SYNC_READ ) error = dev_ioc_sync_read( buffer_xp, lba, 8 );
3171        else
3172        {
3173            printk("\n[ERROR] in %s : illegal asynchronous FAT access\n", __FUNCTION__ );
3174        }
3175
3176        if( error )
3177        {
3178            printk("\n[ERROR] in %s : cannot access IOC device\n", __FUNCTION__ );
3179            return -1;
3180        }
3181
3182#if DEBUG_FATFS_MOVE_PAGE
3183if( DEBUG_FATFS_MOVE_PAGE < cycle )
3184printk("\n[%s] thread[%x,%x] exit %s for page %d in FAT mapper\n",
3185__FUNCTION__, this->process->pid, this->trdid, dev_ioc_cmd_str(cmd_type), page_id );
3186#endif
3187
3188    }
3189    /////////////////////////  inode mapper  ////////////////////////////////////////////
3190    else                       
3191    {
3192
3193#if DEBUG_FATFS_MOVE_PAGE
3194if( DEBUG_FATFS_MOVE_PAGE < cycle )
3195{
3196    vfs_inode_get_name( XPTR( page_cxy , inode_ptr ) , name );
3197    printk("\n[%s] thread[%x,%x] enters %s for page %d in <%s> mapper / cycle %d\n",
3198    __FUNCTION__, this->process->pid, this->trdid, 
3199    dev_ioc_cmd_str( cmd_type ), page_id, name, cycle );
3200}
3201#endif
3202
3203        uint32_t  searched_cluster_id;
3204        uint32_t  first_cluster_id;
3205
3206        // get first_cluster_id from inode extension
3207        void * extend    = hal_remote_lpt( XPTR( page_cxy , &inode_ptr->extend ) );
3208        first_cluster_id = (uint32_t)(intptr_t)extend;
3209
3210        // compute searched_cluster_id
3211        if( page_id == 0 )            // no need to access FAT mapper
3212        {
3213            // searched cluster is first cluster
3214            searched_cluster_id = first_cluster_id;
3215        }
3216        else                        // FAT mapper access required
3217        {
3218            // scan FAT mapper to get searched_cluster_id
3219            error = fatfs_get_cluster( 0,                    // first page in mapper
3220                                       first_cluster_id,
3221                                       page_id,
3222                                       &searched_cluster_id );
3223            if( error )
3224            {
3225                printk("\n[ERROR] in %s : cannot get cluster_id\n", __FUNCTION__ );
3226                return -1;
3227            }
3228        }
3229
3230        // get lba for searched_cluster
3231        uint32_t lba = fatfs_lba_from_cluster( fatfs_ctx , searched_cluster_id );
3232
3233        // access IOC device
3234        if     (cmd_type == IOC_WRITE     ) error = dev_ioc_write( buffer_xp, lba, 8 );
3235        else if(cmd_type == IOC_READ      ) error = dev_ioc_read( buffer_xp, lba, 8 );
3236        else if(cmd_type == IOC_SYNC_READ ) error = dev_ioc_sync_read( buffer_xp, lba, 8 );
3237        else if(cmd_type == IOC_SYNC_WRITE) error = dev_ioc_sync_write( buffer_xp, lba, 8 );
3238        else
3239        {
3240            printk("\n[ERROR] in %s : illegal cmd_type\n", __FUNCTION__ );
3241        }
3242
3243        if( error )
3244        {
3245            printk("\n[ERROR] in %s : cannot access device\n", __FUNCTION__ );
3246            return -1;
3247        }
3248
3249#if DEBUG_FATFS_MOVE_PAGE
3250if( DEBUG_FATFS_MOVE_PAGE < cycle )
3251{
3252    printk("\n[%s] thread[%x,%x] exit %s for page %d in <%s> mapper / cluster_id %x\n",
3253    __FUNCTION__, this->process->pid, this->trdid, 
3254    dev_ioc_cmd_str( cmd_type ), page_id, name, searched_cluster_id );
3255}
3256#endif
3257
3258#if (DEBUG_FATFS_MOVE_PAGE & 1)
3259if( DEBUG_FATFS_MOVE_PAGE < cycle )
3260fatfs_display_fat( searched_cluster_id , 64 );
3261#endif
3262
3263    }
3264
3265    return 0;
3266
3267}  // end fatfs_move_page()
3268
3269
Note: See TracBrowser for help on using the repository browser.