33 #include <PropWare/c++allocate.h> 86 if (NULL != this->m_buf.buf)
87 free(this->m_buf.buf);
89 if (NULL != this->m_fat)
96 PropWare::ErrorCode
mount (
const uint8_t partition = 0) {
97 PropWare::ErrorCode err;
101 else if (3 < partition)
105 check_errors(this->m_driver->start());
106 this->m_fatMod =
false;
107 this->m_nextFileId = 0;
110 if (NULL == this->m_buf.buf)
111 this->m_buf.buf = (uint8_t *) malloc(this->m_sectorSize);
112 if (NULL == this->m_fat)
113 this->m_fat = (uint8_t *) malloc(this->m_sectorSize);
114 if (Utility::empty(this->m_buf.meta->name))
115 this->m_buf.meta->name =
"FAT shared buffer";
120 check_errors(this->read_boot_sector(partition));
121 check_errors(this->common_boot_sector_parser());
122 this->partition_info_parser();
123 check_errors(this->determine_fat_type());
124 this->store_root_info();
125 check_errors(this->read_fat_and_root_sectors());
127 this->m_mounted =
true;
136 if (this->m_mounted) {
137 PropWare::ErrorCode err;
139 if (NULL != this->m_buf.buf) {
140 check_errors(this->m_driver->flush(&this->m_buf));
141 free(this->m_buf.buf);
142 this->m_buf.buf = NULL;
145 if (NULL != this->m_fat) {
146 check_errors(this->flush_fat());
162 return this->m_filesystem;
167 static const uint8_t FAT_16 = 2;
168 static const uint8_t FAT_32 = 4;
169 static const uint8_t BOOT_SECTOR_ID = 0xEB;
170 static const uint8_t BOOT_SECTOR_ID_ADDR = 0;
171 static const uint16_t PARTITION_TABLE_START = 0x1BE;
172 static const uint8_t PARTITION_ID_OFFSET = 0x04;
173 static const uint8_t PARTITION_START_OFFSET = 0x08;
174 static const uint8_t CLUSTER_SIZE_ADDR = 0x0D;
175 static const uint8_t RSVD_SCTR_CNT_ADDR = 0x0E;
176 static const uint8_t NUM_FATS_ADDR = 0x10;
177 static const uint8_t ROOT_ENTRY_CNT_ADDR = 0x11;
178 static const uint8_t LABEL_ADDR = 0x47;
179 static const uint8_t SEC_PER_CLSTR_ADDR = 0x0D;
180 static const uint8_t TOT_SCTR_16_ADDR = 0x13;
181 static const uint8_t FAT_SIZE_16_ADDR = 0x16;
182 static const uint8_t TOT_SCTR_32_ADDR = 0x20;
183 static const uint8_t FAT_SIZE_32_ADDR = 0x24;
184 static const uint8_t ROOT_CLUSTER_ADDR = 0x2c;
185 static const uint16_t FAT12_CLSTR_CNT = 4085;
186 static const uint16_t FAT16_CLSTR_CNT = UINT16_MAX - 10;
188 static const int8_t FREE_CLUSTER = 0;
189 static const int8_t RESERVED_CLUSTER = 1;
190 static const int8_t RSVD_CLSTR_VAL_BEG = -15;
191 static const int8_t RSVD_CLSTR_VAL_END = -10;
192 static const int8_t BAD_CLUSTER = -9;
193 static const int32_t EOC_BEG = -8;
194 static const int32_t EOC_END = -1;
195 static const uint32_t EOC_MASK = 0x0fffffff;
200 uint32_t rootEntryCount;
201 uint32_t rootDirSectors;
202 uint32_t rsvdSectorCount;
203 uint32_t totalSectors;
205 uint32_t dataSectors;
207 uint32_t clusterCount;
219 inline PropWare::ErrorCode read_boot_sector (
const uint8_t partition) {
220 PropWare::ErrorCode err;
223 check_errors(this->m_driver->read_data_block(0, this->m_buf.buf));
224 const uint8_t bootSectorId = this->m_driver->get_byte(BOOT_SECTOR_ID_ADDR, this->m_buf.buf);
227 if (BOOT_SECTOR_ID == bootSectorId) {
230 this->m_initFatInfo.bootSector = 0;
237 const uint16_t partitionRow = PARTITION_TABLE_START + (partition << 4);
238 check_errors(this->is_fat_volume(this->m_buf.buf[partitionRow + PARTITION_ID_OFFSET]));
239 this->m_initFatInfo.bootSector = this->m_driver->get_long(partitionRow + PARTITION_START_OFFSET,
241 check_errors(this->m_driver->read_data_block(this->m_initFatInfo.bootSector, this->m_buf.buf));
247 inline PropWare::ErrorCode is_fat_volume (
const uint8_t partitionId)
const {
248 const static uint8_t PARTITION_IDS[54] = {0x01, 0x04, 0x06, 0x07, 0x08, 0x0B, 0x0C, 0x0E, 0x11, 0x12, 0x14,
249 0x16, 0x17, 0x1B, 0x1C, 0x1E, 0x24, 0x27, 0x28, 0x56, 0x84, 0x86,
250 0x8B, 0x8D, 0x90, 0x92, 0x97, 0x98, 0x9A, 0xAA, 0xB6, 0xBB, 0xBC,
251 0xC0, 0xC1, 0xC6, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCE, 0xD0, 0xD1,
252 0xD4, 0xD6, 0xDB, 0xDE, 0xE1, 0xE4, 0xE5, 0xEF, 0xF2, 0xFE};
254 for (uint8_t i = 0; i <
sizeof(PARTITION_IDS); ++i)
255 if (PARTITION_IDS[i] == partitionId) {
262 inline PropWare::ErrorCode common_boot_sector_parser () {
264 InitFATInfo *i = &this->m_initFatInfo;
267 i->rootEntryCount = this->m_driver->get_short(ROOT_ENTRY_CNT_ADDR, this->m_buf.buf);
270 i->rootDirSectors = (i->rootEntryCount * 32 + this->m_driver->get_sector_size() - 1) >>
271 this->m_driver->get_sector_size_shift();
274 i->rsvdSectorCount = this->m_driver->get_short(RSVD_SCTR_CNT_ADDR, this->m_buf.buf);
277 i->numFATs = this->m_driver->get_byte(NUM_FATS_ADDR, this->m_buf.buf);
281 memcpy(this->m_label, &this->m_buf.buf[LABEL_ADDR], 8);
282 this->m_label[8] =
'\0';
284 uint8_t sectorsPerCluster = this->m_driver->get_byte(SEC_PER_CLSTR_ADDR, this->m_buf.buf);
285 switch (sectorsPerCluster) {
287 this->m_tier1sPerTier2Shift = 0;
290 this->m_tier1sPerTier2Shift = 1;
293 this->m_tier1sPerTier2Shift = 2;
296 this->m_tier1sPerTier2Shift = 3;
299 this->m_tier1sPerTier2Shift = 4;
302 this->m_tier1sPerTier2Shift = 5;
305 this->m_tier1sPerTier2Shift = 6;
308 this->m_tier1sPerTier2Shift = 7;
311 pwOut.
printf(
"Sectors per cluster (bad) = %u\n", sectorsPerCluster);
318 inline void partition_info_parser () {
320 InitFATInfo *i = &this->m_initFatInfo;
323 uint32_t fatSize = this->m_driver->get_short(FAT_SIZE_16_ADDR, this->m_buf.buf);
325 fatSize = this->m_driver->get_long(FAT_SIZE_32_ADDR, this->m_buf.buf);
326 i->FATSize = fatSize;
329 uint32_t totalSectors = this->m_driver->get_short(TOT_SCTR_16_ADDR, this->m_buf.buf);
331 totalSectors = this->m_driver->get_long(TOT_SCTR_32_ADDR, this->m_buf.buf);
332 i->totalSectors = totalSectors;
337 i->dataSectors = totalSectors - (i->rsvdSectorCount + i->numFATs * fatSize + i->rootDirSectors);
338 i->clusterCount = i->dataSectors >> this->m_tier1sPerTier2Shift;
339 this->m_rootDirSectors = (i->rootEntryCount * 32) >> this->m_driver->get_sector_size_shift();
342 inline PropWare::ErrorCode determine_fat_type () {
344 if (FAT12_CLSTR_CNT > this->m_initFatInfo.clusterCount)
346 else if (FAT16_CLSTR_CNT > this->m_initFatInfo.clusterCount) {
347 this->m_filesystem = FAT_16;
348 this->m_entriesPerFatSector_Shift = 8;
350 this->m_filesystem = FAT_32;
351 this->m_entriesPerFatSector_Shift = 7;
357 inline void store_root_info () {
359 this->m_fatStart = this->m_initFatInfo.bootSector + this->m_initFatInfo.rsvdSectorCount;
363 switch (this->m_filesystem) {
365 this->m_rootAddr = this->m_initFatInfo.FATSize * this->m_initFatInfo.numFATs
367 this->m_firstDataAddr = this->m_rootAddr + this->m_rootDirSectors;
370 this->m_firstDataAddr = this->m_rootAddr = this->m_initFatInfo.bootSector +
371 this->m_initFatInfo.rsvdSectorCount + this->m_initFatInfo.FATSize
372 * this->m_initFatInfo.numFATs;
373 this->m_rootAllocUnit = this->m_driver->get_long(ROOT_CLUSTER_ADDR, this->m_buf.buf);
379 this->m_fatSize = this->m_initFatInfo.FATSize;
382 inline PropWare::ErrorCode read_fat_and_root_sectors () {
383 PropWare::ErrorCode err;
386 check_errors(this->m_driver->read_data_block(this->m_fatStart, this->m_fat));
387 this->m_curFatSector = 0;
390 check_errors(this->m_driver->read_data_block(this->m_rootAddr, this->m_buf.buf));
391 this->m_buf.meta->curTier2Addr = this->m_rootAddr;
392 if (FAT_16 == this->m_filesystem) {
393 this->m_dir_firstCluster = (uint32_t) -1;
394 this->m_buf.meta->curTier2 = (uint32_t) -1;
396 this->m_buf.meta->curTier2 = this->m_dir_firstCluster = this->m_rootAllocUnit;
397 check_errors(this->get_fat_value(this->m_buf.meta->curTier2, &this->m_buf.meta->nextTier2));
399 this->m_buf.meta->curTier2Addr = this->m_rootAddr;
400 this->m_buf.meta->curTier1Offset = 0;
405 bool is_eoc (int32_t value)
const {
406 switch (this->m_filesystem) {
408 return EOC_END == value;
411 return EOC_BEG <= value && EOC_END <= value;
427 PropWare::ErrorCode get_fat_value (
const uint32_t fatEntry, uint32_t *value) {
428 PropWare::ErrorCode err;
429 uint32_t firstAvailableAllocUnit;
432 if ((fatEntry >> this->m_entriesPerFatSector_Shift) != this->m_curFatSector) {
434 this->m_curFatSector = fatEntry >> this->m_entriesPerFatSector_Shift;
435 check_errors(this->m_driver->read_data_block(this->m_curFatSector + this->m_fatStart, this->m_fat));
437 firstAvailableAllocUnit = this->m_curFatSector << this->m_entriesPerFatSector_Shift;
444 if (FAT_16 == this->m_filesystem) {
445 *value = this->m_driver->get_short((uint16_t) ((fatEntry - firstAvailableAllocUnit) << 1), this->m_fat);
447 }
else if (FAT_32 == this->m_filesystem) {
448 *value = this->m_driver->get_long((uint16_t) ((fatEntry - firstAvailableAllocUnit) << 2), this->m_fat);
450 *value &= 0x0FFFFFFF;
464 uint32_t compute_tier1_from_tier2 (uint32_t tier2)
const {
465 if (FatFS::FAT_32 == this->m_filesystem)
466 tier2 -= this->m_rootAllocUnit;
469 tier2 <<= this->m_tier1sPerTier2Shift;
470 tier2 += this->m_firstDataAddr;
477 PropWare::ErrorCode extend_current_directory () {
478 return this->extend_fat(this->m_buf.meta);
490 PropWare::ErrorCode err;
491 uint32_t newAllocUnit;
495 if ((bufferMetadata->
curTier2 >> this->m_entriesPerFatSector_Shift) != this->m_curFatSector) {
497 this->m_curFatSector = bufferMetadata->
curTier2 >> this->m_entriesPerFatSector_Shift;
498 check_errors(this->m_driver->read_data_block(this->m_curFatSector + this->m_fatStart, this->m_fat));
503 uint16_t entriesPerFatSector = (uint16_t) (1 << this->m_entriesPerFatSector_Shift);
504 uint16_t allocUnitOffset = (uint16_t) (bufferMetadata->
curTier2 % entriesPerFatSector);
505 uint16_t fatPointerAddress = allocUnitOffset * this->m_filesystem;
506 uint32_t nextSector = this->m_driver->get_long(fatPointerAddress, this->m_fat);
507 if (!this->is_eoc(nextSector))
511 newAllocUnit = this->find_empty_space(1);
514 const uint16_t sectorOffset = (uint16_t) ((bufferMetadata->
curTier2 %
515 (1 << this->m_entriesPerFatSector_Shift)) * this->m_filesystem);
516 if (FAT_16 == this->m_filesystem)
517 this->m_driver->write_short(sectorOffset, this->m_fat, (uint16_t) newAllocUnit);
519 this->m_driver->write_long(sectorOffset, this->m_fat, newAllocUnit);
520 bufferMetadata->
nextTier2 = newAllocUnit;
521 this->m_fatMod =
true;
541 uint32_t find_empty_space (
const uint8_t restore) {
542 uint16_t allocOffset = 0;
543 uint32_t fatSectorAddr = this->m_curFatSector + this->m_fatStart;
549 if (FAT_16 == this->m_filesystem) {
551 while (this->m_driver->get_short(allocOffset, this->m_fat)) {
553 while (this->m_driver->get_short(allocOffset, this->m_fat) && (this->m_sectorSize > allocOffset))
554 allocOffset += FAT_16;
556 if (this->m_sectorSize <= allocOffset) {
559 this->m_driver->read_data_block(++fatSectorAddr, this->m_fat);
562 this->m_driver->write_short(allocOffset, this->m_fat, (uint16_t) EOC_END);
563 this->m_fatMod =
true;
566 if (0 == this->m_curFatSector)
568 allocOffset = (uint16_t) (9 * FAT_32);
571 while (this->m_driver->get_long(allocOffset, this->m_fat) & EOC_MASK) {
573 while ((this->m_driver->get_long(allocOffset, this->m_fat) & EOC_MASK)
574 && (this->m_sectorSize > allocOffset))
575 allocOffset += FAT_32;
578 if (this->m_sectorSize <= allocOffset) {
581 this->m_driver->read_data_block(++fatSectorAddr, this->m_fat);
586 this->m_driver->write_long(allocOffset, this->m_fat, ((uint32_t) EOC_END) & EOC_MASK);
587 this->m_fatMod =
true;
592 if ((fatSectorAddr != (this->m_curFatSector + this->m_fatStart)) && this->m_fatMod) {
594 this->m_driver->read_data_block(this->m_curFatSector + this->m_fatStart, this->m_fat);
596 this->m_curFatSector = fatSectorAddr - this->m_fatStart;
599 retVal = this->m_curFatSector << this->m_entriesPerFatSector_Shift;
600 retVal += allocOffset / this->m_filesystem;
604 PropWare::ErrorCode flush_fat () {
605 PropWare::ErrorCode err;
607 check_errors(m_driver->write_data_block(this->m_fatStart + this->m_curFatSector, this->m_fat));
608 check_errors(m_driver->write_data_block(this->m_fatStart + this->m_curFatSector + this->m_fatSize,
623 PropWare::ErrorCode clear_chain (
const uint32_t head) {
624 PropWare::ErrorCode err;
626 uint32_t next = head;
628 const uint32_t current = next;
629 check_errors(this->get_fat_value(current, &next));
631 const uint32_t firstAvailableAllocUnit = this->m_curFatSector << this->m_entriesPerFatSector_Shift;
632 const uint16_t sectorOffset = (uint16_t) (current - firstAvailableAllocUnit);
634 if (FAT_16 == this->m_filesystem)
635 this->m_driver->write_short(sectorOffset << 1, this->m_fat, 0);
636 else if (FAT_32 == this->m_filesystem)
637 this->m_driver->write_long(sectorOffset << 2, this->m_fat, 0);
638 }
while (!this->is_eoc(next));
640 this->m_fatMod =
true;
645 void print_status (
const bool printBlocks =
false)
const {
646 this->m_logger->println(
"######################################################");
647 this->m_logger->printf(
"# FAT Filesystem Status - PropWare::FatFS@0x%08X #\n", (
unsigned int)
this);
649 this->m_logger->println(
"Driver");
650 this->m_logger->println(
"======");
651 this->m_logger->printf(
"Driver address: 0x%08X\n", (
unsigned int) this->m_driver);
652 this->m_logger->printf(
"Block size: %u\n", this->m_sectorSize);
653 this->m_logger->printf(
"Blocks-per-cluster shift: %u\n", this->m_tier1sPerTier2Shift);
654 this->m_logger->println();
657 this->m_logger->println(
"Filesystem Constants");
658 this->m_logger->println(
"====================");
659 if (!this->m_mounted) {
660 this->m_logger->println(
"\nNot mounted");
665 this->m_logger->println(
"\tInitialization Numbers");
666 this->m_logger->println(
"\t----------------------------");
667 this->m_logger->printf(
"\tNumber of FATs: %u\n", this->m_initFatInfo.numFATs);
668 this->m_logger->printf(
"\tRoot entry count: 0x%08X/%u\n", this->m_initFatInfo.rootEntryCount,
669 this->m_initFatInfo.rootEntryCount);
670 this->m_logger->printf(
"\tRoot dir sectors: 0x%08X/%u\n", this->m_initFatInfo.rootDirSectors,
671 this->m_initFatInfo.rootDirSectors);
672 this->m_logger->printf(
"\tReserved sector count: 0x%08X/%u\n", this->m_initFatInfo.rsvdSectorCount,
673 this->m_initFatInfo.rsvdSectorCount);
674 this->m_logger->printf(
"\tTotal sectors: 0x%08X/%u\n", this->m_initFatInfo.totalSectors,
675 this->m_initFatInfo.totalSectors);
676 this->m_logger->printf(
"\tFAT Start: 0x%08X/%u\n", this->m_fatStart, this->m_fatStart);
677 this->m_logger->printf(
"\tFAT size: 0x%08X/%u\n", this->m_initFatInfo.FATSize, this->m_initFatInfo
679 this->m_logger->printf(
"\tData sectors: 0x%08X/%u\n", this->m_initFatInfo.dataSectors,
680 this->m_initFatInfo.dataSectors);
681 this->m_logger->printf(
"\tBoot sector: 0x%08X/%u\n", this->m_initFatInfo.bootSector,
682 this->m_initFatInfo.bootSector);
683 this->m_logger->printf(
"\tCluster count: 0x%08X/%u\n", this->m_initFatInfo.clusterCount,
684 this->m_initFatInfo.clusterCount);
685 this->m_logger->println();
688 this->m_logger->println(
"\tPartition");
689 this->m_logger->println(
"\t=========");
690 this->m_logger->printf(
"\tLabel: %s\n", this->m_label);
691 switch (this->m_filesystem) {
693 this->m_logger->printf(
"\tFilesystem: FAT 32\n");
696 this->m_logger->printf(
"\tFilesystem: FAT 16\n");
699 this->m_logger->printf(
"\tFilesystem: unknown (%d)\n", this->m_filesystem);
701 this->m_logger->printf(
"\tFirst FAT sector: 0x%08X\n", this->m_fatStart);
702 this->m_logger->printf(
"\tRoot directory alloc. unit: 0x%08X\n", this->m_rootAllocUnit);
703 this->m_logger->printf(
"\tCalculated root directory sector: 0x%08X\n",
704 this->compute_tier1_from_tier2(this->m_rootAllocUnit));
705 this->m_logger->printf(
"\tRoot directory sector: 0x%08X\n", this->m_rootAddr);
706 this->m_logger->printf(
"\tRoot directory size (in sectors): %u\n", this->m_rootDirSectors);
707 this->m_logger->printf(
"\tFirst data sector: 0x%08X\n", this->m_firstDataAddr);
708 this->m_logger->println();
711 this->m_logger->println(
"\tFAT Buffer");
712 this->m_logger->println(
"\t----------");
714 this->m_logger->println();
717 this->m_logger->println(
"\tCommon Buffer");
718 this->m_logger->println(
"\t=============");
719 if (this->m_buf.buf == NULL)
720 this->m_logger->println(
"\tEmpty");
723 this->m_logger->printf(
"\tID: %d\n", bufMeta->
id);
725 this->m_logger->printf(
"\tCur. cluster's start sector: 0x%08X/%u\n", bufMeta->
curTier2Addr,
727 this->m_logger->printf(
"\tCur. sector offset from cluster start: %u\n", bufMeta->
curTier1Offset);
728 this->m_logger->printf(
"\tCurrent allocation unit: 0x%08X/%u\n", bufMeta->
curTier2, bufMeta->
curTier2);
729 this->m_logger->printf(
"\tNext allocation unit: 0x%08X/%u\n", bufMeta->
nextTier2, bufMeta->
nextTier2);
733 this->m_logger->println();
737 InitFATInfo m_initFatInfo;
738 uint8_t m_filesystem;
741 uint32_t m_rootAllocUnit;
743 uint32_t m_rootDirSectors;
744 uint32_t m_firstDataAddr;
746 uint16_t m_entriesPerFatSector_Shift;
750 uint32_t m_curFatSector;
751 uint32_t m_dir_firstCluster;
static void print_block(const Printer &printer, const Buffer &buffer, const size_t words=512, const uint8_t wordsPerLine=16)
Print the formatted contents of a buffer.
Interface for all filesystems, such as FAT 16/32.
Concrete class for writing or modifying a FAT 16/32 file.
uint8_t get_fs_type()
Determine whether the mounted filesystem is FAT16 or FAT32.
Generic definitions and functions for the Parallax Propeller.
~FatFS()
Destructor. Unmounts the filesystem and flushes all buffers.
FatFS(const BlockStorage *driver, const Printer *logger=&pwOut)
Constructor.
A generic interface for all files on the FAT 16/32 filesystem.
const PropWare::Printer pwOut
Most common use of printing in PropWare applications (not thread safe; see PropWare::pwSyncOut for mu...
void printf(const char fmt[], const T first, const Targs...remaining) const
Similar in functionality to the C-standard function printf.
static const char * to_string(const bool b)
Convert a boolean to the string-literal either "true" or "false"
Container class that has formatting methods for human-readable output. This class can be constructed ...
PropWare::ErrorCode mount(const uint8_t partition=0)
Any device that uses blocks as hardware level abstraction.
PropWare::ErrorCode unmount()
Read a file on a FAT 16 or FAT 32 storage device.