/* Arduino SdFat Library Copyright (C) 2009 by William Greiman This file is part of the Arduino SdFat Library This Library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the Arduino SdFat Library. If not, see . */ #include "SdFat.h" //------------------------------------------------------------------------------ // raw block cache // init cacheBlockNumber_to invalid SD block number uint32_t SdVolume::cacheBlockNumber_ = 0XFFFFFFFF; cache_t SdVolume::cacheBuffer_; // 512 byte cache for Sd2Card Sd2Card* SdVolume::sdCard_; // pointer to SD card object uint8_t SdVolume::cacheDirty_ = 0; // cacheFlush() will write block if true uint32_t SdVolume::cacheMirrorBlock_ = 0; // mirror block for second FAT //------------------------------------------------------------------------------ // find a contiguous group of clusters uint8_t SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) { // start of group uint32_t bgnCluster; // flag to save place to start next search uint8_t setStart; // set search start cluster if (*curCluster) { // try to make file contiguous bgnCluster = *curCluster + 1; // don't save new start location setStart = false; } else { // start at likely place for free cluster bgnCluster = allocSearchStart_; // save next search start if one cluster setStart = 1 == count; } // end of group uint32_t endCluster = bgnCluster; // last cluster of FAT uint32_t fatEnd = clusterCount_ + 1; // search the FAT for free clusters for (uint32_t n = 0;; n++, endCluster++) { // can't find space checked all clusters if (n >= clusterCount_) { return false; } // past end - start from beginning of FAT if (endCluster > fatEnd) { bgnCluster = endCluster = 2; } uint32_t f; if (!fatGet(endCluster, &f)) { return false; } if (f != 0) { // cluster in use try next cluster as bgnCluster bgnCluster = endCluster + 1; } else if ((endCluster - bgnCluster + 1) == count) { // done - found space break; } } // mark end of chain if (!fatPutEOC(endCluster)) { return false; } // link clusters while (endCluster > bgnCluster) { if (!fatPut(endCluster - 1, endCluster)) { return false; } endCluster--; } if (*curCluster != 0) { // connect chains if (!fatPut(*curCluster, bgnCluster)) { return false; } } // return first cluster number to caller *curCluster = bgnCluster; // remember possible next free cluster if (setStart) { allocSearchStart_ = bgnCluster + 1; } return true; } //------------------------------------------------------------------------------ uint8_t SdVolume::cacheFlush(uint8_t blocking) { if (cacheDirty_) { if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data, blocking)) { return false; } if (!blocking) { return true; } // mirror FAT tables if (!cacheMirrorBlockFlush(blocking)) { return false; } cacheDirty_ = 0; } return true; } //------------------------------------------------------------------------------ uint8_t SdVolume::cacheMirrorBlockFlush(uint8_t blocking) { if (cacheMirrorBlock_) { if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data, blocking)) { return false; } cacheMirrorBlock_ = 0; } return true; } //------------------------------------------------------------------------------ uint8_t SdVolume::cacheRawBlock(uint32_t blockNumber, uint8_t action) { if (cacheBlockNumber_ != blockNumber) { if (!cacheFlush()) { return false; } if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) { return false; } cacheBlockNumber_ = blockNumber; } cacheDirty_ |= action; return true; } //------------------------------------------------------------------------------ // cache a zero block for blockNumber uint8_t SdVolume::cacheZeroBlock(uint32_t blockNumber) { if (!cacheFlush()) { return false; } // loop take less flash than memset(cacheBuffer_.data, 0, 512); for (uint16_t i = 0; i < 512; i++) { cacheBuffer_.data[i] = 0; } cacheBlockNumber_ = blockNumber; cacheSetDirty(); return true; } //------------------------------------------------------------------------------ // return the size in bytes of a cluster chain uint8_t SdVolume::chainSize(uint32_t cluster, uint32_t* size) const { uint32_t s = 0; do { if (!fatGet(cluster, &cluster)) { return false; } s += 512UL << clusterSizeShift_; } while (!isEOC(cluster)); *size = s; return true; } //------------------------------------------------------------------------------ // Fetch a FAT entry uint8_t SdVolume::fatGet(uint32_t cluster, uint32_t* value) const { if (cluster > (clusterCount_ + 1)) { return false; } uint32_t lba = fatStartBlock_; lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7; if (lba != cacheBlockNumber_) { if (!cacheRawBlock(lba, CACHE_FOR_READ)) { return false; } } if (fatType_ == 16) { *value = cacheBuffer_.fat16[cluster & 0XFF]; } else { *value = cacheBuffer_.fat32[cluster & 0X7F] & FAT32MASK; } return true; } //------------------------------------------------------------------------------ // Store a FAT entry uint8_t SdVolume::fatPut(uint32_t cluster, uint32_t value) { // error if reserved cluster if (cluster < 2) { return false; } // error if not in FAT if (cluster > (clusterCount_ + 1)) { return false; } // calculate block address for entry uint32_t lba = fatStartBlock_; lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7; if (lba != cacheBlockNumber_) { if (!cacheRawBlock(lba, CACHE_FOR_READ)) { return false; } } // store entry if (fatType_ == 16) { cacheBuffer_.fat16[cluster & 0XFF] = value; } else { cacheBuffer_.fat32[cluster & 0X7F] = value; } cacheSetDirty(); // mirror second FAT if (fatCount_ > 1) { cacheMirrorBlock_ = lba + blocksPerFat_; } return true; } //------------------------------------------------------------------------------ // free a cluster chain uint8_t SdVolume::freeChain(uint32_t cluster) { // clear free cluster location allocSearchStart_ = 2; do { uint32_t next; if (!fatGet(cluster, &next)) { return false; } // free cluster if (!fatPut(cluster, 0)) { return false; } cluster = next; } while (!isEOC(cluster)); return true; } //------------------------------------------------------------------------------ /** Initialize a FAT volume. \param[in] dev The SD card where the volume is located. \param[in] part The partition to be used. Legal values for \a part are 1-4 to use the corresponding partition on a device formatted with a MBR, Master Boot Record, or zero if the device is formatted as a super floppy with the FAT boot sector in block zero. \return The value one, true, is returned for success and the value zero, false, is returned for failure. Reasons for failure include not finding a valid partition, not finding a valid FAT file system in the specified partition or an I/O error. */ uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) { uint32_t volumeStartBlock = 0; sdCard_ = dev; // if part == 0 assume super floppy with FAT boot sector in block zero // if part > 0 assume mbr volume with partition table if (part) { if (part > 4) { return false; } if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) { return false; } part_t* p = &cacheBuffer_.mbr.part[part - 1]; if ((p->boot & 0X7F) != 0 || p->totalSectors < 100 || p->firstSector == 0) { // not a valid partition return false; } volumeStartBlock = p->firstSector; } if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) { return false; } bpb_t* bpb = &cacheBuffer_.fbs.bpb; if (bpb->bytesPerSector != 512 || bpb->fatCount == 0 || bpb->reservedSectorCount == 0 || bpb->sectorsPerCluster == 0) { // not valid FAT volume return false; } fatCount_ = bpb->fatCount; blocksPerCluster_ = bpb->sectorsPerCluster; // determine shift that is same as multiply by blocksPerCluster_ clusterSizeShift_ = 0; while (blocksPerCluster_ != (1 << clusterSizeShift_)) { // error if not power of 2 if (clusterSizeShift_++ > 7) { return false; } } blocksPerFat_ = bpb->sectorsPerFat16 ? bpb->sectorsPerFat16 : bpb->sectorsPerFat32; fatStartBlock_ = volumeStartBlock + bpb->reservedSectorCount; // count for FAT16 zero for FAT32 rootDirEntryCount_ = bpb->rootDirEntryCount; // directory start for FAT16 dataStart for FAT32 rootDirStart_ = fatStartBlock_ + bpb->fatCount * blocksPerFat_; // data start for FAT16 and FAT32 dataStartBlock_ = rootDirStart_ + ((32 * bpb->rootDirEntryCount + 511) / 512); // total blocks for FAT16 or FAT32 uint32_t totalBlocks = bpb->totalSectors16 ? bpb->totalSectors16 : bpb->totalSectors32; // total data blocks clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock); // divide by cluster size to get cluster count clusterCount_ >>= clusterSizeShift_; // FAT type is determined by cluster count if (clusterCount_ < 4085) { fatType_ = 12; } else if (clusterCount_ < 65525) { fatType_ = 16; } else { rootDirStart_ = bpb->fat32RootCluster; fatType_ = 32; } return true; }