Line No. | Rev | Author | Line |
---|---|---|---|
1 | 6 | kaklik | /*! \file fat.c \brief FAT16/32 file system driver. */ |
2 | //***************************************************************************** |
||
3 | // |
||
4 | // File Name : 'fat.c' |
||
5 | // Title : FAT16/32 file system driver |
||
6 | // Author : Pascal Stang |
||
7 | // Date : 11/07/2000 |
||
8 | // Revised : 12/12/2000 |
||
9 | // Version : 0.3 |
||
10 | // Target MCU : ATmega103 (should work for Atmel AVR Series) |
||
11 | // Editor Tabs : 4 |
||
12 | // |
||
13 | // This code is based in part on work done by Jesper Hansen for his |
||
14 | // YAMPP MP3 player project. |
||
15 | // |
||
16 | // NOTE: This code is currently below version 1.0, and therefore is considered |
||
17 | // to be lacking in some functionality or documentation, or may not be fully |
||
18 | // tested. Nonetheless, you can expect most functions to work. |
||
19 | // |
||
20 | // This code is distributed under the GNU Public License |
||
21 | // which can be found at http://www.gnu.org/licenses/gpl.txt |
||
22 | // |
||
23 | //***************************************************************************** |
||
24 | |||
25 | |||
26 | #include <avr/io.h> |
||
27 | #include <avr/pgmspace.h> |
||
28 | #include <string.h> |
||
29 | |||
30 | #include "ata.h" |
||
31 | #include "rprintf.h" |
||
32 | #include "debug.h" |
||
33 | |||
34 | #include "fat.h" |
||
35 | #include "fatconf.h" |
||
36 | |||
37 | // globals |
||
38 | // buffers |
||
39 | unsigned char *SectorBuffer = (unsigned char *) FAT_SECTOR_BUFFER_ADDR; |
||
40 | unsigned char *FileNameBuffer = (unsigned char *) FAT_FILENAME_BUFFER_ADDR; |
||
41 | unsigned char *PathNameBuffer = (unsigned char *) FAT_PATHNAME_BUFFER_ADDR; |
||
42 | |||
43 | // filesystem constants/metrics |
||
44 | struct partrecord PartInfo; |
||
45 | unsigned char Fat32Enabled; |
||
46 | unsigned long FirstDataSector; |
||
47 | unsigned short BytesPerSector; |
||
48 | unsigned short SectorsPerCluster; |
||
49 | unsigned long FirstFATSector; |
||
50 | unsigned long RootDirStartCluster; |
||
51 | |||
52 | // operating variables |
||
53 | unsigned long CurrentDirStartCluster; //< current directory starting cluster |
||
54 | struct FileInfoStruct FileInfo; //< file information for last file accessed |
||
55 | unsigned long FatInCache = 0; |
||
56 | |||
57 | |||
58 | /*************************************************************************/ |
||
59 | /*************************************************************************/ |
||
60 | |||
61 | |||
62 | unsigned long fatClustToSect(unsigned long clust) |
||
63 | { |
||
64 | return ((clust-2) * SectorsPerCluster) + FirstDataSector; |
||
65 | } |
||
66 | |||
67 | unsigned int fatClusterSize(void) |
||
68 | { |
||
69 | // return the number of sectors in a disk cluster |
||
70 | return SectorsPerCluster; |
||
71 | } |
||
72 | |||
73 | unsigned char fatInit( unsigned char device) |
||
74 | { |
||
75 | //struct partrecord *pr; |
||
76 | struct bpb710 *bpb; |
||
77 | |||
78 | // read partition table |
||
79 | // TODO.... error checking |
||
80 | ataReadSectors(DRIVE0, 0, 1, SectorBuffer); |
||
81 | // map first partition record |
||
82 | // save partition information to global PartInfo |
||
83 | PartInfo = *((struct partrecord *) ((struct partsector *) SectorBuffer)->psPart); |
||
84 | // PartInfo = *pr; |
||
85 | |||
86 | // Read the Partition BootSector |
||
87 | // **first sector of partition in PartInfo.prStartLBA |
||
88 | ataReadSectors( DRIVE0, PartInfo.prStartLBA, 1, SectorBuffer ); |
||
89 | bpb = (struct bpb710 *) ((struct bootsector710 *) SectorBuffer)->bsBPB; |
||
90 | |||
91 | // setup global disk constants |
||
92 | FirstDataSector = PartInfo.prStartLBA; |
||
93 | if(bpb->bpbFATsecs) |
||
94 | { |
||
95 | // bpbFATsecs is non-zero and is therefore valid |
||
96 | FirstDataSector += bpb->bpbResSectors + bpb->bpbFATs * bpb->bpbFATsecs; |
||
97 | } |
||
98 | else |
||
99 | { |
||
100 | // bpbFATsecs is zero, real value is in bpbBigFATsecs |
||
101 | FirstDataSector += bpb->bpbResSectors + bpb->bpbFATs * bpb->bpbBigFATsecs; |
||
102 | } |
||
103 | SectorsPerCluster = bpb->bpbSecPerClust; |
||
104 | BytesPerSector = bpb->bpbBytesPerSec; |
||
105 | FirstFATSector = bpb->bpbResSectors + PartInfo.prStartLBA; |
||
106 | |||
107 | switch (PartInfo.prPartType) |
||
108 | { |
||
109 | case PART_TYPE_DOSFAT16: |
||
110 | case PART_TYPE_FAT16: |
||
111 | case PART_TYPE_FAT16LBA: |
||
112 | // first directory cluster is 2 by default (clusters range 2->big) |
||
113 | RootDirStartCluster = CLUST_FIRST; |
||
114 | // push data sector pointer to end of root directory area |
||
115 | //FirstDataSector += (bpb->bpbRootDirEnts)/DIRENTRIES_PER_SECTOR; |
||
116 | Fat32Enabled = FALSE; |
||
117 | break; |
||
118 | case PART_TYPE_FAT32LBA: |
||
119 | case PART_TYPE_FAT32: |
||
120 | // bpbRootClust field exists in FAT32 bpb710, but not in lesser bpb's |
||
121 | RootDirStartCluster = bpb->bpbRootClust; |
||
122 | // push data sector pointer to end of root directory area |
||
123 | // need this? FirstDataSector += (bpb->bpbRootDirEnts)/DIRENTRIES_PER_SECTOR; |
||
124 | Fat32Enabled = TRUE; |
||
125 | break; |
||
126 | default: |
||
127 | rprintfProgStrM("Found: No Partition!\r\n"); |
||
128 | //return 1; |
||
129 | break; |
||
130 | } |
||
131 | |||
132 | // set current directory to root (\) |
||
133 | CurrentDirStartCluster = RootDirStartCluster; |
||
134 | PathNameBuffer[0] = '\\'; |
||
135 | PathNameBuffer[1] = 0; |
||
136 | |||
137 | |||
138 | // do debug |
||
139 | #ifdef DEBUG_FAT |
||
140 | switch (PartInfo.prPartType) |
||
141 | { |
||
142 | case PART_TYPE_DOSFAT16: |
||
143 | rprintfProgStrM("Found: DOSFAT 16\r\n"); |
||
144 | break; |
||
145 | case PART_TYPE_FAT16: |
||
146 | rprintfProgStrM("Found: FAT16\r\n"); |
||
147 | break; |
||
148 | case PART_TYPE_FAT16LBA: |
||
149 | rprintfProgStrM("Found: FAT16 LBA\r\n"); |
||
150 | break; |
||
151 | case PART_TYPE_FAT32LBA: |
||
152 | rprintfProgStrM("Found: FAT32 LBA\r\n"); |
||
153 | break; |
||
154 | case PART_TYPE_FAT32: |
||
155 | rprintfProgStrM("Found: FAT32\r\n"); |
||
156 | //return 1; |
||
157 | break; |
||
158 | default: |
||
159 | rprintfProgStrM("Found: No Partition!\r\n"); |
||
160 | //return 1; |
||
161 | break; |
||
162 | } |
||
163 | |||
164 | rprintfProgStrM("First sector : "); rprintfu32(PartInfo.prStartLBA); rprintfCRLF(); |
||
165 | rprintfProgStrM("Size : "); rprintfu32(PartInfo.prSize); rprintfCRLF(); |
||
166 | rprintfProgStrM("bytes/sector : "); rprintfu16(bpb->bpbBytesPerSec); rprintfCRLF(); |
||
167 | rprintfProgStrM("sectors/cluster : "); rprintfu08(bpb->bpbSecPerClust); rprintfCRLF(); |
||
168 | rprintfProgStrM("reserved sectors: "); rprintfu16(bpb->bpbResSectors); rprintfCRLF(); |
||
169 | rprintfProgStrM("FatSectors : "); rprintfu16(bpb->bpbFATsecs); rprintfCRLF(); |
||
170 | rprintfProgStrM("BigFatSectors : "); rprintfu32(bpb->bpbBigFATsecs); rprintfCRLF(); |
||
171 | rprintfProgStrM("Number of Fats : "); rprintfu08(bpb->bpbFATs); rprintfCRLF(); |
||
172 | rprintfProgStrM("First Fat Sector: "); rprintfu32(FirstFATSector); rprintfCRLF(); |
||
173 | rprintfProgStrM("First Data Sect : "); rprintfu32(FirstDataSector); rprintfCRLF(); |
||
174 | rprintfProgStrM("RootDirStartClus: "); rprintfu32(RootDirStartCluster); rprintfCRLF(); |
||
175 | #endif |
||
176 | |||
177 | return 0; |
||
178 | } |
||
179 | |||
180 | ////////////////////////////////////////////////////////////// |
||
181 | |||
182 | unsigned char fatGetDirEntry(unsigned short entry) |
||
183 | { |
||
184 | unsigned long sector; |
||
185 | struct direntry *de = 0; // avoid compiler warning by initializing |
||
186 | struct winentry *we; |
||
187 | unsigned char haveLongNameEntry; |
||
188 | unsigned char gotEntry; |
||
189 | unsigned short b; |
||
190 | int i,index; |
||
191 | char *fnbPtr; |
||
192 | unsigned short entrycount = 0; |
||
193 | |||
194 | // read dir data |
||
195 | sector = fatClustToSect(CurrentDirStartCluster); |
||
196 | |||
197 | haveLongNameEntry = 0; |
||
198 | gotEntry = 0; |
||
199 | |||
200 | index = 16; // crank it up |
||
201 | |||
202 | //while(entrycount < entry) |
||
203 | while(1) |
||
204 | { |
||
205 | if(index == 16) // time for next sector ? |
||
206 | { |
||
207 | ataReadSectors( DRIVE0, sector++, 1, SectorBuffer); |
||
208 | de = (struct direntry *) SectorBuffer; |
||
209 | index = 0; |
||
210 | } |
||
211 | |||
212 | // check the status of this directory entry slot |
||
213 | if(de->deName[0] == 0x00) |
||
214 | { |
||
215 | // slot is empty and this is the end of directory |
||
216 | gotEntry = 0; |
||
217 | break; |
||
218 | } |
||
219 | else if(de->deName[0] == 0xE5) |
||
220 | { |
||
221 | // this is an empty slot |
||
222 | // do nothing and move to the next one |
||
223 | } |
||
224 | else |
||
225 | { |
||
226 | // this is a valid and occupied entry |
||
227 | // is it a part of a long file/dir name? |
||
228 | if(de->deAttributes == ATTR_LONG_FILENAME) |
||
229 | { |
||
230 | // we have a long name entry |
||
231 | // cast this directory entry as a "windows" (LFN: LongFileName) entry |
||
232 | we = (struct winentry *) de; |
||
233 | |||
234 | b = WIN_ENTRY_CHARS*( (we->weCnt-1) & 0x0f); // index into string |
||
235 | fnbPtr = &FileNameBuffer[b]; |
||
236 | for (i=0;i<5;i++) *fnbPtr++ = we->wePart1[i*2]; // copy first part |
||
237 | for (i=0;i<6;i++) *fnbPtr++ = we->wePart2[i*2]; // second part |
||
238 | for (i=0;i<2;i++) *fnbPtr++ = we->wePart3[i*2]; // and third part |
||
239 | if (we->weCnt & WIN_LAST) *fnbPtr = 0; // in case dirnamelength is multiple of 13, add termination |
||
240 | if ((we->weCnt & 0x0f) == 1) haveLongNameEntry = 1; // flag that we have a complete long name entry set |
||
241 | } |
||
242 | else |
||
243 | { |
||
244 | // we have a short name entry |
||
245 | |||
246 | // check if this is the short name entry corresponding |
||
247 | // to the end of a multi-part long name entry |
||
248 | if(haveLongNameEntry) |
||
249 | { |
||
250 | // a long entry name has been collected |
||
251 | if(entrycount == entry) |
||
252 | { |
||
253 | // desired entry has been found, break out |
||
254 | gotEntry = 1; |
||
255 | break; |
||
256 | } |
||
257 | // otherwise |
||
258 | haveLongNameEntry = 0; // clear long name flag |
||
259 | entrycount++; // increment entry counter |
||
260 | } |
||
261 | else |
||
262 | { |
||
263 | // entry is a short name (8.3 format) without a |
||
264 | // corresponding multi-part long name entry |
||
265 | fnbPtr = FileNameBuffer; |
||
266 | for (i=0;i<8;i++) *fnbPtr++ = de->deName[i]; // copy name |
||
267 | *fnbPtr++ = '.'; // insert '.' |
||
268 | for (i=0;i<3;i++) *fnbPtr++ = de->deExtension[i]; // copy extension |
||
269 | *fnbPtr = 0; // null-terminate |
||
270 | |||
271 | if(entrycount == entry) |
||
272 | { |
||
273 | // desired entry has been found, break out |
||
274 | gotEntry = 1; |
||
275 | break; |
||
276 | } |
||
277 | // otherwise |
||
278 | entrycount++; // increment entry counter |
||
279 | } |
||
280 | } |
||
281 | } |
||
282 | // next directory entry |
||
283 | de++; |
||
284 | // next index |
||
285 | index++; |
||
286 | } |
||
287 | |||
288 | // we have a file/dir to return |
||
289 | // store file/dir starting cluster (start of data) |
||
290 | FileInfo.StartCluster = (unsigned long) ((unsigned long)de->deHighClust << 16) + de->deStartCluster; |
||
291 | // store file/dir size |
||
292 | // (note: size field for subdirectory entries is always zero) |
||
293 | FileInfo.Size = de->deFileSize; |
||
294 | // store file/dir attributes |
||
295 | FileInfo.Attr = de->deAttributes; |
||
296 | // store file/dir creation time |
||
297 | FileInfo.CreateTime = de->deCTime[0] | de->deCTime[1]<<8; |
||
298 | // store file/dir creation date |
||
299 | FileInfo.CreateTime = de->deCDate[0] | de->deCDate[1]<<8; |
||
300 | |||
301 | return gotEntry; |
||
302 | } |
||
303 | |||
304 | // change directory into |
||
305 | unsigned char fatChangeDirectory(unsigned short entry) |
||
306 | { |
||
307 | // get the requested directory entry |
||
308 | if( fatGetDirEntry(entry) ) |
||
309 | { |
||
310 | // make sure the entry is a directory |
||
311 | if(FileInfo.Attr & ATTR_DIRECTORY) |
||
312 | { |
||
313 | // change directories into this directory |
||
314 | // check to see if we are changing back to root directory |
||
315 | if(FileInfo.StartCluster) |
||
316 | { |
||
317 | // standard change directory |
||
318 | CurrentDirStartCluster = FileInfo.StartCluster; |
||
319 | } |
||
320 | else |
||
321 | { |
||
322 | // if startCluster pointer is zero, |
||
323 | // a change to the root directory is intended |
||
324 | // change directory to root |
||
325 | CurrentDirStartCluster = RootDirStartCluster; |
||
326 | } |
||
327 | // TODO: handle pathname properly for going up a directory |
||
328 | // set path string |
||
329 | strcat(PathNameBuffer, FileNameBuffer); |
||
330 | strcat(PathNameBuffer, "\\"); |
||
331 | // return success |
||
332 | return TRUE; |
||
333 | } |
||
334 | else |
||
335 | { |
||
336 | // not a directory, cannot CD into a file! |
||
337 | return FALSE; |
||
338 | } |
||
339 | } |
||
340 | else |
||
341 | { |
||
342 | // not a valid entry, cannot CD! |
||
343 | return FALSE; |
||
344 | } |
||
345 | } |
||
346 | |||
347 | void fatPrintDirEntry(void) |
||
348 | { |
||
349 | // print a formatted dir-style output for most recent file |
||
350 | // print date |
||
351 | rprintfNum(10, 2, FALSE, '0', (FileInfo.CreateDate&DD_MONTH_MASK)>>DD_MONTH_SHIFT ); // month |
||
352 | rprintfChar('/'); |
||
353 | rprintfNum(10, 2, FALSE, '0', (FileInfo.CreateDate&DD_DAY_MASK)>>DD_DAY_SHIFT ); // day |
||
354 | rprintfChar('/'); |
||
355 | rprintfNum(10, 4, FALSE, '0', (FileInfo.CreateDate&DD_YEAR_MASK)>>DD_YEAR_SHIFT ); // year |
||
356 | rprintfChar(' '); |
||
357 | |||
358 | // print time |
||
359 | rprintfNum(10, 2, FALSE, '0', (FileInfo.CreateTime&DT_HOURS_MASK)>>DT_HOURS_SHIFT ); // month |
||
360 | rprintfChar(':'); |
||
361 | rprintfNum(10, 2, FALSE, '0', (FileInfo.CreateTime&DT_MINUTES_MASK)>>DT_MINUTES_SHIFT ); // day |
||
362 | rprintfChar(':'); |
||
363 | rprintfNum(10, 2, FALSE, '0', 2*(FileInfo.CreateTime&DT_2SECONDS_MASK)>>DT_2SECONDS_SHIFT ); // seconds |
||
364 | rprintfChar(' '); |
||
365 | |||
366 | // print attributes |
||
367 | if(FileInfo.Attr & ATTR_VOLUME) rprintfChar('V'); else rprintfChar('-'); |
||
368 | if(FileInfo.Attr & ATTR_DIRECTORY) rprintfChar('D'); else rprintfChar('-'); |
||
369 | if(FileInfo.Attr & ATTR_READONLY) rprintfChar('R'); else rprintfChar('-'); |
||
370 | if(FileInfo.Attr & ATTR_HIDDEN) rprintfChar('H'); else rprintfChar('-'); |
||
371 | if(FileInfo.Attr & ATTR_SYSTEM) rprintfChar('S'); else rprintfChar('-'); |
||
372 | if(FileInfo.Attr & ATTR_ARCHIVE) rprintfChar('A'); else rprintfChar('-'); |
||
373 | rprintfChar(' '); |
||
374 | |||
375 | // print filesize |
||
376 | rprintfNum(10, 8, FALSE, ' ', FileInfo.Size); // filesize |
||
377 | rprintfChar(' '); |
||
378 | |||
379 | // print filename |
||
380 | rprintfStr(FileNameBuffer); |
||
381 | } |
||
382 | |||
383 | void fatDumpDirSlot(unsigned short slot) |
||
384 | { |
||
385 | unsigned long sector; |
||
386 | // load correct sector |
||
387 | sector = fatClustToSect(CurrentDirStartCluster); |
||
388 | sector += slot/DIRENTRIES_PER_SECTOR; |
||
389 | // print the entry as a hex table |
||
390 | debugPrintHexTable(32, SectorBuffer+(slot<<5) ); |
||
391 | } |
||
392 | |||
393 | struct FileInfoStruct* fatGetFileInfo(void) |
||
394 | { |
||
395 | return &FileInfo; |
||
396 | } |
||
397 | |||
398 | // return the size of the last directory entry |
||
399 | unsigned long fatGetFilesize(void) |
||
400 | { |
||
401 | return FileInfo.Size; |
||
402 | } |
||
403 | |||
404 | // return the long name of the last directory entry |
||
405 | char* fatGetFilename(void) |
||
406 | { |
||
407 | return FileNameBuffer; |
||
408 | } |
||
409 | |||
410 | // return the directory of the last directory entry |
||
411 | char* fatGetDirname(void) |
||
412 | { |
||
413 | return PathNameBuffer; |
||
414 | } |
||
415 | |||
416 | // load a clusterfull of data |
||
417 | void fatLoadCluster(unsigned long cluster, unsigned char *buffer) |
||
418 | { |
||
419 | register unsigned char i; |
||
420 | // read cluster |
||
421 | //while ( ataReadSectors( DRIVE0, clust2sect(cluster), SectorsPerCluster, buffer) != 0); |
||
422 | for(i=0; i<SectorsPerCluster; i++) |
||
423 | { |
||
424 | ataReadSectors( DRIVE0, fatClustToSect(cluster)+i, 1, buffer+(i<<9) ); |
||
425 | // temporary fix for wierd misaligned cluster problem |
||
426 | // (only when using FAT16?) |
||
427 | // ataReadSectors( DRIVE0, fatClustToSect(cluster+8)+i, 1, buffer+(i<<9) ); |
||
428 | } |
||
429 | } |
||
430 | |||
431 | |||
432 | // find next cluster in the FAT chain |
||
433 | unsigned long fatNextCluster(unsigned long cluster) |
||
434 | { |
||
435 | unsigned long nextCluster; |
||
436 | unsigned long fatMask; |
||
437 | unsigned long fatOffset; |
||
438 | unsigned long sector; |
||
439 | unsigned int offset; |
||
440 | |||
441 | // get fat offset in bytes |
||
442 | if(Fat32Enabled) |
||
443 | { |
||
444 | // four FAT bytes (32 bits) for every cluster |
||
445 | fatOffset = cluster << 2; |
||
446 | // set the FAT bit mask |
||
447 | fatMask = FAT32_MASK; |
||
448 | } |
||
449 | else |
||
450 | { |
||
451 | // two FAT bytes (16 bits) for every cluster |
||
452 | fatOffset = cluster << 1; |
||
453 | // set the FAT bit mask |
||
454 | fatMask = FAT16_MASK; |
||
455 | } |
||
456 | |||
457 | // calculate the FAT sector that we're interested in |
||
458 | sector = FirstFATSector + (fatOffset / BytesPerSector); |
||
459 | // calculate offset of the our entry within that FAT sector |
||
460 | offset = fatOffset % BytesPerSector; |
||
461 | |||
462 | // if we don't already have this FAT chunk loaded, go get it |
||
463 | if (sector != FatInCache) |
||
464 | { |
||
465 | // read sector of FAT table |
||
466 | while (ataReadSectors( DRIVE0, sector, 1, (unsigned char*)FAT_CACHE_ADDR) != 0); |
||
467 | FatInCache = sector; |
||
468 | } |
||
469 | |||
470 | // read the nextCluster value |
||
471 | nextCluster = (*((unsigned long*) &((char*)FAT_CACHE_ADDR)[offset])) & fatMask; |
||
472 | |||
473 | // check to see if we're at the end of the chain |
||
474 | if (nextCluster == (CLUST_EOFE & fatMask)) |
||
475 | nextCluster = 0; |
||
476 | |||
477 | #ifdef DEBUG_FAT |
||
478 | rprintfProgStrM(">"); |
||
479 | rprintfu32(nextCluster); |
||
480 | rprintfCRLF(); |
||
481 | #endif |
||
482 | |||
483 | return nextCluster; |
||
484 | } |
Powered by WebSVN v2.8.3