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

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

Introduce remote_buf.c/.h & socket.c/.h files.
Update dev_nic.c/.h files.

File size: 124.1 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( (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( (mapper_xp != XPTR_NULL) , "mapper pointer is NULL\n" );
1242assert( (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    kmem_req_t    req;
1633
1634    // allocate memory from remote cluster
1635        req.type     = KMEM_KCM;
1636        req.order    = bits_log2( sizeof(fatfs_ctx_t) );
1637    req.flags    = AF_KERNEL | AF_ZERO;
1638 
1639    return XPTR( cxy , kmem_remote_alloc( cxy , &req ) );
1640
1641}  //end faffs_ctx_alloc()
1642
1643///////////////////////////////////////////////
1644error_t  fatfs_ctx_init( xptr_t  fatfs_ctx_xp )
1645{
1646    error_t       error;
1647    kmem_req_t    req;
1648    cxy_t         cxy;             // FATFS context cluster identifier
1649    fatfs_ctx_t * fatfs_ctx_ptr;   // local pointer on FATFS context
1650    uint8_t     * buffer;          // local pointer on 512 bytes buffer
1651    xptr_t        buffer_xp;       // extended pointer on 512 bytes buffer
1652    xptr_t        fat_mapper_xp;   // extended pointer on FAT mapper
1653    mapper_t    * fat_mapper;      // local pointer on FAT mapper
1654
1655    // get FATFS context cluster and local pointer
1656    cxy           = GET_CXY( fatfs_ctx_xp );       
1657    fatfs_ctx_ptr = GET_PTR( fatfs_ctx_xp );
1658   
1659#if DEBUG_FATFS_CTX_INIT
1660uint32_t   cycle = (uint32_t)hal_get_cycles();
1661thread_t * this  = CURRENT_THREAD;
1662if( DEBUG_FATFS_CTX_INIT < cycle )
1663printk("\n[%s] thread[%x,%x] enter for fatfs_ctx (%x,%x) / cycle %d\n",
1664__FUNCTION__ , this->process->pid, this->trdid, cxy, fatfs_ctx_ptr , cycle );
1665#endif
1666
1667    // allocate a 512 bytes buffer in remote cluster, used to store
1668    // temporarily the BOOT sector, and permanently the FS_INFO sector
1669        req.type    = KMEM_KCM;
1670    req.order   = 9;                    // 512 bytes
1671    req.flags   = AF_KERNEL | AF_ZERO;
1672        buffer      = kmem_remote_alloc( cxy , &req );
1673
1674    if( buffer == NULL ) 
1675    {
1676        printk("\n[PANIC] in %s : cannot allocate buffer in cluster %x\n",
1677        __FUNCTION__ , cxy );
1678        return -1;
1679    }
1680     
1681    // build extended pointer on buffer
1682    buffer_xp = XPTR( cxy , buffer );
1683
1684    // load the BOOT record from device
1685    error = dev_ioc_sync_read( buffer_xp , 0 , 1 );
1686
1687    if ( error )
1688    {
1689        printk("\n[PANIC] in %s : cannot access boot record\n", __FUNCTION__ );
1690        return -1;
1691    }
1692
1693#if (DEBUG_FATFS_CTX_INIT & 0x1)
1694uint8_t bs[256];
1695hal_remote_memcpy( XPTR( local_cxy , bs ) , buffer_xp , 256 );
1696if( DEBUG_FATFS_CTX_INIT < cycle )
1697putb( "boot record", bs , 256 );
1698#endif
1699
1700    // get sector size from boot record
1701    uint32_t sector_size = fatfs_get_remote_record( BPB_BYTSPERSEC , buffer_xp );
1702
1703    if ( sector_size != 512 )
1704    {
1705        printk("\n[PANIC] in %s : sector size must be 512 bytes\n", __FUNCTION__ );
1706        return -1;
1707    }
1708
1709    // get cluster size from boot record
1710    uint32_t nb_sectors = fatfs_get_remote_record( BPB_SECPERCLUS , buffer_xp );
1711
1712    if ( nb_sectors != 8 )
1713    {
1714        printk("\n[PANIC] in %s : cluster size must be 8 sectors\n", __FUNCTION__ );
1715        return -1;
1716    }
1717
1718    // get number of FAT copies from boot record
1719    uint32_t nb_fats = fatfs_get_remote_record( BPB_NUMFATS , buffer_xp );
1720
1721    if ( nb_fats != 1 )
1722    {
1723        printk("\n[PANIC] in %s : number of FAT copies must be 1\n", __FUNCTION__ );
1724        return -1;
1725    }
1726
1727    // get number of sectors in FAT from boot record
1728    uint32_t fat_sectors = fatfs_get_remote_record( BPB_FAT32_FATSZ32 , buffer_xp );
1729
1730    if ( (fat_sectors & 0xF) != 0 )
1731    {
1732        printk("\n[PANIC] in %s : FAT size not multiple of 16 sectors\n", __FUNCTION__ );
1733        return -1;
1734    }
1735
1736    // get root cluster from boot record
1737    uint32_t root_cluster = fatfs_get_remote_record( BPB_FAT32_ROOTCLUS , buffer_xp );
1738
1739    if ( root_cluster != 2 ) 
1740    {
1741        printk("\n[PANIC] in %s : root cluster index must be 2\n", __FUNCTION__ );
1742        return -1;
1743    }
1744
1745    // get FAT lba from boot record
1746    uint32_t fat_lba = fatfs_get_remote_record( BPB_RSVDSECCNT , buffer_xp );
1747
1748    // get FS_INFO sector lba from boot record
1749    uint32_t fs_info_lba = fatfs_get_remote_record( BPB_FAT32_FSINFO , buffer_xp );
1750
1751    // load the FS_INFO record from device
1752    error = dev_ioc_sync_read( buffer_xp , fs_info_lba , 1 );
1753
1754    if ( error )
1755    {
1756        printk("\n[PANIC] in %s : cannot access FS_INFO record\n", __FUNCTION__ );
1757        return -1;
1758    }
1759
1760    // get free_clusters number from FS_INFO record
1761    uint32_t free_clusters = fatfs_get_remote_record( FS_FREE_CLUSTERS , buffer_xp );
1762
1763    if ( free_clusters >= fat_sectors << 7 )
1764    {
1765        printk("\n[PANIC] in %s : unconsistent free_clusters\n", __FUNCTION__ );
1766        return -1;
1767    }
1768
1769    // get free_cluster_hint from FS_INFO record
1770    uint32_t free_hint = fatfs_get_remote_record( FS_FREE_CLUSTER_HINT , buffer_xp );
1771
1772    if ( free_hint >= fat_sectors << 7 )
1773    {
1774        printk("\n[PANIC] in %s : unconsistent free_cluster_hint\n", __FUNCTION__ );
1775        return -1;
1776    }
1777
1778    // allocate a mapper for the FAT in remote cluster
1779    fat_mapper_xp  = mapper_create( cxy , FS_TYPE_FATFS );
1780
1781    // get local pointer on FAT mapper
1782    fat_mapper = GET_PTR( fat_mapper_xp );
1783
1784    if ( fat_mapper == NULL )
1785    {
1786        printk("\n[PANIC] in %s : no memory for FAT mapper in cluster %x\n",
1787        __FUNCTION__ , cxy );
1788        return -1;
1789    }
1790
1791#if (DEBUG_FATFS_CTX_INIT & 0x1)
1792if( DEBUG_FATFS_CTX_INIT < cycle )
1793printk("\n[%s] sector_size %d / nb_sectors %d / fat_sectors %x / hint %x\n",
1794__FUNCTION__, sector_size, nb_sectors, fat_sectors, free_hint );
1795#endif
1796
1797    // the inode field is NULL for the FAT mapper
1798    hal_remote_spt( XPTR( cxy , &fat_mapper->inode ) , NULL );
1799
1800    // initialize the FATFS context
1801    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->fat_begin_lba       ), fat_lba );
1802    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->fat_sectors_count   ), fat_sectors );
1803    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->bytes_per_sector    ), sector_size );
1804    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->sectors_per_cluster ), nb_sectors );
1805    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->cluster_begin_lba   ), fat_lba + fat_sectors );
1806    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->root_dir_cluster    ), 2 );
1807    hal_remote_spt( XPTR( cxy , &fatfs_ctx_ptr->fat_mapper          ), fat_mapper );
1808    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->fs_info_lba         ), fs_info_lba );
1809    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->free_clusters       ), free_clusters );
1810    hal_remote_s32( XPTR( cxy , &fatfs_ctx_ptr->free_cluster_hint   ), free_hint );
1811    hal_remote_spt( XPTR( cxy , &fatfs_ctx_ptr->fs_info_buffer      ), buffer );
1812
1813    // initialize FATFS lock
1814    remote_rwlock_init( XPTR( cxy , &fatfs_ctx_ptr->lock ) , LOCK_FATFS_FAT );
1815
1816#if DEBUG_FATFS_CTX_INIT
1817if( DEBUG_FATFS_CTX_INIT < cycle )
1818printk("\n[%s]  thread[%x,%x] exit for fatfs_ctx (%x,%x)\n",
1819__FUNCTION__, this->process->pid, this->trdid, cxy, fatfs_ctx_ptr );
1820#endif
1821
1822        return 0;
1823
1824}  // end fatfs_ctx_init()
1825
1826//////////////////////////////////////////////
1827void fatfs_ctx_destroy( xptr_t  fatfs_ctx_xp )
1828{
1829    kmem_req_t   req;
1830    mapper_t   * fat_mapper;
1831    uint8_t    * fs_info_buffer;
1832
1833    // get FATFS context cluster and local pointer
1834    fatfs_ctx_t * fatfs_ctx_ptr = GET_PTR( fatfs_ctx_xp );
1835    cxy_t         fatfs_ctx_cxy = GET_CXY( fatfs_ctx_xp );
1836
1837    // get pointer on FAT mapper
1838    fat_mapper = hal_remote_lpt( XPTR( fatfs_ctx_cxy , &fatfs_ctx_ptr->fat_mapper ) );
1839
1840    // release FAT mapper
1841    mapper_destroy( XPTR( fatfs_ctx_cxy , fat_mapper ) );
1842
1843    // get pointer on FS_INFO buffer
1844    fs_info_buffer = hal_remote_lpt( XPTR( fatfs_ctx_cxy , &fatfs_ctx_ptr->fs_info_buffer ) );
1845
1846    // release FS_INFO buffer
1847    req.type = KMEM_KCM;
1848    req.ptr  = fs_info_buffer;
1849    kmem_remote_free( fatfs_ctx_cxy , &req );
1850
1851    // release FATFS context descriptor
1852    req.type = KMEM_KCM;
1853    req.ptr  = fatfs_ctx_ptr;
1854    kmem_remote_free( fatfs_ctx_cxy , &req );
1855
1856}  // end fatfs_ctx_destroy()
1857
1858/////////////////////////////////////////////////////////
1859error_t fatfs_add_dentry( xptr_t         parent_inode_xp,
1860                          vfs_dentry_t * dentry_ptr )
1861{
1862    error_t       error;
1863    vfs_inode_t * parent_inode_ptr;   // parent inode local pointer
1864    cxy_t         parent_cxy;         // pparent inode cluster
1865    xptr_t        child_inode_xp;     // extended pointer on child inode
1866    cxy_t         child_cxy;          // child inode cluster
1867    vfs_inode_t * child_inode_ptr;    // child inode local pointer
1868    uint32_t      length;             // dentry name length
1869    uint32_t      nb_lfn;             // number or required LFN
1870    char          sfn[11];            // buffer for SFN name
1871    uint8_t       checksum;           // name checksum
1872    mapper_t    * mapper_ptr;         // local pointer on parent directory mapper
1873    xptr_t        mapper_xp;          // extended pointer on parent directory mapper
1874    uint32_t      size;               // child inode size
1875    uint32_t      type;               // child inode type
1876    void        * extend;             // child inode extension
1877    uint32_t      cluster_id;         // child inode first cluster_id in FATFS
1878
1879    char          child_name[CONFIG_VFS_MAX_NAME_LENGTH];
1880
1881    uint8_t       buf[32];            // local buffer for one FAT32 directory entry
1882 
1883// check arguments
1884assert( (parent_inode_xp != XPTR_NULL) , "parent_inode_xp argument is NULL\n" );
1885assert( (dentry_ptr      != NULL)      , "dentry_ptr argument is NULL\n" );
1886
1887    // get directory inode cluster and local pointer
1888    parent_cxy       = GET_CXY( parent_inode_xp );
1889    parent_inode_ptr = GET_PTR( parent_inode_xp );
1890
1891    // get extended pointers on child inode
1892    child_inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
1893    child_cxy       = GET_CXY( child_inode_xp );
1894    child_inode_ptr = GET_PTR( child_inode_xp );
1895
1896    // get a local copy of the child name
1897    vfs_inode_get_name( child_inode_xp  , child_name );
1898
1899#if DEBUG_FATFS_ADD_DENTRY
1900uint32_t   cycle = (uint32_t)hal_get_cycles();
1901thread_t * this  = CURRENT_THREAD;
1902char       parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
1903vfs_inode_get_name( parent_inode_xp , parent_name );
1904if( DEBUG_FATFS_ADD_DENTRY < cycle )
1905printk("\n[%s]  thread[%x,%x] enter for <%s> in <%s> directory / cycle %d\n",
1906__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cycle );
1907#endif
1908
1909    // get pointers on parent directory mapper
1910    mapper_ptr = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) );
1911    mapper_xp  = XPTR( parent_cxy , mapper_ptr );
1912
1913#if (DEBUG_FATFS_ADD_DENTRY & 1)
1914mapper_display_page( mapper_xp , 0 , 512 );
1915#endif
1916
1917    // get relevant infos from child inode
1918    type       = hal_remote_l32( XPTR( child_cxy , &child_inode_ptr->type ) );
1919    size       = hal_remote_l32( XPTR( child_cxy , &child_inode_ptr->size ) );
1920    extend     = hal_remote_lpt( XPTR( child_cxy , &child_inode_ptr->extend ) );
1921    cluster_id = (uint32_t)(intptr_t)extend;
1922
1923    // analyse child name
1924    error = fatfs_name_format( child_name,
1925                               &length,
1926                               &nb_lfn,
1927                               sfn,
1928                               &checksum );
1929    if ( error )
1930    {
1931        printk("\n[ERROR] in %s : dentry name > 31 bytes\n", __FUNCTION__ );
1932        return -1;
1933    }
1934                               
1935    // Search end of directory with two embedded loops:
1936    // - scan the pages in the mapper
1937    // - scan the entries in each page to find NO_MORE_ENTRY
1938
1939    xptr_t     page_xp;                 // extended pointer on page descriptor
1940    xptr_t     base_xp;                 // extended pointer on page base
1941
1942    // initialise loop variables
1943    uint32_t   page_id = 0;             // page index in mapper
1944    uint32_t   offset  = 0;             // position in page
1945    uint32_t   found   = 0;             // NO_MORE_ENTRY found
1946
1947    // loop on pages in mapper
1948    while ( found == 0 )
1949    {
1950        // get extended pointer on page descriptor in mapper
1951        page_xp  = mapper_get_page( mapper_xp , page_id );
1952
1953        if ( page_xp == XPTR_NULL )
1954        {
1955            printk("\n[ERROR] in %s : cannot extend directory mapper\n",
1956            __FUNCTION__ );
1957            return -1;
1958        }
1959       
1960        // get pointer on page base
1961        base_xp = ppm_page2base( page_xp );
1962
1963        // loop on directory entries in this page
1964        while ( (offset < 4096) && (found == 0) )
1965        {
1966            if ( fatfs_get_remote_record( LDIR_ORD, (base_xp + offset) ) == NO_MORE_ENTRY )
1967            {
1968                found = 1;
1969            } 
1970            else
1971            {
1972                offset = offset + 32;
1973            }
1974        }  // end loop on entries
1975
1976        if ( found == 0 )
1977        {
1978            page_id++;
1979            offset = 0;
1980        }
1981    }  // end loop on pages
1982
1983#if (DEBUG_FATFS_ADD_DENTRY & 1)
1984if( DEBUG_FATFS_ADD_DENTRY < cycle )
1985printk("\n[%s]  thread[%x,%x] found NO_MORE_ENTRY : page_id %d / offset %d\n",
1986__FUNCTION__, this->process->pid, this->trdid, page_id, offset );
1987#endif
1988
1989    // Modify the directory mapper: depending on the name length,
1990    // the new child requires to write (3, 4, or 5) directory entries.
1991    // We build one complete directory entry in a local buffer
1992    // before copying it the remote mapper. We use use a 5 steps FSM
1993    // (one state per entry to be written), that is traversed as :
1994    // LFN3 -> LFN2 -> LFN1 -> NORMAL -> NOMORE
1995    // At most two pages are modified:
1996    // - the page containing the NO_MORE_ENTRY is always modified
1997    // - the following page can be modified if the name spread on two pages.
1998
1999    uint32_t step;          // FSM state
2000
2001    if      ( nb_lfn == 1 ) step = 3;
2002    else if ( nb_lfn == 2 ) step = 4;
2003    else if ( nb_lfn == 3 ) step = 5;
2004   
2005    uint32_t   i;           // byte index in one 32 bytes directory
2006    uint32_t   c;           // character index in name
2007
2008    while ( step )   
2009    {
2010        // this block is only executed when the new name spread
2011        // on two pages, and we need to access a new page in mapper
2012        if ( offset >= 4096 )
2013        {
2014            // copy the modified page to IOC device
2015            error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
2016
2017            if ( error )
2018            {
2019                printk("\n[ERROR] in %s : cannot update directory on device\n",
2020                __FUNCTION__ );
2021                return -1;
2022            }
2023
2024            // get the next page in directory mapper
2025            page_xp  = mapper_get_page( mapper_xp , page_id + 1 );
2026
2027            if ( page_xp == XPTR_NULL )
2028            {
2029                printk("\n[ERROR] in %s : cannot extend directory mapper\n",
2030                __FUNCTION__ );
2031                return -1;
2032            }
2033       
2034            // get pointer on page base
2035            base_xp = ppm_page2base( page_xp );
2036           
2037            // update offset
2038            offset = 0;
2039        }
2040
2041#if (DEBUG_FATFS_ADD_DENTRY & 1)
2042cycle = (uint32_t)hal_get_cycles();
2043if( DEBUG_FATFS_ADD_DENTRY < cycle )
2044printk("\n[%s] FSM step = %d / offset = %x / nb_lfn = %d / cycle %d\n",
2045__FUNCTION__, step, offset, nb_lfn, cycle );
2046#endif
2047
2048        // write one FATFS directory entry (32 bytes) per iteration
2049        switch ( step )
2050        {
2051            case 5:   // write LFN3 entry
2052            {
2053                c = 26;
2054
2055                // write 32 bytes in local buf
2056                for ( i = 0 ; i < 32 ; i++ )
2057                {
2058                    if (i == 0)
2059                    {
2060                        if ( nb_lfn == 3) buf[i] = 0x43;
2061                        else              buf[i] = 0x03;
2062                    }
2063                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
2064                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
2065                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
2066                              ( c < length ) )
2067                    {
2068                                          buf[i] = child_name[c];
2069                                          c++;
2070                    }
2071                    else if (i == 11)     buf[i] = 0x0F;
2072                    else if (i == 13)     buf[i] = checksum;
2073                    else                  buf[i] = 0x00;
2074                }
2075
2076                // copy 32 bytes from local buffer to remote mapper
2077                hal_remote_memcpy( base_xp + offset , XPTR( local_cxy  , buf ) , 32 );
2078
2079                step--;
2080                break;
2081            }
2082            case 4:   // write LFN2 entry 
2083            {
2084                c = 13;
2085
2086                // write 32 bytes in local buf
2087                for ( i = 0 ; i < 32 ; i++ )
2088                {
2089                    if (i == 0)
2090                    {
2091                        if ( nb_lfn == 2) buf[i] = 0x42;
2092                        else              buf[i] = 0x02;
2093                    }
2094                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
2095                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
2096                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
2097                              ( c < length ) )
2098                    {
2099                                          buf[i] = child_name[c];
2100                                          c++;
2101                    }
2102                    else if (i == 11)     buf[i] = 0x0F;
2103                    else if (i == 13)     buf[i] = checksum;
2104                    else                  buf[i] = 0x00;
2105                }
2106
2107                // copy 32 bytes from local buffer to remote mapper
2108                hal_remote_memcpy( base_xp + offset , XPTR( local_cxy  , buf ) , 32 );
2109
2110                step--;
2111                break;
2112            }
2113            case 3:   // Write LFN1 entry   
2114            {
2115                c = 0;
2116
2117                // write 32 bytes in local buf
2118                for ( i = 0 ; i < 32 ; i++ )
2119                {
2120                    if (i == 0)
2121                    {
2122                        if ( nb_lfn == 1) buf[i] = 0x41;
2123                        else              buf[i] = 0x01;
2124                    }
2125                    else if ( ( ((i >= 1 ) && (i<=10) && ((i&1)==1))   ||
2126                                ((i >= 14) && (i<=25) && ((i&1)==0))   ||
2127                                ((i >= 28) && (i<=31) && ((i&1)==0)) ) &&
2128                              ( c < length ) )
2129                    {
2130                                          buf[i] = child_name[c];
2131                                          c++;
2132                    }
2133                    else if (i == 11)     buf[i] = 0x0F;
2134                    else if (i == 13)     buf[i] = checksum;
2135                    else                  buf[i] = 0x00;
2136                }
2137
2138                // copy 32 bytes from local buffer to remote mapper
2139                hal_remote_memcpy( base_xp + offset , XPTR( local_cxy  , buf ) , 32 );
2140
2141                step--;
2142                break;
2143            }
2144            case 2:   // write NORMAL entry     
2145            {
2146                // write 32 bytes in local buf
2147                for ( i = 0 ; i < 32 ; i++ )
2148                {
2149                    if      ( i < 11 )                               // 8.3 SFN
2150                    {
2151                                          buf[i] = sfn[i];
2152                    }
2153                    else if (i == 11)                                // ATTR
2154                    {
2155                        if (type == INODE_TYPE_DIR)  buf[i] = 0x10;
2156                        else                         buf[i] = 0x20;
2157                    }
2158                    else if (i == 20)     buf[i] = cluster_id>>16;   // cluster.B2
2159                    else if (i == 21)     buf[i] = cluster_id>>24;   // cluster.B3
2160                    else if (i == 26)     buf[i] = cluster_id>>0;    // cluster.B0
2161                    else if (i == 27)     buf[i] = cluster_id>>8;    // cluster.B1
2162                    else if (i == 28)     buf[i] = size>>0;          // size.B0
2163                    else if (i == 29)     buf[i] = size>>8;          // size.B1
2164                    else if (i == 30)     buf[i] = size>>16;         // size.B2
2165                    else if (i == 31)     buf[i] = size>>24;         // size.B3
2166                    else                  buf[i] = 0x00;
2167                }
2168
2169                // copy 32 bytes from local buffer to remote mapper
2170                hal_remote_memcpy( base_xp + offset , XPTR( local_cxy  , buf ) , 32 );
2171
2172                // set the dentry "extend" field
2173                hal_remote_spt( XPTR( parent_cxy , &dentry_ptr->extend ),
2174                                (void *)(intptr_t)(((page_id << 12) + offset) >> 5 ) );
2175                step--;
2176                break;
2177            }
2178            case 1:   // write NOMORE entry 
2179            {
2180                hal_remote_s32( base_xp + offset , 0 );
2181
2182                step--;
2183                break;
2184            }
2185
2186        } // end switch step
2187
2188        offset += 32;
2189
2190    } // end while     
2191
2192    // copy the modified page to the IOC device
2193    error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
2194
2195    if ( error )
2196    {
2197        printk("\n[ERROR] in %s : cannot update directory on device\n",
2198        __FUNCTION__ );
2199        return -1;
2200    }
2201
2202#if DEBUG_FATFS_ADD_DENTRY
2203cycle = (uint32_t)hal_get_cycles();
2204if( DEBUG_FATFS_ADD_DENTRY < cycle )
2205printk("\n[%s]  thread[%x,%x] exit for <%s> in <%s> directory\n",
2206__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name );
2207#endif
2208
2209#if (DEBUG_FATFS_ADD_DENTRY & 1)
2210mapper_display_page( mapper_xp , 0 , 512 );
2211#endif
2212
2213    return 0;
2214
2215}  // end fatfs_add_dentry()
2216
2217////////////////////////////////////////////////////////////
2218error_t fatfs_remove_dentry( xptr_t         parent_inode_xp,
2219                             vfs_dentry_t * dentry_ptr )
2220{
2221    error_t       error;
2222    vfs_inode_t * parent_inode_ptr;   // parent inode local pointer
2223    cxy_t         parent_cxy;         // pparent inode cluster
2224    xptr_t        child_inode_xp;     // extended pointer on child inode
2225    cxy_t         child_cxy;          // child inode cluster
2226    vfs_inode_t * child_inode_ptr;    // child inode local pointer
2227    xptr_t        mapper_xp;          // extended pointer on mapper
2228    mapper_t    * mapper_ptr;         // local pointer on mapper
2229    xptr_t        page_xp;            // extended pointer on mapper page descriptor
2230    xptr_t        base_xp;            // extended pointer on mapper page base
2231    uint32_t      dentry_id;          // FAT32 directory entry index
2232    uint32_t      page_id;            // page index in directory mapper
2233    uint32_t      offset;             // offset in this mapper page
2234
2235    char          child_name[CONFIG_VFS_MAX_NAME_LENGTH];
2236
2237// check arguments
2238assert( (parent_inode_xp != XPTR_NULL) , "parent_inode_xp argument is NULL\n" );
2239assert( (dentry_ptr      != NULL)      , "dentry_ptr argument is NULL\n" );
2240
2241    // get directory inode cluster and local pointer
2242    parent_cxy       = GET_CXY( parent_inode_xp );
2243    parent_inode_ptr = GET_PTR( parent_inode_xp );
2244
2245    // get extended pointers on child inode
2246    child_inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
2247    child_cxy       = GET_CXY( child_inode_xp );
2248    child_inode_ptr = GET_PTR( child_inode_xp );
2249
2250    // get a local copy of the child name
2251    vfs_inode_get_name( child_inode_xp  , child_name );
2252
2253#if DEBUG_FATFS_REMOVE_DENTRY
2254uint32_t   cycle = (uint32_t)hal_get_cycles();
2255thread_t * this  = CURRENT_THREAD;
2256char       parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
2257vfs_inode_get_name( parent_inode_xp , parent_name );
2258if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
2259printk("\n[%s]  thread[%x,%x] enter for <%s> in <%s> directory / cycle %d\n",
2260__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cycle );
2261#endif
2262
2263    // get pointers on directory mapper
2264    mapper_ptr = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) );
2265    mapper_xp  = XPTR( parent_cxy , mapper_ptr );
2266
2267    // compute number of LFN entries
2268    uint32_t nb_lfn;
2269    uint32_t name_length = strlen( child_name );
2270
2271    if      ( name_length <= 13 ) nb_lfn  = 1;
2272    else if ( name_length <= 26 ) nb_lfn  = 2;
2273    else                          nb_lfn  = 3;
2274
2275    // We must invalidate (2,3,4) 32 bytes entries:
2276    // - the NORMAL entry, registered in dentry->extend.
2277    // - the (1,2,3) preceding LFN entries.
2278    // At most two pages are modified:
2279    // - the page containing the NORMAL entry is always modified.
2280    // - the preceding page is modified when the name spread on two pages.
2281
2282    // get NORMAL entry index from dentry extension
2283    dentry_id  = (uint32_t)(intptr_t)hal_remote_lpt( XPTR( parent_cxy , &dentry_ptr->extend ) ); 
2284
2285    // get page index and offset in parent directory mapper
2286    page_id  = dentry_id >> 7;
2287    offset   = (dentry_id & 0x7F)<<5;
2288
2289#if DEBUG_FATFS_REMOVE_DENTRY & 1
2290if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
2291printk("\n[%s] dentry_id %x / page_id %x / offset %x\n",
2292__FUNCTION__, dentry_id, page_id, offset );
2293#endif
2294
2295    // get extended pointer on page descriptor
2296    page_xp  = mapper_get_page( mapper_xp , page_id );
2297
2298    if ( page_xp == XPTR_NULL )
2299    {
2300        printk("\n[ERROR] in %s : cannot access directory mapper\n", __FUNCTION__ );
2301        return -1;
2302    }
2303       
2304    // get extended pointer on page base
2305    base_xp = ppm_page2base( page_xp );
2306
2307    // invalidate NORMAL entry in directory cache
2308    hal_remote_sb( base_xp + offset , 0xE5 );
2309
2310    // invalidate LFN entries
2311    while ( nb_lfn )
2312    {
2313        // this block is only executed when the removed name
2314        // spread on two mapper pages
2315        if (offset == 0)  // we must load page (page_id - 1)
2316        {
2317            // copy the modified page to the IOC device
2318            error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
2319
2320            if ( error )
2321            {
2322                printk("\n[ERROR] in %s : cannot update directory on device\n",
2323                __FUNCTION__ );
2324                return -1;
2325            }
2326
2327            // get extended pointer on page descriptor
2328            page_xp  = mapper_get_page( mapper_xp , page_id );
2329
2330            if ( page_xp == XPTR_NULL )
2331            {
2332                printk("\n[ERROR] in %s : cannot access directory mapper\n", __FUNCTION__ );
2333                return -1;
2334            }
2335       
2336            // get extended pointer on page base
2337            base_xp = ppm_page2base( page_xp );
2338
2339            // update offset
2340            offset = 4096;
2341        }
2342
2343        offset = offset - 32;
2344
2345// check for LFN entry
2346assert( (fatfs_get_remote_record( DIR_ATTR, base_xp + offset ) == ATTR_LONG_NAME_MASK ),
2347"this directory entry must be a LFN\n");
2348
2349        // invalidate LFN entry
2350        hal_remote_sb( base_xp + offset , 0xE5 );
2351
2352        nb_lfn--;
2353    }     
2354
2355    // copy the modified page to the IOC device
2356    error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );   
2357   
2358    if ( error )
2359    {
2360        printk("\n[ERROR] in %s : cannot update directory on device\n",
2361        __FUNCTION__ );
2362        return -1;
2363    }
2364
2365#if DEBUG_FATFS_REMOVE_DENTRY
2366cycle = (uint32_t)hal_get_cycles();
2367if( DEBUG_FATFS_REMOVE_DENTRY < cycle )
2368printk("\n[%s] thread[%x,%x] exit for <%s> in <%s> directory\n",
2369__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name );
2370#endif
2371
2372    return 0;
2373
2374}  // end fatfs_remove_dentry
2375
2376/////////////////////////////////////////////////////////////////////
2377error_t fatfs_new_dentry_from_mapper( xptr_t         parent_inode_xp,
2378                                      vfs_dentry_t * dentry_ptr )
2379{
2380    uint32_t       cluster_id;        // directory entry first FATFS cluster
2381    uint32_t       size;              // directory entry size
2382    bool_t         is_dir;            // directory entry type (file/dir)
2383    cxy_t          parent_cxy;        // parent inode cluster 
2384    vfs_inode_t  * parent_inode_ptr;  // parent inode local pointer
2385    mapper_t     * parent_mapper;     // pointer on parent directory mapper
2386    xptr_t         child_inode_xp;    // extended pointer on child inode
2387    cxy_t          child_cxy;         // child inode cluster 
2388    vfs_inode_t  * child_inode_ptr;   // child inode local pointer
2389    error_t        error;
2390
2391    uint8_t        buf[32];           // FAT32 directory entry local copy
2392
2393    char           parent_name[CONFIG_VFS_MAX_NAME_LENGTH];     // local parent name copy
2394    char           child_name[CONFIG_VFS_MAX_NAME_LENGTH];      // local child name copy
2395
2396// check arguments
2397assert( (parent_inode_xp != XPTR_NULL)  , "parent_inode_xp is NULL\n" );
2398assert( (dentry_ptr      != NULL ) , "dentry_ptr is NULL\n" );
2399
2400    // get parent inode cluster and local pointer
2401    parent_cxy       = GET_CXY( parent_inode_xp );
2402    parent_inode_ptr = GET_PTR( parent_inode_xp );
2403
2404    // get child inode cluster and pointers
2405    child_inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
2406    child_cxy       = GET_CXY( child_inode_xp );
2407    child_inode_ptr = GET_PTR( child_inode_xp );
2408
2409    // get child and parent names
2410    vfs_inode_get_name( parent_inode_xp , parent_name );
2411    vfs_inode_get_name( child_inode_xp  , child_name );
2412
2413#if DEBUG_FATFS_NEW_DENTRY_FROM
2414uint32_t   cycle = (uint32_t)hal_get_cycles();
2415thread_t * this  = CURRENT_THREAD;
2416if( DEBUG_FATFS_NEW_DENTRY_FROM < cycle )
2417printk("\n[%s]  thread[%x,%x] enter for child <%s> in parent <%s> / cycle %d\n",
2418__FUNCTION__, this->process->pid, this->trdid, child_name , parent_name , cycle );
2419#endif
2420
2421    // get local pointer on parent mapper
2422    parent_mapper = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) );
2423
2424    // try to get pointer and index of directory entry in mapper
2425    uint8_t      * entry = NULL;
2426    uint32_t       index = 0;
2427
2428    error  = fatfs_scan_directory( XPTR( parent_cxy , parent_mapper ),
2429                                   child_name, 
2430                                   &entry,
2431                                   &index );
2432   
2433    // an error can be non fatal, for a new (created) entry
2434    if( error )  return -1;
2435
2436    // get local copy of found directory entry
2437    hal_remote_memcpy( XPTR( local_cxy  , buf ),
2438                       XPTR( parent_cxy , entry ), 32 );
2439
2440    // get relevant infos from directory entry
2441    cluster_id = (fatfs_get_record( DIR_FST_CLUS_HI , buf ) << 16) |
2442                 (fatfs_get_record( DIR_FST_CLUS_LO , buf )      ) ;
2443    is_dir     = (fatfs_get_record( DIR_ATTR        , buf ) & ATTR_DIRECTORY );
2444    size       =  fatfs_get_record( DIR_FILE_SIZE   , buf );
2445
2446    // update the child inode "type", "size", and "extend" fields
2447    vfs_inode_type_t type = (is_dir) ? INODE_TYPE_DIR : INODE_TYPE_FILE;
2448
2449    hal_remote_s32( XPTR( child_cxy , &child_inode_ptr->type   ) , type );
2450    hal_remote_s32( XPTR( child_cxy , &child_inode_ptr->size   ) , size );
2451    hal_remote_s32( XPTR( child_cxy , &child_inode_ptr->extend ) , cluster_id );
2452
2453    // update the dentry "extend" field
2454    hal_remote_spt( XPTR( parent_cxy , &dentry_ptr->extend ) , (void *)(intptr_t)index );
2455
2456#if DEBUG_FATFS_NEW_DENTRY_FROM
2457cycle = (uint32_t)hal_get_cycles();
2458if( DEBUG_FATFS_NEW_DENTRY_FROM < cycle )
2459printk("\n[%s]  thread[%x,%x] exit for <%s> in <%s> / cluster_id %x / size %d\n",
2460__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cluster_id, size );
2461#endif
2462
2463#if (DEBUG_FATFS_NEW_DENTRY_FROM & 1)
2464if( DEBUG_FATFS_NEW_DENTRY_FROM < cycle )
2465fatfs_display_fat( cluster_id , 32 );
2466#endif
2467
2468    return 0;
2469
2470}  // end fatfs_new_dentry_from_mapper()
2471
2472///////////////////////////////////////////////////////////////////
2473error_t fatfs_new_dentry_to_mapper( xptr_t         parent_inode_xp,
2474                                    vfs_dentry_t * dentry_ptr )
2475{
2476    uint32_t       cluster_id;        // directory entry cluster
2477    cxy_t          parent_cxy;        // parent inode cluster identifier 
2478    vfs_inode_t  * parent_inode_ptr;  // child inode local pointer
2479    xptr_t         child_inode_xp;    // extended pointer on child inode
2480    cxy_t          child_cxy;         // child inode cluster identifier 
2481    vfs_inode_t  * child_inode_ptr;   // child inode local pointer
2482    error_t        error;
2483
2484    char           parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
2485    char           child_name[CONFIG_VFS_MAX_NAME_LENGTH];
2486
2487// check arguments
2488assert( (parent_inode_xp != XPTR_NULL) , "parent_inode_xp argument is NULL\n" );
2489assert( (dentry_ptr      != NULL)      , "dentry_ptr argument NULL\n" );
2490
2491    // get child inode cluster and local pointer
2492    parent_cxy       = GET_CXY( parent_inode_xp );
2493    parent_inode_ptr = GET_PTR( parent_inode_xp );
2494
2495    // get child inode cluster and pointers
2496    child_inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
2497    child_cxy       = GET_CXY( child_inode_xp );
2498    child_inode_ptr = GET_PTR( child_inode_xp );
2499
2500    // get child and parent names
2501    vfs_inode_get_name( parent_inode_xp , parent_name );
2502    vfs_inode_get_name( child_inode_xp  , child_name );
2503
2504#if DEBUG_FATFS_NEW_DENTRY_TO_MAP
2505uint32_t   cycle = (uint32_t)hal_get_cycles();
2506thread_t * this  = CURRENT_THREAD;
2507if( DEBUG_FATFS_NEW_DENTRY_TO_MAP < cycle )
2508printk("\n[%s]  thread[%x,%x] enter for child <%s> in parent <%s> / cycle %d\n",
2509__FUNCTION__, this->process->pid, this->trdid, child_name , parent_name , cycle );
2510#endif
2511
2512    // 1. allocate one FATFS cluster (update FAT and FSINFO)
2513    error = fatfs_cluster_alloc( 0 , &cluster_id );
2514
2515    if( error )
2516    {
2517        printk("\n[ERROR] in %s : cannot find a free cluster_id\n",
2518        __FUNCTION__ );
2519        return -1;
2520    }
2521       
2522    // 2. register cluster_id in inode descriptor
2523    hal_remote_spt( XPTR( child_cxy , &child_inode_ptr->extend ),
2524                    (void*)(intptr_t)cluster_id );
2525   
2526    // 3. introduce dentry in the directory mapper
2527    error = fatfs_add_dentry( parent_inode_xp , dentry_ptr );
2528       
2529    if( error )
2530    {
2531        printk("\n[ERROR] in %s : cannot update parent directory mapper\n",
2532        __FUNCTION__ );
2533        // TODO release cluster_id [AG]
2534        return -1;
2535    }
2536
2537#if DEBUG_FATFS_NEW_DENTRY_TO_MAP
2538if( DEBUG_FATFS_NEW_DENTRY_TO_MAP < cycle )
2539printk("\n[%s]  thread[%x,%x] exit for <%s> in <%s> / cluster_id %x\n",
2540__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, cluster_id );
2541#endif
2542
2543    return 0;
2544
2545}  // end fatfs_new_dentry_to mapper()
2546
2547
2548////////////////////////////////////////////////////////////
2549error_t fatfs_update_dentry( xptr_t         parent_inode_xp,
2550                             vfs_dentry_t * dentry_ptr )
2551{
2552    cxy_t         parent_cxy;        // parent directory cluster identifier
2553    vfs_inode_t * parent_inode_ptr;  // extended pointer on parent directory inode
2554    mapper_t    * parent_mapper_ptr; // local pointer on parent directory mapper
2555    xptr_t        parent_mapper_xp;  // extended pointer on parent directory mapper
2556    xptr_t        child_inode_xp;    // extended pointer on child inode
2557    cxy_t         child_cxy;         // child inode cluster identifier
2558    vfs_inode_t * child_inode_ptr;   // extended pointer on child inode
2559
2560    uint32_t      current_size;      // current size in directory mapper
2561    uint32_t      new_size;          // new size (from child inode)
2562   
2563    uint32_t      entry_id;          // directory entry index in parent directory mapper
2564    uint32_t      page_id;           // page_index in parent directory mapper
2565    uint32_t      offset;            // directory entry offset in page
2566    xptr_t        page_xp;           // extended pointer on page descriptor
2567    xptr_t        base_xp;           // extended pointer on page base
2568    xptr_t        entry_xp;          // extended pointer on directory entry
2569
2570    error_t       error;
2571
2572    char       parent_name[CONFIG_VFS_MAX_NAME_LENGTH];
2573    char       child_name[CONFIG_VFS_MAX_NAME_LENGTH];
2574
2575// check arguments
2576assert( (parent_inode_xp  != XPTR_NULL) , "parent_inode_xp argument is NULL\n" );
2577assert( (dentry_ptr       != NULL)      , "dentry_ptr argument is NULL\n" );
2578
2579    // get parent directory cluster ans local pointer
2580    parent_inode_ptr = GET_PTR( parent_inode_xp );
2581    parent_cxy       = GET_CXY( parent_inode_xp );
2582
2583    // get extended pointer on child inode
2584    child_inode_xp  = hal_remote_l64( XPTR( parent_cxy , &dentry_ptr->child_xp ) );
2585
2586    // get child and parent names
2587    vfs_inode_get_name( parent_inode_xp , parent_name );
2588    vfs_inode_get_name( child_inode_xp  , child_name );
2589
2590#if DEBUG_FATFS_UPDATE_DENTRY
2591uint32_t   cycle = (uint32_t)hal_get_cycles();
2592thread_t * this  = CURRENT_THREAD;
2593if( DEBUG_FATFS_UPDATE_DENTRY < cycle )
2594printk("\n[%s]  thread[%x,%x] enter for <%s> in <%s> / new_size %d / cycle %d\n",
2595__FUNCTION__, this->process->pid, this->trdid, child_name, parent_name, new_size, cycle );
2596#endif
2597
2598    // get child inode cluster and local pointer
2599    child_cxy       = GET_CXY( child_inode_xp );
2600    child_inode_ptr = GET_PTR( child_inode_xp );
2601 
2602    // get size from child inode
2603    new_size = hal_remote_l32( XPTR( child_cxy , &child_inode_ptr->size ) );
2604 
2605    // get local and extended pointers on parent directory mapper
2606    parent_mapper_ptr = hal_remote_lpt( XPTR( parent_cxy , &parent_inode_ptr->mapper ) );
2607    parent_mapper_xp  = XPTR( parent_cxy , parent_mapper_ptr );
2608
2609    // get directory entry index from dentry extension
2610    entry_id = (intptr_t)hal_remote_lpt( XPTR( parent_cxy , &dentry_ptr->extend ) );
2611
2612    // get page index and offset in parent directory mapper
2613    page_id  = entry_id >> 7;
2614    offset   = (entry_id & 0x7F) << 5;
2615
2616    // get extended pointers on page descriptor and page base
2617    page_xp = mapper_get_page( parent_mapper_xp , page_id );     
2618    base_xp = ppm_page2base( page_xp ); 
2619
2620    // build extended pointer on directory entry
2621    entry_xp = base_xp + offset;
2622
2623    // get current size from directory mapper
2624    current_size = fatfs_get_remote_record( DIR_FILE_SIZE , entry_xp );
2625
2626    // update dentry in mapper & device only if required
2627    if( new_size != current_size )
2628    {
2629        // set size field in FAT32 directory entry
2630        fatfs_set_remote_record( DIR_FILE_SIZE , entry_xp , new_size );
2631
2632        // synchronously update the modified mapper page on device
2633        error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );
2634
2635        if( error )
2636        { 
2637            printk("\n[ERROR] in %s : cannot update parent directory <%s> on device\n",
2638            __FUNCTION__, parent_name );
2639            return -1;
2640        }
2641    }
2642
2643#if DEBUG_FATFS_UPDATE_DENTRY
2644cycle = (uint32_t)hal_get_cycles();
2645if( DEBUG_FATFS_UPDATE_DENTRY < cycle )
2646printk("\n[%s]  thread[%x,%x] exit for <%s> in <%s> directory / size %d / cycle %d\n",
2647__FUNCTION__, this->process->pid, this->trdid, parent_name, child->name, new_size, cycle );
2648#endif
2649
2650    return 0;
2651
2652}  // end fatfs_update_dentry()
2653
2654///////////////////////////////////////////////////////
2655error_t fatfs_get_user_dir( struct vfs_inode_s * inode,
2656                            struct dirent      * array, 
2657                            uint32_t             max_dirent,
2658                            uint32_t             min_dentry,
2659                            bool_t               detailed,
2660                            uint32_t           * entries,
2661                            bool_t             * done )
2662{
2663    // Two embedded loops to scan the directory mapper:
2664    // - scan the parent directory mapper pages starting always from page 0
2665    // - scan the 32 bytes NORMAL/LFN directory entries in each page
2666    // Only valid dentries are copied : dentry_id >= min_dentry && dirent_id < dirent_max
2667
2668#if DEBUG_FATFS_GET_USER_DIR
2669char       inode_name[CONFIG_VFS_MAX_NAME_LENGTH];
2670uint32_t   cycle = (uint32_t)hal_get_cycles();
2671thread_t * this  = CURRENT_THREAD;
2672vfs_inode_get_name( XPTR( local_cxy , inode ) , inode_name );
2673if( DEBUG_FATFS_GET_USER_DIR < cycle )
2674printk("\n[%s]  thread[%x,%x] enter for inode <%s> / cycle %d\n",
2675__FUNCTION__, this->process->pid, this->trdid, inode_name , cycle );
2676#endif
2677
2678    mapper_t * mapper    = inode->mapper;
2679    xptr_t     mapper_xp = XPTR( local_cxy , mapper );
2680
2681// check mapper pointer
2682assert( (mapper != NULL) , "mapper is NULL\n");
2683   
2684// TODO handle the detailed flag
2685assert( (detailed == false), "detailed argument not supported/n");
2686
2687    char       cname[CONFIG_VFS_MAX_NAME_LENGTH];  // name extracted from each dentry
2688
2689    char       lfn1[16];           // buffer for one partial cname
2690    char       lfn2[16];           // buffer for one partial cname
2691    char       lfn3[16];           // buffer for one partial cname
2692    xptr_t     page_xp;            // extended pointer on page descriptor
2693    xptr_t     base_xp;            // extended pointer on page base
2694    uint8_t  * base;               // local pointer on page base
2695    uint8_t    attr;               // directory entry ATTR field
2696    uint8_t    ord;                // directory entry ORD field
2697    uint32_t   seq;                // sequence index
2698    uint32_t   lfn       = 0;      // LFN entries number
2699    uint32_t   offset    = 0;      // byte offset in page
2700    uint32_t   page_id   = 0;      // page index in mapper
2701    uint32_t   dentry_id = 0;      // valid (i.e. copied) dentry index in mapper
2702    uint32_t   dirent_id = 0;      // slot index in dirent array to initialize
2703    bool_t     end       = false;  // true if end of directory found
2704
2705    // loop on mapper pages
2706    while ( (end == false) && (dirent_id < max_dirent) )
2707    {
2708        // get one page from mapper
2709        page_xp = mapper_get_page( mapper_xp , page_id );
2710
2711        if( page_xp == XPTR_NULL) return -1;
2712
2713        // get page base
2714        base_xp = ppm_page2base( page_xp );
2715        base    = (uint8_t *)GET_PTR( base_xp );
2716
2717#if (DEBUG_FATFS_GET_USER_DIR & 0x1)
2718if( DEBUG_FATFS_GET_USER_DIR < cycle )
2719mapper_display_page( mapper_xp , page_id , 256 );
2720#endif
2721        // loop on NORMAL/LFN (32 bytes) directory entries in this page
2722        while( (end == false) && (offset < 4096) )
2723        {
2724            // compute condition to copy one dentry to dirent array
2725            bool_t valid = (dentry_id >= min_dentry) && (dirent_id <  max_dirent );
2726
2727            attr = fatfs_get_record( DIR_ATTR , base + offset );   
2728            ord  = fatfs_get_record( LDIR_ORD , base + offset );   
2729
2730            if (ord == NO_MORE_ENTRY)                 // no more entry => break
2731            {
2732                end = true;
2733            }
2734            else if ( ord == FREE_ENTRY )             // free entry => skip
2735            {
2736                offset = offset + 32;
2737            }
2738            else if ( attr == 0x28 )                  // volune_id => skip
2739            {
2740                offset = offset + 32;
2741            }
2742            else if ( attr == ATTR_LONG_NAME_MASK )   // LFN entry
2743            {
2744                if( valid )
2745                {
2746                    // get partial cname
2747                    seq = ord & 0x3;
2748                    lfn = (seq > lfn) ? seq : lfn;   
2749                    if      ( seq == 1 ) fatfs_get_name_from_long( base + offset, lfn1 );
2750                    else if ( seq == 2 ) fatfs_get_name_from_long( base + offset, lfn2 );
2751                    else if ( seq == 3 ) fatfs_get_name_from_long( base + offset, lfn3 );
2752                }
2753                offset = offset + 32;
2754            }
2755            else                                     // NORMAL entry
2756            {
2757                // increment dentry_id
2758                dentry_id++;
2759
2760                if( valid )
2761                {
2762                    // build the complete cname
2763                    if      ( lfn == 0 )
2764                    {
2765                        fatfs_get_name_from_short( base + offset , cname );
2766                    }
2767                    else if ( lfn == 1 )
2768                    {
2769                        strcpy( cname      , lfn1 );
2770                    }   
2771                    else if ( lfn == 2 ) 
2772                    {
2773                        strcpy( cname      , lfn1 );
2774                        strcpy( cname + 13 , lfn2 );
2775                    }
2776                    else if ( lfn == 3 ) 
2777                    {
2778                        strcpy( cname      , lfn1 );
2779                        strcpy( cname + 13 , lfn2 );
2780                        strcpy( cname + 26 , lfn3 );
2781                    }
2782                   
2783                    // copy cname into dirent array
2784                    strcpy( array[dirent_id].d_name , cname ); 
2785
2786                    // increment dirent_id
2787                    dirent_id++;
2788                }
2789                offset = offset + 32;
2790                lfn    = 0;
2791            }
2792        }  // end loop on directory entries in page
2793
2794        page_id++;
2795        offset = 0;
2796
2797    }  // end loop on pages
2798
2799    // return result of scan
2800    *done    = end;
2801    *entries = dirent_id;
2802
2803#if DEBUG_FATFS_GET_USER_DIR
2804cycle = (uint32_t)hal_get_cycles();
2805if( DEBUG_FATFS_GET_USER_DIR < cycle )
2806printk("\n[%s]  thread[%x,%x] exit for inode <%s> / %d entries / cycle %d\n",
2807__FUNCTION__, this->process->pid, this->trdid, inode_name, dirent_id, cycle );
2808#endif
2809
2810    return 0;
2811
2812}  // end fatfs_get_user_dir()
2813
2814///////////////////////////////////////////
2815error_t fatfs_sync_inode( xptr_t inode_xp )
2816{
2817    cxy_t         inode_cxy;      // remote inode cluster
2818    vfs_inode_t * inode_ptr;      // remote inode local pointer
2819    mapper_t    * mapper;         // remote inode mapper local pointer
2820    uint32_t      size;           // remote inode size in bytes
2821    uint32_t      type;           // remote inode type
2822    xptr_t        rt_xp;          // extended pointer on mapper radix tree
2823    uint32_t      npages;         // number of pages in mapper
2824    uint32_t      page_id;        // current page index in mapper
2825    xptr_t        page_xp;        // extended pointer on current page
2826    page_t      * page_ptr;       // local pointer on current page
2827    uint32_t      flags;          // current page flags
2828    error_t       error;
2829
2830// check inode pointer and cluster index
2831assert( (inode_xp != XPTR_NULL)          , "inode pointer undefined\n" );
2832
2833#if DEBUG_FATFS_SYNC_INODE
2834char       name[CONFIG_VFS_MAX_NAME_LENGTH];
2835uint32_t   cycle = (uint32_t)hal_get_cycles();
2836thread_t * this  = CURRENT_THREAD;
2837vfs_inode_get_name( inode_xp , name );
2838if( DEBUG_FATFS_SYNC_INODE < cycle )
2839printk("\n[%s] thread[%x,%x] enter for <%s> / cycle %d\n",
2840__FUNCTION__ , this->process->pid, this->trdid, name, cycle );
2841#endif
2842
2843    // get inode cluster and local pointer
2844    inode_cxy = GET_CXY( inode_xp );
2845    inode_ptr = GET_PTR( inode_xp );
2846
2847    //get inode mapper pointer
2848    mapper    = hal_remote_lpt( XPTR( inode_cxy , &inode_ptr->mapper ) );
2849
2850assert( (mapper != NULL) , "mapper pointer is NULL\n" );     
2851
2852    // get inode type and size
2853    size = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->size ) );
2854    type = hal_remote_l32( XPTR( inode_cxy , &inode_ptr->type ) );
2855
2856assert( (type == INODE_TYPE_FILE) , "inode is not a file\n" );     
2857
2858    // compute number of pages
2859    npages = size >> CONFIG_PPM_PAGE_SHIFT;
2860    if( size & CONFIG_PPM_PAGE_MASK ) npages++; 
2861         
2862    // build pointers on mapper radix tree
2863    rt_xp = XPTR( inode_cxy , &mapper->rt );
2864
2865    // scan all pages
2866    for( page_id = 0 ; page_id < npages ; page_id++ )
2867    {
2868        // get page descriptor from mapper
2869        page_xp = grdxt_remote_lookup( rt_xp , page_id );
2870
2871        // check all existing pages
2872        if ( page_xp != XPTR_NULL )
2873        {
2874            // get page cluster and local pointer
2875            page_ptr = GET_PTR( page_xp );
2876
2877            // get page flags
2878            flags = hal_remote_l32( XPTR( inode_cxy , &page_ptr->flags ) );
2879   
2880            if ( flags & PG_DIRTY )
2881            {
2882
2883#if (DEBUG_FATFS_SYNC_INODE & 1)
2884if( DEBUG_FATFS_SYNC_INODE < cycle )
2885printk("\n[%s] thread[%x,%x] synchronizes page %d of <%s> mapper to IOC device\n",
2886__FUNCTION__, page_id, name );
2887#endif
2888                // move page from mapper to device
2889                error = fatfs_move_page( page_xp , IOC_WRITE );
2890
2891                if ( error )  return -1;
2892
2893                // reset page dirty flag
2894                ppm_page_undo_dirty( page_xp );
2895            }
2896        }
2897    }  // end loop on pages
2898
2899#if DEBUG_FATFS_SYNC_INODE
2900cycle = (uint32_t)hal_get_cycles();
2901if( DEBUG_FATFS_SYNC_INODE < cycle )
2902printk("\n[%s] thread[%x,%x] exit for <%s>\n",
2903__FUNCTION__ , this->process->pid, this->trdid, name );
2904#endif
2905
2906    return 0;
2907
2908}  // end fatfs_sync_inode()
2909
2910//////////////////////////////
2911error_t fatfs_sync_fat( void )
2912{
2913
2914    fatfs_ctx_t * fatfs_ctx; 
2915    cxy_t         fat_cxy;
2916    mapper_t    * mapper_ptr;
2917    xptr_t        mapper_xp;
2918    uint32_t      start_page_id;
2919    uint32_t      found_page_id;
2920    page_t      * page_ptr;
2921    xptr_t        page_xp;
2922    uint32_t      flags;
2923    error_t       error;
2924
2925#if DEBUG_FATFS_SYNC_FAT
2926uint32_t   cycle = (uint32_t)hal_get_cycles();
2927thread_t * this  = CURRENT_THREAD;
2928if( DEBUG_FATFS_SYNC_FAT < cycle )
2929printk("\n[%s] thread[%x,%x] enter / cycle %d\n",
2930__FUNCTION__ , this->process->pid, this->trdid, cycle );
2931#endif
2932   
2933    // get FAT cluster
2934    fat_cxy = CONFIG_VFS_ROOT_CXY;
2935   
2936    // get FAT mapper pointers 
2937    fatfs_ctx  = fs_context[FS_TYPE_FATFS].extend;
2938    mapper_ptr = fatfs_ctx->fat_mapper;
2939    mapper_xp  = XPTR( fat_cxy , mapper_ptr );
2940
2941    // get pointers on remote FAT mapper radix tree
2942    grdxt_t  * rt_ptr = &mapper_ptr->rt;
2943    xptr_t     rt_xp  = XPTR( fat_cxy , rt_ptr );
2944
2945    // initialise page_id
2946    start_page_id = 0;
2947
2948    // scan FAT mapper
2949    while( 1 )
2950    {
2951        // get one page
2952        page_xp = grdxt_remote_get_first( rt_xp , start_page_id , &found_page_id );
2953
2954        // exit loop when no more page found
2955        if ( page_xp != XPTR_NULL ) break;
2956
2957        // get page flags
2958        page_ptr = GET_PTR( page_xp );
2959        flags    = hal_remote_l32( XPTR( fat_cxy , &page_ptr->flags ) );
2960
2961        if ( flags & PG_DIRTY )
2962        {
2963
2964#if (DEBUG_FATFS_SYNC_FAT & 1)
2965if( DEBUG_FATFS_SYNC_FAT < cycle )
2966printk("\n[%s] thread[%x,%x] synchronizes page %d from FAT mapper to IOC device\n",
2967__FUNCTION__, page_id );
2968#endif
2969            // move page from mapper to device
2970            error = fatfs_move_page( page_xp , IOC_SYNC_WRITE );
2971
2972            if ( error )  return -1;
2973
2974            // reset page dirty flag
2975            ppm_page_undo_dirty( page_xp );
2976        }
2977
2978        // update loop variable
2979        start_page_id = found_page_id + 1;
2980
2981    }  // end loop on pages
2982
2983#if DEBUG_FATFS_SYNC_FAT
2984cycle = (uint32_t)hal_get_cycles();
2985if( DEBUG_FATFS_SYNC_FAT < cycle )
2986printk("\n[%s] thread[%x,%x] exit\n",
2987__FUNCTION__ , this->process->pid, this->trdid );
2988#endif
2989
2990    return 0;
2991
2992}  // end fatfs_sync_fat()
2993
2994//////////////////////////////////////////////
2995error_t fatfs_release_inode( xptr_t inode_xp )
2996{
2997    vfs_ctx_t   * vfs_ctx;           // local pointer on VFS context (same in all clusters)
2998    cxy_t         fat_cxy;           // FAT cluster identifier
2999    fatfs_ctx_t * fatfs_ctx_ptr;     // local pointer on FATFS context in FAT cluster
3000    xptr_t        fatfs_ctx_xp;      // extended pointer on FATFS-context in FAT cluster
3001    mapper_t    * fat_mapper_ptr;    // local pointer on FAT mapper
3002    xptr_t        fat_mapper_xp;     // extended pointer on FAT mapper
3003    xptr_t        lock_xp;           // extended pointer on lock protecting FAT.
3004    xptr_t        first_xp;          // extended pointer on inode extension
3005    uint32_t      first_cluster_id;  // first cluster index for released inode
3006    vfs_inode_t * inode_ptr;         // local pointer on target inode
3007    cxy_t         inode_cxy;         // target inode cluster identifier
3008    error_t       error;
3009
3010// check inode pointer
3011assert( (inode_xp != XPTR_NULL) , "inode pointer is NULL\n" );
3012
3013    // get inode cluster and local pointer
3014    inode_ptr     = GET_PTR( inode_xp );
3015    inode_cxy     = GET_CXY( inode_xp );
3016
3017    // get first_cluster_id from inode extension
3018    first_xp         = XPTR( inode_cxy , &inode_ptr->extend );
3019    first_cluster_id = (uint32_t)(intptr_t)hal_remote_lpt( first_xp );
3020
3021// check first cluster index
3022assert( (first_cluster_id != 0) , "inode extend is NULL\n" );
3023
3024#if DEBUG_FATFS_RELEASE_INODE
3025char       name[CONFIG_VFS_MAX_NAME_LENGTH];
3026uint32_t   cycle = (uint32_t)hal_get_cycles();
3027thread_t * this  = CURRENT_THREAD;
3028vfs_inode_get_name( inode_xp , name );
3029if( DEBUG_FATFS_RELEASE_INODE < cycle )
3030printk("\n[%s] thread[%x,%x] enter for <%s> / first_cluster_id %x / cycle %d\n",
3031__FUNCTION__ , this->process->pid, this->trdid, name, first_cluster_id, cycle );
3032#endif
3033
3034    // get local pointer on VFS context (same in all clusters)
3035    vfs_ctx = &fs_context[FS_TYPE_FATFS];
3036
3037    // get FAT cluster
3038    fat_cxy = CONFIG_VFS_ROOT_CXY;
3039
3040    // get pointers on FATFS context in FAT cluster
3041    fatfs_ctx_ptr = hal_remote_lpt( XPTR( fat_cxy , &vfs_ctx->extend ) );
3042    fatfs_ctx_xp  = XPTR( fat_cxy , fatfs_ctx_ptr ); 
3043
3044    // get FAT mapper pointers
3045    fat_mapper_ptr = hal_remote_lpt( XPTR( fat_cxy , &fatfs_ctx_ptr->fat_mapper ) );
3046        fat_mapper_xp  = XPTR( fat_cxy , fat_mapper_ptr );
3047   
3048    // build extended pointer on FAT lock in FAT cluster
3049    lock_xp = XPTR( fat_cxy , &fatfs_ctx_ptr->lock );
3050
3051    // take FAT lock in write mode
3052    remote_rwlock_wr_acquire( lock_xp );
3053
3054#if (DEBUG_FATFS_RELEASE_INODE & 0x11 == 0x11)
3055mapper_display_page( fat_mapper_xp , 0 , 4096 );
3056#endif
3057
3058    // call the recursive function to release all clusters from FAT mapper
3059    uint32_t dirty_page_min = 0xFFFFFFFF;
3060    uint32_t dirty_page_max = 0;
3061
3062    if ( fatfs_recursive_release( fat_mapper_xp,
3063                                  fatfs_ctx_xp,
3064                                  first_cluster_id,
3065                                  &dirty_page_min,
3066                                  &dirty_page_max ) )
3067    {
3068        printk("\n[ERROR] in %s : cannot update FAT mapper\n", __FUNCTION__ );
3069        remote_rwlock_wr_release( lock_xp );
3070        return -1;
3071    }
3072
3073#if (DEBUG_FATFS_RELEASE_INODE & 1)
3074if( DEBUG_FATFS_RELEASE_INODE < cycle )
3075printk("\n[%s] inode <%s> removed from FAT mapper\n", __FUNCTION__, name );
3076#endif
3077
3078#if (DEBUG_FATFS_RELEASE_INODE & 0x11 == 0x11)
3079mapper_display_page( fat_mapper_xp , 0 , 4096 );
3080#endif
3081
3082    // update FAT on IOC device (from FAT mapper)
3083    error = fatfs_update_ioc_fat( fatfs_ctx_xp,
3084                                  dirty_page_min,
3085                                  dirty_page_max );
3086
3087    if( error )
3088    {
3089        printk("\n[ERROR] in %s : cannot update FAT on IOC device\n", __FUNCTION__ );
3090        remote_rwlock_wr_release( lock_xp );
3091        return -1;
3092    }
3093
3094#if (DEBUG_FATFS_RELEASE_INODE & 1)
3095if( DEBUG_FATFS_RELEASE_INODE < cycle )
3096printk("\n[%s] inode <%s> removed from FAT on IOC device\n", __FUNCTION__, name );
3097#endif
3098
3099    // update FS-INFO on IOC device (from FATFS context)
3100    error = fatfs_update_ioc_fsinfo( fatfs_ctx_xp );
3101
3102    if( error )
3103    {
3104        printk("\n[ERROR] in %s: cannot update FSINFO on IOC device\n", __FUNCTION__ );
3105        remote_rwlock_wr_release( lock_xp );
3106        return -1;
3107    }
3108
3109#if (DEBUG_FATFS_RELEASE_INODE & 1)
3110if( DEBUG_FATFS_RELEASE_INODE < cycle )
3111printk("\n[%s] updated FS_INFO on IOC device for >%s>\n", __FUNCTION__, name );
3112#endif
3113
3114    // release FAT lock
3115    remote_rwlock_wr_release( lock_xp );
3116
3117#if DEBUG_FATFS_RELEASE_INODE
3118cycle = (uint32_t)hal_get_cycles();
3119if( DEBUG_FATFS_RELEASE_INODE < cycle )
3120printk("\n[%s] thread[%x,%x] removed <%s> inode from FATFS / cycle %d\n",
3121__FUNCTION__ , this->process->pid, this->trdid, name, cycle );
3122#endif
3123
3124    return 0;
3125
3126}  // end fatfs_release_inode()
3127
3128/////////////////////////////////////////////////
3129error_t fatfs_move_page( xptr_t          page_xp,
3130                         ioc_cmd_type_t  cmd_type )
3131{
3132    error_t       error = 0;
3133
3134    vfs_inode_t * inode_ptr;
3135    mapper_t    * mapper_ptr;     
3136    uint32_t      page_id;     // page index in mapper
3137
3138#if DEBUG_FATFS_MOVE_PAGE
3139uint32_t   cycle = (uint32_t)hal_get_cycles();
3140thread_t * this  = CURRENT_THREAD;
3141char       name[CONFIG_VFS_MAX_NAME_LENGTH];
3142#endif
3143
3144    // get page cluster and local pointer
3145    cxy_t    page_cxy = GET_CXY( page_xp );
3146    page_t * page_ptr = GET_PTR( page_xp );
3147
3148    // get mapper pointer and page index from page descriptor
3149    mapper_ptr = hal_remote_lpt( XPTR( page_cxy , &page_ptr->mapper ) );
3150    page_id    = hal_remote_l32( XPTR( page_cxy , &page_ptr->index ) );
3151
3152    // get pointer on local FATFS context
3153    fatfs_ctx_t * fatfs_ctx = fs_context[FS_TYPE_FATFS].extend;
3154
3155    // get page base address
3156    xptr_t    buffer_xp  = ppm_page2base( page_xp );
3157 
3158    // get inode pointer from mapper
3159    inode_ptr  = hal_remote_lpt( XPTR( page_cxy , &mapper_ptr->inode ) );
3160
3161    //////////////////////////////  FAT mapper  /////////////////////////////////////////
3162    if( inode_ptr == NULL )
3163    {
3164
3165#if DEBUG_FATFS_MOVE_PAGE
3166if( DEBUG_FATFS_MOVE_PAGE < cycle )
3167printk("\n[%s] thread[%x,%x] enters %s for  page %d in FAT mapper / cycle %d\n",
3168__FUNCTION__, this->process->pid, this->trdid, dev_ioc_cmd_str(cmd_type), page_id, cycle );
3169#endif
3170        // get lba from FATFS context and page_id
3171        uint32_t      lba = fatfs_ctx->fat_begin_lba + (page_id << 3);
3172 
3173        // access IOC device
3174        if     (cmd_type == IOC_SYNC_WRITE) error = dev_ioc_sync_write( buffer_xp, lba, 8 );
3175        else if(cmd_type == IOC_SYNC_READ ) error = dev_ioc_sync_read( buffer_xp, lba, 8 );
3176        else
3177        {
3178            printk("\n[ERROR] in %s : illegal asynchronous FAT access\n", __FUNCTION__ );
3179        }
3180
3181        if( error )
3182        {
3183            printk("\n[ERROR] in %s : cannot access IOC device\n", __FUNCTION__ );
3184            return -1;
3185        }
3186
3187#if DEBUG_FATFS_MOVE_PAGE
3188if( DEBUG_FATFS_MOVE_PAGE < cycle )
3189printk("\n[%s] thread[%x,%x] exit %s for page %d in FAT mapper\n",
3190__FUNCTION__, this->process->pid, this->trdid, dev_ioc_cmd_str(cmd_type), page_id );
3191#endif
3192
3193    }
3194    /////////////////////////  inode mapper  ////////////////////////////////////////////
3195    else                       
3196    {
3197
3198#if DEBUG_FATFS_MOVE_PAGE
3199if( DEBUG_FATFS_MOVE_PAGE < cycle )
3200{
3201    vfs_inode_get_name( XPTR( page_cxy , inode_ptr ) , name );
3202    printk("\n[%s] thread[%x,%x] enters %s for page %d in <%s> mapper / cycle %d\n",
3203    __FUNCTION__, this->process->pid, this->trdid, 
3204    dev_ioc_cmd_str( cmd_type ), page_id, name, cycle );
3205}
3206#endif
3207
3208        uint32_t  searched_cluster_id;
3209        uint32_t  first_cluster_id;
3210
3211        // get first_cluster_id from inode extension
3212        void * extend    = hal_remote_lpt( XPTR( page_cxy , &inode_ptr->extend ) );
3213        first_cluster_id = (uint32_t)(intptr_t)extend;
3214
3215        // compute searched_cluster_id
3216        if( page_id == 0 )            // no need to access FAT mapper
3217        {
3218            // searched cluster is first cluster
3219            searched_cluster_id = first_cluster_id;
3220        }
3221        else                        // FAT mapper access required
3222        {
3223            // scan FAT mapper to get searched_cluster_id
3224            error = fatfs_get_cluster( 0,                    // first page in mapper
3225                                       first_cluster_id,
3226                                       page_id,
3227                                       &searched_cluster_id );
3228            if( error )
3229            {
3230                printk("\n[ERROR] in %s : cannot get cluster_id\n", __FUNCTION__ );
3231                return -1;
3232            }
3233        }
3234
3235        // get lba for searched_cluster
3236        uint32_t lba = fatfs_lba_from_cluster( fatfs_ctx , searched_cluster_id );
3237
3238        // access IOC device
3239        if     (cmd_type == IOC_WRITE     ) error = dev_ioc_write( buffer_xp, lba, 8 );
3240        else if(cmd_type == IOC_READ      ) error = dev_ioc_read( buffer_xp, lba, 8 );
3241        else if(cmd_type == IOC_SYNC_READ ) error = dev_ioc_sync_read( buffer_xp, lba, 8 );
3242        else if(cmd_type == IOC_SYNC_WRITE) error = dev_ioc_sync_write( buffer_xp, lba, 8 );
3243        else
3244        {
3245            printk("\n[ERROR] in %s : illegal cmd_type\n", __FUNCTION__ );
3246        }
3247
3248        if( error )
3249        {
3250            printk("\n[ERROR] in %s : cannot access device\n", __FUNCTION__ );
3251            return -1;
3252        }
3253
3254#if DEBUG_FATFS_MOVE_PAGE
3255if( DEBUG_FATFS_MOVE_PAGE < cycle )
3256{
3257    printk("\n[%s] thread[%x,%x] exit %s for page %d in <%s> mapper / cluster_id %x\n",
3258    __FUNCTION__, this->process->pid, this->trdid, 
3259    dev_ioc_cmd_str( cmd_type ), page_id, name, searched_cluster_id );
3260}
3261#endif
3262
3263#if (DEBUG_FATFS_MOVE_PAGE & 1)
3264if( DEBUG_FATFS_MOVE_PAGE < cycle )
3265fatfs_display_fat( searched_cluster_id , 64 );
3266#endif
3267
3268    }
3269
3270    return 0;
3271
3272}  // end fatfs_move_page()
3273
3274
Note: See TracBrowser for help on using the repository browser.