source: trunk/kernel/vfs/fatfs.c @ 210

Last change on this file since 210 was 188, checked in by alain, 7 years ago

Redefine the PIC device API.

File size: 15.2 KB
Line 
1/*
2 * fatfs.c - FATFS file system API implementation.
3 *
4 * Author    Mohamed Lamine Karaoui (2014,2015)
5 *           Alain Greiner (2016,2017)
6 *
7 * Copyright (c) UPMC Sorbonne Universites
8 *
9 * This file is part of ALMOS-MKH.
10 *
11 * ALMOS-MKH is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; version 2.0 of the License.
14 *
15 * ALMOS-MKH is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25
26#include <hal_types.h>
27#include <hal_special.h>
28#include <printk.h>
29#include <kmem.h>
30#include <ppm.h>
31#include <vfs.h>
32#include <rpc.h>
33#include <mapper.h>
34#include <cluster.h>
35#include <dev_ioc.h>
36#include <fatfs.h>
37
38
39//////////////////////////////////////////////////////////////////////////////////////////
40//          Extern  variables         
41//////////////////////////////////////////////////////////////////////////////////////////
42
43extern vfs_ctx_t          fs_context[FS_TYPES_NR];   // allocated in vfs.c file
44
45extern remote_barrier_t   global_barrier;            // allocated in kernel_init.c
46 
47//////////////////////////////////////////////////////////////////////////////////////////
48//              FATFS private functions
49//////////////////////////////////////////////////////////////////////////////////////////
50
51//////////////////////////////////////////////////////////////////////////////////////////
52// This function returns the LBA of the first sector of a FAT cluster.
53// This function can be called by any thread running in any cluster.
54//////////////////////////////////////////////////////////////////////////////////////////
55// @ ctx          :     pointer on FATFS context.
56// @ cluster  : cluster index in FATFS.
57// @ return the lba value.
58//////////////////////////////////////////////////////////////////////////////////////////
59static inline uint32_t fatfs_lba_from_cluster( fatfs_ctx_t * ctx,
60                                               uint32_t      cluster )
61{
62    return (ctx->cluster_begin_lba + ((cluster - 2) << 3));
63}
64
65/////////////////////////////////////////////
66error_t fatfs_get_cluster( mapper_t * mapper,
67                           uint32_t   first_cluster,
68                           uint32_t   searched_page,
69                           uint32_t * cluster )
70{
71    page_t   * current_page_desc;      // pointer on current page descriptor
72    uint32_t * current_page_buffer;    // pointer on current page (array of uint32_t)
73    uint32_t   current_page_index;     // index of current page in mapper
74    uint32_t   current_page_offset;    // offset of slot in current page
75    uint32_t   page_count_in_file;     // index of page in file (index in linked list)
76    uint32_t   current_cluster;        // content of current FAT slot
77
78    // compute number of FAT slots per PPM page
79    uint32_t slots_per_page = CONFIG_PPM_PAGE_SIZE >> 2;
80
81    // initialize loop variable
82    current_page_index  = first_cluster / slots_per_page;
83    current_page_offset = first_cluster % slots_per_page;
84    page_count_in_file  = 0;
85
86    // scan FAT (i.e. traverse FAT linked list)
87    while( page_count_in_file <= searched_page )
88    {
89        // get pointer on current page descriptor
90        current_page_desc = mapper_get_page( mapper , current_page_index );
91
92        if( current_page_desc == NULL ) return EIO;
93
94        // get pointer on buffer for current page
95        current_page_buffer = (uint32_t *)ppm_page2vaddr( current_page_desc );
96
97        // get FAT slot content
98        current_cluster = current_page_buffer[current_page_offset];
99
100        // update loop variables
101        current_page_index  = current_cluster / slots_per_page;
102        current_page_offset = current_cluster % slots_per_page;
103        page_count_in_file++;
104    }
105   
106    // return success
107    *cluster = current_cluster;
108    return 0;
109
110}  // end fatfs_get_cluster()
111
112///////////////////////////////////////////////////////////////////////////////////////
113// This static function return an integer record value (one, two, or four bytes)
114// from a memory buffer, taking into account endianness.
115///////////////////////////////////////////////////////////////////////////////////////
116// @ offset        : first byte of record in buffer.
117// @ size          : record length in bytes (1/2/4).
118// @ buffer        : pointer on buffer base.
119// @ little endian : the most significant byte has the highest address when true.
120// @ return the integer value in a 32 bits word.
121///////////////////////////////////////////////////////////////////////////////////////
122static uint32_t get_record_from_buffer( uint32_t    offset,
123                                        uint32_t    size,
124                                        uint8_t   * buffer,
125                                        uint32_t    little_endian )
126{
127    uint32_t n;
128    uint32_t res  = 0;
129
130    if ( little_endian)
131    {
132        for( n = size ; n > 0 ; n-- ) res = (res<<8) | buffer[offset+n-1];
133    }
134    else
135    {
136        for( n = 0 ; n < size ; n++ ) res = (res<<8) | buffer[offset+n];
137    }
138    return res;
139
140}  // end get_record_from_buffer()
141
142
143
144////////////////////////////////////////////////////////////////////////////////////////
145// This function returns the FATFS cluster index of a page identified by its page
146// index in the file, using the FAT mapper. It scans the FAT mapper, starting from the
147// FATFS cluster index allocated to the first page of the file, until it reaches the
148// searched page. The FAT mapper is automatically updated in case of miss.
149// This function can be called by any thread running in any cluster, as it uses the
150// RPC_FATFS_GET_CLUSTER to access the remote FAT mapper if required.
151// We use a RPC to scan the FAT because the RPC_FIFO will avoid contention
152// in the cluster containing the FAT mapper, and the RPC latency is not critical
153// compared to the device access latency.
154////////////////////////////////////////////////////////////////////////////////////////
155// @ ctx               : pointer on local FATFS context.
156// @ first_cluster : first cluster allocated to a file in FATFS.
157// @ page_index    : index of searched page in file (one page occupies one cluster).
158// @ cluster_index : [out] pointer on buffer for FATFS cluster index.
159// @ return 0 if success / return EIO if a FAT cluster miss cannot be solved.
160////////////////////////////////////////////////////////////////////////////////////////
161static error_t fatfs_cluster_from_index( fatfs_ctx_t * ctx,
162                                         uint32_t      first_cluster,
163                                         uint32_t      page_index,
164                                         uint32_t    * cluster_index )
165{
166    uint32_t searched_cluster;   // searched FATFS cluster index
167    error_t  error;
168
169    // get extended pointer on FAT mapper
170    xptr_t fat_mapper_xp = ctx->fat_mapper_xp;
171
172    // get cluster cxy and local pointer on FAT mapper
173    cxy_t      fat_mapper_cxy = GET_CXY( fat_mapper_xp );
174    mapper_t * fat_mapper_ptr = (mapper_t *)GET_PTR( fat_mapper_xp );
175
176    if( fat_mapper_cxy == local_cxy )    // FAT mapper is local
177    {
178        error = fatfs_get_cluster( fat_mapper_ptr,
179                                   first_cluster,
180                                   page_index,
181                                   &searched_cluster );
182    }
183    else                                 // FAT mapper is remote
184    {
185        rpc_fatfs_get_cluster_client( fat_mapper_cxy,
186                                      fat_mapper_ptr,
187                                      first_cluster,
188                                      page_index,
189                                      &searched_cluster,
190                                      &error );
191    }
192   
193    if( error )
194    {
195        printk("\n[ERROR] in %s : cannot access FAT\n", __FUNCTION__ );
196        return error;
197    }
198
199    // return success
200    *cluster_index = searched_cluster;
201    return 0;
202
203}  // end fatfs_cluster_from_index()
204
205
206
207
208///////////////////////////////////////////////////////////////////////////////////////
209// Generic API : the following functions are called by the kernel
210//               and must be defined by all supported file systems.
211///////////////////////////////////////////////////////////////////////////////////////
212
213///////////////////////////////
214fatfs_ctx_t * fatfs_ctx_alloc()
215{
216    kmem_req_t    req;
217        req.type    = KMEM_FATFS_CTX;
218        req.size    = sizeof(fatfs_ctx_t);
219    req.flags   = AF_KERNEL | AF_ZERO;
220
221        return (fatfs_ctx_t *)kmem_alloc( &req );
222}
223
224//////////////////////////////////////////////
225void fatfs_ctx_init( fatfs_ctx_t * fatfs_ctx )
226{
227    error_t       error;
228    kmem_req_t    req;
229    uint8_t     * buffer;
230
231    fatfs_dmsg("\n[INFO] %s : enters at cycle %d\n", 
232               __FUNCTION__ , hal_get_cycles() );
233
234    // allocate memory for FATFS context
235        req.type    = KMEM_FATFS_CTX;
236        req.size    = sizeof(fatfs_ctx_t);
237    req.flags   = AF_KERNEL | AF_ZERO;
238
239        fatfs_ctx = (fatfs_ctx_t *)kmem_alloc( &req );
240
241    nolock_assert( (fatfs_ctx != NULL) , __FUNCTION__ ,
242                   "cannot allocate memory for FATFS context\n" );
243
244    // allocate a 512 bytes buffer to store the boot record
245        req.type    = KMEM_512_BYTES;
246    req.flags   = AF_KERNEL | AF_ZERO;
247        buffer      = (uint8_t *)kmem_alloc( &req );
248
249    nolock_assert( (buffer != NULL) , __FUNCTION__ ,
250                   "cannot allocate memory for 512 bytes buffer\n" );
251     
252    // load the boot record from device
253    // using a synchronous access to IOC device 
254    error = dev_ioc_sync_read( buffer , 0 , 1 );
255
256    nolock_assert( (error == 0) , __FUNCTION__ ,
257                   "cannot access boot record\n" );
258
259#if CONFIG_FATFS_DEBUG
260    uint32_t   line;
261    uint32_t   byte = 0;
262    printk("\n*** boot record at cycle %d ***\n", hal_get_cycles() );
263    for ( line = 0 ; line < 32 ; line++ )
264    {
265        printk(" %X | %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x |\n",
266               byte,
267               buffer[byte+ 0],buffer[byte+ 1],buffer[byte+ 2],buffer[byte+ 3],
268               buffer[byte+ 4],buffer[byte+ 5],buffer[byte+ 6],buffer[byte+ 7],
269               buffer[byte+ 8],buffer[byte+ 9],buffer[byte+10],buffer[byte+11],
270               buffer[byte+12],buffer[byte+13],buffer[byte+14],buffer[byte+15] );
271
272         byte += 16;
273    }
274#endif
275
276    // check sector size from boot record
277    uint32_t sector_size = get_record_from_buffer( BPB_BYTSPERSEC , buffer , 1 );
278
279    nolock_assert( (sector_size == 512) , __FUNCTION__ ,
280                   "sector size must be 512 bytes\n" );
281
282    // check cluster size from boot record
283    uint32_t nb_sectors = get_record_from_buffer( BPB_SECPERCLUS , buffer , 1 );
284
285    nolock_assert( (nb_sectors == 8) , __FUNCTION__ ,
286                   "cluster size must be 8 sectors\n" );
287
288    // check number of FAT copies from boot record
289    uint32_t nb_fats = get_record_from_buffer( BPB_NUMFATS , buffer , 1 );
290
291    nolock_assert( (nb_fats == 1) , __FUNCTION__ ,
292                   "number of FAT copies must be 1\n" );
293
294    // get & check number of sectors in FAT from boot record
295    uint32_t fat_sectors = get_record_from_buffer( BPB_FAT32_FATSZ32 , buffer , 1 );
296
297    nolock_assert( ((fat_sectors & 0xF) == 0) , __FUNCTION__ ,
298                   "FAT not multiple of 16 sectors\n");
299
300    // get and check root cluster from boot record
301    uint32_t root_cluster = get_record_from_buffer( BPB_FAT32_ROOTCLUS , buffer , 1 );
302
303    nolock_assert( (root_cluster == 2) , __FUNCTION__ ,
304                   "root cluster index must be  2\n");
305
306    // get FAT lba from boot record
307    uint32_t fat_lba = get_record_from_buffer( BPB_RSVDSECCNT , buffer , 1 );
308
309    // release the 512 bytes buffer
310    req.type = KMEM_512_BYTES;
311    req.ptr  = buffer;
312    kmem_free( &req );
313
314    // allocate a mapper for the FAT itself
315    mapper_t * fat_mapper = mapper_create();
316
317    assert( (fat_mapper != NULL) , __FUNCTION__ , "no memory for FAT mapper" );
318
319    // initialize the FATFS context
320    fatfs_ctx->fat_begin_lba         = fat_lba;
321    fatfs_ctx->fat_sectors_count     = fat_sectors; 
322    fatfs_ctx->bytes_per_sector      = sector_size;
323    fatfs_ctx->sectors_per_cluster   = nb_sectors;
324    fatfs_ctx->cluster_begin_lba     = fat_lba + fat_sectors;
325    fatfs_ctx->root_dir_cluster      = 2;
326    fatfs_ctx->last_allocated_sector = 0;    // TODO ???
327    fatfs_ctx->last_allocated_index  = 0;    // TODO ???
328    fatfs_ctx->fat_mapper_xp         = XPTR( local_cxy , fat_mapper );
329
330    fatfs_dmsg("\n*** FAT context ***\n" 
331               "- fat_sectors     = %d\n"
332               "- sector size     = %d\n"
333               "- cluster size    = %d\n"
334               "- fat_first_lba   = %d\n"
335               "- data_first_lba  = %d\n"
336               "- mapper          = %l\n",
337               fatfs_ctx->fat_sectors_count,
338               fatfs_ctx->bytes_per_sector,
339               fatfs_ctx->bytes_per_cluster,
340               fatfs_ctx->fat_begin_lba,
341               fatfs_ctx->cluster_begin_lba,
342               fatfs_ctx->fat_mapper_xp );
343
344}  // end fatfs_ctx_init()
345
346/////////////////////////////////////////////////
347void fatfs_ctx_destroy( fatfs_ctx_t * fatfs_ctx )
348{
349    kmem_req_t    req;
350    req.type = KMEM_FATFS_CTX;
351    req.ptr  = fatfs_ctx;
352    kmem_free( &req );
353}
354
355////////////////////////////////////////////////
356static error_t fatfs_access_page( page_t * page,
357                                  bool_t   is_read )
358{
359    // get memory buffer base address
360    uint8_t * buffer = (uint8_t *)ppm_page2vaddr( page );
361 
362    // get pointer on source mapper and page index from page descriptor
363    mapper_t * mapper      = page->mapper;
364    uint32_t   page_index  = page->index;
365
366    // get VFS inode pointer from mapper
367    vfs_inode_t * vfs_inode = mapper->inode;
368
369    // get first cluster index from VFS inode
370    uint32_t  first_cluster = (uint32_t)(intptr_t)vfs_inode->extend;
371
372    // get FATFS context pointer from VFS context
373    fatfs_ctx_t * fatfs_ctx = (fatfs_ctx_t *)fs_context[FS_TYPE_FATFS].extend;
374
375    // get number of sectors
376    uint32_t count = fatfs_ctx->sectors_per_cluster;
377
378    // compute FATFS_cluster index for the accessed page
379    uint32_t cluster = 0;
380    error_t  error = fatfs_cluster_from_index( fatfs_ctx,
381                                               first_cluster,
382                                               page_index,
383                                               &cluster );
384    if( error ) return EIO;
385
386    // get lba from cluster
387    uint32_t lba = fatfs_lba_from_cluster( fatfs_ctx , cluster );
388
389    // access device
390    if( is_read ) error = dev_ioc_read ( buffer , lba , count );
391    else          error = dev_ioc_write( buffer , lba , count );     
392
393    if( error )
394    {
395        printk("\n[ERROR] in %s : cannot access IOC device\n", __FUNCTION__ );
396        return error;
397    } 
398
399    // successful access
400    return 0;
401}
402
403////////////////////////////////////////////////
404error_t fatfs_write_page( struct page_s * page )
405{
406    bool_t is_read = false;
407    return fatfs_access_page( page , is_read );
408}
409
410///////////////////////////////////////////////
411error_t fatfs_read_page( struct page_s * page )
412{
413    bool_t is_read = true;
414    return fatfs_access_page( page , is_read );
415}
416
Note: See TracBrowser for help on using the repository browser.