diff options
Diffstat (limited to 'Marlin/src/sd/SdVolume.cpp')
-rw-r--r-- | Marlin/src/sd/SdVolume.cpp | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/Marlin/src/sd/SdVolume.cpp b/Marlin/src/sd/SdVolume.cpp new file mode 100644 index 0000000..e262c88 --- /dev/null +++ b/Marlin/src/sd/SdVolume.cpp @@ -0,0 +1,405 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program 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 program 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 this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +/** + * sd/SdVolume.cpp + * + * Arduino SdFat Library + * Copyright (c) 2009 by William Greiman + * + * This file is part of the Arduino Sd2Card Library + */ + +#include "../inc/MarlinConfig.h" + +#if ENABLED(SDSUPPORT) + +#include "SdVolume.h" + +#include "../MarlinCore.h" + +#if !USE_MULTIPLE_CARDS + // raw block cache + uint32_t SdVolume::cacheBlockNumber_; // current block number + cache_t SdVolume::cacheBuffer_; // 512 byte cache for Sd2Card + Sd2Card* SdVolume::sdCard_; // pointer to SD card object + bool SdVolume::cacheDirty_; // cacheFlush() will write block if true + uint32_t SdVolume::cacheMirrorBlock_; // mirror block for second FAT +#endif // USE_MULTIPLE_CARDS + +// find a contiguous group of clusters +bool SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) { + if (ENABLED(SDCARD_READONLY)) return false; + + // start of group + uint32_t bgnCluster; + // end of group + uint32_t endCluster; + // last cluster of FAT + uint32_t fatEnd = clusterCount_ + 1; + + // flag to save place to start next search + bool 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 = count == 1; + } + // end of group + endCluster = bgnCluster; + + // 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; +} + +bool SdVolume::cacheFlush() { + #if DISABLED(SDCARD_READONLY) + 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; + } + #endif + return true; +} + +bool SdVolume::cacheRawBlock(uint32_t blockNumber, bool dirty) { + if (cacheBlockNumber_ != blockNumber) { + if (!cacheFlush()) return false; + if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) return false; + cacheBlockNumber_ = blockNumber; + } + if (dirty) cacheDirty_ = true; + return true; +} + +// return the size in bytes of a cluster chain +bool SdVolume::chainSize(uint32_t cluster, uint32_t* size) { + 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 +bool SdVolume::fatGet(uint32_t cluster, uint32_t* value) { + uint32_t lba; + if (cluster > (clusterCount_ + 1)) return false; + if (FAT12_SUPPORT && fatType_ == 12) { + uint16_t index = cluster; + index += index >> 1; + lba = fatStartBlock_ + (index >> 9); + if (!cacheRawBlock(lba, CACHE_FOR_READ)) return false; + index &= 0x1FF; + uint16_t tmp = cacheBuffer_.data[index]; + index++; + if (index == 512) { + if (!cacheRawBlock(lba + 1, CACHE_FOR_READ)) return false; + index = 0; + } + tmp |= cacheBuffer_.data[index] << 8; + *value = cluster & 1 ? tmp >> 4 : tmp & 0xFFF; + return true; + } + + if (fatType_ == 16) + lba = fatStartBlock_ + (cluster >> 8); + else if (fatType_ == 32) + lba = fatStartBlock_ + (cluster >> 7); + else + return false; + + if (lba != cacheBlockNumber_ && !cacheRawBlock(lba, CACHE_FOR_READ)) + return false; + + *value = (fatType_ == 16) ? cacheBuffer_.fat16[cluster & 0xFF] : (cacheBuffer_.fat32[cluster & 0x7F] & FAT32MASK); + return true; +} + +// Store a FAT entry +bool SdVolume::fatPut(uint32_t cluster, uint32_t value) { + if (ENABLED(SDCARD_READONLY)) return false; + + uint32_t lba; + // error if reserved cluster + if (cluster < 2) return false; + + // error if not in FAT + if (cluster > (clusterCount_ + 1)) return false; + + if (FAT12_SUPPORT && fatType_ == 12) { + uint16_t index = cluster; + index += index >> 1; + lba = fatStartBlock_ + (index >> 9); + if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) return false; + // mirror second FAT + if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; + index &= 0x1FF; + uint8_t tmp = value; + if (cluster & 1) { + tmp = (cacheBuffer_.data[index] & 0xF) | tmp << 4; + } + cacheBuffer_.data[index] = tmp; + index++; + if (index == 512) { + lba++; + index = 0; + if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) return false; + // mirror second FAT + if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; + } + tmp = value >> 4; + if (!(cluster & 1)) { + tmp = ((cacheBuffer_.data[index] & 0xF0)) | tmp >> 4; + } + cacheBuffer_.data[index] = tmp; + return true; + } + + if (fatType_ == 16) + lba = fatStartBlock_ + (cluster >> 8); + else if (fatType_ == 32) + lba = fatStartBlock_ + (cluster >> 7); + else + return false; + + if (!cacheRawBlock(lba, CACHE_FOR_WRITE)) return false; + + // store entry + if (fatType_ == 16) + cacheBuffer_.fat16[cluster & 0xFF] = value; + else + cacheBuffer_.fat32[cluster & 0x7F] = value; + + // mirror second FAT + if (fatCount_ > 1) cacheMirrorBlock_ = lba + blocksPerFat_; + return true; +} + +// free a cluster chain +bool 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; +} + +/** Volume free space in clusters. + * + * \return Count of free clusters for success or -1 if an error occurs. + */ +int32_t SdVolume::freeClusterCount() { + uint32_t free = 0; + uint16_t n; + uint32_t todo = clusterCount_ + 2; + + if (fatType_ == 16) + n = 256; + else if (fatType_ == 32) + n = 128; + else // put FAT12 here + return -1; + + for (uint32_t lba = fatStartBlock_; todo; todo -= n, lba++) { + if (!cacheRawBlock(lba, CACHE_FOR_READ)) return -1; + NOMORE(n, todo); + if (fatType_ == 16) { + for (uint16_t i = 0; i < n; i++) + if (cacheBuffer_.fat16[i] == 0) free++; + } + else { + for (uint16_t i = 0; i < n; i++) + if (cacheBuffer_.fat32[i] == 0) free++; + } + #ifdef ESP32 + // Needed to reset the idle task watchdog timer on ESP32 as reading the complete FAT may easily + // block for 10+ seconds. yield() is insufficient since it blocks lower prio tasks (e.g., idle). + static millis_t nextTaskTime = 0; + const millis_t ms = millis(); + if (ELAPSED(ms, nextTaskTime)) { + vTaskDelay(1); // delay 1 tick (Minimum. Usually 10 or 1 ms depending on skdconfig.h) + nextTaskTime = ms + 1000; // tickle the task manager again in 1 second + } + #endif // ESP32 + } + return free; +} + +/** 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 true for success, false 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. + */ +bool SdVolume::init(Sd2Card* dev, uint8_t part) { + uint32_t totalBlocks, volumeStartBlock = 0; + fat32_boot_t* fbs; + + sdCard_ = dev; + fatType_ = 0; + allocSearchStart_ = 2; + cacheDirty_ = 0; // cacheFlush() will write block if true + cacheMirrorBlock_ = 0; + cacheBlockNumber_ = 0xFFFFFFFF; + + // 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) + return false; // not a valid partition + volumeStartBlock = p->firstSector; + } + if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) return false; + fbs = &cacheBuffer_.fbs32; + if (fbs->bytesPerSector != 512 || + fbs->fatCount == 0 || + fbs->reservedSectorCount == 0 || + fbs->sectorsPerCluster == 0) { + // not valid FAT volume + return false; + } + fatCount_ = fbs->fatCount; + blocksPerCluster_ = fbs->sectorsPerCluster; + // determine shift that is same as multiply by blocksPerCluster_ + clusterSizeShift_ = 0; + while (blocksPerCluster_ != _BV(clusterSizeShift_)) { + // error if not power of 2 + if (clusterSizeShift_++ > 7) return false; + } + blocksPerFat_ = fbs->sectorsPerFat16 ? + fbs->sectorsPerFat16 : fbs->sectorsPerFat32; + + fatStartBlock_ = volumeStartBlock + fbs->reservedSectorCount; + + // count for FAT16 zero for FAT32 + rootDirEntryCount_ = fbs->rootDirEntryCount; + + // directory start for FAT16 dataStart for FAT32 + rootDirStart_ = fatStartBlock_ + fbs->fatCount * blocksPerFat_; + + // data start for FAT16 and FAT32 + dataStartBlock_ = rootDirStart_ + ((32 * fbs->rootDirEntryCount + 511) / 512); + + // total blocks for FAT16 or FAT32 + totalBlocks = fbs->totalSectors16 ? + fbs->totalSectors16 : fbs->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; + if (!FAT12_SUPPORT) return false; + } + else if (clusterCount_ < 65525) + fatType_ = 16; + else { + rootDirStart_ = fbs->fat32RootCluster; + fatType_ = 32; + } + return true; +} + +#endif // SDSUPPORT |