| [258] | 1 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 2 | // File     : fat32.c | 
|---|
|  | 3 | // Date     : 01/09/2013 | 
|---|
|  | 4 | // Authors  : Marco Jankovic, Cesar Fuguet & Alain Greiner | 
|---|
|  | 5 | // Copyright (c) UPMC-LIP6 | 
|---|
|  | 6 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [569] | 7 | // The fat32.h and fat32.c files define a library of access functions | 
|---|
| [300] | 8 | // to a FAT32 disk on a block device. It is intended to be used | 
|---|
| [258] | 9 | // by the GIET_VM nano-kernel for both the boot code and the kernel code. | 
|---|
|  | 10 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 11 | // Implementation notes: | 
|---|
|  | 12 | // 1. the "lba" (Logical Block Address) is the physical sector index on | 
|---|
|  | 13 | //    the block device. The physical sector size is supposed to be 512 bytes. | 
|---|
|  | 14 | // 2. the "cluster" variable is actually a cluster index. A cluster contains | 
|---|
|  | 15 | //    typically 8 sectors (4K bytes) and the cluster index is a 32 bits word. | 
|---|
| [569] | 16 | // 3. This FAT32 library uses a FAT cache whose storage capacity is one sector. | 
|---|
| [258] | 17 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 18 |  | 
|---|
|  | 19 | #include <giet_config.h> | 
|---|
| [530] | 20 | #include <hard_config.h> | 
|---|
| [258] | 21 | #include <fat32.h> | 
|---|
| [530] | 22 | #include <utils.h> | 
|---|
|  | 23 | #include <vmem.h> | 
|---|
|  | 24 | #include <bdv_driver.h> | 
|---|
|  | 25 | #include <hba_driver.h> | 
|---|
|  | 26 | #include <sdc_driver.h> | 
|---|
|  | 27 | #include <rdk_driver.h> | 
|---|
|  | 28 | #include <mmc_driver.h> | 
|---|
| [458] | 29 | #include <tty0.h> | 
|---|
| [258] | 30 |  | 
|---|
|  | 31 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [417] | 32 | //      Global variable : internal FAT representation | 
|---|
| [258] | 33 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 34 |  | 
|---|
| [530] | 35 | extern fat32_fs_t _fat; | 
|---|
| [258] | 36 |  | 
|---|
| [530] | 37 | extern unsigned int _ptabs_vaddr[GIET_NB_VSPACE_MAX][X_SIZE][Y_SIZE]; | 
|---|
|  | 38 |  | 
|---|
| [569] | 39 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 40 | // This function computes the memory buffer physical address, and calls | 
|---|
| [569] | 41 | // the proper IOC driver depending on the subtype (BDV / HBA / SDC / SPI / RDK). | 
|---|
| [530] | 42 | // The use_irq argument allows to activate the descheduling mode, if it | 
|---|
|  | 43 | // supported by the IOC driver subtype | 
|---|
| [569] | 44 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 45 | // Return 0 in case of success, return -1 in case of error | 
|---|
| [569] | 46 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 47 | static | 
|---|
|  | 48 | int _fat_ioc_access( unsigned int use_irq, | 
|---|
|  | 49 | unsigned int to_mem, | 
|---|
|  | 50 | unsigned int lba, | 
|---|
|  | 51 | unsigned int buf_vaddr, | 
|---|
|  | 52 | unsigned int count ) | 
|---|
|  | 53 | { | 
|---|
|  | 54 | // compute memory buffer physical address | 
|---|
|  | 55 | unsigned int       flags;         // for _v2p_translate | 
|---|
|  | 56 | unsigned long long buf_paddr;     // buffer physical address | 
|---|
|  | 57 |  | 
|---|
|  | 58 | if ( ((_get_mmu_mode() & 0x4) == 0 ) || USE_IOC_RDK )  // identity | 
|---|
|  | 59 | { | 
|---|
|  | 60 | buf_paddr = (unsigned long long)buf_vaddr; | 
|---|
|  | 61 | } | 
|---|
|  | 62 | else                                // V2P translation required | 
|---|
|  | 63 | { | 
|---|
|  | 64 | buf_paddr = _v2p_translate( buf_vaddr , &flags ); | 
|---|
|  | 65 | } | 
|---|
|  | 66 |  | 
|---|
| [569] | 67 | #if GIET_DEBUG_FAT | 
|---|
| [530] | 68 | unsigned int procid  = _get_procid(); | 
|---|
|  | 69 | unsigned int x       = procid >> (Y_WIDTH + P_WIDTH); | 
|---|
|  | 70 | unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH)-1); | 
|---|
|  | 71 | unsigned int p       = procid & ((1<<P_WIDTH)-1); | 
|---|
| [569] | 72 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 73 | _printf("\n[DEBUG FAT] P[%d,%d,%d] enters _fat_ioc_access() at cycle %d\n" | 
|---|
|  | 74 | "  to_mem = %d / vaddr = %x / paddr = %l / sectors = %d / lba = %x\n", | 
|---|
|  | 75 | x, y , p, _get_proctime(), to_mem, buf_vaddr, buf_paddr, count, lba ); | 
|---|
|  | 76 | #endif | 
|---|
|  | 77 |  | 
|---|
|  | 78 |  | 
|---|
| [551] | 79 | #if GIET_NO_HARD_CC     // L1 cache inval (virtual addresses) | 
|---|
|  | 80 | if ( to_mem ) _dcache_buf_invalidate( buf_vaddr, count<<9 ); | 
|---|
|  | 81 | #endif | 
|---|
| [530] | 82 |  | 
|---|
|  | 83 |  | 
|---|
| [551] | 84 | #if   ( USE_IOC_BDV )   // call the proper driver | 
|---|
| [530] | 85 | return( _bdv_access( use_irq , to_mem , lba , buf_paddr , count ) ); | 
|---|
|  | 86 | #elif ( USE_IOC_HBA ) | 
|---|
|  | 87 | return( _hba_access( use_irq , to_mem , lba , buf_paddr , count ) ); | 
|---|
| [569] | 88 | #elif ( USE_IOC_SDC ) | 
|---|
|  | 89 | return( _sdc_access( use_irq , to_mem , lba , buf_paddr , count ) ); | 
|---|
| [530] | 90 | #elif ( USE_IOC_SPI ) | 
|---|
| [569] | 91 | return( _spi_access( use_irq , to_mem , lba , buf_paddr , count ) ); | 
|---|
| [530] | 92 | #elif ( USE_IOC_RDK ) | 
|---|
|  | 93 | return( _rdk_access( use_irq , to_mem , lba , buf_paddr , count ) ); | 
|---|
|  | 94 | #else | 
|---|
|  | 95 | _printf("\n[FAT ERROR] in _fat_ioc_access() : no IOC driver\n"); | 
|---|
|  | 96 | _exit(); | 
|---|
|  | 97 | #endif | 
|---|
|  | 98 |  | 
|---|
|  | 99 | }  // end _fat_ioc_access() | 
|---|
|  | 100 |  | 
|---|
|  | 101 |  | 
|---|
| [258] | 102 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 103 | // This function displays the content of the FAT cache | 
|---|
|  | 104 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [569] | 105 | #if GIET_DEBUG_FAT | 
|---|
| [530] | 106 | static | 
|---|
| [503] | 107 | void _display_fat_cache() | 
|---|
| [258] | 108 | { | 
|---|
|  | 109 | unsigned int line; | 
|---|
|  | 110 | unsigned int word; | 
|---|
|  | 111 | unsigned int temp[9]; | 
|---|
|  | 112 |  | 
|---|
|  | 113 | temp[8] = 0; | 
|---|
|  | 114 |  | 
|---|
| [417] | 115 | _puts("\n*********************** fat_cache_lba = "); | 
|---|
| [530] | 116 | _putx( _fat.cache_lba ); | 
|---|
| [417] | 117 | _puts(" *****************************\n"); | 
|---|
|  | 118 |  | 
|---|
|  | 119 | for ( line = 0 ; line < 16 ; line++ ) | 
|---|
| [258] | 120 | { | 
|---|
| [417] | 121 | // display line index | 
|---|
|  | 122 | _putx( line ); | 
|---|
|  | 123 | _puts(" : "); | 
|---|
| [258] | 124 |  | 
|---|
| [417] | 125 | // display 8*4 bytes hexa | 
|---|
| [258] | 126 | for ( word=0 ; word<8 ; word++ ) | 
|---|
|  | 127 | { | 
|---|
|  | 128 | unsigned int byte  = (line<<5) + (word<<2); | 
|---|
| [530] | 129 | unsigned int hexa  = (_fat.fat_cache[byte  ]<<24) | | 
|---|
|  | 130 | (_fat.fat_cache[byte+1]<<16) | | 
|---|
|  | 131 | (_fat.fat_cache[byte+2]<< 8) | | 
|---|
|  | 132 | (_fat.fat_cache[byte+3]); | 
|---|
| [417] | 133 | _putx( hexa ); | 
|---|
|  | 134 | _puts(" | "); | 
|---|
| [258] | 135 |  | 
|---|
|  | 136 | // prepare display ascii | 
|---|
| [530] | 137 | temp[word] = _fat.fat_cache[byte]         | | 
|---|
|  | 138 | (_fat.fat_cache[byte+1]<<8)  | | 
|---|
|  | 139 | (_fat.fat_cache[byte+2]<<16) | | 
|---|
|  | 140 | (_fat.fat_cache[byte+3]<<24) ; | 
|---|
| [258] | 141 | } | 
|---|
|  | 142 |  | 
|---|
|  | 143 | // display data ascii | 
|---|
| [417] | 144 | _puts( (char*)temp ); | 
|---|
|  | 145 | _puts("\n"); | 
|---|
| [258] | 146 | } | 
|---|
| [417] | 147 | _puts("***************************************************************************\n"); | 
|---|
|  | 148 |  | 
|---|
| [503] | 149 | } // end _display_fat_cache() | 
|---|
| [530] | 150 | #endif | 
|---|
| [291] | 151 |  | 
|---|
| [258] | 152 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 153 | // This function displays the FAT descriptor. | 
|---|
|  | 154 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 155 | #if GIET_DEBUG_FAT | 
|---|
|  | 156 | static | 
|---|
|  | 157 | void _fat_print() | 
|---|
|  | 158 | { | 
|---|
|  | 159 | _printf("\n########################## FAT32 ################################" | 
|---|
|  | 160 | "\nFAT initialised                  %x" | 
|---|
|  | 161 | "\nSector Size  (bytes)             %x" | 
|---|
|  | 162 | "\nSectors per cluster              %x" | 
|---|
|  | 163 | "\nFAT region first lba             %x" | 
|---|
|  | 164 | "\nData region first lba            %x" | 
|---|
|  | 165 | "\nNumber of sectors for one FAT    %x" | 
|---|
|  | 166 | "\nNumber of free clusters          %x" | 
|---|
|  | 167 | "\nLast allocated cluster           %x" | 
|---|
|  | 168 | "\n#################################################################\n", | 
|---|
|  | 169 | _fat.initialised, | 
|---|
|  | 170 | _fat.sector_size, | 
|---|
|  | 171 | _fat.sectors_per_cluster, | 
|---|
|  | 172 | _fat.fat_lba, | 
|---|
|  | 173 | _fat.data_lba, | 
|---|
|  | 174 | _fat.fat_sectors, | 
|---|
|  | 175 | _fat.number_free_cluster, | 
|---|
|  | 176 | _fat.last_cluster_allocated ); | 
|---|
|  | 177 | } // end _fat_print() | 
|---|
|  | 178 | #endif | 
|---|
|  | 179 |  | 
|---|
|  | 180 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [258] | 181 | // This function returns the length of a FAT field. This field is identified | 
|---|
| [417] | 182 | // by an (offset,length) mnemonic defined in fat32.h file. | 
|---|
| [258] | 183 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 184 | static inline | 
|---|
|  | 185 | int get_length( int offset, | 
|---|
|  | 186 | int length ) | 
|---|
| [258] | 187 | { | 
|---|
|  | 188 | return length; | 
|---|
|  | 189 | } | 
|---|
|  | 190 |  | 
|---|
|  | 191 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
| [291] | 192 | // Write one 32 bits word "value" in a char[] buffer. | 
|---|
| [417] | 193 | // The modified field in buffer is defined by the offset and size arguments. | 
|---|
| [291] | 194 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 195 | static | 
|---|
|  | 196 | void _write_entry( unsigned int   offset, | 
|---|
|  | 197 | unsigned int   size, | 
|---|
|  | 198 | char*          buffer, | 
|---|
|  | 199 | unsigned int   value ) | 
|---|
| [291] | 200 | { | 
|---|
|  | 201 | unsigned int turn = 0; | 
|---|
|  | 202 | unsigned int res  = value; | 
|---|
|  | 203 | unsigned int mask = 0x000000ff; | 
|---|
|  | 204 |  | 
|---|
|  | 205 | while( turn != size - 1 ) | 
|---|
|  | 206 | { | 
|---|
|  | 207 | buffer[ offset + turn ] = res & mask; | 
|---|
|  | 208 | res = res >> 8; | 
|---|
|  | 209 | turn++; | 
|---|
|  | 210 | } | 
|---|
|  | 211 | buffer[offset + turn] = res & mask; | 
|---|
|  | 212 | } | 
|---|
|  | 213 |  | 
|---|
|  | 214 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
| [258] | 215 | // Read one 32 bits word in a char[] buffer, taking endianness into account. | 
|---|
| [417] | 216 | // The analysed field in buffer is defined by the offset and size arguments. | 
|---|
| [258] | 217 | ////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 218 | static | 
|---|
|  | 219 | unsigned int _read_entry( unsigned int   offset, | 
|---|
|  | 220 | unsigned int   size, | 
|---|
|  | 221 | char*          buffer, | 
|---|
|  | 222 | unsigned int   little_indian ) | 
|---|
| [258] | 223 | { | 
|---|
|  | 224 | unsigned int turn; | 
|---|
|  | 225 | unsigned int res  = 0; | 
|---|
|  | 226 | unsigned int mask = 0x000000ff; | 
|---|
|  | 227 |  | 
|---|
|  | 228 | if( little_indian ) | 
|---|
|  | 229 | { | 
|---|
|  | 230 | turn = size; | 
|---|
|  | 231 | while( turn != 1 ) | 
|---|
|  | 232 | { | 
|---|
|  | 233 | res = res | (buffer[offset + (turn-1)] & mask); | 
|---|
|  | 234 | res = res << 8; | 
|---|
|  | 235 | turn--; | 
|---|
|  | 236 | } | 
|---|
|  | 237 | res = (buffer[offset + (turn-1)] & mask) | res; | 
|---|
|  | 238 | } | 
|---|
|  | 239 | else | 
|---|
|  | 240 | { | 
|---|
|  | 241 | turn = 0; | 
|---|
|  | 242 | while( turn != size - 1 ) | 
|---|
|  | 243 | { | 
|---|
|  | 244 |  | 
|---|
|  | 245 | res = res  | (buffer[ offset + turn ] & mask ); | 
|---|
|  | 246 | res = res << 8; | 
|---|
|  | 247 | turn++; | 
|---|
|  | 248 | } | 
|---|
|  | 249 | res = res | (buffer[offset + turn] & mask); | 
|---|
|  | 250 | } | 
|---|
|  | 251 | return res; | 
|---|
|  | 252 | } | 
|---|
|  | 253 |  | 
|---|
|  | 254 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 255 | // This function retuns the cluster index from the lba of a DATA sector. | 
|---|
|  | 256 | // The lba must be larger than the lba of the first DATA sector. | 
|---|
|  | 257 | // The DATA region indexing starts a cluster 2. | 
|---|
|  | 258 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 259 | static inline | 
|---|
|  | 260 | unsigned int lba_to_cluster( unsigned int lba ) | 
|---|
| [258] | 261 | { | 
|---|
| [530] | 262 | if (lba < _fat.data_lba ) return 0; | 
|---|
| [258] | 263 |  | 
|---|
| [530] | 264 | return ( (lba - _fat.data_lba) / _fat.sectors_per_cluster) + 2; | 
|---|
| [258] | 265 | } | 
|---|
|  | 266 |  | 
|---|
|  | 267 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 268 | // This function retuns the lba of first sector in DATA region | 
|---|
|  | 269 | // from the cluster index. The cluster index must be larger than 2. | 
|---|
|  | 270 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 271 | static inline | 
|---|
|  | 272 | unsigned int cluster_to_lba( unsigned int cluster ) | 
|---|
| [258] | 273 | { | 
|---|
|  | 274 | if ( cluster < 2 ) return 0; | 
|---|
|  | 275 |  | 
|---|
| [530] | 276 | return  (_fat.sectors_per_cluster * (cluster - 2)) + _fat.data_lba; | 
|---|
| [258] | 277 | } | 
|---|
|  | 278 |  | 
|---|
|  | 279 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 280 | // This function search the FAT (using the FAT cache), and returns | 
|---|
| [300] | 281 | // the next cluster index from the current cluster index in the FAT. | 
|---|
| [258] | 282 | // remark: a sector of FAT contains 128 cluster indexes. | 
|---|
|  | 283 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 284 | static | 
|---|
|  | 285 | unsigned int _get_next_cluster( unsigned int use_irq, | 
|---|
|  | 286 | unsigned int cluster ) | 
|---|
| [258] | 287 | { | 
|---|
|  | 288 | // compute lba of the sector containing the cluster index | 
|---|
| [530] | 289 | unsigned int lba = _fat.fat_lba + (cluster / 128); | 
|---|
| [258] | 290 |  | 
|---|
| [530] | 291 | if ( lba != _fat.cache_lba )      // miss in fat_cache | 
|---|
| [258] | 292 | { | 
|---|
|  | 293 | // access fat | 
|---|
| [530] | 294 | if( _fat_ioc_access( use_irq, | 
|---|
|  | 295 | 1,               // read | 
|---|
|  | 296 | lba, | 
|---|
|  | 297 | (unsigned int)_fat.fat_cache, | 
|---|
|  | 298 | 1 ) )            // one sector | 
|---|
| [258] | 299 | { | 
|---|
| [530] | 300 | _printf("[FAT_ERROR] in get_next_cluster_id() : cannot read block %x", | 
|---|
|  | 301 | lba ); | 
|---|
| [258] | 302 | return 1; | 
|---|
|  | 303 | } | 
|---|
| [530] | 304 | _fat.cache_lba = lba; | 
|---|
|  | 305 | } | 
|---|
| [258] | 306 |  | 
|---|
| [417] | 307 | unsigned int next = _read_entry( ((cluster % 128) * 4), | 
|---|
|  | 308 | 4, | 
|---|
| [530] | 309 | _fat.fat_cache, | 
|---|
| [417] | 310 | 1 ); | 
|---|
| [569] | 311 | #if GIET_DEBUG_FAT | 
|---|
| [530] | 312 | unsigned int procid  = _get_procid(); | 
|---|
|  | 313 | unsigned int x       = procid >> (Y_WIDTH + P_WIDTH); | 
|---|
|  | 314 | unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH)-1); | 
|---|
|  | 315 | unsigned int p       = procid & ((1<<P_WIDTH)-1); | 
|---|
| [569] | 316 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 317 | _printf("\n[DEBUG FAT] P[%d,%d,%d] in _get_next_cluster() :  next = %x\n", | 
|---|
|  | 318 | x , y , p , next ); | 
|---|
| [569] | 319 | if ( (_get_proctime() > GIET_DEBUG_FAT) && (GIET_DEBUG_FAT & 0x1) ) | 
|---|
| [530] | 320 | _display_fat_cache(); | 
|---|
| [417] | 321 | #endif | 
|---|
|  | 322 |  | 
|---|
|  | 323 | return next; | 
|---|
| [258] | 324 | } | 
|---|
|  | 325 |  | 
|---|
|  | 326 | ////////////////////////////////////////////////////// | 
|---|
|  | 327 | static inline unsigned char to_upper(unsigned char c) | 
|---|
|  | 328 | { | 
|---|
|  | 329 | if (c >= 'a' && c <= 'z') return (c & ~(0x20)); | 
|---|
|  | 330 | else                      return c; | 
|---|
|  | 331 | } | 
|---|
|  | 332 |  | 
|---|
|  | 333 | //////////////////////////////////////////////////////////////// | 
|---|
|  | 334 | // This function is a filter: | 
|---|
|  | 335 | // Return the c character if c is a legal short name character | 
|---|
|  | 336 | // Return the '_' character if c is illegal | 
|---|
|  | 337 | //////////////////////////////////////////////////////////////// | 
|---|
| [530] | 338 | static | 
|---|
|  | 339 | unsigned char illegal_short(unsigned char c) | 
|---|
| [258] | 340 | { | 
|---|
|  | 341 | const unsigned char illegal_char [] =";+=[]â,\"*\\<>/?:|\0"; | 
|---|
|  | 342 | short i = 0; | 
|---|
|  | 343 | while (illegal_char[i]!='\0') | 
|---|
|  | 344 | { | 
|---|
|  | 345 | if (c == illegal_char[i]) | 
|---|
|  | 346 | return '_'; | 
|---|
|  | 347 | i++; | 
|---|
|  | 348 | } | 
|---|
|  | 349 | return c; | 
|---|
|  | 350 | } | 
|---|
|  | 351 |  | 
|---|
|  | 352 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 353 | // This function test if the string argument is a legal SFN (Short File Name) | 
|---|
|  | 354 | // and copies this name (removing the .) in the sfn_string argument. | 
|---|
|  | 355 | // Criteria for a Short File Name are: | 
|---|
|  | 356 | // - separator is '.' (extension is not mandatory) | 
|---|
|  | 357 | // - 1 <= name length <= 8 | 
|---|
|  | 358 | // - 0 <= extension length <= 3 | 
|---|
|  | 359 | // - no illegal character (see illega_short() function) | 
|---|
|  | 360 | // Return 1 if it string is a legal SFN | 
|---|
|  | 361 | // Return 0 if not legal SFN | 
|---|
|  | 362 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 363 | static | 
|---|
|  | 364 | int is_short( char* string, | 
|---|
|  | 365 | char* sfn_string) | 
|---|
| [258] | 366 | { | 
|---|
|  | 367 | int s_size   = 0; | 
|---|
|  | 368 | int dot_ext  = 0;       // dot offset in the filename | 
|---|
|  | 369 | int name_len = 0;       // length of file name | 
|---|
|  | 370 | int ext_len  = 0;       // length of extension | 
|---|
|  | 371 | int i        = 0; | 
|---|
|  | 372 | int sc_i     = 0; | 
|---|
|  | 373 | char ch; | 
|---|
|  | 374 |  | 
|---|
|  | 375 | if(string[0] == '.' && string[1] == '\0') | 
|---|
|  | 376 | { | 
|---|
|  | 377 | sfn_string[0] = '.'; | 
|---|
| [354] | 378 | return 1; | 
|---|
|  | 379 | } | 
|---|
|  | 380 | if(string[0] == '.' && string[1] == '.' && string[2] == '\0') | 
|---|
|  | 381 | { | 
|---|
|  | 382 | sfn_string[0] = '.'; | 
|---|
|  | 383 | sfn_string[1] = '.'; | 
|---|
|  | 384 | return 1; | 
|---|
|  | 385 | } | 
|---|
|  | 386 | sfn_string[11] = '\0'; | 
|---|
|  | 387 | while (string[s_size] != '\0') | 
|---|
|  | 388 | { | 
|---|
|  | 389 | if (string[s_size] == '.') | 
|---|
|  | 390 | { | 
|---|
|  | 391 | dot_ext = s_size; | 
|---|
|  | 392 | ext_len = -1; | 
|---|
|  | 393 | } | 
|---|
|  | 394 | ext_len++; | 
|---|
|  | 395 | s_size++; | 
|---|
|  | 396 | } | 
|---|
|  | 397 | if (dot_ext != 0) | 
|---|
|  | 398 | { | 
|---|
|  | 399 | name_len = s_size - ext_len - 1; | 
|---|
|  | 400 | } | 
|---|
|  | 401 | else | 
|---|
|  | 402 | { | 
|---|
|  | 403 | name_len = s_size; | 
|---|
|  | 404 | ext_len = 0; | 
|---|
|  | 405 | } | 
|---|
|  | 406 | if ( ext_len > 3 || ( name_len > 8)) | 
|---|
|  | 407 | { | 
|---|
|  | 408 | return 0; | 
|---|
|  | 409 | } | 
|---|
|  | 410 | if (dot_ext != 0) | 
|---|
|  | 411 | { | 
|---|
|  | 412 | while (i != ext_len) | 
|---|
|  | 413 | { | 
|---|
|  | 414 | ch = to_upper(string[dot_ext + 1 + i]); | 
|---|
|  | 415 | ch = illegal_short(ch); | 
|---|
|  | 416 | sfn_string[8+i] = ch; | 
|---|
|  | 417 | i++; | 
|---|
|  | 418 | } | 
|---|
|  | 419 | } | 
|---|
|  | 420 | i = 0; | 
|---|
|  | 421 | sc_i = 0; | 
|---|
|  | 422 | while (i!= name_len) | 
|---|
|  | 423 | { | 
|---|
|  | 424 | ch = to_upper(string[i]); | 
|---|
|  | 425 | ch = illegal_short(ch); | 
|---|
|  | 426 | if (ch != '.') sfn_string[sc_i++] = ch; | 
|---|
|  | 427 | i++; | 
|---|
|  | 428 | } | 
|---|
|  | 429 | return 1; | 
|---|
| [258] | 430 | } | 
|---|
|  | 431 |  | 
|---|
| [291] | 432 | /////////////////////////////////////////////////////////////////////// | 
|---|
|  | 433 | // This function analyses the pathname argument, from the character | 
|---|
|  | 434 | // defined by the *nb_read argument. | 
|---|
|  | 435 | // It copies the found name (between '/') in the name[] buffer, | 
|---|
|  | 436 | // and updates the nb_read argument. | 
|---|
|  | 437 | // Return 1 if name found, Return 0 if NUL character found, | 
|---|
|  | 438 | /////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 439 | static | 
|---|
|  | 440 | int get_name_from_path( char*          pathname, | 
|---|
|  | 441 | char*          name, | 
|---|
|  | 442 | unsigned int*  nb_read ) | 
|---|
| [291] | 443 | { | 
|---|
|  | 444 | if ( pathname[*nb_read] == 0 ) return 0; | 
|---|
|  | 445 |  | 
|---|
|  | 446 | int i = (pathname[*nb_read] == '/')? (*nb_read) + 1 : *nb_read; | 
|---|
|  | 447 | int j = 0; | 
|---|
|  | 448 |  | 
|---|
|  | 449 | while(pathname[i] != '/' && pathname[i] != '\0') | 
|---|
|  | 450 | { | 
|---|
|  | 451 | name[j] = pathname[i]; | 
|---|
|  | 452 | j++; | 
|---|
|  | 453 | i++; | 
|---|
|  | 454 | } | 
|---|
|  | 455 | name[j] = 0; | 
|---|
|  | 456 |  | 
|---|
|  | 457 | if ( pathname[i] == '/' ) *nb_read += j+1; | 
|---|
|  | 458 | else                      *nb_read += j; | 
|---|
|  | 459 |  | 
|---|
|  | 460 | return 1; | 
|---|
|  | 461 | } | 
|---|
|  | 462 |  | 
|---|
| [258] | 463 | //////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 464 | static int get_name_from_short( char* dir_entry,     // input:  SFN dir_entry | 
|---|
|  | 465 | char* entry_name )   // output: name | 
|---|
|  | 466 | { | 
|---|
|  | 467 | unsigned int i   = 0; | 
|---|
|  | 468 | unsigned int length = get_length(DIR_NAME); | 
|---|
|  | 469 |  | 
|---|
|  | 470 | while ( i < length ) | 
|---|
|  | 471 | { | 
|---|
|  | 472 | entry_name[i] = dir_entry[i]; | 
|---|
|  | 473 | i++; | 
|---|
|  | 474 | } | 
|---|
|  | 475 | entry_name[i] = '\0'; | 
|---|
|  | 476 |  | 
|---|
|  | 477 | return i; | 
|---|
|  | 478 | } | 
|---|
| [291] | 479 |  | 
|---|
| [258] | 480 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
| [291] | 481 | static int get_name_from_long( char *dir_entry,     // input : LFN dir_entry | 
|---|
|  | 482 | char *entry_name )   // output : name | 
|---|
| [258] | 483 | { | 
|---|
|  | 484 | unsigned int   entry_name_offset   = 0; | 
|---|
|  | 485 | unsigned int   dir_entry_offset    = get_length(LDIR_ORD); | 
|---|
|  | 486 | unsigned int   l_name_1            = get_length(LDIR_NAME_1); | 
|---|
|  | 487 | unsigned int   l_name_2            = get_length(LDIR_NAME_2); | 
|---|
|  | 488 | unsigned int   l_name_3            = get_length(LDIR_NAME_3); | 
|---|
|  | 489 | unsigned int   l_attr              = get_length(LDIR_ATTR); | 
|---|
|  | 490 | unsigned int   l_type              = get_length(LDIR_TYPE); | 
|---|
|  | 491 | unsigned int   l_chksum            = get_length(LDIR_CHKSUM); | 
|---|
|  | 492 | unsigned int   l_rsvd              = get_length(LDIR_RSVD); | 
|---|
|  | 493 |  | 
|---|
|  | 494 | unsigned int j            = 0; | 
|---|
|  | 495 | unsigned int eof          = 0; | 
|---|
|  | 496 |  | 
|---|
|  | 497 | while ( (dir_entry_offset != DIR_ENTRY_SIZE)  && (!eof) ) | 
|---|
|  | 498 | { | 
|---|
|  | 499 | while (j != l_name_1 && !eof ) | 
|---|
|  | 500 | { | 
|---|
|  | 501 | if ( (dir_entry[dir_entry_offset] == 0x00) || | 
|---|
|  | 502 | (dir_entry[dir_entry_offset] == 0xFF) ) | 
|---|
|  | 503 | { | 
|---|
|  | 504 | eof = 1; | 
|---|
|  | 505 | continue; | 
|---|
|  | 506 | } | 
|---|
|  | 507 | entry_name[entry_name_offset] = dir_entry[dir_entry_offset]; | 
|---|
|  | 508 | dir_entry_offset += 2; | 
|---|
|  | 509 | j += 2; | 
|---|
|  | 510 | entry_name_offset++; | 
|---|
|  | 511 | } | 
|---|
|  | 512 |  | 
|---|
|  | 513 | dir_entry_offset += (l_attr + l_type + l_chksum); | 
|---|
|  | 514 | j = 0; | 
|---|
|  | 515 |  | 
|---|
|  | 516 | while (j != l_name_2 && !eof ) | 
|---|
|  | 517 | { | 
|---|
|  | 518 | if ( (dir_entry[dir_entry_offset] == 0x00) || | 
|---|
|  | 519 | (dir_entry[dir_entry_offset] == 0xFF) ) | 
|---|
|  | 520 | { | 
|---|
|  | 521 | eof = 1; | 
|---|
|  | 522 | continue; | 
|---|
|  | 523 | } | 
|---|
|  | 524 | entry_name[entry_name_offset] = dir_entry[dir_entry_offset]; | 
|---|
|  | 525 | dir_entry_offset += 2; | 
|---|
|  | 526 | j += 2; | 
|---|
|  | 527 | entry_name_offset++; | 
|---|
|  | 528 | } | 
|---|
|  | 529 |  | 
|---|
|  | 530 | dir_entry_offset += l_rsvd; | 
|---|
|  | 531 | j = 0; | 
|---|
|  | 532 |  | 
|---|
|  | 533 | while (j != l_name_3 && !eof ) | 
|---|
|  | 534 | { | 
|---|
|  | 535 | if ( (dir_entry[dir_entry_offset] == 0x00) || | 
|---|
|  | 536 | (dir_entry[dir_entry_offset] == 0xFF) ) | 
|---|
|  | 537 | { | 
|---|
|  | 538 | eof = 1; | 
|---|
|  | 539 | continue; | 
|---|
|  | 540 | } | 
|---|
|  | 541 | entry_name[entry_name_offset] = dir_entry[dir_entry_offset]; | 
|---|
|  | 542 | dir_entry_offset += 2; | 
|---|
|  | 543 | j += 2; | 
|---|
|  | 544 | entry_name_offset++; | 
|---|
|  | 545 | } | 
|---|
|  | 546 | } | 
|---|
|  | 547 | entry_name[entry_name_offset] = '\0'; | 
|---|
|  | 548 |  | 
|---|
|  | 549 | return entry_name_offset; | 
|---|
|  | 550 | } // end get_name_from_long() | 
|---|
|  | 551 |  | 
|---|
| [530] | 552 | /////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [291] | 553 | // This function update a DIR_ENTRY, write a new value into a specific field | 
|---|
|  | 554 | // (ex : DIR_FILE_SIZE, when we want update the file size after a fat_write) | 
|---|
|  | 555 | // Return 0 in case of success, > 0 if failure. | 
|---|
| [530] | 556 | /////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [291] | 557 | // TODO : make this function less complex | 
|---|
| [530] | 558 | /////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 559 | static inline | 
|---|
|  | 560 | unsigned int _update_entry( unsigned int use_irq, | 
|---|
|  | 561 | unsigned int fd_id, | 
|---|
|  | 562 | unsigned int field, | 
|---|
|  | 563 | unsigned int size, | 
|---|
|  | 564 | unsigned int value ) | 
|---|
| [291] | 565 | { | 
|---|
|  | 566 | char dir_entry[32];   // buffer to store a full directory_entry | 
|---|
|  | 567 | char name_entry[14];  // buffer to store a 13 characters (partial) name | 
|---|
|  | 568 | char file_name[256];  // buffer to store the name (not pathname) of the file | 
|---|
|  | 569 |  | 
|---|
| [530] | 570 | char sfn_string[12]  = {[0 ... 10] = ' ', '\0'};     // buffer Short File Name | 
|---|
|  | 571 | unsigned int lba     = _fat.fd[fd_id].lba_dir_entry;  // Lba of file dir_entry | 
|---|
| [291] | 572 | unsigned int is_sfn; | 
|---|
| [530] | 573 | unsigned int attr    = 0;                            // dir entry attribute | 
|---|
|  | 574 | unsigned int ord     = 0;                            // dir entry sequence | 
|---|
|  | 575 | unsigned int found   = 0;                            // name found | 
|---|
|  | 576 | unsigned int offset  = 0;                            // offset in fat_cache | 
|---|
| [417] | 577 | unsigned int i       = 0; | 
|---|
|  | 578 | unsigned int nb_read = 0; | 
|---|
| [291] | 579 |  | 
|---|
|  | 580 | for ( i = 0 ; i < 32 ; i++ ) dir_entry[i]  = 0; | 
|---|
|  | 581 | for ( i = 0 ; i < 14 ; i++ ) name_entry[i] = 0; | 
|---|
|  | 582 |  | 
|---|
|  | 583 | // Get the name of the file. | 
|---|
| [530] | 584 | while ( get_name_from_path( _fat.fd[fd_id].name, file_name, &nb_read ) ) | 
|---|
| [291] | 585 | { | 
|---|
|  | 586 | } | 
|---|
|  | 587 |  | 
|---|
| [417] | 588 | // Format file_name to SFN format | 
|---|
|  | 589 | is_sfn = is_short( file_name, sfn_string ); | 
|---|
| [291] | 590 |  | 
|---|
| [530] | 591 | // access FAT | 
|---|
|  | 592 | if ( _fat_ioc_access( use_irq, | 
|---|
|  | 593 | 1,               // read | 
|---|
|  | 594 | lba, | 
|---|
|  | 595 | (unsigned int)_fat.fat_cache, | 
|---|
|  | 596 | 1 ) )            // one sector | 
|---|
| [291] | 597 | { | 
|---|
| [530] | 598 | _printf("\n[FAT ERROR] in _update_entry() cannot read sector %x\n", | 
|---|
|  | 599 | lba ); | 
|---|
| [291] | 600 | return 1; | 
|---|
|  | 601 | } | 
|---|
| [530] | 602 | _fat.cache_lba = lba; | 
|---|
| [291] | 603 |  | 
|---|
|  | 604 | // - the offset increment is an integer number of directory entry (32 bytes) | 
|---|
|  | 605 | // - the exit condition is success (name found) or failure (end of directory) | 
|---|
|  | 606 | while ( !found ) | 
|---|
|  | 607 | { | 
|---|
| [530] | 608 | attr = _read_entry( DIR_ATTR, _fat.fat_cache + offset, 0 ); | 
|---|
|  | 609 | ord  = _read_entry( LDIR_ORD, _fat.fat_cache + offset, 0 ); | 
|---|
| [291] | 610 |  | 
|---|
|  | 611 | if ( is_sfn == 1 )                                     // searched name is short | 
|---|
|  | 612 | { | 
|---|
|  | 613 | if      ( (ord != FREE_ENTRY ) && | 
|---|
|  | 614 | (ord != NO_MORE_ENTRY) && | 
|---|
|  | 615 | (attr == ATTR_LONG_NAME_MASK) )    // LFN entry : skipped | 
|---|
|  | 616 | { | 
|---|
|  | 617 | offset     = offset + ((ord & 0xF) * DIR_ENTRY_SIZE); | 
|---|
|  | 618 | continue; | 
|---|
|  | 619 | } | 
|---|
|  | 620 | else if ( (attr != ATTR_LONG_NAME_MASK) && | 
|---|
|  | 621 | (ord  != FREE_ENTRY) && | 
|---|
|  | 622 | (ord  != NO_MORE_ENTRY ) )         // SFN entry : checked | 
|---|
|  | 623 | { | 
|---|
| [530] | 624 | memcpy( dir_entry, _fat.fat_cache + offset, DIR_ENTRY_SIZE ); | 
|---|
| [291] | 625 | } | 
|---|
|  | 626 | else if (ord == NO_MORE_ENTRY )            // end of directory : return | 
|---|
|  | 627 | { | 
|---|
| [530] | 628 | _printf("\n[FAT ERROR] in _update_entry() : reaches end\n"); | 
|---|
| [291] | 629 | return 1; | 
|---|
|  | 630 | } | 
|---|
|  | 631 | else                                                                           // free entry : skipped | 
|---|
|  | 632 | { | 
|---|
|  | 633 | offset = offset + DIR_ENTRY_SIZE; | 
|---|
|  | 634 | continue; | 
|---|
|  | 635 | } | 
|---|
|  | 636 | } | 
|---|
|  | 637 | else                                           // searched name is long | 
|---|
|  | 638 | { | 
|---|
|  | 639 | if      ( (attr == ATTR_LONG_NAME_MASK) && | 
|---|
|  | 640 | (ord != FREE_ENTRY) && | 
|---|
|  | 641 | (ord != NO_MORE_ENTRY) )           // LFN entry : checked | 
|---|
|  | 642 | { | 
|---|
| [530] | 643 | memcpy( dir_entry, _fat.fat_cache + offset, DIR_ENTRY_SIZE ); | 
|---|
| [291] | 644 | } | 
|---|
|  | 645 | else if ( (attr != ATTR_LONG_NAME_MASK) && | 
|---|
|  | 646 | (ord  != FREE_ENTRY) && | 
|---|
|  | 647 | (ord  != NO_MORE_ENTRY))               // SFN entry : skipped | 
|---|
|  | 648 | { | 
|---|
|  | 649 | offset = offset + DIR_ENTRY_SIZE; | 
|---|
|  | 650 | continue; | 
|---|
|  | 651 | } | 
|---|
|  | 652 | else if (ord == NO_MORE_ENTRY )                        // end of directory : return | 
|---|
|  | 653 | { | 
|---|
| [530] | 654 | _printf("\n[FAT ERROR] in _update_entry() reaches end\n"); | 
|---|
| [291] | 655 | return 1; | 
|---|
|  | 656 | } | 
|---|
|  | 657 | else                                       // free entry : skipped | 
|---|
|  | 658 | { | 
|---|
|  | 659 | offset = offset + DIR_ENTRY_SIZE; | 
|---|
|  | 660 | continue; | 
|---|
|  | 661 | } | 
|---|
|  | 662 | } | 
|---|
|  | 663 |  | 
|---|
|  | 664 | // testing the name extracted from dir entry | 
|---|
|  | 665 |  | 
|---|
|  | 666 | if ( is_sfn == 1 )                             // searched name is short | 
|---|
|  | 667 | { | 
|---|
|  | 668 | get_name_from_short( dir_entry, name_entry ); | 
|---|
|  | 669 |  | 
|---|
| [417] | 670 | if ( _strncmp( (char*)sfn_string, (char*)name_entry, 13 ) == 0 ) | 
|---|
| [291] | 671 | { | 
|---|
| [530] | 672 | _write_entry(offset + field, size, _fat.fat_cache, value); | 
|---|
| [291] | 673 | found = 1; | 
|---|
|  | 674 | } | 
|---|
|  | 675 | else                                                                       // no matching : skip | 
|---|
|  | 676 | { | 
|---|
|  | 677 | offset = offset + DIR_ENTRY_SIZE; | 
|---|
|  | 678 | } | 
|---|
|  | 679 | } | 
|---|
|  | 680 | else                                           // searched name is long | 
|---|
|  | 681 | { | 
|---|
|  | 682 | get_name_from_long( dir_entry, name_entry ); | 
|---|
|  | 683 |  | 
|---|
|  | 684 | unsigned shift = ((ord & 0xf) - 1) * 13; | 
|---|
| [530] | 685 | if ( _strncmp( (char*)(file_name + shift), | 
|---|
|  | 686 | (char*)name_entry, 13 ) == 0 ) | 
|---|
| [291] | 687 | { | 
|---|
|  | 688 | if ( (ord & 0xf) == 1 ) | 
|---|
|  | 689 | { | 
|---|
|  | 690 | offset = offset + DIR_ENTRY_SIZE; | 
|---|
| [530] | 691 | _write_entry(offset + field, size, _fat.fat_cache, value); | 
|---|
| [291] | 692 | found = 1; | 
|---|
|  | 693 | } | 
|---|
|  | 694 | offset = offset + DIR_ENTRY_SIZE; | 
|---|
|  | 695 | continue; | 
|---|
|  | 696 | } | 
|---|
|  | 697 | else                                                                       // no matching : skip | 
|---|
|  | 698 | { | 
|---|
|  | 699 | offset = offset + ((ord & 0xf) * DIR_ENTRY_SIZE) + DIR_ENTRY_SIZE; | 
|---|
|  | 700 | } | 
|---|
|  | 701 | } | 
|---|
|  | 702 | } | 
|---|
|  | 703 |  | 
|---|
| [530] | 704 | // write block to FAT | 
|---|
|  | 705 | if ( _fat_ioc_access( use_irq, | 
|---|
|  | 706 | 0,                // write | 
|---|
|  | 707 | lba, | 
|---|
|  | 708 | (unsigned int)_fat.fat_cache, | 
|---|
|  | 709 | 1 ) )             // one sector | 
|---|
|  | 710 | { | 
|---|
|  | 711 | _printf("\n[FAT ERROR] in _update_entry() cannot write sector %x\n", | 
|---|
|  | 712 | lba ); | 
|---|
|  | 713 | return 1; | 
|---|
|  | 714 | } | 
|---|
|  | 715 |  | 
|---|
|  | 716 | return 0; | 
|---|
|  | 717 | } // end _update_entry() | 
|---|
|  | 718 |  | 
|---|
|  | 719 |  | 
|---|
| [291] | 720 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 721 | // This function update the FS_INFO block: | 
|---|
| [295] | 722 | // last cluster allocated and number of free cluster. | 
|---|
| [291] | 723 | // Return 0 in case of success, > 0 if failure. | 
|---|
|  | 724 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 725 | static inline | 
|---|
|  | 726 | unsigned int _update_fs_info( unsigned int use_irq ) | 
|---|
| [291] | 727 | { | 
|---|
| [530] | 728 | unsigned int lba = _fat.fs_info_lba; | 
|---|
| [291] | 729 |  | 
|---|
|  | 730 | #if GIET_DEBUG_FAT | 
|---|
| [569] | 731 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 732 | _printf("\n[DEBUG FAT] _update_fs_info()\n"); | 
|---|
| [291] | 733 | #endif | 
|---|
|  | 734 |  | 
|---|
| [530] | 735 | // update FAT cache in case of miss | 
|---|
|  | 736 | if ( lba != _fat.cache_lba ) | 
|---|
| [291] | 737 | { | 
|---|
| [530] | 738 | if ( _fat_ioc_access( use_irq, | 
|---|
|  | 739 | 1,                 // read | 
|---|
|  | 740 | lba, | 
|---|
|  | 741 | (unsigned int)_fat.fat_cache, | 
|---|
|  | 742 | 1 ) )              // one sector | 
|---|
| [291] | 743 | { | 
|---|
| [530] | 744 | _printf("\n[FAT_ERROR] in _update_fs_info() cannot read block\n"); | 
|---|
| [291] | 745 | return 1; | 
|---|
|  | 746 | } | 
|---|
| [530] | 747 | _fat.cache_lba = lba; | 
|---|
| [291] | 748 | } | 
|---|
|  | 749 |  | 
|---|
| [530] | 750 | _write_entry( FS_FREE_CLUSTER     , _fat.fat_cache, _fat.number_free_cluster ); | 
|---|
|  | 751 | _write_entry( FS_FREE_CLUSTER_HINT, _fat.fat_cache, _fat.last_cluster_allocated ); | 
|---|
|  | 752 |  | 
|---|
|  | 753 | // write bloc to FAT | 
|---|
|  | 754 | if ( _fat_ioc_access( use_irq, | 
|---|
|  | 755 | 0,                // write | 
|---|
|  | 756 | lba, | 
|---|
|  | 757 | (unsigned int)_fat.fat_cache, | 
|---|
|  | 758 | 1 ) )             // one sector | 
|---|
|  | 759 | { | 
|---|
|  | 760 | _printf("\n[FAT_ERROR] in _update_fs_info() cannot write block\n"); | 
|---|
|  | 761 | return 1; | 
|---|
|  | 762 | } | 
|---|
|  | 763 |  | 
|---|
|  | 764 | return 0; | 
|---|
|  | 765 | }  // end _update_fs_info() | 
|---|
|  | 766 |  | 
|---|
| [291] | 767 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 768 | // This function update FAT, write a new value into cluster index. | 
|---|
|  | 769 | // Used by the function _fat_allocate to update the chaining of clusters. | 
|---|
|  | 770 | // Return 0 in case of success, > 0 if failure. | 
|---|
|  | 771 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 772 | static inline | 
|---|
|  | 773 | unsigned int _update_fat( unsigned int use_irq, | 
|---|
|  | 774 | unsigned int cluster, | 
|---|
|  | 775 | unsigned int value  ) | 
|---|
| [291] | 776 | { | 
|---|
| [530] | 777 | unsigned int lba = _fat.fat_lba + (cluster / 128); | 
|---|
| [291] | 778 |  | 
|---|
|  | 779 | #if GIET_DEBUG_FAT | 
|---|
| [569] | 780 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 781 | _printf("\n[DEBUG FAT] _update_fat() : cluster = %x / value = %x\n", | 
|---|
|  | 782 | cluster, value ); | 
|---|
| [291] | 783 | #endif | 
|---|
|  | 784 |  | 
|---|
| [530] | 785 | // update FAT cache in case of miss | 
|---|
|  | 786 | if ( lba != _fat.cache_lba ) | 
|---|
| [291] | 787 | { | 
|---|
| [530] | 788 | if ( _fat_ioc_access( use_irq, | 
|---|
|  | 789 | 1,                 // read | 
|---|
|  | 790 | lba, | 
|---|
|  | 791 | (unsigned int)_fat.fat_cache, | 
|---|
|  | 792 | 1 ) )              // one sector | 
|---|
| [291] | 793 | { | 
|---|
| [530] | 794 | _printf("\n[FAT_ERROR] in _update_fat() cannot read block %x\n", | 
|---|
|  | 795 | lba ); | 
|---|
| [291] | 796 | return 1; | 
|---|
|  | 797 | } | 
|---|
| [530] | 798 | _fat.cache_lba = lba; | 
|---|
| [291] | 799 | } | 
|---|
| [530] | 800 |  | 
|---|
|  | 801 | _write_entry( ((cluster % 128) << 2), 4, _fat.fat_cache, value ); | 
|---|
|  | 802 |  | 
|---|
|  | 803 | // write bloc to FAT | 
|---|
|  | 804 | if ( _fat_ioc_access( use_irq, | 
|---|
|  | 805 | 0,                // write | 
|---|
|  | 806 | lba, | 
|---|
|  | 807 | (unsigned int)_fat.fat_cache, | 
|---|
|  | 808 | 1 ) )             // one sector | 
|---|
|  | 809 | { | 
|---|
|  | 810 | _printf("\n[FAT_ERROR] in _update_fat() cannot write block %x\n", | 
|---|
|  | 811 | lba ); | 
|---|
|  | 812 | return 1; | 
|---|
|  | 813 | } | 
|---|
|  | 814 |  | 
|---|
|  | 815 | return 0; | 
|---|
| [417] | 816 | } // end update_fat() | 
|---|
| [291] | 817 |  | 
|---|
|  | 818 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [443] | 819 | // This function allocate count clusters to a file by calling the | 
|---|
| [417] | 820 | // _update_fat function that takes care to update the chaining of clusters. | 
|---|
| [291] | 821 | // return 0 if success, -1 if failure | 
|---|
|  | 822 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 823 | static inline | 
|---|
|  | 824 | int _fat_allocate( unsigned int use_irq, | 
|---|
|  | 825 | unsigned int fd_id, | 
|---|
|  | 826 | unsigned int count ) | 
|---|
| [291] | 827 | { | 
|---|
| [530] | 828 | unsigned int next_cluster = _fat.fd[fd_id].first_cluster;    // first cluster | 
|---|
|  | 829 | unsigned int cluster_to_allocate = count;                   // to allocate | 
|---|
|  | 830 | unsigned int last_cluster_file;                             // Last cluster | 
|---|
|  | 831 | unsigned int free_cluster = _fat.last_cluster_allocated + 1; // First free | 
|---|
| [291] | 832 |  | 
|---|
|  | 833 | // Check if free_cluster is really free (must be true) | 
|---|
| [530] | 834 | if ( _get_next_cluster( use_irq , free_cluster ) != FREE_CLUSTER) | 
|---|
| [291] | 835 | { | 
|---|
| [503] | 836 | _printf("\n[FAT ERROR] in _fat_allocate() : first free cluster not free\n"); | 
|---|
| [291] | 837 | return -1; | 
|---|
|  | 838 | } | 
|---|
|  | 839 | // Check if FAT contains enough cluster free for this allocation | 
|---|
| [530] | 840 | if ( count > _fat.number_free_cluster ) | 
|---|
| [291] | 841 | { | 
|---|
| [503] | 842 | _printf("\n[FAT ERROR] in _fat_allocate() : Not enough free cluster(s)\n"); | 
|---|
| [291] | 843 | return -1; | 
|---|
|  | 844 | } | 
|---|
|  | 845 |  | 
|---|
|  | 846 | #if GIET_DEBUG_FAT | 
|---|
| [569] | 847 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 848 | _printf("\n[DEBUG FAT] _fat_allocate() for fd = %d\n", fd_id ); | 
|---|
| [291] | 849 | #endif | 
|---|
|  | 850 |  | 
|---|
|  | 851 | // Get the last cluster allocated for the file (seek END_OF_CHAIN_CLUSTER). | 
|---|
| [503] | 852 | do | 
|---|
|  | 853 | { | 
|---|
| [291] | 854 | last_cluster_file = next_cluster; | 
|---|
| [530] | 855 | next_cluster      = _get_next_cluster( use_irq , next_cluster ); | 
|---|
| [503] | 856 | } | 
|---|
|  | 857 | while ( next_cluster < END_OF_CHAIN_CLUSTER ); | 
|---|
| [291] | 858 |  | 
|---|
| [443] | 859 | // Loop on the number of clusters to be allocated | 
|---|
| [291] | 860 | while ( cluster_to_allocate > 0 ) | 
|---|
|  | 861 | { | 
|---|
|  | 862 |  | 
|---|
|  | 863 | #if GIET_DEBUG_FAT | 
|---|
| [569] | 864 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 865 | _printf("\n[DEBUG FAT] cluster to update = %x / free cluster = %x / count = %d\n", | 
|---|
| [503] | 866 | last_cluster_file , free_cluster , cluster_to_allocate ); | 
|---|
| [291] | 867 | #endif | 
|---|
|  | 868 |  | 
|---|
|  | 869 | // update, in the FAT, the value of last cluster allocated by the index | 
|---|
|  | 870 | // of free cluster. | 
|---|
| [530] | 871 | if ( _update_fat( use_irq , last_cluster_file , free_cluster ) ) | 
|---|
| [291] | 872 | { | 
|---|
| [503] | 873 | _printf("\n[FAT ERROR] in _fat_allocate() : update fat failed\n"); | 
|---|
| [291] | 874 | return -1; | 
|---|
|  | 875 | } | 
|---|
|  | 876 |  | 
|---|
|  | 877 | cluster_to_allocate = cluster_to_allocate - 1; | 
|---|
|  | 878 | // Last cluster allocated is then free_cluster | 
|---|
|  | 879 | last_cluster_file = free_cluster; | 
|---|
|  | 880 |  | 
|---|
|  | 881 | // Last cluster to allocate done, then we must close the chain of clusters | 
|---|
|  | 882 | if ( cluster_to_allocate == 0 ) | 
|---|
|  | 883 | { | 
|---|
|  | 884 | // update, in the FAT, the value of the last cluster allocated by | 
|---|
|  | 885 | // END_OF_CHAIN_CLUSTER | 
|---|
| [530] | 886 | if ( _update_fat( use_irq , last_cluster_file , END_OF_CHAIN_CLUSTER ) ) | 
|---|
| [291] | 887 | { | 
|---|
| [503] | 888 | _printf("\n[FAT ERROR] in _fat_allocate() : update fat failed\n"); | 
|---|
| [291] | 889 | return -1; | 
|---|
|  | 890 | } | 
|---|
|  | 891 | } | 
|---|
|  | 892 |  | 
|---|
|  | 893 | free_cluster = free_cluster + 1; | 
|---|
|  | 894 |  | 
|---|
|  | 895 | // Check if free_cluster is really free (must be true) | 
|---|
| [530] | 896 | if ( _get_next_cluster( use_irq , free_cluster ) != FREE_CLUSTER) | 
|---|
| [291] | 897 | { | 
|---|
| [503] | 898 | _printf("\n[FAT ERROR] in _fat_allocate() : free_cluster not free\n"); | 
|---|
| [291] | 899 | return -1; | 
|---|
|  | 900 | } | 
|---|
|  | 901 | } | 
|---|
|  | 902 |  | 
|---|
|  | 903 | // Update field number_free_cluster and last_cluster_allocated | 
|---|
|  | 904 | // of structure fat for next fat_allocate | 
|---|
| [530] | 905 | _fat.last_cluster_allocated = last_cluster_file; | 
|---|
|  | 906 | _fat.number_free_cluster    = _fat.number_free_cluster - count; | 
|---|
| [291] | 907 |  | 
|---|
| [530] | 908 | if ( _update_fs_info( use_irq ) ) | 
|---|
| [291] | 909 | { | 
|---|
| [503] | 910 | _printf("\n[FAT ERROR] in _fat_allocate() : update fs_info failed\n"); | 
|---|
| [291] | 911 | return -1; | 
|---|
|  | 912 | } | 
|---|
|  | 913 |  | 
|---|
|  | 914 | return 0; | 
|---|
| [417] | 915 | }  // end _fat_allocate() | 
|---|
| [291] | 916 |  | 
|---|
| [530] | 917 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [258] | 918 | // This function read the blocks defined by the cluster index argument, in a data | 
|---|
| [417] | 919 | // region containing a directory to search the name of a file/firectory. | 
|---|
|  | 920 | // It returns the cluster index of the file/directory when the name has been found, | 
|---|
|  | 921 | // as well as the file size, and the lba. | 
|---|
|  | 922 | // We consider 3 types of directory entries: | 
|---|
|  | 923 | // - SFN : directory entry associated to a Short File Name (8.3) | 
|---|
|  | 924 | // - LFN : directory entry associated to a Long File Name | 
|---|
|  | 925 | // - XTN : directory entry containing only a name extension for a Long File Name | 
|---|
|  | 926 | // The cluster index is always stored in a SFN or LFN entry. | 
|---|
|  | 927 | // Return cluster index if name found / Return -1 if not found. | 
|---|
| [530] | 928 | ////////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 929 | static | 
|---|
|  | 930 | int _scan_directory( unsigned int   use_irq,         // use descheduling mode | 
|---|
|  | 931 | unsigned int   cluster,         // cluster of dir_entry | 
|---|
|  | 932 | char*          file_name,       // searched file/dir name | 
|---|
|  | 933 | unsigned int*  file_size,       // file size | 
|---|
|  | 934 | unsigned int*  lba_dir_entry )  // lba of dir_entry | 
|---|
| [258] | 935 | { | 
|---|
| [569] | 936 | char dir_entry[32];       // buffer to store a full directory_entry | 
|---|
|  | 937 | char name_entry[14];      // buffer to store a 13 characters (partial) name | 
|---|
| [258] | 938 |  | 
|---|
| [530] | 939 | char sfn_string[12]    = {[0 ... 10] = ' ', '\0'};  // buffer Short File Name | 
|---|
|  | 940 | unsigned int  is_sfn   = is_short(file_name, sfn_string); // file_name is short | 
|---|
|  | 941 | unsigned int  offset   = 0;                         // byte offset in block | 
|---|
| [569] | 942 | unsigned int  block_id = _fat.sectors_per_cluster;  // sector index | 
|---|
| [530] | 943 | unsigned int  lba      = cluster_to_lba(cluster);   // lba of cluster | 
|---|
|  | 944 | unsigned int  attr     = 0;                         // dir entry attribute | 
|---|
|  | 945 | unsigned int  ord      = 0;                         // dir entry sequence | 
|---|
|  | 946 | unsigned int  found    = 0;                         // searched name found | 
|---|
|  | 947 | unsigned int  long_name_found = 0;                  // matching XTN found | 
|---|
|  | 948 | unsigned int  searched_cluster;                     // searched cluster index | 
|---|
| [417] | 949 |  | 
|---|
| [258] | 950 | #if GIET_DEBUG_FAT | 
|---|
| [417] | 951 | unsigned int procid  = _get_procid(); | 
|---|
| [429] | 952 | unsigned int x       = procid >> (Y_WIDTH + P_WIDTH); | 
|---|
|  | 953 | unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH)-1); | 
|---|
|  | 954 | unsigned int p       = procid & ((1<<P_WIDTH)-1); | 
|---|
| [569] | 955 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [551] | 956 | _printf("\n[DEBUG FAT] _scan_directory() : P[%d,%d,%d] enters for %s\n", | 
|---|
| [503] | 957 | x, y, p, file_name ); | 
|---|
| [258] | 958 | #endif | 
|---|
|  | 959 |  | 
|---|
|  | 960 | unsigned int  i; | 
|---|
|  | 961 | for( i = 0 ; i < 32 ; i++ ) dir_entry[i]  = 0; | 
|---|
|  | 962 | for( i = 0 ; i < 14 ; i++ ) name_entry[i] = 0; | 
|---|
|  | 963 |  | 
|---|
| [417] | 964 | // load first sector from DATA region into FAT cache | 
|---|
| [258] | 965 | // other sectors will be loaded inside loop as required | 
|---|
| [530] | 966 | if( _fat_ioc_access( use_irq, | 
|---|
|  | 967 | 1,               // read | 
|---|
|  | 968 | lba, | 
|---|
|  | 969 | (unsigned int)_fat.fat_cache, | 
|---|
|  | 970 | 1 ) )            // one sector | 
|---|
| [258] | 971 | { | 
|---|
| [530] | 972 | _printf("[\nFAT ERROR] in _scan_directory() : cannot read sector %x\n", | 
|---|
|  | 973 | lba ); | 
|---|
| [258] | 974 | return -1; | 
|---|
|  | 975 | } | 
|---|
| [530] | 976 | _fat.cache_lba = lba; | 
|---|
| [258] | 977 |  | 
|---|
| [569] | 978 | #if GIET_DEBUG_FAT | 
|---|
|  | 979 | if ( (_get_proctime() > GIET_DEBUG_FAT) && (GIET_DEBUG_FAT & 0x1) ) | 
|---|
| [503] | 980 | _display_fat_cache(); | 
|---|
| [358] | 981 | #endif | 
|---|
|  | 982 |  | 
|---|
| [417] | 983 | // in this loop we scan all names in the directory identified by cluster index | 
|---|
| [258] | 984 | // - the offset increment is an integer number of directory entry (32 bytes) | 
|---|
|  | 985 | // - the exit condition is success (name found) or failure (end of directory) | 
|---|
| [417] | 986 | while( found == 0 ) | 
|---|
| [258] | 987 | { | 
|---|
|  | 988 | // load a new sector if required | 
|---|
|  | 989 | if (offset >= 512) | 
|---|
|  | 990 | { | 
|---|
|  | 991 | if ( block_id )           // not a new cluster | 
|---|
|  | 992 | { | 
|---|
|  | 993 | lba += 1; | 
|---|
|  | 994 | block_id --; | 
|---|
|  | 995 | } | 
|---|
|  | 996 | else                                          // get next cluster | 
|---|
|  | 997 | { | 
|---|
| [530] | 998 | cluster = _get_next_cluster( use_irq , cluster ); | 
|---|
| [258] | 999 |  | 
|---|
|  | 1000 | if ( cluster >= END_OF_CHAIN_CLUSTER  ) return END_OF_CHAIN_CLUSTER; | 
|---|
|  | 1001 |  | 
|---|
|  | 1002 | lba      = cluster_to_lba(cluster); | 
|---|
| [530] | 1003 | block_id = _fat.sectors_per_cluster; | 
|---|
| [258] | 1004 | } | 
|---|
| [530] | 1005 | if( _fat_ioc_access( use_irq, | 
|---|
|  | 1006 | 1,               // read | 
|---|
|  | 1007 | lba, | 
|---|
|  | 1008 | (unsigned int)_fat.fat_cache, | 
|---|
|  | 1009 | 1 ) )            // one sector | 
|---|
| [258] | 1010 | { | 
|---|
| [530] | 1011 | _printf("\n[FAT ERROR] in _scan_directory() : cannot read sector %x\n", | 
|---|
|  | 1012 | lba); | 
|---|
| [258] | 1013 | return -1; | 
|---|
|  | 1014 | } | 
|---|
| [530] | 1015 | _fat.cache_lba = lba; | 
|---|
| [258] | 1016 | block_id--; | 
|---|
|  | 1017 | offset = offset % 512; | 
|---|
|  | 1018 | } | 
|---|
|  | 1019 |  | 
|---|
| [417] | 1020 | // store the directory entry pointed by offset in dir_entry buffer, | 
|---|
|  | 1021 | // if it a possible candidate for the searched name | 
|---|
| [258] | 1022 |  | 
|---|
| [530] | 1023 | attr = _read_entry( DIR_ATTR, _fat.fat_cache + offset, 0); | 
|---|
|  | 1024 | ord  = _read_entry( LDIR_ORD, _fat.fat_cache + offset, 0); | 
|---|
| [417] | 1025 |  | 
|---|
|  | 1026 | if ( is_sfn == 1 )                                // searched name is short | 
|---|
|  | 1027 | { | 
|---|
|  | 1028 | if      ( (ord != FREE_ENTRY ) && | 
|---|
|  | 1029 | (ord != NO_MORE_ENTRY) && | 
|---|
|  | 1030 | (attr == ATTR_LONG_NAME_MASK) )    // EXT entry : skipped | 
|---|
| [258] | 1031 | { | 
|---|
| [417] | 1032 | offset     = offset + ((ord & 0xF) * DIR_ENTRY_SIZE); | 
|---|
| [258] | 1033 | } | 
|---|
| [417] | 1034 | else if ( (attr != ATTR_LONG_NAME_MASK) && | 
|---|
|  | 1035 | (ord  != FREE_ENTRY) && | 
|---|
|  | 1036 | (ord  != NO_MORE_ENTRY ) )         // SFN entry : to be checked | 
|---|
| [258] | 1037 | { | 
|---|
| [530] | 1038 | memcpy( dir_entry, _fat.fat_cache + offset, DIR_ENTRY_SIZE ); | 
|---|
| [417] | 1039 |  | 
|---|
|  | 1040 | get_name_from_short( dir_entry, name_entry ); | 
|---|
|  | 1041 |  | 
|---|
|  | 1042 | if ( _strncmp( (char*)sfn_string, | 
|---|
|  | 1043 | (char*)name_entry, 13 ) == 0 )  // short name found | 
|---|
| [258] | 1044 | { | 
|---|
| [417] | 1045 | found = 1; | 
|---|
| [258] | 1046 | } | 
|---|
| [417] | 1047 | else | 
|---|
| [258] | 1048 | { | 
|---|
|  | 1049 | offset = offset + DIR_ENTRY_SIZE; | 
|---|
|  | 1050 | } | 
|---|
|  | 1051 | } | 
|---|
| [417] | 1052 | else if (ord == NO_MORE_ENTRY )              // end of directory : return | 
|---|
| [258] | 1053 | { | 
|---|
| [417] | 1054 | return END_OF_CHAIN_CLUSTER; | 
|---|
| [258] | 1055 | } | 
|---|
| [417] | 1056 | } | 
|---|
|  | 1057 | else                                      // searched name is long | 
|---|
|  | 1058 | { | 
|---|
|  | 1059 | if( (attr == ATTR_LONG_NAME_MASK) && | 
|---|
|  | 1060 | (ord != FREE_ENTRY) && | 
|---|
|  | 1061 | (ord != NO_MORE_ENTRY) )                 // EXT entry : to be checked | 
|---|
| [258] | 1062 | { | 
|---|
| [530] | 1063 | memcpy( dir_entry, _fat.fat_cache + offset, DIR_ENTRY_SIZE ); | 
|---|
| [417] | 1064 |  | 
|---|
| [258] | 1065 | get_name_from_long( dir_entry, name_entry ); | 
|---|
|  | 1066 |  | 
|---|
|  | 1067 | unsigned shift = ((ord & 0xf) - 1) * 13; | 
|---|
| [417] | 1068 | if ( _strncmp( (char*)(file_name + shift), | 
|---|
|  | 1069 | (char*)name_entry, 13 ) == 0 )  // matching EXT | 
|---|
| [258] | 1070 | { | 
|---|
| [417] | 1071 | if( (ord & 0xf) == 1 )                    // long name found | 
|---|
|  | 1072 | { | 
|---|
|  | 1073 | long_name_found = 1; | 
|---|
|  | 1074 | } | 
|---|
| [258] | 1075 | } | 
|---|
| [417] | 1076 | offset = offset + DIR_ENTRY_SIZE; | 
|---|
|  | 1077 | } | 
|---|
|  | 1078 | else if( (attr != ATTR_LONG_NAME_MASK) && | 
|---|
|  | 1079 | (ord  != FREE_ENTRY) && | 
|---|
|  | 1080 | (ord  != NO_MORE_ENTRY) ) | 
|---|
|  | 1081 | { | 
|---|
|  | 1082 | if ( long_name_found )                   // LFN entry | 
|---|
| [258] | 1083 | { | 
|---|
| [530] | 1084 | memcpy( dir_entry, _fat.fat_cache + offset, DIR_ENTRY_SIZE ); | 
|---|
| [417] | 1085 | found = 1; | 
|---|
| [258] | 1086 | } | 
|---|
| [417] | 1087 | else                                     // SFN entry: must be skipped | 
|---|
|  | 1088 | { | 
|---|
|  | 1089 | offset = offset + DIR_ENTRY_SIZE; | 
|---|
|  | 1090 | } | 
|---|
| [258] | 1091 | } | 
|---|
| [417] | 1092 | else if (ord == NO_MORE_ENTRY )                              // end of directory : return | 
|---|
|  | 1093 | { | 
|---|
|  | 1094 | return END_OF_CHAIN_CLUSTER; | 
|---|
|  | 1095 | } | 
|---|
| [258] | 1096 | } | 
|---|
| [417] | 1097 | }  // end while | 
|---|
| [258] | 1098 |  | 
|---|
| [417] | 1099 | // returns cluster index | 
|---|
|  | 1100 | *file_size       = _read_entry( DIR_FILE_SIZE, dir_entry, 1 ); | 
|---|
|  | 1101 | *lba_dir_entry   = lba; | 
|---|
|  | 1102 | searched_cluster = (_read_entry( DIR_FST_CLUS_HI, dir_entry, 1 ) << 16) | | 
|---|
|  | 1103 | (_read_entry( DIR_FST_CLUS_LO, dir_entry, 1 )      ) ; | 
|---|
| [258] | 1104 |  | 
|---|
| [417] | 1105 | #if GIET_DEBUG_FAT | 
|---|
| [569] | 1106 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 1107 | _printf("\n[DEBUG FAT] _scan_directory() : P[%d,%d,%d] found %s" | 
|---|
| [503] | 1108 | " : cluster = %x\n", x, y, p, file_name, searched_cluster ); | 
|---|
| [417] | 1109 | #endif | 
|---|
|  | 1110 |  | 
|---|
|  | 1111 | return searched_cluster; | 
|---|
|  | 1112 | } // end _scan_directory() | 
|---|
|  | 1113 |  | 
|---|
|  | 1114 |  | 
|---|
| [258] | 1115 | ////////////////////////////////////////////////////////////////////// | 
|---|
|  | 1116 | // This function create a new entry in a directory identified | 
|---|
|  | 1117 | // by "dir_cluster". The name is defined by "name". | 
|---|
|  | 1118 | // The type (dir/file) is defined by "is_file". | 
|---|
|  | 1119 | // Returns cluster index if success, Returns -1 if error. | 
|---|
|  | 1120 | ////////////////////////////////////////////////////////////////////// | 
|---|
| [417] | 1121 | static int _fat_create( char*           name, | 
|---|
|  | 1122 | unsigned int    is_file, | 
|---|
|  | 1123 | unsigned int    dir_cluster ) | 
|---|
| [258] | 1124 | { | 
|---|
| [503] | 1125 | _printf("\n[FAT ERROR] _fat_create() not implemented\n"); | 
|---|
| [258] | 1126 | return 0; | 
|---|
|  | 1127 | }  //end _fat_create() | 
|---|
|  | 1128 |  | 
|---|
|  | 1129 |  | 
|---|
|  | 1130 |  | 
|---|
|  | 1131 | ////////////// Extern functions ////////////////////////////////////////// | 
|---|
|  | 1132 |  | 
|---|
|  | 1133 | ////////////////////////////////////////////////////////////////////////// | 
|---|
| [458] | 1134 | // This function initializes the FAT structure, including the open | 
|---|
|  | 1135 | // files descriptors array and the lock protecting the FAT, | 
|---|
|  | 1136 | // from informations found in the boot record. | 
|---|
|  | 1137 | // This should be done only once. | 
|---|
| [258] | 1138 | ////////////////////////////////////////////////////////////////////////// | 
|---|
| [503] | 1139 | // Return 0 if success, exit if failure | 
|---|
| [258] | 1140 | ////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 1141 | int _fat_init( unsigned int use_irq ) | 
|---|
| [258] | 1142 | { | 
|---|
|  | 1143 |  | 
|---|
|  | 1144 | #if GIET_DEBUG_FAT | 
|---|
| [295] | 1145 | unsigned int procid  = _get_procid(); | 
|---|
| [429] | 1146 | unsigned int x       = procid >> (Y_WIDTH + P_WIDTH); | 
|---|
|  | 1147 | unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH)-1); | 
|---|
|  | 1148 | unsigned int p       = procid & ((1<<P_WIDTH)-1); | 
|---|
| [569] | 1149 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 1150 | _printf("\n[DEBUG FAT] P[%d,%d,%d] enters _fat_init\n",x,y,p); | 
|---|
| [258] | 1151 | #endif | 
|---|
| [458] | 1152 |  | 
|---|
|  | 1153 | // FAT initialisation should be done only once | 
|---|
| [530] | 1154 | if ( _fat.initialised == FAT_INITIALISED ) | 
|---|
| [458] | 1155 | { | 
|---|
| [530] | 1156 | _printf("\n[FAT ERROR] Strange, FAT already initialised...\n"); | 
|---|
| [503] | 1157 | _exit(); | 
|---|
| [458] | 1158 | } | 
|---|
|  | 1159 |  | 
|---|
| [300] | 1160 | // load Boot Record (VBR) into fat cache | 
|---|
| [530] | 1161 | if ( _fat_ioc_access( use_irq, | 
|---|
|  | 1162 | 1,                   // read | 
|---|
|  | 1163 | 0,                   // sector index | 
|---|
|  | 1164 | (unsigned int)_fat.fat_cache, | 
|---|
|  | 1165 | 1 ) )                // one sector | 
|---|
| [258] | 1166 | { | 
|---|
| [503] | 1167 | _printf("\n[FAT ERROR] in _fat_init() cannot load VBR\n"); | 
|---|
|  | 1168 | _exit(); | 
|---|
| [258] | 1169 | } | 
|---|
| [530] | 1170 | _fat.cache_lba = 0; | 
|---|
| [258] | 1171 |  | 
|---|
| [503] | 1172 | #if GIET_DEBUG_FAT > 1 | 
|---|
| [569] | 1173 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 1174 | _printf("\n[DEBUG FAT] _fat_init() : Boot Sector Loaded\n"); | 
|---|
| [569] | 1175 | if ( (_get_proctime() > GIET_DEBUG_FAT) && (GIET_DEBUG_FAT & 0x1) ) | 
|---|
| [503] | 1176 | _display_fat_cache(); | 
|---|
| [258] | 1177 | #endif | 
|---|
|  | 1178 |  | 
|---|
|  | 1179 | // checking various FAT32 assuptions from boot sector | 
|---|
| [530] | 1180 | if( _read_entry( BPB_BYTSPERSEC, _fat.fat_cache, 1 ) != 512 ) | 
|---|
| [258] | 1181 | { | 
|---|
| [503] | 1182 | _printf("\n[FAT ERROR] The sector size must be 512 bytes\n"); | 
|---|
|  | 1183 | _exit(); | 
|---|
| [258] | 1184 | } | 
|---|
| [530] | 1185 | if( _read_entry( BPB_NUMFATS, _fat.fat_cache, 1 ) != 1 ) | 
|---|
| [258] | 1186 | { | 
|---|
| [503] | 1187 | _printf("\n[FAT ERROR] The number of FAT copies in FAT region must be 1\n"); | 
|---|
|  | 1188 | _exit(); | 
|---|
| [258] | 1189 | } | 
|---|
| [530] | 1190 | if( (_read_entry( BPB_FAT32_FATSZ32, _fat.fat_cache, 1 ) & 0xF) != 0 ) | 
|---|
| [258] | 1191 | { | 
|---|
| [530] | 1192 | _printf("\n[FAT ERROR] The FAT region must be multiple of 32 sectors\n"); | 
|---|
| [503] | 1193 | _exit(); | 
|---|
| [258] | 1194 | } | 
|---|
| [530] | 1195 | if( _read_entry( BPB_FAT32_ROOTCLUS, _fat.fat_cache, 1 ) != 2 ) | 
|---|
| [258] | 1196 | { | 
|---|
| [503] | 1197 | _printf("\n[FAT ERROR] The first cluster index must be 2\n"); | 
|---|
|  | 1198 | _exit(); | 
|---|
| [258] | 1199 | } | 
|---|
| [300] | 1200 | // FS Info always in sector 1 | 
|---|
| [530] | 1201 | _fat.fs_info_lba         = _read_entry( BPB_FAT32_FSINFO, _fat.fat_cache, 1 ); | 
|---|
| [258] | 1202 |  | 
|---|
| [300] | 1203 | // initialise fat descriptor from VBR | 
|---|
| [530] | 1204 | _fat.sectors_per_cluster = _read_entry( BPB_SECPERCLUS, _fat.fat_cache, 1 ); | 
|---|
|  | 1205 | _fat.sector_size         = _read_entry( BPB_BYTSPERSEC, _fat.fat_cache, 1 ); | 
|---|
|  | 1206 | _fat.cluster_size        = _fat.sectors_per_cluster * 512; | 
|---|
|  | 1207 | _fat.fat_sectors         = _read_entry( BPB_FAT32_FATSZ32, _fat.fat_cache, 1 ); | 
|---|
|  | 1208 | _fat.fat_lba             = _read_entry( BPB_RSVDSECCNT, _fat.fat_cache, 1 ); | 
|---|
|  | 1209 | _fat.data_lba            = _fat.fat_lba + _fat.fat_sectors; | 
|---|
|  | 1210 | _fat.initialised         = FAT_INITIALISED; | 
|---|
| [258] | 1211 |  | 
|---|
| [458] | 1212 | // initalise the lock protecting the FAT | 
|---|
| [530] | 1213 | _spin_lock_init( &_fat.fat_lock ); | 
|---|
| [458] | 1214 |  | 
|---|
| [258] | 1215 | // initialise file descriptor array | 
|---|
| [530] | 1216 | unsigned int   n; | 
|---|
|  | 1217 | for( n = 0 ; n < GIET_OPEN_FILES_MAX ; n++ ) _fat.fd[n].used = 0; | 
|---|
| [258] | 1218 |  | 
|---|
| [291] | 1219 | #if GIET_DEBUG_FAT | 
|---|
| [569] | 1220 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 1221 | _printf("\n[DEBUG FAT] _fat_init() : FS_INFO Sector = %x\n", _fat.fs_info_lba ); | 
|---|
| [291] | 1222 | #endif | 
|---|
|  | 1223 |  | 
|---|
|  | 1224 | // load FS_INFO into fat cache | 
|---|
| [530] | 1225 | if ( _fat_ioc_access( use_irq, | 
|---|
|  | 1226 | 1,                  // read | 
|---|
|  | 1227 | _fat.fs_info_lba, | 
|---|
|  | 1228 | (unsigned int)_fat.fat_cache, | 
|---|
|  | 1229 | 1 ) )               // one sector | 
|---|
| [291] | 1230 | { | 
|---|
| [503] | 1231 | _printf("\n[FAT ERROR] in _fat_init() cannot load FS_INFO Sector\n"); | 
|---|
|  | 1232 | _exit(); | 
|---|
| [291] | 1233 | } | 
|---|
| [530] | 1234 | _fat.cache_lba = _fat.fs_info_lba; | 
|---|
| [291] | 1235 |  | 
|---|
| [530] | 1236 | _fat.number_free_cluster    = _read_entry( FS_FREE_CLUSTER     , _fat.fat_cache, 1); | 
|---|
|  | 1237 | _fat.last_cluster_allocated = _read_entry( FS_FREE_CLUSTER_HINT, _fat.fat_cache, 1); | 
|---|
| [291] | 1238 |  | 
|---|
|  | 1239 | #if GIET_DEBUG_FAT | 
|---|
| [569] | 1240 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [358] | 1241 | _fat_print(); | 
|---|
| [530] | 1242 | _printf("\n[DEBUG FAT] P[%d,%d,%d] exit _fat_init()\n", x,y,p ); | 
|---|
| [291] | 1243 | #endif | 
|---|
|  | 1244 |  | 
|---|
| [258] | 1245 | return 0; | 
|---|
|  | 1246 | }  // end _fat_init() | 
|---|
|  | 1247 |  | 
|---|
|  | 1248 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
| [354] | 1249 | // This function checks that the kernel FAT structure has been initialised. | 
|---|
| [458] | 1250 | // It searches a file identified by the "pathname" argument. | 
|---|
| [258] | 1251 | // It starts from root (cluster 2) to scan successively each subdirectory. | 
|---|
|  | 1252 | // When the file is not found, but the path is found, and "creat" is set, | 
|---|
|  | 1253 | // a new file is created and introduced in the directory. | 
|---|
|  | 1254 | // Finally, it sets a new open file in the file descriptors array. | 
|---|
| [354] | 1255 | // The same file can be open several times by differents tasks. | 
|---|
| [258] | 1256 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 1257 | // Returns file descriptor index if success, returns -1 if error. | 
|---|
|  | 1258 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 1259 | int _fat_open( unsigned int use_irq,       // use descheduling mode if possible | 
|---|
| [258] | 1260 | char*        pathname, | 
|---|
|  | 1261 | unsigned int creat ) | 
|---|
|  | 1262 | { | 
|---|
| [354] | 1263 | char                 name[256];        // buffer for one name in pathname | 
|---|
|  | 1264 | unsigned int         nb_read;              // number of characters written in name[] | 
|---|
|  | 1265 | unsigned int         cluster;          // current cluster index when scanning FAT | 
|---|
|  | 1266 | unsigned int         dir_cluster;      // previous cluster index when scanning FAT | 
|---|
|  | 1267 | unsigned int         fd_id;            // index when scanning file descriptors array | 
|---|
|  | 1268 | unsigned int         file_size = 0;    // number of bytes | 
|---|
|  | 1269 | unsigned int         last_name = 0;    // directory containing file name is reached | 
|---|
|  | 1270 | unsigned int         lba       = 0;    // lba of dir_entry for this file | 
|---|
| [258] | 1271 |  | 
|---|
|  | 1272 | #if GIET_DEBUG_FAT | 
|---|
| [295] | 1273 | unsigned int procid  = _get_procid(); | 
|---|
| [429] | 1274 | unsigned int x       = procid >> (Y_WIDTH + P_WIDTH); | 
|---|
|  | 1275 | unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH)-1); | 
|---|
|  | 1276 | unsigned int p       = procid & ((1<<P_WIDTH)-1); | 
|---|
| [569] | 1277 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [551] | 1278 | _printf("\n[DEBUG FAT] _fat_open() : P[%d,%d,%d] enters for path %s\n", | 
|---|
| [503] | 1279 | x, y, p, pathname ); | 
|---|
| [258] | 1280 | #endif | 
|---|
|  | 1281 |  | 
|---|
| [458] | 1282 | // checking creat argument | 
|---|
| [295] | 1283 | if ( creat ) | 
|---|
|  | 1284 | { | 
|---|
| [503] | 1285 | _printf("\n[FAT ERROR] in _fat_open() : create not supported yet\n"); | 
|---|
| [295] | 1286 | return -1; | 
|---|
|  | 1287 | } | 
|---|
|  | 1288 |  | 
|---|
| [458] | 1289 | // checking FAT initialised | 
|---|
| [530] | 1290 | if( _fat.initialised != FAT_INITIALISED ) | 
|---|
| [458] | 1291 | { | 
|---|
| [503] | 1292 | _printf("\n[FAT ERROR] in _fat_open() : FAT not initialised\n"); | 
|---|
| [458] | 1293 | return -1; | 
|---|
|  | 1294 | } | 
|---|
| [295] | 1295 | // takes the FAT lock for exclusive access | 
|---|
| [530] | 1296 | _spin_lock_acquire( &_fat.fat_lock ); | 
|---|
| [295] | 1297 |  | 
|---|
|  | 1298 | #if GIET_DEBUG_FAT | 
|---|
| [569] | 1299 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 1300 | _printf("\n[DEBUG FAT] _fat_open() : P[%d,%d,%d] takes the FAT lock\n", | 
|---|
| [503] | 1301 | x, y, p ); | 
|---|
| [295] | 1302 | #endif | 
|---|
| [258] | 1303 |  | 
|---|
| [263] | 1304 | // Scan the directories, starting from the root directory (cluster 2) | 
|---|
| [258] | 1305 | // - The get_name_from_path() function extracts (successively) | 
|---|
|  | 1306 | //   each directory name from the pathname, and store it in name[] buffer | 
|---|
| [417] | 1307 | // - The _scan_directory() function scan one (or several) cluster(s) containing | 
|---|
| [258] | 1308 | //   a directory looking for name[], and return the cluster index | 
|---|
|  | 1309 | //   corresponding to the directory/file found. | 
|---|
|  | 1310 | nb_read     = 0; | 
|---|
|  | 1311 | cluster     = 2; | 
|---|
|  | 1312 | last_name   = 0; | 
|---|
|  | 1313 | while ( get_name_from_path( pathname, name, &nb_read) ) | 
|---|
|  | 1314 | { | 
|---|
|  | 1315 |  | 
|---|
|  | 1316 | #if GIET_DEBUG_FAT | 
|---|
| [569] | 1317 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 1318 | _printf("\n[DEBUG FAT] _fat_open() : P[%d,%d,%d] search file/dir %s\n", | 
|---|
| [503] | 1319 | x, y, p, name ); | 
|---|
| [258] | 1320 | #endif | 
|---|
| [291] | 1321 |  | 
|---|
| [258] | 1322 | // test if we reach the last name (file name) | 
|---|
|  | 1323 | if( pathname[nb_read] == 0 ) | 
|---|
|  | 1324 | { | 
|---|
|  | 1325 | last_name   = 1; | 
|---|
| [569] | 1326 | dir_cluster = cluster;   // this is the lowest directory | 
|---|
| [258] | 1327 | } | 
|---|
|  | 1328 |  | 
|---|
|  | 1329 | // scan current directory | 
|---|
| [530] | 1330 | cluster = _scan_directory( use_irq , cluster , name , &file_size , &lba ); | 
|---|
| [258] | 1331 |  | 
|---|
|  | 1332 | if( cluster == END_OF_CHAIN_CLUSTER && last_name && creat ) | 
|---|
|  | 1333 | { | 
|---|
| [417] | 1334 | cluster = _fat_create( name, 1, dir_cluster ); | 
|---|
| [258] | 1335 | } | 
|---|
|  | 1336 | else if ( cluster == END_OF_CHAIN_CLUSTER ) | 
|---|
|  | 1337 | { | 
|---|
| [503] | 1338 | _printf("\n[FAT ERROR] in _fat_open() cannot found %s\n", name ); | 
|---|
| [530] | 1339 | _spin_lock_release( &_fat.fat_lock ); | 
|---|
| [258] | 1340 | return -1; | 
|---|
|  | 1341 | } | 
|---|
|  | 1342 | } | 
|---|
|  | 1343 |  | 
|---|
| [417] | 1344 | #if GIET_DEBUG_FAT | 
|---|
| [569] | 1345 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 1346 | _printf("\n[DEBUG FAT] P[%d,%d,%d] in _fat_open() : cluster for %s = %x\n", | 
|---|
| [503] | 1347 | x, y, p, pathname, cluster ); | 
|---|
| [417] | 1348 | #endif | 
|---|
|  | 1349 |  | 
|---|
| [258] | 1350 | // check the next value for cluster index found | 
|---|
| [530] | 1351 | unsigned next = _get_next_cluster( use_irq , cluster ); | 
|---|
| [258] | 1352 |  | 
|---|
|  | 1353 | if ( (next != BAD_CLUSTER) && (next != FREE_CLUSTER) ) | 
|---|
|  | 1354 | { | 
|---|
|  | 1355 | // Search an empty slot scanning open file descriptors array | 
|---|
|  | 1356 | fd_id = 0; | 
|---|
| [530] | 1357 | while ( _fat.fd[fd_id].used != 0 && fd_id < GIET_OPEN_FILES_MAX ) | 
|---|
| [258] | 1358 | { | 
|---|
|  | 1359 | fd_id++; | 
|---|
|  | 1360 | } | 
|---|
|  | 1361 |  | 
|---|
|  | 1362 | // set file descriptor if found empty slot | 
|---|
|  | 1363 | if ( fd_id < GIET_OPEN_FILES_MAX ) | 
|---|
|  | 1364 | { | 
|---|
| [530] | 1365 | _fat.fd[fd_id].used          = 1; | 
|---|
|  | 1366 | _fat.fd[fd_id].first_cluster = cluster; | 
|---|
|  | 1367 | _fat.fd[fd_id].file_size     = file_size; | 
|---|
|  | 1368 | _fat.fd[fd_id].lba_dir_entry = lba; | 
|---|
|  | 1369 | _strcpy( _fat.fd[fd_id].name, pathname ); | 
|---|
| [258] | 1370 |  | 
|---|
|  | 1371 | #if GIET_DEBUG_FAT | 
|---|
| [569] | 1372 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 1373 | _printf("\n[DEBUG FAT] _fat_open() : P[%d,%d,%d] exit : fd = %d for file %s\n", | 
|---|
| [503] | 1374 | x, y, p, fd_id, pathname ); | 
|---|
| [258] | 1375 | #endif | 
|---|
|  | 1376 |  | 
|---|
| [295] | 1377 | // release FAT lock | 
|---|
| [530] | 1378 | _spin_lock_release( &_fat.fat_lock ); | 
|---|
| [295] | 1379 |  | 
|---|
| [258] | 1380 | return fd_id; | 
|---|
|  | 1381 | } | 
|---|
|  | 1382 | else | 
|---|
|  | 1383 | { | 
|---|
| [503] | 1384 | _printf("\n[FAT ERROR] in _fat_open() for file %s : fd array full\n", | 
|---|
|  | 1385 | pathname ); | 
|---|
| [530] | 1386 | _spin_lock_release( &_fat.fat_lock ); | 
|---|
| [258] | 1387 | return -1; | 
|---|
|  | 1388 | } | 
|---|
|  | 1389 | } | 
|---|
|  | 1390 | else | 
|---|
|  | 1391 | { | 
|---|
| [503] | 1392 | _printf("\n[FAT ERROR] in _fat_open() for file %s : bad cluster\n", | 
|---|
|  | 1393 | pathname ); | 
|---|
| [530] | 1394 | _spin_lock_release( &_fat.fat_lock ); | 
|---|
| [258] | 1395 | return -1; | 
|---|
|  | 1396 | } | 
|---|
|  | 1397 | } // end _fat_open() | 
|---|
|  | 1398 |  | 
|---|
|  | 1399 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 1400 | // For an open file, identified by the file descriptor index, transfer | 
|---|
|  | 1401 | // an integer number of sectors from block device to a memory buffer. | 
|---|
|  | 1402 | // If the number of requested sectors exceeds the file size, it is reduced. | 
|---|
|  | 1403 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 1404 | // Returns number of sectors transfered if success, < 0 if error. | 
|---|
|  | 1405 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 1406 | int _fat_read( unsigned int use_irq,    // use descheduling mode if possible | 
|---|
| [258] | 1407 | unsigned int fd_id,      // file descriptor | 
|---|
|  | 1408 | void*        buffer,     // target buffer base address | 
|---|
|  | 1409 | unsigned int count,      // number of sector to read | 
|---|
|  | 1410 | unsigned int offset )    // nuber of sectors to skip in file | 
|---|
|  | 1411 | { | 
|---|
| [530] | 1412 | unsigned int spc = _fat.sectors_per_cluster; | 
|---|
| [258] | 1413 |  | 
|---|
|  | 1414 | unsigned int file_size;         // number of bytes in file | 
|---|
|  | 1415 | unsigned int file_sectors;      // number of sectors in file | 
|---|
|  | 1416 | unsigned int total_sectors;     // actual number of sectors to be transfered | 
|---|
|  | 1417 | unsigned int cluster;           // cluster index | 
|---|
|  | 1418 | unsigned int clusters_to_skip;  // number of clusters to skip because offset | 
|---|
|  | 1419 | unsigned int sectors_to_skip;   // number of sectors to skip in first iteration | 
|---|
|  | 1420 |  | 
|---|
|  | 1421 | // arguments checking | 
|---|
|  | 1422 | if ( fd_id >= GIET_OPEN_FILES_MAX ) | 
|---|
|  | 1423 | { | 
|---|
| [503] | 1424 | _printf("\n[FAT ERROR] in _fat_read() : illegal file descriptor index\n"); | 
|---|
| [258] | 1425 | return -1; | 
|---|
|  | 1426 | } | 
|---|
| [530] | 1427 | if ( _fat.fd[fd_id].used != 1 ) | 
|---|
| [258] | 1428 | { | 
|---|
| [503] | 1429 | _printf("\n[FAT ERROR] in _fat_read() : file not open\n"); | 
|---|
| [258] | 1430 | return -1; | 
|---|
|  | 1431 | } | 
|---|
|  | 1432 | if ( ((unsigned int)buffer & 0x1FF) != 0 ) | 
|---|
|  | 1433 | { | 
|---|
| [503] | 1434 | _printf("\n[FAT ERROR] in _fat_read() : memory buffer not sector aligned\n"); | 
|---|
| [258] | 1435 | return -1; | 
|---|
|  | 1436 | } | 
|---|
| [358] | 1437 |  | 
|---|
|  | 1438 | // compute file size as a number of sectors | 
|---|
| [530] | 1439 | file_size    = _fat.fd[fd_id].file_size; | 
|---|
| [358] | 1440 | if ( file_size & 0x1FF ) file_sectors = (file_size >> 9) + 1; | 
|---|
|  | 1441 | else                     file_sectors = (file_size >> 9); | 
|---|
|  | 1442 |  | 
|---|
| [258] | 1443 | if ( offset >= file_sectors ) | 
|---|
|  | 1444 | { | 
|---|
| [503] | 1445 | _printf("\n[FAT ERROR] offset larger than number of sectors\n"); | 
|---|
| [258] | 1446 | return -1; | 
|---|
|  | 1447 | } | 
|---|
|  | 1448 |  | 
|---|
|  | 1449 | // compute total number of sectors to read | 
|---|
|  | 1450 | if ( file_sectors < (offset + count) ) total_sectors = file_sectors - offset; | 
|---|
|  | 1451 | else                                   total_sectors = count; | 
|---|
|  | 1452 |  | 
|---|
|  | 1453 | // compute clusters and sectors to be skipped | 
|---|
|  | 1454 | clusters_to_skip = offset / spc; | 
|---|
|  | 1455 | sectors_to_skip  = offset % spc; | 
|---|
|  | 1456 |  | 
|---|
|  | 1457 | // get first cluster index | 
|---|
| [530] | 1458 | cluster = _fat.fd[fd_id].first_cluster; | 
|---|
| [258] | 1459 |  | 
|---|
|  | 1460 | #if GIET_DEBUG_FAT | 
|---|
| [354] | 1461 | unsigned int procid  = _get_procid(); | 
|---|
| [429] | 1462 | unsigned int x       = procid >> (Y_WIDTH + P_WIDTH); | 
|---|
|  | 1463 | unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH)-1); | 
|---|
|  | 1464 | unsigned int p       = procid & ((1<<P_WIDTH)-1); | 
|---|
| [569] | 1465 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 1466 | _printf("\n[DEBUG FAT] _fat_read() : P[%d,%d,%d] enters for file %s\n" | 
|---|
| [503] | 1467 | " - buffer vbase     = %x\n" | 
|---|
|  | 1468 | " - skipped sectors  = %x\n" | 
|---|
|  | 1469 | " - read sectors     = %x\n" | 
|---|
|  | 1470 | " - first cluster    = %x\n" | 
|---|
|  | 1471 | " - skipped clusters = %x\n", | 
|---|
| [530] | 1472 | x, y, p, _fat.fd[fd_id].name, (unsigned int)buffer, | 
|---|
|  | 1473 | offset, count, cluster, clusters_to_skip ); | 
|---|
| [258] | 1474 | #endif | 
|---|
|  | 1475 |  | 
|---|
|  | 1476 | // compute index of first cluster to be loaded | 
|---|
|  | 1477 | while ( clusters_to_skip ) | 
|---|
|  | 1478 | { | 
|---|
| [530] | 1479 | cluster = _get_next_cluster( use_irq , cluster ); | 
|---|
| [258] | 1480 | clusters_to_skip--; | 
|---|
|  | 1481 | } | 
|---|
|  | 1482 |  | 
|---|
|  | 1483 | // variables used in the loop on clusters | 
|---|
|  | 1484 | int             todo_sectors;   // number of sectors still to be loaded | 
|---|
|  | 1485 | unsigned int    lba;            // first sector index on device | 
|---|
|  | 1486 | unsigned int    iter_sectors;   // number of sectors to load in iteration | 
|---|
| [530] | 1487 | unsigned int    dst;            // pointer on target buffer | 
|---|
| [258] | 1488 |  | 
|---|
|  | 1489 | // initialize these variables for the first iteration | 
|---|
|  | 1490 | todo_sectors  = total_sectors; | 
|---|
| [530] | 1491 | dst           = (unsigned int)buffer; | 
|---|
| [258] | 1492 | lba           = cluster_to_lba(cluster) + sectors_to_skip; | 
|---|
|  | 1493 | if( total_sectors < (spc - sectors_to_skip) ) iter_sectors = total_sectors; | 
|---|
|  | 1494 | else                                          iter_sectors = spc - sectors_to_skip; | 
|---|
|  | 1495 |  | 
|---|
| [358] | 1496 | // loop on the clusters: one IOC access per cluster | 
|---|
| [258] | 1497 | while ( todo_sectors > 0 ) | 
|---|
|  | 1498 | { | 
|---|
|  | 1499 |  | 
|---|
|  | 1500 | #if GIET_DEBUG_FAT | 
|---|
| [569] | 1501 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 1502 | _printf("\n[DEBUG FAT] _fat_read() : P[%d,%d,%d] makes an IOC read\n" | 
|---|
|  | 1503 | "  cluster = %x / buffer = %x / lba = %x / sectors = %d\n", | 
|---|
|  | 1504 | x, y, p, cluster, dst, lba, iter_sectors ); | 
|---|
| [258] | 1505 | #endif | 
|---|
|  | 1506 |  | 
|---|
| [530] | 1507 | if( _fat_ioc_access( use_irq, | 
|---|
|  | 1508 | 1,                 // read | 
|---|
|  | 1509 | lba, | 
|---|
|  | 1510 | dst,               // buffer address | 
|---|
|  | 1511 | iter_sectors ) )   // number of sectors | 
|---|
| [258] | 1512 | { | 
|---|
| [503] | 1513 | _printf("\n[FAT ERROR] in _fat_read() cannot load block %x", lba ); | 
|---|
| [258] | 1514 | return -1; | 
|---|
|  | 1515 | } | 
|---|
|  | 1516 |  | 
|---|
|  | 1517 | // update variables for next iteration | 
|---|
| [530] | 1518 | cluster      = _get_next_cluster( use_irq , cluster ); | 
|---|
| [258] | 1519 | todo_sectors = todo_sectors - iter_sectors; | 
|---|
|  | 1520 | dst          = dst + (iter_sectors << 9); | 
|---|
|  | 1521 | lba          = cluster_to_lba(cluster); | 
|---|
|  | 1522 | if ( todo_sectors > spc ) iter_sectors = spc; | 
|---|
|  | 1523 | else                      iter_sectors = todo_sectors; | 
|---|
|  | 1524 | } | 
|---|
|  | 1525 |  | 
|---|
|  | 1526 | // returns number of sectors actually transfered | 
|---|
|  | 1527 | return total_sectors; | 
|---|
|  | 1528 |  | 
|---|
|  | 1529 | }  // end _fat_read() | 
|---|
|  | 1530 |  | 
|---|
|  | 1531 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 1532 | // For an open file, identified by the file descriptor index, transfer | 
|---|
|  | 1533 | // an integer number of sectors from a memory buffer to block device. | 
|---|
|  | 1534 | // Allocate new clusters if the offset+count larger than current file size, | 
|---|
|  | 1535 | // but the offset should be smaller than the current file size... | 
|---|
|  | 1536 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 1537 | // Returns number of sectors written if success, < 0 if error. | 
|---|
|  | 1538 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
| [530] | 1539 | int _fat_write( unsigned int use_irq,    // use descheduling mode if possible | 
|---|
| [258] | 1540 | unsigned int fd_id,      // file descriptor | 
|---|
|  | 1541 | void*        buffer,     // target buffer base address | 
|---|
|  | 1542 | unsigned int count,      // number of sector to write | 
|---|
|  | 1543 | unsigned int offset )    // nuber of sectors to skip in file | 
|---|
|  | 1544 | { | 
|---|
| [259] | 1545 |  | 
|---|
| [530] | 1546 | unsigned int spc = _fat.sectors_per_cluster; | 
|---|
| [259] | 1547 |  | 
|---|
|  | 1548 | unsigned int file_size;         // number of bytes in file | 
|---|
|  | 1549 | unsigned int file_sectors;      // number of sectors in file | 
|---|
|  | 1550 | unsigned int cluster;           // cluster index | 
|---|
|  | 1551 | unsigned int clusters_to_skip;  // number of clusters to skip because offset | 
|---|
|  | 1552 | unsigned int sectors_to_skip;   // number of sectors to skip in first iteration | 
|---|
|  | 1553 | unsigned int allocate;          // need allocate or not | 
|---|
| [291] | 1554 | unsigned int current_cluster;   // number of cluster allocated to the file | 
|---|
|  | 1555 | unsigned int required_cluster;  // number of cluster needed for the write | 
|---|
| [259] | 1556 |  | 
|---|
|  | 1557 | // compute file size as a number of sectors | 
|---|
| [530] | 1558 | file_size    = _fat.fd[fd_id].file_size; | 
|---|
| [259] | 1559 | if ( file_size & 0x1FF ) file_sectors = (file_size >> 9) + 1; | 
|---|
|  | 1560 | else                     file_sectors = (file_size >> 9); | 
|---|
|  | 1561 |  | 
|---|
| [291] | 1562 | // Compute the number of clusters occupied by the file | 
|---|
|  | 1563 | current_cluster = file_sectors / spc; | 
|---|
|  | 1564 |  | 
|---|
|  | 1565 | // Compute the number of clusters that will occupy the file (after fat_write) | 
|---|
|  | 1566 | required_cluster = (count + offset) / spc; | 
|---|
|  | 1567 |  | 
|---|
|  | 1568 | // Check if we need to allocate new cluster(s) for the file | 
|---|
|  | 1569 | allocate = ( required_cluster > current_cluster ); | 
|---|
|  | 1570 |  | 
|---|
| [259] | 1571 | #if GIET_DEBUG_FAT | 
|---|
| [295] | 1572 | unsigned int procid  = _get_procid(); | 
|---|
| [429] | 1573 | unsigned int x       = procid >> (Y_WIDTH + P_WIDTH); | 
|---|
|  | 1574 | unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH)-1); | 
|---|
|  | 1575 | unsigned int p       = procid & ((1<<P_WIDTH)-1); | 
|---|
| [569] | 1576 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 1577 | _printf("\n[DEBUG FAT] _fat_write() : P[%d,%d,%d] enters for file %s\n" | 
|---|
| [503] | 1578 | " - buffer vbase    = %x\n" | 
|---|
|  | 1579 | " - skipped sectors = %x\n" | 
|---|
|  | 1580 | " - write sectors   = %x\n" | 
|---|
|  | 1581 | " - file sectors    = %x\n" | 
|---|
|  | 1582 | " - need_allocate   = %d\n", | 
|---|
| [530] | 1583 | x, y, p, _fat.fd[fd_id].name, (unsigned int)buffer, | 
|---|
|  | 1584 | offset, count, file_sectors, allocate ); | 
|---|
| [259] | 1585 | #endif | 
|---|
|  | 1586 |  | 
|---|
|  | 1587 | // arguments checking | 
|---|
|  | 1588 | if ( fd_id >= GIET_OPEN_FILES_MAX ) | 
|---|
|  | 1589 | { | 
|---|
| [503] | 1590 | _printf("\n[FAT ERROR] in _fat_write() : illegal file descriptor index\n"); | 
|---|
| [259] | 1591 | return -1; | 
|---|
|  | 1592 | } | 
|---|
| [530] | 1593 | if ( _fat.fd[fd_id].used != 1 ) | 
|---|
| [259] | 1594 | { | 
|---|
| [503] | 1595 | _printf("\n[FAT ERROR] in _fat_write() : file not open\n"); | 
|---|
| [259] | 1596 | return -1; | 
|---|
|  | 1597 | } | 
|---|
|  | 1598 | if ( ((unsigned int)buffer & 0x1FF) != 0 ) | 
|---|
|  | 1599 | { | 
|---|
| [503] | 1600 | _printf("\n[FAT ERROR] in _fat_write() : memory buffer not sector aligned\n"); | 
|---|
| [259] | 1601 | return -1; | 
|---|
|  | 1602 | } | 
|---|
|  | 1603 |  | 
|---|
| [291] | 1604 | if ( allocate  ) | 
|---|
|  | 1605 | { | 
|---|
| [530] | 1606 | if ( _fat_allocate( use_irq , fd_id, (required_cluster-current_cluster) ) ) | 
|---|
| [291] | 1607 | { | 
|---|
| [503] | 1608 | _printf("\n[FAT ERROR] in _fat_write() : fat_allocate failed\n"); | 
|---|
| [291] | 1609 | return -1; | 
|---|
|  | 1610 | } | 
|---|
|  | 1611 | } | 
|---|
|  | 1612 |  | 
|---|
| [259] | 1613 | // compute clusters and sectors to be skipped | 
|---|
|  | 1614 | clusters_to_skip = offset / spc; | 
|---|
|  | 1615 | sectors_to_skip  = offset % spc; | 
|---|
|  | 1616 |  | 
|---|
|  | 1617 | // get first cluster index | 
|---|
| [530] | 1618 | cluster = _fat.fd[fd_id].first_cluster; | 
|---|
| [259] | 1619 |  | 
|---|
|  | 1620 | #if GIET_DEBUG_FAT | 
|---|
| [569] | 1621 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 1622 | _printf("\n[DEBUG FAT] _fat_write() : P[%d,%d,%d] get cluster %x\n", | 
|---|
| [503] | 1623 | x, y, p, cluster ); | 
|---|
| [259] | 1624 | #endif | 
|---|
|  | 1625 |  | 
|---|
|  | 1626 | // compute index of first cluster to be loaded | 
|---|
|  | 1627 | while ( clusters_to_skip ) | 
|---|
|  | 1628 | { | 
|---|
| [530] | 1629 | cluster = _get_next_cluster( use_irq , cluster ); | 
|---|
| [259] | 1630 | clusters_to_skip--; | 
|---|
|  | 1631 | } | 
|---|
|  | 1632 |  | 
|---|
|  | 1633 | // variables used in the loop on clusters | 
|---|
|  | 1634 | int             todo_sectors;   // number of sectors still to be loaded | 
|---|
|  | 1635 | unsigned int    lba;            // first sector index on device | 
|---|
|  | 1636 | unsigned int    iter_sectors;   // number of sectors to load in iteration | 
|---|
| [530] | 1637 | unsigned int    src;            // pointer on target buffer | 
|---|
| [259] | 1638 |  | 
|---|
|  | 1639 | // initialize these variables for the first iteration | 
|---|
|  | 1640 | todo_sectors  = count; | 
|---|
| [530] | 1641 | src           = (unsigned int)buffer; | 
|---|
| [259] | 1642 | lba           = cluster_to_lba(cluster) + sectors_to_skip; | 
|---|
|  | 1643 | if( count < (spc - sectors_to_skip) ) iter_sectors = count; | 
|---|
|  | 1644 | else                                  iter_sectors = spc - sectors_to_skip; | 
|---|
|  | 1645 |  | 
|---|
|  | 1646 | // loop on the clusters | 
|---|
|  | 1647 | while ( todo_sectors > 0 ) | 
|---|
|  | 1648 | { | 
|---|
|  | 1649 |  | 
|---|
|  | 1650 | #if GIET_DEBUG_FAT | 
|---|
| [569] | 1651 | if ( _get_proctime() > GIET_DEBUG_FAT ) | 
|---|
| [530] | 1652 | _printf("\n[DEBUG FAT] _fat_write() : P[%d,%d,%d] makes an IOC write" | 
|---|
|  | 1653 | "  cluster = %x / buffer = %x / lba = %x / sectors = %d\n", | 
|---|
|  | 1654 | x, y, p, cluster, src, lba, iter_sectors ); | 
|---|
| [259] | 1655 | #endif | 
|---|
|  | 1656 |  | 
|---|
| [530] | 1657 | if( _fat_ioc_access( use_irq, | 
|---|
|  | 1658 | 0,                 // write | 
|---|
|  | 1659 | lba, | 
|---|
|  | 1660 | src,               // source buffer address | 
|---|
|  | 1661 | iter_sectors ) )   // number of sectors | 
|---|
| [259] | 1662 | { | 
|---|
| [503] | 1663 | _printf("\n[FAT ERROR] in _fat_write() cannot write block %x\n", lba ); | 
|---|
| [259] | 1664 | return -1; | 
|---|
|  | 1665 | } | 
|---|
|  | 1666 |  | 
|---|
|  | 1667 | // update variables for next iteration | 
|---|
| [530] | 1668 | cluster      = _get_next_cluster( use_irq , cluster ); | 
|---|
| [259] | 1669 | todo_sectors = todo_sectors - iter_sectors; | 
|---|
|  | 1670 | src          = src + (iter_sectors << 9); | 
|---|
|  | 1671 | lba          = cluster_to_lba(cluster); | 
|---|
|  | 1672 | if ( todo_sectors > spc ) iter_sectors = spc; | 
|---|
|  | 1673 | else                      iter_sectors = todo_sectors; | 
|---|
|  | 1674 | } | 
|---|
| [291] | 1675 |  | 
|---|
|  | 1676 | // Update structure file descriptor, field file_size with | 
|---|
|  | 1677 | // the new file size if the file is bigger than the previous file | 
|---|
|  | 1678 | if ( ( offset + count ) > file_sectors ) | 
|---|
|  | 1679 | { | 
|---|
| [530] | 1680 | _fat.fd[fd_id].file_size = (count + offset) << 9; | 
|---|
| [291] | 1681 | } | 
|---|
|  | 1682 |  | 
|---|
|  | 1683 | // Update entry of directory with the new value | 
|---|
|  | 1684 | // of file size (Field : DIR_FILE_SIZE) | 
|---|
| [530] | 1685 | if ( _update_entry( use_irq, fd_id , DIR_FILE_SIZE , _fat.fd[fd_id].file_size ) ) | 
|---|
| [291] | 1686 | { | 
|---|
| [530] | 1687 | _printf("\n[FAT ERROR] in _fat_write() update entry failed\n"); | 
|---|
|  | 1688 | return -1; | 
|---|
| [291] | 1689 | } | 
|---|
| [259] | 1690 |  | 
|---|
|  | 1691 | // returns number of sectors actually transfered | 
|---|
|  | 1692 | return count; | 
|---|
| [417] | 1693 | }  // end _fat_write() | 
|---|
| [258] | 1694 |  | 
|---|
|  | 1695 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [260] | 1696 | // Return stats of a file identified by "fd". | 
|---|
|  | 1697 | // (Only the file_size in sectors for this moment) | 
|---|
|  | 1698 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 1699 | // Returns file size (on sectors) on success, -1 on failure. | 
|---|
|  | 1700 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 1701 | int _fat_fstat( unsigned int fd_id ) | 
|---|
|  | 1702 | { | 
|---|
|  | 1703 | unsigned int file_size    = 0; | 
|---|
|  | 1704 | unsigned int file_sectors = 0; | 
|---|
|  | 1705 |  | 
|---|
|  | 1706 | if( (fd_id < GIET_OPEN_FILES_MAX) ) | 
|---|
|  | 1707 | { | 
|---|
| [530] | 1708 | file_size = _fat.fd[fd_id].file_size; | 
|---|
| [260] | 1709 |  | 
|---|
|  | 1710 | if ( file_size & 0x1FF ) file_sectors = (file_size >> 9) + 1; | 
|---|
|  | 1711 | else                     file_sectors = (file_size >> 9); | 
|---|
|  | 1712 |  | 
|---|
|  | 1713 | return file_sectors; | 
|---|
|  | 1714 | } | 
|---|
|  | 1715 | else | 
|---|
|  | 1716 | { | 
|---|
| [503] | 1717 | _printf("\n[FAT ERROR] in _fat_fstat() : illegal file descriptor index\n"); | 
|---|
| [260] | 1718 | return -1; | 
|---|
|  | 1719 | } | 
|---|
|  | 1720 | } // end _fat_fstat() | 
|---|
|  | 1721 |  | 
|---|
|  | 1722 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [258] | 1723 | // Close the file identified by the file_descriptor index. | 
|---|
|  | 1724 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 1725 | // Returns 0 on success, -1 on failure. | 
|---|
|  | 1726 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
|  | 1727 | int _fat_close( unsigned int fd_id ) | 
|---|
|  | 1728 | { | 
|---|
|  | 1729 | if( (fd_id < GIET_OPEN_FILES_MAX) ) | 
|---|
|  | 1730 | { | 
|---|
| [530] | 1731 | _fat.fd[fd_id].used = 0; | 
|---|
| [258] | 1732 | return 0; | 
|---|
|  | 1733 | } | 
|---|
|  | 1734 | else | 
|---|
|  | 1735 | { | 
|---|
| [503] | 1736 | _printf("\n[FAT ERROR] in _fat_close() : illegal file descriptor index\n"); | 
|---|
| [258] | 1737 | return -1; | 
|---|
|  | 1738 | } | 
|---|
|  | 1739 | } // end fat_close() | 
|---|
|  | 1740 |  | 
|---|
| [530] | 1741 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [258] | 1742 | // The following function implement the user_level system call. | 
|---|
| [417] | 1743 | // The flags argument is not used, as file access modes are not implemented yet. | 
|---|
| [530] | 1744 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [258] | 1745 | // Return the file descriptor index if success / return -1 if failure | 
|---|
| [530] | 1746 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [258] | 1747 | int _fat_user_open( char*  pathname,         // absolute pathname from root | 
|---|
|  | 1748 | unsigned int flags )     // unused: TODO | 
|---|
|  | 1749 | { | 
|---|
| [530] | 1750 | return _fat_open( 1,        // use descheduling mode if possible | 
|---|
|  | 1751 | pathname, | 
|---|
|  | 1752 | 0 );      // no creation if not found | 
|---|
| [258] | 1753 | } | 
|---|
|  | 1754 |  | 
|---|
| [530] | 1755 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [258] | 1756 | // The following function implement the user_level system call. | 
|---|
| [530] | 1757 | // This function should be modified to respect the UNIX specification. | 
|---|
|  | 1758 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [258] | 1759 | // Return number of sectors actually transfered if success / return -1 if failure | 
|---|
| [530] | 1760 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [258] | 1761 | int _fat_user_read( unsigned int fd,        // file descriptor index | 
|---|
|  | 1762 | void*        buffer,    // destination buffer | 
|---|
|  | 1763 | unsigned int count,     // number of sectors to read | 
|---|
|  | 1764 | unsigned int offset )   // number of sectors to skip | 
|---|
|  | 1765 | { | 
|---|
| [530] | 1766 | return _fat_read( 1,        // use descheduling mode if possible | 
|---|
| [258] | 1767 | fd, | 
|---|
|  | 1768 | buffer, | 
|---|
|  | 1769 | count, | 
|---|
|  | 1770 | offset ); | 
|---|
|  | 1771 | } | 
|---|
|  | 1772 |  | 
|---|
| [530] | 1773 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [258] | 1774 | // The following function implement the user_level system call. | 
|---|
|  | 1775 | // This function should be modified to respect the UNIX specification. | 
|---|
| [530] | 1776 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [258] | 1777 | // Return number of sectors actually transfered if success / return -1 if failure | 
|---|
| [530] | 1778 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [258] | 1779 | int _fat_user_write( unsigned int fd,       // file descriptor | 
|---|
|  | 1780 | void*        buffer,   // source buffer | 
|---|
|  | 1781 | unsigned int count,    // number of sectors to write | 
|---|
|  | 1782 | unsigned int offset )  // number of sectors to skip on file | 
|---|
|  | 1783 | { | 
|---|
| [530] | 1784 | return _fat_write( 1,       // use descheduling mode if possible | 
|---|
| [258] | 1785 | fd, | 
|---|
|  | 1786 | buffer, | 
|---|
|  | 1787 | count, | 
|---|
|  | 1788 | offset ); | 
|---|
|  | 1789 | } | 
|---|
|  | 1790 |  | 
|---|
| [530] | 1791 | ///////////////////////////////////////////////////////////////////////////////// | 
|---|
| [258] | 1792 | int _fat_user_lseek( unsigned int fd_id, | 
|---|
|  | 1793 | unsigned int offset, | 
|---|
|  | 1794 | unsigned int whence ) | 
|---|
|  | 1795 | { | 
|---|
| [503] | 1796 | _printf("\n[GIET ERROR] _fat_user_lseek() not implemented\n"); | 
|---|
| [258] | 1797 | _exit(); | 
|---|
|  | 1798 | return 0; | 
|---|
|  | 1799 | } | 
|---|
|  | 1800 |  | 
|---|
|  | 1801 |  | 
|---|
|  | 1802 | // Local Variables: | 
|---|
|  | 1803 | // tab-width: 4 | 
|---|
|  | 1804 | // c-basic-offset: 4 | 
|---|
|  | 1805 | // c-file-offsets:((innamespace . 0)(inline-open . 0)) | 
|---|
|  | 1806 | // indent-tabs-mode: nil | 
|---|
|  | 1807 | // End: | 
|---|
|  | 1808 | // vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4 | 
|---|
|  | 1809 |  | 
|---|