-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfat16.c
1743 lines (1589 loc) · 75.8 KB
/
fat16.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include <string.h>
#include "printf_P.h"
#include "timer0.h"
#include "fat16.h"
#include "sdc.h"
#include "uart1.h"
/*
FAT16 Drive Layout:
Description Offset
Volume Boot Sector Start of Partition
Fat Tables Start + # of Reserved Sectors
Root Directory Entry Start + # of Reserved + (# of Sectors Per FAT * 2)
Data Area (Starts with Cluster #2) Start + # of Reserved + (# of Sectors Per FAT * 2) + ((Maximum Root Directory Entries * 32) / Bytes per Sector)
*/
/*
________________________________________________________________________________________________________________________________________
Structure of an partition entry
________________________________________________________________________________________________________________________________________
Partition Entry is 16 bytes long
*/
typedef struct
{
uint8_t PartitionState; // Current State of Partition (00h=Inactive, 80h=Active)
uint8_t BeginningHead; // Beginning of Partition - Head
uint16_t BeginningCylSec; // Beginning of Partition - Cylinder/Sector (See Below)
uint8_t Type; // Type of Partition (See List Below)
uint8_t EndHead; // End of Partition - Head
uint16_t EndCylSec; // End of Partition - Cylinder/Sector
uint32_t NoSectorsBeforePartition; // Number of Sectors between the MBR and the First Sector in the Partition
uint32_t NoSectorsPartition ; // Number of Sectors in the Partition
} __attribute__((packed)) PartitionEntry_t;
/*
Coding of Cylinder/Sector words
Cylinder is 10 bits: [7:0] at [15:8] and [9:8] at [7:6]
Sector is 5 bits: [5:0] at [5:0]
*/
// Partition Types:
#define PART_TYPE_UNKNOWN 0x00
#define PART_TYPE_FAT12 0x01
#define PART_TYPE_XENIX 0x02
#define PART_TYPE_FAT16_ST_32_MB 0x04
#define PART_TYPE_EXTDOS 0x05
#define PART_TYPE_FAT16_LT_32_MB 0x06
#define PART_TYPE_NTFS 0x07
#define PART_TYPE_FAT32 0x0B
#define PART_TYPE_FAT32LBA 0x0C
#define PART_TYPE_FAT16LBA 0x0E
#define PART_TYPE_EXTDOSLBA 0x0F
#define PART_TYPE_EISA 0x12
#define PART_TYPE_ONTRACK 0x33
#define PART_TYPE_NOVELL 0x40
#define PART_TYPE_DYNAMIC 0x42
#define PART_TYPE_PCIX 0x4B
#define PART_TYPE_LINUX_SWAP 0x82
#define PART_TYPE_LINUX_NATIVE 0x83
#define PART_TYPE_LINUX_LVM 0x8E
#define PART_TYPE_PHOENIXSAVE 0xA0
#define PART_TYPE_FREEBSD 0xA5
#define PART_TYPE_OPENBSD 0xA6
#define PART_TYPE_NETNBSD 0xA9
#define PART_TYPE_CPM 0xDB
#define PART_TYPE_DBFS 0xE0
#define PART_TYPE_BBT 0xFF
/*
________________________________________________________________________________________________________________________________________
Structure of the MasterBootRecord
________________________________________________________________________________________________________________________________________
Master Boot Record is 512 bytes long
The Master Boot Record is the same for pretty much all Operating Systems.
It is located on the first Sector of the Hard Drive, at Cylinder 0, Head 0, Sector 1
*/
typedef struct
{
uint8_t ExecutableCode[446]; // 446 bytes for machine start code
PartitionEntry_t PartitionEntry1; // 16 bytes for partition entry 1
PartitionEntry_t PartitionEntry2; // 16 bytes for partition entry 2
PartitionEntry_t PartitionEntry3; // 16 bytes for partition entry 3
PartitionEntry_t PartitionEntry4; // 16 bytes for partition entry 4
uint16_t ExecutableMarker; // BIOS-Signature (0x55 0xAA)
} __attribute__((packed)) MBR_Entry_t;
/*
________________________________________________________________________________________________________________________________________
Structure of the VolumeBootRecord
________________________________________________________________________________________________________________________________________
The Volume Boot Record is 512 bytes long
This information is located in the first sector of every partition.
*/
typedef struct
{
uint8_t JumpCode[3]; // Jump Code + NOP
int8_t OEMName[8]; // OEM Name
uint16_t BytesPerSector; // Bytes Per Sector
uint8_t SectorsPerCluster; // Sectors Per Cluster
uint16_t ReservedSectors; // Reserved Sectors
uint8_t NoFATCopies; // Number of Copies of FAT
uint16_t MaxRootEntries; // Maximum Root Directory Entries
uint16_t NoSectorsInPartSml32MB; // Number of Sectors in Partition Smaller than 32 MB
uint8_t MediaDescriptor; // Media Descriptor (0xF8 for Hard Disks)
uint16_t SectorsPerFAT; // Sectors Per FAT
uint16_t SectorsPerTrack; // Sectors Per Track
uint16_t NoHeads; // Number of Heads
uint32_t NoHiddenSectors; // Number of Hidden Sectors in Partition
uint32_t NoSectors; // Number of Sectors in Partition
uint16_t DriveNo; // Logical Drive Number of Partition
uint8_t ExtendedSig; // Extended Signature (0x29)
uint32_t SerialNo; // Serial Number of the Partition
int8_t VolumeName[11]; // Volume Name of the Partititon
int8_t FATName[8]; // FAT Name (FAT16)
uint8_t ExecutableCode[446]; // 446 bytes for machine start code
uint16_t ExecutableMarker; // Executable Marker (0x55 0xAA)
} __attribute__((packed)) VBR_Entry_t;
/*
________________________________________________________________________________________________________________________________________
Structure of an directory entry
________________________________________________________________________________________________________________________________________
Directory entry is 32 bytes.
*/
typedef struct
{
int8_t Name[8]; // 8 bytes name, padded with spaces.
uint8_t Extension[3]; // 3 bytes extension, padded with spaces.
uint8_t Attribute; // attribute of the directory entry (unused,archive,read-only,system,directory,volume)
uint8_t Reserved[10]; // reserved bytes within the directory entry.
uint32_t DateTime; // date and time of last write access to the file or directory.
uint16_t StartCluster; // first cluster of the file or directory.
uint32_t Size; // size of the file or directory in bytes.
} __attribute__((packed)) DirEntry_t;
#define SLOT_EMPTY 0x00 // slot has never been used
#define SLOT_E5 0x05 // the real value is 0xe5
#define SLOT_DELETED 0xE5 // file in this slot deleted
#define ATTR_NONE 0x00 // normal file
#define ATTR_READONLY 0x01 // file is readonly
#define ATTR_HIDDEN 0x02 // file is hidden
#define ATTR_SYSTEM 0x04 // file is a system file
#define ATTR_VOLUMELABEL 0x08 // entry is a volume label
#define ATTR_LONG_FILENAME 0x0F // this is a long filename entry
#define ATTR_SUBDIRECTORY 0x10 // entry is a directory name
#define ATTR_ARCHIVE 0x20 // file is new or modified
/*
________________________________________________________________________________________________________________________________________
Structure of an entry within the fileallocationtable.
________________________________________________________________________________________________________________________________________
*/
typedef struct
{
uint16_t NextCluster; // the next cluster of the file.
} __attribute__((packed)) Fat16Entry_t;
// secial fat entries
#define FAT16_CLUSTER_FREE 0x0000
#define FAT16_CLUSTER_RESERVED 0x0001
#define FAT16_CLUSTER_USED_MIN 0x0002
#define FAT16_CLUSTER_USED_MAX 0xFFEF
#define FAT16_CLUSTER_ROOTDIR_MIN 0xFFF0
#define FAT16_CLUSTER_ROOTDIR_MAX 0xFFF6
#define FAT16_CLUSTER_BAD 0xFFF7
#define FAT16_CLUSTER_LAST_MIN 0xFFF8
#define FAT16_CLUSTER_LAST_MAX 0xFFFF
/*****************************************************************************************************************************************/
/* */
/* Global variables needed for read- or write-acces to the FAT16- filesystem. */
/* */
/*****************************************************************************************************************************************/
#define MBR_SECTOR 0x00 // the masterboot record is located in sector 0.
#define DIRENTRY_SIZE 32 //bytes
#define DIRENTRIES_PER_SECTOR BYTES_PER_SECTOR/DIRENTRY_SIZE
#define FAT16_BYTES 2
#define FAT16_ENTRIES_PER_SECTOR BYTES_PER_SECTOR/FAT16_BYTES
#define FSTATE_UNUSED 0
#define FSTATE_USED 1
typedef struct
{
uint8_t IsValid; // 0 means invalid, else valid
uint8_t SectorsPerCluster; // how many sectors does a cluster contain?
uint8_t FatCopies; // Numbers of copies of the FAT
uint16_t MaxRootEntries; // Possible number of entries in the root directory.
uint16_t SectorsPerFat; // how many sectors does a fat16 contain?
uint32_t FirstFatSector; // sector of the start of the fat
uint32_t FirstRootDirSector; // sector of the rootdirectory
uint32_t FirstDataSector; // sector of the first cluster containing data (cluster2).
uint32_t LastDataSector; // the last data sector of the partition
} Partition_t;
Partition_t Partition; // Structure holds partition information
File_t FilePointer[FILE_MAX_OPEN]; // Allocate Memmoryspace for each filepointer used.
/****************************************************************************************************************************************/
/* Function: FileDateTime(DateTime_t *); */
/* */
/* Description: This function calculates the DOS date time from a pointer to a time structure. */
/* */
/* Returnvalue: Returns the DOS date time. */
/****************************************************************************************************************************************/
uint32_t FileDateTime(DateTime_t * pTimeStruct)
{
uint32_t datetime = 0;
if((pTimeStruct == 0) || !(pTimeStruct->Valid)) return datetime;
datetime |= (0x0000007FL & (uint32_t)(pTimeStruct->Year - 1980))<<25; // set year
datetime |= (0x0000000FL & (uint32_t)(pTimeStruct->Month))<<21; // set month
datetime |= (0x0000001FL & (uint32_t)(pTimeStruct->Day))<<16;
datetime |= (0x0000001FL & (uint32_t)(pTimeStruct->Hour))<<11;
datetime |= (0x0000003FL & (uint32_t)(pTimeStruct->Min))<<5;
datetime |= (0x0000001FL & (uint32_t)(pTimeStruct->Sec/2));
return datetime;
}
/****************************************************************************************************************************************/
/* Function: LockFilePointer(); */
/* */
/* Description: This function trys to lock a free file pointer. */
/* */
/* Returnvalue: Returns the Filepointer on success or 0. */
/****************************************************************************************************************************************/
File_t * LockFilePointer(void)
{
uint8_t i;
File_t * File = 0;
for(i = 0; i < FILE_MAX_OPEN; i++)
{
if(FilePointer[i].State == FSTATE_UNUSED) // found an unused one
{
File = &FilePointer[i]; // set pointer to that entry
FilePointer[i].State = FSTATE_USED; // mark it as used
break;
}
}
return(File);
}
/****************************************************************************************************************************************/
/* Function: UnlockFilePointer(file_t *); */
/* */
/* Description: This function trys to unlock a file pointer. */
/* */
/* Returnvalue: Returns 1 if file pointer was freed else 0. */
/****************************************************************************************************************************************/
uint8_t UnlockFilePointer(File_t * file)
{
uint8_t cnt;
if(file == NULL) return(0);
for(cnt = 0; cnt < FILE_MAX_OPEN; cnt++)
{
if(&FilePointer[cnt] == file) // filepointer to be freed found?
{
file->State = FSTATE_UNUSED;
file->FirstSectorOfFirstCluster = 0; // Sectorpointer to the first sector of the first datacluster of the file.
file->FirstSectorOfCurrCluster = 0;
file->SectorOfCurrCluster = 0; // Pointer to the cluster which is edited at the moment.
file->SectorOfCurrCluster = 0; // The sector which is edited at the moment (cluster_pointer + sector_index).
file->ByteOfCurrSector = 0; // The bytelocation within the current sector (cluster_pointer + sector_index + byte_index).
file->Mode = 0; // mode of fileoperation (read,write)
file->Size = 0; // the size of the opend file in bytes.
file->Position = 0; // pointer to a character within the file 0 < fileposition < filesize
file->SectorInCache = 0; // the last sector read, wich is still in the sectorbuffer.
file->DirectorySector = 0; // the sectorposition where the directoryentry has been made.
file->DirectoryIndex = 0; // the index to the directoryentry within the specified sector.
file->Attribute = 0; // the attribute of the file opened.
file = NULL;
return(1);
}
}
return(0);
}
/****************************************************************************************************************************************/
/* Function: SeperateDirName(int8_t*, int8_t*); */
/* */
/* Description: This function seperates the first dirname from filepath and brings them */
/* into the needed format ('test.txt' -> 'TEST TXT') */
/* The subpath is the pointer to the remaining substring if the filepath */
/* */
/* Returnvalue: Return NULL on error or pointer to subpath */
/****************************************************************************************************************************************/
int8_t* SeperateDirName(const int8_t *filepath, int8_t *dirname)
{
int8_t* subpath = NULL;
uint8_t readpointer = 0;
uint8_t writepointer = 0;
// search subpath from beginning of filepath
subpath = NULL;
readpointer = 0;
if(filepath[0] == '/') readpointer = 1; // ignore first '/'
while(subpath == NULL) // search the filepath until a subpath was found.
{
if(((filepath[readpointer] == 0) || (filepath[readpointer] == '/'))) // if '/' found or end of filepath reached
{
subpath = (int8_t*)&filepath[readpointer]; // store the position of the first "/" found after the beginning of the filenpath
}
readpointer++;
}
// clear dirname with spaces
dirname[11] = 0; // terminate dirname
for(writepointer = 0; writepointer < 11; writepointer++) dirname[writepointer] = ' ';
writepointer = 0;
// start seperating the dirname from the filepath.
readpointer = 0;
if(filepath[0] == '/') readpointer = 1; // ignore first '/'
while( &filepath[readpointer] < subpath)
{
if(writepointer >= 11) return(NULL); // dirname to long
if(filepath[readpointer] == '.') // seperating dirname and extension.
{
if(writepointer <= 8)
{
readpointer++; // next character in filename
writepointer = 8; // jump to start of extension
}
else return(NULL); // dirbasename to long
}
else
{
if((0x60 < filepath[readpointer]) && (filepath[readpointer] < 0x7B))
{
dirname[writepointer] = (filepath[readpointer] - 0x20); // all characters must be upper case.
}
else
{
dirname[writepointer] = filepath[readpointer];
}
readpointer++;
writepointer++;
}
}
return(subpath);
}
/**************************************************************************************************************************************+*/
/* Function: Fat16ClusterToSector( uint16_t cluster); */
/* */
/* Description: This function converts a cluster number given by the fat to the corresponding */
/* sector that points to the start of the data area that is represented by the cluster number. */
/* */
/* Returnvalue: The sector number with the data area of the given cluster */
/****************************************************************************************************************************************/
uint32_t Fat16ClusterToSector(uint16_t cluster)
{
if(!Partition.IsValid) return 0;
if (cluster < 2) cluster = 2; // the 0. and 1. cluster in the fat are used for the media descriptor
return ( (cluster - 2) * Partition.SectorsPerCluster) + Partition.FirstDataSector; // the first data sector is represented by the 2nd cluster
}
/****************************************************************************************************************************************/
/* Function: SectorToFat16Cluster( uint32_t sector); */
/* */
/* Description: This function converts a given sector number given to the corresponding */
/* cluster number in the fat that represents this data area. */
/* */
/* Returnvalue: The cluster number representing the data area of the sector. */
/****************************************************************************************************************************************/
uint16_t SectorToFat16Cluster(uint32_t sector)
{
if(!Partition.IsValid) return 0;
return ((uint16_t)((sector - Partition.FirstDataSector) / Partition.SectorsPerCluster) + 2);
}
/****************************************************************************************************************************************/
/* Function: Fat16_Deinit(void); */
/* */
/* Description: This function uninitializes the fat 16 api */
/* */
/* Returnvalue: The function returns "0" on success */
/****************************************************************************************************************************************/
uint8_t Fat16_Deinit(void)
{
int16_t returnvalue = 0;
uint8_t cnt;
// declare the filepointers as unused.
for(cnt = 0; cnt < FILE_MAX_OPEN; cnt++)
{
if(FilePointer[cnt].State == FSTATE_USED)
{
returnvalue += fclose_(&FilePointer[cnt]); // try to close open file pointers
}
}
SDC_Deinit(); // uninitialize interface to sd-card
Partition.IsValid = 0; // mark data in partition structure as invalid
return(returnvalue);
}
/****************************************************************************************************************************************/
/* Function: Fat16_Init(void); */
/* */
/* Description: This function reads the Masterbootrecord and finds the position of the Volumebootrecord, the FAT and the Rootdir */
/* and stores the information in global variables. */
/* */
/* Returnvalue: The function returns "0" if the filesystem is initialized. */
/****************************************************************************************************************************************/
uint8_t Fat16_Init(void)
{
uint8_t cnt = 0;
uint32_t partitionfirstsector;
VBR_Entry_t *VBR;
MBR_Entry_t *MBR;
File_t *file;
uint8_t result = 0;
printf("\r\n FAT16 init...");
Partition.IsValid = 0;
// declare the filepointers as unused.
for(cnt = 0; cnt < FILE_MAX_OPEN; cnt++)
{
FilePointer[cnt].State = FSTATE_UNUSED;
}
// set current file pinter to first position in list
file = &FilePointer[0];
// try to initialise the sd-card.
if(SD_SUCCESS != SDC_Init())
{
printf("SD-Card could not be initialized.");
result = 1;
goto end;
}
// SD-Card is initialized successfully
if(SD_SUCCESS != SDC_GetSector((uint32_t)MBR_SECTOR,file->Cache)) // Read the MasterBootRecord
{
printf("Error reading the MBR.");
result = 2;
goto end;
}
MBR = (MBR_Entry_t *)file->Cache; // Enter the MBR using the structure MBR_Entry_t.
if((MBR->PartitionEntry1.Type == PART_TYPE_FAT16_ST_32_MB) ||
(MBR->PartitionEntry1.Type == PART_TYPE_FAT16_LT_32_MB) ||
(MBR->PartitionEntry1.Type == PART_TYPE_FAT16LBA))
{
// get sector offset 1st partition
partitionfirstsector = MBR->PartitionEntry1.NoSectorsBeforePartition;
// Start of Partition is the Volume Boot Sector
if(SD_SUCCESS != SDC_GetSector(partitionfirstsector,file->Cache)) // Read the volume boot record
{
printf("Error reading the VBR.");
result = 3;
goto end;
}
}
else // maybe the medium has no partition assuming sector 0 is the vbr
{
partitionfirstsector = 0;
}
VBR = (VBR_Entry_t *) file->Cache; // Enter the VBR using the structure VBR_Entry_t.
if(VBR->BytesPerSector != BYTES_PER_SECTOR)
{
printf("VBR: Sector size not supported.");
result = 4;
goto end;
}
Partition.SectorsPerCluster = VBR->SectorsPerCluster; // Number of sectors per cluster. Depends on the memorysize of the sd-card.
Partition.FatCopies = VBR->NoFATCopies; // Number of fatcopies.
Partition.MaxRootEntries = VBR->MaxRootEntries; // How many Entries are possible in the rootdirectory (FAT16 allows max. 512 entries).
Partition.SectorsPerFat = VBR->SectorsPerFAT; // The number of sectors per FAT.
/* Calculate the sectorpositon of the FAT, the Rootdirectory and the first Datacluster. */
// Calculate the position of the FileAllocationTable:
// Start + # of Reserved Sectors
Partition.FirstFatSector = (uint32_t)(partitionfirstsector + (uint32_t)(VBR->ReservedSectors));
// Calculate the position of the Rootdirectory:
// Start + # of Reserved Sectors + (# of Sectors Per FAT * # of FAT Copies)
Partition.FirstRootDirSector = Partition.FirstFatSector + (uint32_t)((uint32_t)Partition.SectorsPerFat*(uint32_t)Partition.FatCopies);
// Calculate the position of the first datacluster:
// Start + # of Reserved + (# of Sectors Per FAT * # of FAT Copies) + ((Maximum Root Directory Entries * 32) / Bytes per Sector)
Partition.FirstDataSector = Partition.FirstRootDirSector + (uint32_t)(Partition.MaxRootEntries>>4); // assuming 512 Byte Per Sector
// Calculate the last data sector
if(VBR->NoSectors == 0)
{
printf("VBR: Bad number of sectors.");
result = 5;
goto end;
}
Partition.LastDataSector = Partition.FirstDataSector + VBR->NoSectors - 1;
// check for FAT16 in VBR of first partition
if(!((VBR->FATName[0]=='F') && (VBR->FATName[1]=='A') && (VBR->FATName[2]=='T') && (VBR->FATName[3]=='1')&&(VBR->FATName[4]=='6')))
{
printf("VBR: Partition ist not FAT16 type.");
result = 6;
goto end;
}
Partition.IsValid = 1; // mark data in partition structure as valid
result = 0;
end:
if(result != 0) Fat16_Deinit();
else printf(" ...ok");
return(result);
}
/****************************************************************************************************************************************/
/* Function: Fat16_IsValid(void); */
/* */
/* Description: This function return the Fat 15 filesystem state */
/* */
/* Returnvalue: The function returns "1" on success */
/****************************************************************************************************************************************/
uint8_t Fat16_IsValid(void)
{
return(Partition.IsValid);
}
/****************************************************************************************************************************************/
/* Function: ClearCurrCluster(File_t*); */
/* */
/* Description: This function fills the current cluster with 0. */
/* */
/* Returnvalue: The function returns 1 on success else 0. */
/****************************************************************************************************************************************/
uint8_t ClearCurrCluster(File_t * file)
{
uint8_t retvalue = 1;
uint32_t i;
if((!Partition.IsValid) || (file == NULL)) return(0);
for(i = 0; i < BYTES_PER_SECTOR; i++) file->Cache[i] = 0; // clear file cache
for(i = 0; i < Partition.SectorsPerCluster; i++)
{
file->SectorInCache = file->FirstSectorOfCurrCluster + i;
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))
{
Fat16_Deinit();
retvalue = 0;
}
}
return(retvalue);
}
/*****************************************************************************************************************************************/
/* Function: GetNextCluster(File_t* ); */
/* */
/* Description: This function finds the next datacluster of the file specified with File *File. */
/* */
/* Returnvalue: The function returns the next cluster or 0 if the last cluster has already reached. */
/*****************************************************************************************************************************************/
uint16_t GetNextCluster(File_t * file)
{
uint16_t cluster = 0;
uint32_t fat_byte_offset, sector, byte;
Fat16Entry_t * fat;
if((!Partition.IsValid) || (file == NULL)) return(cluster);
// if sector is within the data area
if((Partition.FirstDataSector <= file->FirstSectorOfCurrCluster)&& (file->FirstSectorOfCurrCluster <= Partition.LastDataSector))
{
// determine current file cluster
cluster = SectorToFat16Cluster(file->FirstSectorOfCurrCluster);
// calculate byte offset in the fat for corresponding entry
fat_byte_offset = ((uint32_t)cluster)<<1; // two FAT bytes (16 bits) for every cluster
// calculate the sector that contains the current cluster within the fat
sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR);
// calculate byte offset of the current cluster within that fat sector
byte = fat_byte_offset % BYTES_PER_SECTOR;
// read this sector to the file cache
if(file->SectorInCache != sector)
{
file->SectorInCache = sector; // update sector stored in buffer
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read sector from sd-card
{
Fat16_Deinit();
return (cluster);
}
}
// read the next cluster from cache
fat = (Fat16Entry_t *)(&(file->Cache[byte]));
cluster = fat->NextCluster;
// if last cluster fat entry
if(FAT16_CLUSTER_LAST_MIN <= cluster)
{
cluster = 0;
}
else
{
file->FirstSectorOfCurrCluster = Fat16ClusterToSector(cluster);
file->SectorOfCurrCluster = 0;
file->ByteOfCurrSector = 0;
}
}
return(cluster);
}
/****************************************************************************************************************************************/
/* Function: FindNextFreeCluster(File_t *); */
/* */
/* Description: This function looks in the fat to find the next free cluster */
/* */
/* Returnvalue: The function returns the cluster number of the next free cluster found within the fat. */
/****************************************************************************************************************************************/
uint16_t FindNextFreeCluster(File_t *file)
{
uint32_t fat_sector; // current sector within the fat relative to the first sector of the fat.
uint32_t curr_sector; // current sector
uint16_t fat_entry; // index to an fatentry within the actual sector (256 fatentries are possible within one sector).
uint16_t free_cluster = 0; // next free cluster number.
Fat16Entry_t * fat;
if((!Partition.IsValid) || (file == NULL)) return(0);
// start searching for an empty cluster at the beginning of the fat.
fat_sector = 0;
do
{
curr_sector = Partition.FirstFatSector + fat_sector; // calculate sector to read
file->SectorInCache = curr_sector; // upate the sector number of file cache.
if( SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read sector of fat from sd-card.
{
Fat16_Deinit();
return(free_cluster);
}
fat = (Fat16Entry_t *)file->Cache; // set fat pointer to file cache
for(fat_entry = 0; fat_entry < FAT16_ENTRIES_PER_SECTOR; fat_entry++) // look for an free cluster at all entries in this sector of the fat.
{
if(fat[fat_entry].NextCluster == FAT16_CLUSTER_FREE) // empty cluster found!!
{
fat[fat_entry].NextCluster = FAT16_CLUSTER_LAST_MAX; // mark this fat-entry as used
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) // and save the sector at the sd-card.
{
Fat16_Deinit();
return(free_cluster);
}
free_cluster = (uint16_t)(fat_sector * FAT16_ENTRIES_PER_SECTOR + (uint32_t)fat_entry);
fat_entry = FAT16_ENTRIES_PER_SECTOR; // terminate the search for a free cluster in this sector.
}
}
fat_sector++; // continue the search in next fat sector
// repeat until the end of the fat is reached and no free cluster has been found so far
}while((fat_sector < Partition.SectorsPerFat) && (!free_cluster));
return(free_cluster);
}
/****************************************************************************************************************************************************/
/* Function: int16_t fseek_(File_t *, int32_t *, uint8_t) */
/* */
/* Description: This function sets the pointer of the stream relative to the position */
/* specified by origin (SEEK_SET, SEEK_CUR, SEEK_END) */
/* Returnvalue: Is 1 if seek was successful */
/****************************************************************************************************************************************************/
int16_t fseek_(File_t * const file, int32_t offset, int16_t origin)
{
int32_t fposition = 0;
int16_t retvalue = 1;
if((!Partition.IsValid) || (file == NULL)) return(0);
switch(origin)
{
case SEEK_SET: // Fileposition relative to the beginning of the file.
fposition = 0;
break;
case SEEK_END: // Fileposition relative to the end of the file.
fposition = (int32_t)file->Size;
break;
case SEEK_CUR: // Fileposition relative to the current position of the file.
default:
fposition = file->Position;
break;
}
fposition += offset;
if((fposition >= 0) && (fposition <= (int32_t)file->Size)) // is the pointer still within the file?
{
// reset file position to start of the file
file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster;
file->SectorOfCurrCluster = 0;
file->ByteOfCurrSector = 0;
file->Position = 0;
while(file->Position < fposition) // repeat until the current position is less than target
{
file->Position++; // increment file position
file->ByteOfCurrSector++; // next byte in current sector
if(file->ByteOfCurrSector >= BYTES_PER_SECTOR)
{
file->ByteOfCurrSector = 0; // reading at the beginning of new sector.
file->SectorOfCurrCluster++; // continue reading in next sector
if(file->SectorOfCurrCluster >= Partition.SectorsPerCluster) // if end of cluster is reached, the next datacluster has to be searched in the FAT.
{
if(GetNextCluster(file)) // Sets the clusterpointer of the file to the next datacluster.
{
file->SectorOfCurrCluster = 0;
}
else // the last cluster was allready reached
{
file->SectorOfCurrCluster--; // jump back to the ast sector in the last cluster
file->ByteOfCurrSector = BYTES_PER_SECTOR; // set ByteOfCurrSector one byte over sector end
}
}
}
}
}
if(file->Position == fposition) retvalue = 0;
return(retvalue);
}
/****************************************************************************************************************************************/
/* Function: uint16_t DeleteClusterChain(File *file); */
/* */
/* Description: This function trances along a cluster chain in the fat and frees all clusters visited. */
/* */
/****************************************************************************************************************************************/
uint8_t DeleteClusterChain(uint16_t StartCluster)
{
uint16_t cluster;
uint32_t fat_byte_offset, sector, byte;
Fat16Entry_t * fat;
uint8_t buffer[BYTES_PER_SECTOR];
uint32_t sector_in_buffer = 0;
uint8_t repeat = 0;
if(!Partition.IsValid) return 0;
cluster = StartCluster; // init chain trace
// calculate byte offset in the fat for corresponding entry
fat_byte_offset = ((uint32_t)cluster)<<1; // two FAT bytes (16 bits) for every cluster
// calculate the sector that contains the current cluster within the fat
sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR);
// calculate byte offset of the current cluster within that fat sector
byte = fat_byte_offset % BYTES_PER_SECTOR;
do
{
if(sector != sector_in_buffer)
{
// read this sector to buffer
sector_in_buffer = sector;
if(SD_SUCCESS != SDC_GetSector(sector_in_buffer, buffer)) return 0; // read sector from sd-card
}
// read the next cluster from cache
fat = (Fat16Entry_t *)(&(buffer[byte]));
cluster = fat->NextCluster;
if((FAT16_CLUSTER_USED_MIN <= cluster) && (cluster <= FAT16_CLUSTER_USED_MAX) ) repeat = 1;
else repeat = 0;
fat->NextCluster = FAT16_CLUSTER_FREE; // mark current cluster as free
// calculate byte offset in the fat for corresponding entry
fat_byte_offset = ((uint32_t)cluster)<<1; // two FAT bytes (16 bits) for every cluster
// calculate the sector that contains the current cluster within the fat
sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR);
// calculate byte offset of the current cluster within that fat sector
byte = fat_byte_offset % BYTES_PER_SECTOR;
// if new sector is not the sector in buffer or the last cluster in the chain was traced
if((sector != sector_in_buffer) || !repeat)
{ // write sector in buffer
if(SD_SUCCESS != SDC_PutSector(sector_in_buffer,buffer)) return 0;
}
}
while(repeat);
return 1;
}
/****************************************************************************************************************************************/
/* Function: uint16_t AppendCluster(File *file); */
/* */
/* Description: This function looks in the fat to find the next free cluster and appends it to the file. */
/* */
/* Returnvalue: The function returns the appened cluster number or 0 of no cluster was appended. */
/****************************************************************************************************************************************/
uint16_t AppendCluster(File_t *file)
{
uint16_t last_cluster, new_cluster = 0;
uint32_t fat_byte_offset, sector, byte;
Fat16Entry_t * fat;
if((!Partition.IsValid) || (file == NULL)) return(new_cluster);
new_cluster = FindNextFreeCluster(file); // the next free cluster found on the disk.
if(new_cluster)
{ // A free cluster was found and can be added to the end of the file.
fseek_(file, 0, SEEK_END); // jump to the end of the file
last_cluster = SectorToFat16Cluster(file->FirstSectorOfCurrCluster); // determine current file cluster
fat_byte_offset = ((uint32_t)last_cluster)<<1;
sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR);
byte = fat_byte_offset % BYTES_PER_SECTOR;
if(file->SectorInCache != sector)
{
file->SectorInCache = sector; // update sector stored in buffer
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read sector from sd-card
{
Fat16_Deinit();
return(0);
}
}
fat = (Fat16Entry_t *)(&(file->Cache[byte]));
fat->NextCluster = new_cluster; // append the free cluster to the end of the file in the FAT.
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) // save the modified sector to the FAT.
{
Fat16_Deinit();
return(0);
}
file->FirstSectorOfCurrCluster = Fat16ClusterToSector(new_cluster);
file->SectorOfCurrCluster = 0;
file->ByteOfCurrSector = 0;
}
return(new_cluster);
}
/****************************************************************************************************************************************************/
/* Function: DirectoryEntryExist(int8_t *, uint8_t, uint8_t, File_t *) */
/* */
/* Description: This function searches all possible dir entries until the file or directory is found or the end of the directory is reached */
/* */
/* Returnvalue: This function returns 1 if the directory entry specified was found. */
/****************************************************************************************************************************************************/
uint8_t DirectoryEntryExist(int8_t *dirname, uint8_t attribfilter, uint8_t attribmask, File_t *file)
{
uint32_t dir_sector, max_dir_sector, curr_sector;
uint16_t dir_entry = 0;
uint16_t end_of_directory_not_reached = 0;
uint8_t i = 0;
uint8_t direntry_exist = 0;
DirEntry_t * dir;
// if incomming pointers are useless return immediatly
if((!Partition.IsValid) || (file == NULL) || (dirname == NULL)) return(direntry_exist);
// dir entries can be searched only in filesclusters that have
// a corresponding dir entry with adir-flag set in its attribute
// or direct within the root directory area
file->FirstSectorOfFirstCluster = 0;
// no current directory exist therefore assume searching in the root
if(file->DirectorySector == 0)
{
max_dir_sector = (Partition.MaxRootEntries * DIRENTRY_SIZE)/BYTES_PER_SECTOR;
file->FirstSectorOfFirstCluster = Partition.FirstRootDirSector;
}
// within the root directory area we can read sectors sequentially until the end of this area
else if((Partition.FirstRootDirSector <= file->DirectorySector) && (file->DirectorySector < Partition.FirstDataSector))
{
max_dir_sector = (Partition.MaxRootEntries * DIRENTRY_SIZE)/BYTES_PER_SECTOR;
}
// within the data clusters we can read sectors sequentially only within the cluster
else if((Partition.FirstDataSector <= file->DirectorySector) && (file->DirectorySector <= Partition.LastDataSector))
{
max_dir_sector = Partition.SectorsPerCluster; // limit max secters before next cluster
}
else return (direntry_exist); // bad sector range for directory sector of the file
// if search area is not defined yet
if(file->FirstSectorOfFirstCluster == 0)
{
// check if the directory entry of current file is existent and has the dir-flag set
file->SectorInCache = file->DirectorySector; // update the sector number of file cache.
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector.
{
Fat16_Deinit();
return(direntry_exist);
}
dir = (DirEntry_t *)file->Cache; // set pointer to directory
switch((uint8_t)dir[file->DirectoryIndex].Name[0]) // check if current directory exist
{
case SLOT_EMPTY:
case SLOT_DELETED:
// the directrory pointer of this file points to a deleted or not existen directory
// therefore no file or subdirectory can be created
return (direntry_exist);
break;
default: // and is a real directory
if((dir[file->DirectoryIndex].Attribute & ATTR_SUBDIRECTORY) != ATTR_SUBDIRECTORY)
{ // current file is not a directory therefore no file or subdirectory can be created here
return (direntry_exist);
}
break;
}
file->FirstSectorOfFirstCluster = Fat16ClusterToSector(dir[file->DirectoryIndex].StartCluster);
}
// update current file data area position to start of first cluster
file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster;
file->SectorOfCurrCluster = 0;
file->ByteOfCurrSector = 0;
do // loop over all data clusters of the current directory entry
{
dir_sector = 0; // reset sector counter within a new cluster
do // loop over all sectors of a cluster or all sectors of the root directory
{
curr_sector = file->FirstSectorOfCurrCluster + dir_sector; // calculate sector number
file->SectorInCache = curr_sector; // upate the sector number of file cache.
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read the sector
{
Fat16_Deinit();
return(direntry_exist);
}
dir = (DirEntry_t *)file->Cache; // set pointer to directory
// search all directory entries within that sector
for(dir_entry = 0; dir_entry < DIRENTRIES_PER_SECTOR; dir_entry++)
{ // check for existing dir entry
switch((uint8_t)dir[dir_entry].Name[0])
{
case SLOT_EMPTY:
case SLOT_DELETED:
// ignore empty or deleted dir entries
break;
default:
// if existing check attributes before names are compared will safe performance
if ((dir[dir_entry].Attribute & attribmask) != attribfilter) break; // attribute must match
// then compare the name to the giveb dirname (first 11 characters include 8 chars of basename and 3 chars extension.)
i = 0;
while((i < 11) && (dir[dir_entry].Name[i] == dirname[i])) i++;
if (i < 10) break; // names does not match
// if dirname and attribute have matched
file->Attribute = dir[dir_entry].Attribute; // store attribute of found dir entry
file->FirstSectorOfFirstCluster = Fat16ClusterToSector(dir[dir_entry].StartCluster); // set sector of first data cluster
file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster;
file->SectorOfCurrCluster = 0;
file->ByteOfCurrSector = 0;
file->DirectorySector = curr_sector; // current sector
file->DirectoryIndex = dir_entry; // current direntry in current sector
file->Size = dir[dir_entry].Size;
direntry_exist = 1; // mark as found
dir_entry = DIRENTRIES_PER_SECTOR; // stop for-loop
} // end of first byte of name check
}
dir_sector++; // search next sector
// stop if we reached the end of the cluster or the end of the root dir
}while((dir_sector < max_dir_sector) && (!direntry_exist));
// if we are seaching in the data area and the file not found in this cluster so take next cluster.
if(!direntry_exist && ( Partition.FirstDataSector <= file->FirstSectorOfCurrCluster))
{
end_of_directory_not_reached = GetNextCluster(file); // updates File->FirstSectorOfCurrCluster
}
}while((end_of_directory_not_reached) && (!direntry_exist)); // repeat until a next cluster exist an no
return(direntry_exist);
}
/****************************************************************************************************************************************/
/* Function: CreateDirectoryEntry(int8_t *, uint16_t, File_t *) */
/* */
/* Description: This function looks for the next free position in the directory and creates an entry. */
/* The type of an directory entry is specified by the file attribute. */
/* */
/* Returnvalue: Return 0 on error */
/****************************************************************************************************************************************/
uint8_t CreateDirectoryEntry(int8_t *dirname, uint8_t attrib, File_t *file)
{
uint32_t dir_sector, max_dir_sector, curr_sector;
uint16_t dir_entry = 0;
uint16_t subdircluster, dircluster = 0;
uint16_t end_of_directory_not_reached = 0;
uint8_t i = 0;
uint8_t retvalue = 0;
DirEntry_t* dir;
if((!Partition.IsValid) || (file == NULL) || (dirname == NULL)) return (retvalue);
// It is not checked here that the dir entry that should be created is already existent!
// Dir entries can be created only in file-clusters that have
// the dir-flag set in its attribute or within the root directory
file->FirstSectorOfFirstCluster = 0;
// no current directory exist therefore assume creating in the root