You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
MicroDexed/third-party/USBHost_t36/USBFilesystemFormatter.cpp

1000 lines
35 KiB

#include <Arduino.h>
#include "utility/USBFilesystemFormatter.h"
#ifndef DBG_FAIL_MACRO
#define DBG_FAIL_MACRO
#endif
uint16_t toUpcase(uint16_t chr);
//=============================================
#define DBG_FILE "USBFilesystemFormatter.cpp"
//Set to 0 for debug info
//#define DBG_Print 0
#if defined(DBG_Print)
#define DBGPrintf Serial.printf
#else
void inline DBGPrintf(...) {};
#endif
#define PRINT_FORMAT_PROGRESS 1
#if !PRINT_FORMAT_PROGRESS
#define writeMsg(str)
void inline writeMsgF(...) {};
#elif defined(__AVR__)
#define writeMsg(str) if (m_pr) m_pr->print(F(str))
#define writeMsgF if (m_pr) m_pr->printf
#else // PRINT_FORMAT_PROGRESS
#define writeMsg(str) if (m_pr) m_pr->write(str)
#define writeMsgF if (m_pr) m_pr->printf
#endif // PRINT_FORMAT_PROGRESS
//=============================================
// Set nonzero to use calculated CHS in MBR. Should not be required.
#define USE_LBA_TO_CHS 1
// Constants for file system structure optimized for flash.
uint16_t const BU16 = 128;
uint16_t BU32 = 8192;
// Assume 512 byte sectors.
const uint16_t BYTES_PER_SECTOR = 512;
const uint16_t SECTORS_PER_MB = 0X100000/BYTES_PER_SECTOR;
const uint16_t FAT16_ROOT_ENTRY_COUNT = 512;
const uint16_t FAT16_ROOT_SECTOR_COUNT =
32*FAT16_ROOT_ENTRY_COUNT/BYTES_PER_SECTOR;
//Support for exFat formatting
const uint32_t BOOT_BACKUP_OFFSET = 12;
const uint16_t SECTOR_MASK = BYTES_PER_SECTOR - 1;
const uint8_t BYTES_PER_SECTOR_SHIFT = 9;
const uint16_t MINIMUM_UPCASE_SKIP = 512;
const uint32_t BITMAP_CLUSTER = 2;
const uint32_t UPCASE_CLUSTER = 3;
const uint32_t ROOT_CLUSTER = 4;
//=============================================
bool USBFilesystemFormatter::format(USBFilesystem &fs, uint8_t fat_type, uint8_t* secBuf, print_t* pr)
{
// We can extract the device and partition from the fs object.
DBGPrintf("\n### USBFilesystemFormatter::formatFAT called\n");
DBGPrintf("\tFattype: FS:%u Opt:%u\n", fs.mscfs.fatType(), fat_type);
if (fat_type == 0) fat_type = fs.mscfs.fatType();
switch (fat_type) {
case FAT_TYPE_FAT16:
case FAT_TYPE_FAT32:
//case FAT_TYPE_FAT12
return formatFAT(*fs.device, fs, fs.partition, fat_type, secBuf, pr);
case FAT_TYPE_EXFAT:
return formatExFAT(*fs.device, fs, fs.partition, fat_type, secBuf, pr);
}
return false;
}
//====================================================
bool USBFilesystemFormatter::formatFAT(USBDrive &dev, USBFilesystem &fs, uint8_t part, uint8_t fat_type, uint8_t* secBuf, print_t* pr)
{
DBGPrintf("\n### USBFilesystemFormatter::formatFAT called\n");
bool rtn;
m_secBuf = secBuf;
m_pr = pr;
writeMsg("Begin format Fat File system\n");
//m_dev = partVol.blockDevice();
m_part = part-1; // convert to 0 biased.
uint32_t firstLBA; //comes from getpartitioninfo in pfslib
uint32_t sectorCount;
uint32_t mbrLBA; //comes from getpartitioninfo in pfslib
uint8_t mbrPart;
int mbrType;
char volName[32];
//findPartion using partition number non-zero biased - it does the zero biasing in the function
int vt = dev.findPartition(part, mbrType, firstLBA, sectorCount, mbrLBA, mbrPart);
DBGPrintf("Part:%u vt:%u first:%u, count:%u MBR:%u MBR Part:%u MBR Type: %u\n", part, vt, firstLBA, sectorCount, mbrLBA, mbrPart, mbrType);
if (vt == 0) return false; // got an invalid volume
// yes could have used some of the directly...
m_sectorCount = sectorCount;
m_part_relativeSectors = firstLBA;
m_mbrLBA = mbrLBA;
m_mbrPart = mbrPart;
m_capacityMB = (m_sectorCount + SECTORS_PER_MB - 1)/SECTORS_PER_MB;
writeMsgF("Capacity in MB: %u\n", m_capacityMB);
// m_capacityMB = (uint32_t) (fs.totalSize()/1000000);
if(m_capacityMB > 32768) {
writeMsgF("Volume is greater(%u) than 32MB, Need to format as exFAT!!\n", m_capacityMB);
return false;
}
bool has_volume_label = fs.mscfs.getVolumeLabel(volName, sizeof(volName));
if (has_volume_label) {
writeMsgF("Volume name:(%s)\n", volName);
}
DBGPrintf("\nPFsFatFormatter::format................");
DBGPrintf("Sector Count: %d, Sectors/MB: %d\n", m_sectorCount, SECTORS_PER_MB);
DBGPrintf("Partition Capacity (MB): %d\n", m_capacityMB);
DBGPrintf("Partition Capacity - from fs (MB): %d\n", (uint32_t) (fs.totalSize()/1000000));
DBGPrintf("Fat Type: %d\n", fs.mscfs.fatType());
DBGPrintf(" m_dataStart:%u\n", fs.mscfs.dataStartSector());
DBGPrintf(" m_sectorsPerCluster:%u\n",fs.mscfs.sectorsPerCluster());
DBGPrintf(" m_relativeSectors:%u\n", m_part_relativeSectors);
DBGPrintf(" Fat start sector:%u\n", fs.mscfs.fatStartSector());
DBGPrintf(" Cluster Count:%u\n", fs.mscfs.clusterCount());
DBGPrintf(" Bytes per Cluster:%u\n", fs.mscfs.bytesPerCluster());
DBGPrintf("\n");
if (m_capacityMB <= 6) {
writeMsg("Card is too small.\r\n");
return false;
} else if (m_capacityMB <= 16) {
m_sectorsPerCluster = 2;
} else if (m_capacityMB <= 32) {
m_sectorsPerCluster = 4;
} else if (m_capacityMB <= 64) {
m_sectorsPerCluster = 8;
} else if (m_capacityMB <= 128) {
m_sectorsPerCluster = 16;
} else if (m_capacityMB <= 1024) {
m_sectorsPerCluster = 32;
} else if (m_capacityMB <= 32768) {
m_sectorsPerCluster = 64;
} else {
// SDXC cards
m_sectorsPerCluster = 128;
}
//rtn = m_sectorCount < 0X400000 ? makeFat16() :makeFat32();
if(fat_type == 16 && m_sectorCount < 0X400000 ) {
writeMsg("format makeFAT16\r\n");
rtn = makeFat16(dev);
} else if(fat_type == 32) {
writeMsg("format makeFAT32\r\n");
rtn = makeFat32(dev);
} else {
rtn = false;
}
return rtn;
}
//====================================================================================
bool USBFilesystemFormatter::makeFat16(USBDrive &m_dev) {
DBGPrintf(" MAKEFAT16\n");
uint32_t nc;
uint32_t r;
PbsFat_t* pbs = reinterpret_cast<PbsFat_t*>(m_secBuf);
for (m_dataStart = 2*BU16; ; m_dataStart += BU16) {
nc = (m_sectorCount - m_dataStart)/m_sectorsPerCluster;
m_fatSize = (nc + 2 + (BYTES_PER_SECTOR/2) - 1)/(BYTES_PER_SECTOR/2);
r = BU16 + 1 + 2*m_fatSize + FAT16_ROOT_SECTOR_COUNT;
if (m_dataStart >= r) {
m_relativeSectors = m_dataStart - r + BU16;
break;
}
}
//nc = (m_sectorCount - m_dataStart)/m_sectorsPerCluster;
//m_fatSize = (nc + 2 + (BYTES_PER_SECTOR/2) - 1)/(BYTES_PER_SECTOR/2);
DBGPrintf("m_relativeSectors: %u, m_fatSize: %u, m_dataStart: %u\n",m_relativeSectors, m_fatSize, m_dataStart) ;
// check valid cluster count for FAT16 volume
if (nc < 4085 || nc >= 65525) {
writeMsg("Bad cluster count\r\n");
return false;
}
m_reservedSectorCount = 1;
m_fatStart = m_relativeSectors + m_reservedSectorCount;
m_totalSectors = nc*m_sectorsPerCluster
+ 2*m_fatSize + m_reservedSectorCount + 32;
if (m_totalSectors < 65536) {
m_partType = 0X04;
} else {
m_partType = 0X06;
}
//Added to keep relative sectors straight
m_relativeSectors = m_part_relativeSectors;
m_fatStart = m_relativeSectors + m_reservedSectorCount;
m_dataStart= m_fatStart + 2 * m_fatSize + FAT16_ROOT_SECTOR_COUNT;
m_totalSectors = m_sectorCount;
DBGPrintf("partType: %d, m_relativeSectors: %u, fatStart: %u, fatDatastart: %u, totalSectors: %u\n", m_partType, m_relativeSectors, m_fatStart, m_dataStart, m_totalSectors);
// write MBR
writeMsg("Writing MBR...");
if (!writeFatMbr(m_dev)) {
return false;
}
initPbs();
setLe16(pbs->bpb.bpb16.rootDirEntryCount, FAT16_ROOT_ENTRY_COUNT);
setLe16(pbs->bpb.bpb16.sectorsPerFat16, m_fatSize);
pbs->bpb.bpb16.physicalDriveNumber = 0X80;
pbs->bpb.bpb16.extSignature = EXTENDED_BOOT_SIGNATURE;
setLe32(pbs->bpb.bpb16.volumeSerialNumber, 1234567);
for (size_t i = 0; i < sizeof(pbs->bpb.bpb16.volumeLabel); i++) {
pbs->bpb.bpb16.volumeLabel[i] = ' ';
}
pbs->bpb.bpb16.volumeType[0] = 'F';
pbs->bpb.bpb16.volumeType[1] = 'A';
pbs->bpb.bpb16.volumeType[2] = 'T';
pbs->bpb.bpb16.volumeType[3] = '1';
pbs->bpb.bpb16.volumeType[4] = '6';
if (!writeSector(m_dev, m_relativeSectors, m_secBuf)) {
return false;
}
return initFatDir(m_dev, 16, m_dataStart - m_fatStart);
}
bool USBFilesystemFormatter::makeFat32(USBDrive &m_dev) {
DBGPrintf(" MAKEFAT32\n");
uint32_t nc;
uint32_t r;
PbsFat_t* pbs = reinterpret_cast<PbsFat_t*>(m_secBuf);
FsInfo_t* fsi = reinterpret_cast<FsInfo_t*>(m_secBuf);
m_relativeSectors = BU32;
for (m_dataStart = 2*BU32; ; m_dataStart += BU32) {
nc = (m_sectorCount - m_dataStart)/m_sectorsPerCluster;
m_fatSize = (nc + 2 + (BYTES_PER_SECTOR/4) - 1)/(BYTES_PER_SECTOR/4);
r = m_relativeSectors + 9 + 2*m_fatSize;
DBGPrintf("m_dataStart: %u, m_fatSize: %u, r: %u\n", m_dataStart, m_fatSize, r);
if (m_dataStart >= r) {
break;
}
}
//nc = (m_sectorCount - m_dataStart)/m_sectorsPerCluster;
//m_fatSize = (nc + 2 + (BYTES_PER_SECTOR/4) - 1)/(BYTES_PER_SECTOR/4);
DBGPrintf(" m_part: %d\n", m_part);
DBGPrintf(" m_sectorCount: %d\n", m_sectorCount);
DBGPrintf(" m_dataStart: %d\n", m_dataStart);
DBGPrintf(" m_sectorsPerCluster: %d\n", m_sectorsPerCluster);
DBGPrintf(" nc: %d\n", nc);
DBGPrintf(" m_fatSize: %d\n", m_fatSize);
// error if too few clusters in FAT32 volume
if (nc < 65525) {
writeMsg("Bad cluster count\r\n");
return false;
}
m_reservedSectorCount = m_dataStart - m_relativeSectors - 2*m_fatSize;
m_fatStart = m_relativeSectors + m_reservedSectorCount;
m_totalSectors = nc*m_sectorsPerCluster + m_dataStart - m_relativeSectors;
// type depends on address of end sector
// max CHS has lba = 16450560 = 1024*255*63
if ((m_relativeSectors + m_totalSectors) <= 16450560) {
// FAT32 with CHS and LBA
m_partType = 0X0B;
} else {
// FAT32 with only LBA
m_partType = 0X0C;
}
//Write MBR
//Added to keep relative sectors straight
m_relativeSectors = m_part_relativeSectors;
m_fatStart = m_relativeSectors + m_reservedSectorCount;
m_dataStart = m_relativeSectors + m_dataStart;
m_totalSectors = m_sectorCount;
DBGPrintf("[makeFat32] partType: %d, m_relativeSectors: %u, fatStart: %u, fatDatastart: %u, totalSectors: %u\n", m_partType, m_relativeSectors, m_fatStart, m_dataStart, m_totalSectors);
if (!writeFatMbr(m_dev)) {
writeMsg("Failed to write MBR!!");
return false;
}
initPbs();
setLe32(pbs->bpb.bpb32.sectorsPerFat32, m_fatSize);
setLe32(pbs->bpb.bpb32.fat32RootCluster, 2);
setLe16(pbs->bpb.bpb32.fat32FSInfoSector, 1);
setLe16(pbs->bpb.bpb32.fat32BackBootSector, 6);
pbs->bpb.bpb32.physicalDriveNumber = 0X80;
pbs->bpb.bpb32.extSignature = EXTENDED_BOOT_SIGNATURE;
setLe32(pbs->bpb.bpb32.volumeSerialNumber, 1234567);
for (size_t i = 0; i < sizeof(pbs->bpb.bpb32.volumeLabel); i++) {
pbs->bpb.bpb32.volumeLabel[i] = ' ';
}
pbs->bpb.bpb32.volumeType[0] = 'F';
pbs->bpb.bpb32.volumeType[1] = 'A';
pbs->bpb.bpb32.volumeType[2] = 'T';
pbs->bpb.bpb32.volumeType[3] = '3';
pbs->bpb.bpb32.volumeType[4] = '2';
writeMsg("Writing Partition Boot Sector\n");
if (!writeSector(m_dev, m_relativeSectors, m_secBuf) ||
!writeSector(m_dev, m_relativeSectors + 6, m_secBuf)) {
return false;
}
// write extra boot area and backup
memset(m_secBuf, 0 , BYTES_PER_SECTOR);
setLe32(fsi->trailSignature, FSINFO_TRAIL_SIGNATURE);
if (!writeSector(m_dev, m_relativeSectors + 2, m_secBuf) ||
!writeSector(m_dev, m_relativeSectors + 8, m_secBuf)) {
return false;
}
// write FSINFO sector and backup
setLe32(fsi->leadSignature, FSINFO_LEAD_SIGNATURE);
setLe32(fsi->structSignature, FSINFO_STRUCT_SIGNATURE);
setLe32(fsi->freeCount, 0XFFFFFFFF);
setLe32(fsi->nextFree, 0XFFFFFFFF);
writeMsg("Writing FSInfo Sector\n");
if (!writeSector(m_dev, m_relativeSectors + 1, m_secBuf) ||
!writeSector(m_dev, m_relativeSectors + 7, m_secBuf)) {
return false;
}
writeMsg("Writing FAT\n");
return initFatDir(m_dev, 32, 2*m_fatSize + m_sectorsPerCluster);
}
//------------------------------------------------------------------------------
bool USBFilesystemFormatter::writeFatMbr(USBDrive &m_dev) {
if (m_mbrLBA == 0xFFFFFFFFUL) {
DBGPrintf(" writeMBR - GPT entry so dont update\n");
return true;
}
memset(m_secBuf, 0, BYTES_PER_SECTOR);
// The relative sectors stuff is setup based off of the logicalMBR...
uint32_t relativeSectors = m_relativeSectors - m_mbrLBA;
MbrSector_t* mbr = reinterpret_cast<MbrSector_t*>(m_secBuf);
MbrPart_t *pt = &mbr->part[m_mbrPart];
if (!m_dev.readSector(m_mbrLBA, m_secBuf)) {
writeMsg("Didn't read MBR Sector !!!\n");
return false;
}
DBGPrintf("(writeFatMbr)[m_capacityMB,m_relativeSectors,relativeSectors,relativeSectors + m_totalSectors -1] %u, %u, %u, %u\n",m_capacityMB,m_relativeSectors,relativeSectors,relativeSectors + m_totalSectors -1);
#if USE_LBA_TO_CHS
lbaToMbrChs(pt->beginCHS, m_capacityMB, m_relativeSectors);
lbaToMbrChs(pt->endCHS, m_capacityMB,
m_relativeSectors + m_totalSectors -1);
#else // USE_LBA_TO_CHS
pt->beginCHS[0] = 1;
pt->beginCHS[1] = 1;
pt->beginCHS[2] = 0;
pt->endCHS[0] = 0XFE;
pt->endCHS[1] = 0XFF;
pt->endCHS[2] = 0XFF;
#endif // USE_LBA_TO_CHS
pt->type = m_partType;
setLe32(pt->relativeSectors, relativeSectors);
setLe32(pt->totalSectors, m_totalSectors);
setLe16(mbr->signature, MBR_SIGNATURE);
return writeSector(m_dev, m_mbrLBA, m_secBuf);
}
//-----------------------------------------------------------------------------
#define CSECTORS_PER_WRITE 32
bool USBFilesystemFormatter::initFatDir(USBDrive &m_dev, uint8_t fatType, uint32_t sectorCount) {
DBGPrintf("PFsFatFormatter::initFatDir(%u, %u)\n", fatType, sectorCount);
size_t n;
uint32_t fat_sector = 1;
DBGPrintf("Writing FAT ");
if (sectorCount >= CSECTORS_PER_WRITE) {
uint8_t *large_buffer_alloc = (uint8_t *)malloc(BYTES_PER_SECTOR * CSECTORS_PER_WRITE + 32);
if (large_buffer_alloc) {
uint8_t *large_buffer = (uint8_t *)(((uintptr_t)large_buffer_alloc + 31) & ~((uintptr_t)(31)));
DBGPrintf("\tbuffer:%p alligned:%p\n", large_buffer_alloc, large_buffer);
memset(large_buffer, 0, BYTES_PER_SECTOR * CSECTORS_PER_WRITE);
uint32_t sectors_remaining = sectorCount;
uint32_t loops_per_dot = sectorCount/(32*CSECTORS_PER_WRITE);
uint32_t loop_count = 0;
while (sectors_remaining >= CSECTORS_PER_WRITE) {
if (!m_dev.writeSectors(m_fatStart + fat_sector, large_buffer, CSECTORS_PER_WRITE)) {
return false;
}
fat_sector += CSECTORS_PER_WRITE;
sectors_remaining -= CSECTORS_PER_WRITE;
if (++loop_count == loops_per_dot) {
DBGPrintf(".");
loop_count = 0;
}
}
if (sectors_remaining) {
if (!m_dev.writeSectors(m_fatStart + fat_sector, large_buffer, sectors_remaining)) {
return false;
}
fat_sector += sectors_remaining;
}
free(large_buffer_alloc);
}
}
if (fat_sector < sectorCount) {
memset(m_secBuf, 0, BYTES_PER_SECTOR);
for (; fat_sector < sectorCount; fat_sector++) {
if (!writeSector(m_dev, m_fatStart + fat_sector, m_secBuf)) {
return false;
}
if ((fat_sector%(sectorCount/32)) == 0) {
DBGPrintf(".");
}
}
}
DBGPrintf("\r\n");
// Allocate reserved clusters and root for FAT32.
m_secBuf[0] = 0XF8;
n = fatType == 16 ? 4 : 12;
for (size_t i = 1; i < n; i++) {
m_secBuf[i] = 0XFF;
}
return writeSector(m_dev, m_fatStart, m_secBuf) &&
writeSector(m_dev, m_fatStart + m_fatSize, m_secBuf);
}
//------------------------------------------------------------------------------
void USBFilesystemFormatter::initPbs() {
PbsFat_t* pbs = reinterpret_cast<PbsFat_t*>(m_secBuf);
memset(m_secBuf, 0, BYTES_PER_SECTOR);
pbs->jmpInstruction[0] = 0XEB;
pbs->jmpInstruction[1] = 0X76;
pbs->jmpInstruction[2] = 0X90;
for (uint8_t i = 0; i < sizeof(pbs->oemName); i++) {
pbs->oemName[i] = ' ';
}
setLe16(pbs->bpb.bpb16.bytesPerSector, BYTES_PER_SECTOR);
pbs->bpb.bpb16.sectorsPerCluster = m_sectorsPerCluster;
setLe16(pbs->bpb.bpb16.reservedSectorCount, m_reservedSectorCount);
pbs->bpb.bpb16.fatCount = 2;
// skip rootDirEntryCount
// skip totalSectors16
pbs->bpb.bpb16.mediaType = 0XF8;
// skip sectorsPerFat16
// skip sectorsPerTrack
// skip headCount
setLe32(pbs->bpb.bpb16.hidddenSectors, m_relativeSectors);
setLe32(pbs->bpb.bpb16.totalSectors32, m_totalSectors);
// skip rest of bpb
setLe16(pbs->signature, PBR_SIGNATURE);
}
//------------------------------------------------------------------------------
bool USBFilesystemFormatter::writeSector(USBDrive &m_dev, uint32_t sector, const uint8_t* src) {
// sandbox support
if ((sector < m_minSector) || (sector > m_maxSector)) {
DBGPrintf("!!! Sandbox Error: %u <= %u <= %u - Press any key to continue\n",
m_minSector, sector, m_maxSector);
while (Serial.read() == -1);
while (Serial.read() != -1) ;
}
return m_dev.writeSector(sector, src);
}
//===============================================================
//exFat Formatting
//===============================================================
bool USBFilesystemFormatter::formatExFAT(USBDrive &dev, USBFilesystem &fs, uint8_t part, uint8_t fat_type, uint8_t* secBuf, print_t* pr) {
DBGPrintf("\n### USBFilesystemFormatter::formatExFAT called\n");
ExFatPbs_t* pbs;
DirUpcase_t* dup;
DirBitmap_t* dbm;
DirLabel_t* label;
uint32_t bitmapSize;
uint32_t checksum = 0;
uint32_t clusterCount;
uint32_t clusterHeapOffset;
uint32_t fatLength;
uint32_t fatOffset;
uint32_t m;
uint32_t ns;
uint32_t sector;
uint32_t sectorsPerCluster;
uint32_t sectorCount;
uint8_t sectorsPerClusterShift;
uint8_t vs;
uint32_t firstLBA;
uint32_t mbrLBA;
uint8_t mbrPart;
int mbrType;
char volName[32];
m_secBuf = secBuf;
m_pr = pr;
writeMsg("Begin format ExFat File system\n");
//m_dev = partVol.blockDevice();
//m_part = partVol.part()-1; // convert to 0 biased.
m_part = part-1; // convert to 0 biased.
int vt = dev.findPartition(part, mbrType, firstLBA, sectorCount, mbrLBA, mbrPart);
DBGPrintf("Part:%u vt:%u first:%u, count:%u MBR:%u MBR Part:%u Type:%u\n", part, (uint8_t)vt, firstLBA, sectorCount, mbrLBA, mbrPart, mbrType);
if (vt == 0) return false; // got an invalid volume
// yes could have used some of the directly...
m_relativeSectors = firstLBA;
m_sectorCount = sectorCount;
m_mbrLBA = mbrLBA;
m_mbrPart = mbrPart;
bool has_volume_label = fs.mscfs.getVolumeLabel(volName, sizeof(volName));
if (has_volume_label) {
writeMsgF("Volume name:(%s)\n", volName);
}
#if defined(DBG_PRINT)
DBGPrintf(" m_sectorsPerCluster:%u\n", fs.mscfs.sectorsPerCluster());
DBGPrintf(" m_relativeSectors:%u\n", m_relativeSectors);
DBGPrintf(" m_fatStartSector: %u\n", fs.mscfs.fatStartSector());
DBGPrintf(" m_fatType: %d\n", fs.mscfs.fatType());
DBGPrintf(" m_clusterCount: %u\n", fs.mscfs.clusterCount());
DBGPrintf(" m_totalSectors: %u\n", sectorCount);
DBGPrintf("\n");
#endif
// Min size is 512 MB
if (sectorCount < 0X100000) {
DBG_FAIL_MACRO;
goto fail;
}
//BYTES_PER_SECTOR = partVol.getExFatVol()->bytesPerSector();
//SECTOR_MASK = BYTES_PER_SECTOR - 1;
//ROOT_CLUSTER = partVol.getExFatVol()->rootDirectoryCluster();
pbs = reinterpret_cast<ExFatPbs_t*>(secBuf);
if (!dev.readSector(firstLBA, m_secBuf)) {
DBG_FAIL_MACRO;
goto fail;
}
// Determine partition layout.
for (m = 1, vs = 0; m && sectorCount > m; m <<= 1, vs++) {}
sectorsPerClusterShift = vs < 29 ? 8 : (vs - 11)/2;
//DBGPrintf("Calculate sectorsPerClusterShift = %u\n", vs < 29 ? 8 : (vs - 11)/2);
//1 << n is the same as raising 2 to the power n
sectorsPerCluster = 1UL << sectorsPerClusterShift;
//DBGPrintf("Calculated sectorsPerCluster = %u\n", 1UL << sectorsPerClusterShift);
//The FatLength field shall describe the length, in sectors, of each FAT table
// At least (ClusterCount + 2) * 2^2/ 2^BytesPerSectorShift rounded up to the nearest integer
// At most (ClusterHeapOffset - FatOffset) / NumberOfFats rounded down to the nearest integer
fatLength = 1UL << (vs < 27 ? 13 : (vs + 1)/2); //original
//DBGPrintf("Calculated fatLength1 = %u\n",1UL << (vs < 27 ? 13 : (vs + 1)/2));
//The ClusterCount field shall describe the number of clusters the Cluster Heap contains
// (VolumeLength - ClusterHeapOffset) / 2^SectorsPerClusterShift rounded down to the nearest integer, which is exactly the number of clusters which can fit between the beginning of the Cluster Heap and the end of the volume
// 232- 11, which is the maximum number of clusters a FAT can describe
clusterCount = (sectorCount - 4*fatLength) >> sectorsPerClusterShift; //original
//DBGPrintf("Calculated clusterCount = %u\n", (sectorCount - 4*fatLength) >> sectorsPerClusterShift);
//The ClusterHeapOffset field shall describe the volume-relative sector offset of the Cluster Heap
// At least FatOffset + FatLength * NumberOfFats, to account for the sectors all the preceding regions consume
// At most 2^32- 1 or VolumeLength - (ClusterCount * 2^SectorsPerClusterShift), whichever calculation is less
clusterHeapOffset = 2*fatLength; //original
//DBGPrintf("\tclusterHeapOffset: %u %u\n",getLe32(pbs->bpb.clusterHeapOffset), clusterHeapOffset);
//The FatOffset field shall describe the volume-relative sector offset of the First FAT
// At least 24, which accounts for the sectors the Main Boot and Backup Boot regions consume
// At most ClusterHeapOffset - (FatLength * NumberOfFats), which accounts for the sectors the Cluster Heap consumes
fatOffset = fatLength;
//DBGPrintf("Calculated fatOffset = %u\n", clusterHeapOffset - fatLength);
//The PartitionOffset field shall describe the media-relative sector offset of the partition which hosts the given exFAT volume
partitionOffset = m_relativeSectors;
//The VolumeLength field shall describe the size of the given exFAT volume in sectors
// At least 2^20/ 2^BytesPerSectorShift, which ensures the smallest volume is no less than 1MB
// At most 264- 1, the largest value this field can describe
volumeLength = sectorCount;
#if defined(DBG_PRINT)
DBGPrintf("VS: %d\n", vs);
DBGPrintf("sectorsPerClusterShift: %u,\n sectorsPerCluster: %u,\n fatLength %u\n", sectorsPerClusterShift, sectorsPerCluster, fatLength);
DBGPrintf("fatOffset: %u,\n partitionOffset: %u\n", fatOffset, partitionOffset);
DBGPrintf("clusterHeapOffset: %u,\n clusterCount: %u,\n volumeLength %u\n", clusterHeapOffset, clusterCount, volumeLength);
DBGPrintf("cluster 2 bitmap: %u\n",partitionOffset + clusterHeapOffset);
DBGPrintf("Up case table: %u\n",partitionOffset + clusterHeapOffset + sectorsPerCluster);
// =============================== DEBUG
pbs = reinterpret_cast<ExFatPbs_t*>(secBuf);
if (m_dev->readSector(firstLBA, m_secBuf)) {
DBGPrintf("\n *** PBS data ***\n");
DBGPrintf("\tFirstLBA: %u\n", firstLBA);
DBGPrintf("\tpartitionOffset: %llu %u\n",getLe64(pbs->bpb.partitionOffset), partitionOffset);
DBGPrintf("\tvolumeLength: %llu %u\n",getLe64(pbs->bpb.volumeLength), volumeLength);
DBGPrintf("\tfatOffset: %u %u\n",getLe32(pbs->bpb.fatOffset), fatOffset);
DBGPrintf("\tfatLength: %u %u\n",getLe32(pbs->bpb.fatLength), fatLength);
DBGPrintf("\tclusterHeapOffset: %u %u\n",getLe32(pbs->bpb.clusterHeapOffset), clusterHeapOffset);
DBGPrintf("\tclusterCount: %u %u\n",getLe32(pbs->bpb.clusterCount), clusterCount);
DBGPrintf("\trootDirectoryCluster: %u %u\n",getLe32(pbs->bpb.rootDirectoryCluster), ROOT_CLUSTER);
DBGPrintf("\tvolumeSerialNumber: %u %u\n",getLe32(pbs->bpb.volumeSerialNumber), sectorCount);
DBGPrintf("\tfileSystemRevision: %u %u\n",getLe16(pbs->bpb.fileSystemRevision), 0X100);
DBGPrintf("\tvolumeFlags: %u %u\n",getLe16(pbs->bpb.volumeFlags), 0);
//Serial.println("*** Hit any key to continue $ to abort");
//int ch;
//while((ch=Serial.read()) == -1);
//while(Serial.read() != -1);
//if (ch == '$') { DBGPrintf("*** Aborted ***"); return false; }
}
#endif
//-------------------- WRITE MBR ----------
if (!writeExFatMbr(dev)) {
DBG_FAIL_MACRO;
goto fail;
}
// Debug Set the sand box
setWriteSandBox(firstLBA, firstLBA + sectorCount - 1);
writeMsg( "Writing Partition Boot Sector\n");
// Partition Boot sector.
memset(secBuf, 0, BYTES_PER_SECTOR);
pbs = reinterpret_cast<ExFatPbs_t*>(secBuf);
pbs->jmpInstruction[0] = 0XEB;
pbs->jmpInstruction[1] = 0X76;
pbs->jmpInstruction[2] = 0X90;
pbs->oemName[0] = 'E';
pbs->oemName[1] = 'X';
pbs->oemName[2] = 'F';
pbs->oemName[3] = 'A';
pbs->oemName[4] = 'T';
pbs->oemName[5] = ' ';
pbs->oemName[6] = ' ';
pbs->oemName[7] = ' ';
setLe64(pbs->bpb.partitionOffset, partitionOffset);
setLe64(pbs->bpb.volumeLength, volumeLength);
setLe32(pbs->bpb.fatOffset, fatOffset);
setLe32(pbs->bpb.fatLength, fatLength);
setLe32(pbs->bpb.clusterHeapOffset, clusterHeapOffset);
setLe32(pbs->bpb.clusterCount, clusterCount);
setLe32(pbs->bpb.rootDirectoryCluster, ROOT_CLUSTER);
setLe32(pbs->bpb.volumeSerialNumber, sectorCount);
setLe16(pbs->bpb.fileSystemRevision, 0X100);
setLe16(pbs->bpb.volumeFlags, 0);
pbs->bpb.bytesPerSectorShift = BYTES_PER_SECTOR_SHIFT;
pbs->bpb.sectorsPerClusterShift = sectorsPerClusterShift;
pbs->bpb.numberOfFats = 1;
pbs->bpb.driveSelect = 0X80;
pbs->bpb.percentInUse = 0;
// Fill boot code like official SDFormatter.
for (size_t i = 0; i < sizeof(pbs->bootCode); i++) {
pbs->bootCode[i] = 0XF4;
}
setLe16(pbs->signature, PBR_SIGNATURE);
for (size_t i = 0; i < BYTES_PER_SECTOR; i++) {
if (i == offsetof(ExFatPbs_t, bpb.volumeFlags[0]) ||
i == offsetof(ExFatPbs_t, bpb.volumeFlags[1]) ||
i == offsetof(ExFatPbs_t, bpb.percentInUse)) {
continue;
}
checksum = exFatChecksum(checksum, secBuf[i]);
}
sector = partitionOffset;
#if defined(DBG_PRINT)
DBGPrintf("\tWriting Sector: %d\n", sector-partitionOffset);
#endif
if (!writeSector(dev, sector, secBuf) ||
!writeSector(dev, sector + BOOT_BACKUP_OFFSET , secBuf)) {
DBG_FAIL_MACRO;
goto fail;
}
writeMsg( "Write eight Extended Boot Sectors\n");
sector++;
#if defined(DBG_PRINT)
DBGPrintf("\tWriting Sector: %d\n", sector-partitionOffset);
#endif
// Write eight Extended Boot Sectors.
memset(secBuf, 0, BYTES_PER_SECTOR);
setLe16(pbs->signature, PBR_SIGNATURE);
for (int j = 0; j < 8; j++) {
for (size_t i = 0; i < BYTES_PER_SECTOR; i++) {
checksum = exFatChecksum(checksum, secBuf[i]);
}
if (!writeSector(dev, sector, secBuf) ||
!writeSector(dev, sector + BOOT_BACKUP_OFFSET , secBuf)) {
DBG_FAIL_MACRO;
goto fail;
}
sector++;
}
writeMsg( "Write OEM Parameter Sector and reserved sector\n");
#if defined(DBG_PRINT)
DBGPrintf("\tWriting Sector: %d\n", sector-partitionOffset);
#endif
// Write OEM Parameter Sector and reserved sector.
memset(secBuf, 0, BYTES_PER_SECTOR);
for (int j = 0; j < 2; j++) {
for (size_t i = 0; i < BYTES_PER_SECTOR; i++) {
checksum = exFatChecksum(checksum, secBuf[i]);
}
if (!writeSector(dev, sector, secBuf) ||
!writeSector(dev, sector + BOOT_BACKUP_OFFSET , secBuf)) {
DBG_FAIL_MACRO;
goto fail;
}
sector++;
}
writeMsg( "Write Boot CheckSum Sector\n");
#if defined(DBG_PRINT)
DBGPrintf("\tWriting Sector: %d\n", sector-partitionOffset);
#endif
// Write Boot CheckSum Sector.
for (size_t i = 0; i < BYTES_PER_SECTOR; i += 4) {
setLe32(secBuf + i, checksum);
}
if (!writeSector(dev, sector, secBuf) ||
!writeSector(dev, sector + BOOT_BACKUP_OFFSET , secBuf)) {
DBG_FAIL_MACRO;
goto fail;
}
// Initialize FAT.
writeMsg( "Writing exFAT ");
sector = partitionOffset + fatOffset;
//The + 2 is because the first two entries in a FAT do not represent clusters.
// The FatEntry[0] field shall describe the media type in the first byte (the lowest order byte) and shall contain FFh
// in the remaining three bytes. The media type (the first byte) should be F8h
//Media type is generally ignored so my bug of not setting it correctly was not caught earlier.
// The FatEntry[1] field only exists due to historical precedence and does not describe anything of interest.
//The )*4 is because entries are four bytes. The expression rounds up to a whole number of sectors.
ns = ((clusterCount + 2)*4 + BYTES_PER_SECTOR - 1)/BYTES_PER_SECTOR;
#if defined(DBG_PRINT)
DBGPrintf("\tWriting Sector: %d, ns: %u\n", sector-partitionOffset, ns);
#endif
memset(secBuf, 0, BYTES_PER_SECTOR);
// Allocate two reserved clusters, bitmap, upcase, and root clusters.
secBuf[0] = 0XF8;
for (size_t i = 1; i < 20; i++) {
secBuf[i] = 0XFF;
}
for (uint32_t i = 0; i < ns; i++) {
if (i%(ns/32) == 0) {
writeMsg( ".");
}
if (!writeSector(dev, sector + i, secBuf)) {
DBG_FAIL_MACRO;
goto fail;
}
if (i == 0) {
memset(secBuf, 0, BYTES_PER_SECTOR);
}
}
writeMsg( "\r\n");
//==================================================================
writeMsg( "Write cluster two, bitmap\n");
// Write cluster two, bitmap.
sector = partitionOffset + clusterHeapOffset;
// The 7)/8 converts clusterCount to bytes rounded up to whole bytes.
bitmapSize = (clusterCount + 7)/8;
ns = (bitmapSize + BYTES_PER_SECTOR - 1)/BYTES_PER_SECTOR;
#if defined(DBG_PRINT)
DBGPrintf("\tWriting Sector: %d\n", sector-partitionOffset);
DBGPrintf("sectorsPerCluster: %d, bitmapSize: %d, ns: %d\n", sectorsPerCluster, bitmapSize, ns);
#endif
if (ns > sectorsPerCluster) {
DBG_FAIL_MACRO;
goto fail;
}
memset(secBuf, 0, BYTES_PER_SECTOR);
// Allocate clusters for bitmap, upcase, and root.
secBuf[0] = 0X7;
for (uint32_t i = 1; i < ns; i++) {
if (!writeSector(dev, sector + i, secBuf)) {
DBG_FAIL_MACRO;
goto fail;
}
if (i == 0) {
secBuf[0] = 0;
}
}
// Write cluster three, upcase table.
writeMsg( "Writing upcase table\r\n");
#if defined(DBG_PRINT)
DBGPrintf("\tWriting Sector: %d\n", clusterHeapOffset + sectorsPerCluster);
#endif
if (!writeUpcase(dev, partitionOffset + clusterHeapOffset + sectorsPerCluster)) {
DBG_FAIL_MACRO;
goto fail;
}
if (m_upcaseSize > BYTES_PER_SECTOR*sectorsPerCluster) {
DBG_FAIL_MACRO;
goto fail;
}
// Initialize first sector of root.
writeMsg( "Writing root\r\n");
ns = sectorsPerCluster;
sector = partitionOffset + clusterHeapOffset + 2*sectorsPerCluster;
#if defined(DBG_PRINT)
DBGPrintf("\tWriting 1st Sector of root: %d\n", clusterHeapOffset + 2*sectorsPerCluster);
#endif
memset(secBuf, 0, BYTES_PER_SECTOR);
// Unused Label entry.
label = reinterpret_cast<DirLabel_t*>(secBuf);
label->type = EXFAT_TYPE_LABEL & 0X7F;
// bitmap directory entry.
dbm = reinterpret_cast<DirBitmap_t*>(secBuf + 32);
dbm->type = EXFAT_TYPE_BITMAP;
setLe32(dbm->firstCluster, BITMAP_CLUSTER);
setLe64(dbm->size, bitmapSize);
// upcase directory entry.
dup = reinterpret_cast<DirUpcase_t*>(secBuf +64);
dup->type = EXFAT_TYPE_UPCASE;
setLe32(dup->checksum, m_upcaseChecksum);
setLe32(dup->firstCluster, UPCASE_CLUSTER);
setLe64(dup->size, m_upcaseSize);
// Write root, cluster four.
for (uint32_t i = 0; i < ns; i++) {
if (!writeSector(dev, sector + i, secBuf)) {
DBG_FAIL_MACRO;
goto fail;
}
if (i == 0) {
memset(secBuf, 0, BYTES_PER_SECTOR);
}
}
writeMsg( "Format done\r\n");
// what happens if I tell the partion to begin again?
//partVol.begin(dev, true, m_part+1); // need to 1 bias again...
//DBGPrintf("free clusters after begin on partVol: %u\n", partVol.freeClusterCount());
if (has_volume_label) {
writeMsg( "Set Volume Label\n");
fs.mscfs.setVolumeLabel(volName);
}
dev.syncDevice();
setWriteSandBox(0, 0xffffffff);
return true;
fail:
writeMsg( "Format failed\r\n");
setWriteSandBox(0, 0xffffffff);
return false;
}
//------------------------------------------------------------------------------
bool USBFilesystemFormatter::writeExFatMbr(USBDrive &m_dev) {
// make Master Boot Record. Use fake CHS.
if (m_mbrLBA == 0xFFFFFFFFUL) {
DBGPrintf(" writeMBR - GPT entry so dont update\n");
return true;
}
memset(m_secBuf, 0, BYTES_PER_SECTOR);
// Need to handle EXT wmere MBR may be EXtended Boot record
MbrSector_t* mbr = reinterpret_cast<MbrSector_t*>(m_secBuf);
MbrPart_t *pt = &mbr->part[m_mbrPart];
if (!m_dev.readSector(m_mbrLBA, m_secBuf)) writeMsg("DIDN't GET SECTOR BUFFER");
pt->beginCHS[0] = 0x20;
pt->beginCHS[1] = 0x21;
pt->beginCHS[2] = 0;
pt->type = 7;
pt->endCHS[0] = 0XFE;
pt->endCHS[1] = 0XFF;
pt->endCHS[2] = 0XFF;
setLe32(pt->relativeSectors, partitionOffset - m_mbrLBA); // should be relative to the start...
setLe32(pt->totalSectors, volumeLength);
setLe16(mbr->signature, MBR_SIGNATURE);
//DBGPrintf(" m_relativeSectors:%u\n", getLe32(pt->relativeSectors));
//DBGPrintf(" m_totalSectors:%u\n", getLe32(pt->totalSectors));
return writeSector(m_dev, m_mbrLBA, m_secBuf);
}
//------------------------------------------------------------------------------
bool USBFilesystemFormatter::syncUpcase(USBDrive &m_dev) {
uint16_t index = m_upcaseSize & SECTOR_MASK;
if (!index) {
return true;
}
for (size_t i = index; i < BYTES_PER_SECTOR; i++) {
m_secBuf[i] = 0;
}
return writeSector(m_dev, m_upcaseSector, m_secBuf);
}
//------------------------------------------------------------------------------
bool USBFilesystemFormatter::writeUpcaseByte(USBDrive &m_dev, uint8_t b) {
uint16_t index = m_upcaseSize & SECTOR_MASK;
m_secBuf[index] = b;
m_upcaseChecksum = exFatChecksum(m_upcaseChecksum, b);
m_upcaseSize++;
if (index == SECTOR_MASK) {
return writeSector(m_dev, m_upcaseSector++, m_secBuf);
}
return true;
}
//------------------------------------------------------------------------------
bool USBFilesystemFormatter::writeUpcaseUnicode(USBDrive &m_dev, uint16_t unicode) {
return writeUpcaseByte(m_dev, unicode) && writeUpcaseByte(m_dev, unicode >> 8);
}
//------------------------------------------------------------------------------
bool USBFilesystemFormatter::writeUpcase(USBDrive &m_dev, uint32_t sector) {
uint32_t n;
uint32_t ns;
uint32_t ch = 0;
uint16_t uc;
m_upcaseSize = 0;
m_upcaseChecksum = 0;
m_upcaseSector = sector;
while (ch < 0X10000) {
uc = toUpcase(ch);
if (uc != ch) {
if (!writeUpcaseUnicode(m_dev, uc)) {
DBG_FAIL_MACRO;
goto fail;
}
ch++;
} else {
for (n = ch + 1; n < 0X10000 && n == toUpcase(n); n++) {}
ns = n - ch;
if (ns >= MINIMUM_UPCASE_SKIP) {
if (!writeUpcaseUnicode(m_dev, 0XFFFF) || !writeUpcaseUnicode(m_dev, ns)) {
DBG_FAIL_MACRO;
goto fail;
}
ch = n;
} else {
while (ch < n) {
if (!writeUpcaseUnicode(m_dev, ch++)) {
DBG_FAIL_MACRO;
goto fail;
}
}
}
}
}
if (!syncUpcase(m_dev)) {
DBG_FAIL_MACRO;
goto fail;
}
return true;
fail:
return false;
}