/* 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(void) { if (cacheDirty_) { if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data)) { return false; } // mirror FAT tables if (cacheMirrorBlock_) { if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data)) { return false; } cacheMirrorBlock_ = 0; } cacheDirty_ = 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; }