/** * @private * @brief Flushes all Write cache buffers with no active Handles. * * @param pIoman IOMAN Object. * * @return FF_ERR_NONE on Success. **/ FF_ERROR FF_FlushCache(FF_IOMAN *pIoman) { FF_T_UINT16 i,x; if(!pIoman) { return FF_ERR_NULL_POINTER | FF_FLUSHCACHE; } FF_PendSemaphore(pIoman->pSemaphore); { for(i = 0; i < pIoman->CacheSize; i++) { if((pIoman->pBuffers + i)->NumHandles == 0 && (pIoman->pBuffers + i)->Modified == FF_TRUE) { FF_BlockWrite(pIoman, (pIoman->pBuffers + i)->Sector, 1, (pIoman->pBuffers + i)->pBuffer); // Buffer has now been flushed, mark it as a read buffer and unmodified. (pIoman->pBuffers + i)->Mode = FF_MODE_READ; (pIoman->pBuffers + i)->Modified = FF_FALSE; // Search for other buffers that used this sector, and mark them as modified // So that further requests will result in the new sector being fetched. for(x = 0; x < pIoman->CacheSize; x++) { if(x != i) { if((pIoman->pBuffers + x)->Sector == (pIoman->pBuffers + i)->Sector && (pIoman->pBuffers + x)->Mode == FF_MODE_READ) { (pIoman->pBuffers + x)->Modified = FF_TRUE; } } } } } } FF_ReleaseSemaphore(pIoman->pSemaphore); return FF_ERR_NONE; }
/** * @public * @brief Unmounts the active partition. * * @param pIoman FF_IOMAN Object. * * @return FF_ERR_NONE on success. **/ FF_ERROR FF_UnmountPartition(FF_IOMAN *pIoman) { FF_ERROR RetVal = FF_ERR_NONE; #ifdef FF_MIRROR_FATS_UMOUNT FF_T_UINT i, y; FF_BUFFER *pBuffer; #endif if(!pIoman || !pIoman->pPartition) { return FF_ERR_NULL_POINTER | FF_UNMOUNTPARTITION; } if (!pIoman->pPartition->PartitionMounted) return FF_ERR_NONE; FF_PendSemaphore(pIoman->pSemaphore); // Ensure that there are no File Handles { if(!FF_ActiveHandles(pIoman)) { if(pIoman->FirstFile == NULL) { // Release Semaphore to call this function! FF_ReleaseSemaphore(pIoman->pSemaphore); RetVal = FF_FlushCache(pIoman); // Flush any unwritten sectors to disk. if(FF_isERR(RetVal)) { return RetVal; } // Reclaim Semaphore FF_PendSemaphore(pIoman->pSemaphore); pIoman->pPartition->PartitionMounted = FF_FALSE; #ifdef FF_MIRROR_FATS_UMOUNT FF_ReleaseSemaphore(pIoman->pSemaphore); for(i = 0; i < pIoman->pPartition->SectorsPerFAT; i++) { pBuffer = FF_GetBuffer(pIoman, pIoman->pPartition->FatBeginLBA + i, FF_MODE_READ); if(!pBuffer) { RetVal = FF_ERR_DEVICE_DRIVER_FAILED | FF_UNMOUNTPARTITION; break; } for(y = 0; y < pIoman->pPartition->NumFATS; y++) { FF_BlockWrite(pIoman, pIoman->pPartition->FatBeginLBA + (y*pIoman->pPartition->SectorsPerFAT) + i, 1, pBuffer->pBuffer, FF_FALSE); } } FF_PendSemaphore(pIoman->pSemaphore); #endif } else { RetVal = FF_ERR_IOMAN_ACTIVE_HANDLES | FF_UNMOUNTPARTITION; } } else { RetVal = FF_ERR_IOMAN_ACTIVE_HANDLES | FF_UNMOUNTPARTITION; // Active handles found on the cache. } } FF_ReleaseSemaphore(pIoman->pSemaphore); return RetVal; }
/** * @private * @brief Releases a buffer resource. * * @param pIoman Pointer to an FF_IOMAN object. * @param pBuffer Pointer to an FF_BUFFER object. * **/ FF_ERROR FF_ReleaseBuffer(FF_IOMAN *pIoman, FF_BUFFER *pBuffer) { FF_ERROR Error = FF_ERR_NONE; // Protect description changes with a semaphore. FF_PendSemaphore(pIoman->pSemaphore); { if (pBuffer->NumHandles) { pBuffer->NumHandles--; } else { //printf ("FF_ReleaseBuffer: buffer not claimed\n"); } #ifdef FF_CACHE_WRITE_THROUGH if(pBuffer->Modified == FF_TRUE) { Error = FF_BlockWrite(pIoman, pBuffer->Sector, 1, pBuffer->pBuffer, FF_TRUE); if(!FF_isERR(Error)) { // Ensure if an error occurs its still possible to write the block again. pBuffer->Modified = FF_FALSE; } } #endif } FF_ReleaseSemaphore(pIoman->pSemaphore); return Error; }
static FF_ERROR FF_WriteClusters(FF_FILE *pFile, FF_T_UINT32 Count, FF_T_UINT8 *buffer) { FF_T_UINT32 ulSectors; FF_T_UINT32 SequentialClusters = 0; FF_T_UINT32 nItemLBA; FF_T_SINT32 slRetVal; FF_ERROR Error; while(Count != 0) { if((Count - 1) > 0) { SequentialClusters = FF_GetSequentialClusters(pFile->pIoman, pFile->AddrCurrentCluster, (Count - 1), &Error); if(Error) { return Error; } } ulSectors = (SequentialClusters + 1) * pFile->pIoman->pPartition->SectorsPerCluster; nItemLBA = FF_Cluster2LBA(pFile->pIoman, pFile->AddrCurrentCluster); nItemLBA = FF_getRealLBA(pFile->pIoman, nItemLBA); slRetVal = FF_BlockWrite(pFile->pIoman, nItemLBA, ulSectors, buffer); if(slRetVal < 0) { return slRetVal; } Count -= (SequentialClusters + 1); pFile->AddrCurrentCluster = FF_TraverseFAT(pFile->pIoman, pFile->AddrCurrentCluster, (SequentialClusters + 1), &Error); if(Error) { return Error; } pFile->CurrentCluster += (SequentialClusters + 1); buffer += ulSectors * pFile->pIoman->BlkSize; SequentialClusters = 0; } return 0; }
FF_BUFFER *FF_GetBuffer(FF_IOMAN *pIoman, FF_T_UINT32 Sector, FF_T_UINT8 Mode) { FF_BUFFER *pBuffer; FF_BUFFER *pBufLRU = NULL; FF_BUFFER *pBufLHITS = NULL; FF_BUFFER *pBufMatch = NULL; FF_T_UINT32 i; while(!pBufMatch) { FF_PendSemaphore(pIoman->pSemaphore); { pBuffer = pIoman->pBuffers; // HT if a perfect match has priority, find that first for(i = 0; i < pIoman->CacheSize; i++, pBuffer++) { pBuffer = (pIoman->pBuffers + i); if(pBuffer->Sector == Sector && pBuffer->Valid == FF_TRUE) { pBufMatch = pBuffer; break; // Why look further if you found a perfect match? } } if(pBufMatch) { // A Match was found process! if(Mode == FF_MODE_READ && pBufMatch->Mode == FF_MODE_READ) { pBufMatch->NumHandles += 1; pBufMatch->Persistance += 1; FF_ReleaseSemaphore(pIoman->pSemaphore); return pBufMatch; } if(pBufMatch->Mode == FF_MODE_WRITE && pBufMatch->NumHandles == 0) { // This buffer has no attached handles. pBufMatch->Mode = Mode; pBufMatch->NumHandles = 1; pBufMatch->Persistance += 1; FF_ReleaseSemaphore(pIoman->pSemaphore); return pBufMatch; } if(pBufMatch->Mode == FF_MODE_READ && Mode == FF_MODE_WRITE && pBufMatch->NumHandles == 0) { pBufMatch->Mode = Mode; pBufMatch->Modified = FF_TRUE; pBufMatch->NumHandles = 1; pBufMatch->Persistance += 1; FF_ReleaseSemaphore(pIoman->pSemaphore); return pBufMatch; } pBufMatch = NULL; // Sector is already in use, keep yielding until its available! } else { pBuffer = pIoman->pBuffers; for(i = 0; i < pIoman->CacheSize; i++, pBuffer++) { if(pBuffer->NumHandles == 0) { pBuffer->LRU += 1; if(!pBufLRU) { pBufLRU = pBuffer; } if(!pBufLHITS) { pBufLHITS = pBuffer; } if(pBuffer->LRU >= pBufLRU->LRU) { if(pBuffer->LRU == pBufLRU->LRU) { if(pBuffer->Persistance > pBufLRU->Persistance) { pBufLRU = pBuffer; } } else { pBufLRU = pBuffer; } } if(pBuffer->Persistance < pBufLHITS->Persistance) { pBufLHITS = pBuffer; } } } if(pBufLRU) { // Process the suitable candidate. if(pBufLRU->Modified == FF_TRUE) { FF_BlockWrite(pIoman, pBufLRU->Sector, 1, pBufLRU->pBuffer); } pBufLRU->Mode = Mode; pBufLRU->Persistance = 1; pBufLRU->LRU = 0; pBufLRU->NumHandles = 1; pBufLRU->Sector = Sector; if(Mode == FF_MODE_WRITE) { pBufLRU->Modified = FF_TRUE; } else { pBufLRU->Modified = FF_FALSE; } FF_BlockRead(pIoman, Sector, 1, pBufLRU->pBuffer); pBufLRU->Valid = FF_TRUE; FF_ReleaseSemaphore(pIoman->pSemaphore); return pBufLRU; } } } FF_ReleaseSemaphore(pIoman->pSemaphore); FF_Yield(); // Better to go asleep to give low-priority task a chance to release buffer(s) } return pBufMatch; // Return the Matched Buffer! }
FF_BUFFER *FF_GetBuffer(FF_IOMAN *pIoman, FF_T_UINT32 Sector, FF_T_UINT8 Mode) { FF_BUFFER *pBuffer; FF_BUFFER *pBufLRU; // FF_BUFFER *pBufLHITS = NULL; // Wasn't use anymore? FF_BUFFER *pBufMatch = NULL; FF_T_SINT32 RetVal; FF_T_INT LoopCount = FF_GETBUFFER_WAIT_TIME; FF_T_INT cacheSize = pIoman->CacheSize; if (cacheSize <= 0) { return NULL; } while(!pBufMatch) { if (!--LoopCount) { // // *pError = FF_ERR_IOMAN_GETBUFFER_TIMEOUT; // return NULL; } FF_PendSemaphore(pIoman->pSemaphore); { for(pBuffer = pIoman->pBuffers; pBuffer < pIoman->pBuffers + cacheSize; pBuffer++) { if(pBuffer->Sector == Sector && pBuffer->Valid) { pBufMatch = pBuffer; break; // Don't look further if you found a perfect match } } if(pBufMatch) { // A Match was found process! if(Mode == FF_MODE_READ && pBufMatch->Mode == FF_MODE_READ) { pBufMatch->NumHandles += 1; pBufMatch->Persistance += 1; break; } if(pBufMatch->NumHandles == 0) { pBufMatch->Mode = (Mode & FF_MODE_RD_WR); if((Mode & FF_MODE_WRITE) != 0) { // This buffer has no attached handles. pBufMatch->Modified = FF_TRUE; } pBufMatch->NumHandles = 1; pBufMatch->Persistance += 1; break; } pBufMatch = NULL; // Sector is already in use, keep yielding until its available! } else { pBufLRU = NULL; // So put them to NULL here for(pBuffer = pIoman->pBuffers; pBuffer < pIoman->pBuffers + cacheSize; pBuffer++) { if(pBuffer->NumHandles) continue; // Occupied pBuffer->LRU += 1; if(!pBufLRU) { pBufLRU = pBuffer; } if(pBuffer->LRU > pBufLRU->LRU || (pBuffer->LRU == pBufLRU->LRU && pBuffer->Persistance > pBufLRU->Persistance)) { pBufLRU = pBuffer; } } // Choose a suitable buffer! if(pBufLRU) { // Process the suitable candidate. if(pBufLRU->Modified == FF_TRUE) { // Along with the FF_TRUE parameter to indicate semapahore has been claimed RetVal = FF_BlockWrite(pIoman, pBufLRU->Sector, 1, pBufLRU->pBuffer, FF_TRUE); if (RetVal < 0) { pBufMatch = NULL; break; } } if (Mode == FF_MODE_WR_ONLY) { memset (pBufLRU->pBuffer, '\0', pIoman->BlkSize); } else { RetVal = FF_BlockRead(pIoman, Sector, 1, pBufLRU->pBuffer, FF_TRUE); if (RetVal < 0) { pBufMatch = NULL; break; } } pBufLRU->Mode = (Mode & FF_MODE_RD_WR); pBufLRU->Persistance = 1; pBufLRU->LRU = 0; pBufLRU->NumHandles = 1; pBufLRU->Sector = Sector; pBufLRU->Modified = (Mode & FF_MODE_WRITE) != 0; pBufLRU->Valid = FF_TRUE; pBufMatch = pBufLRU; break; } } } FF_ReleaseSemaphore(pIoman->pSemaphore); // Better to go asleep to give low-priority task a chance to release buffer(s) FF_Sleep (FF_GETBUFFER_SLEEP_TIME); } // while(!pBufMatch) FF_ReleaseSemaphore(pIoman->pSemaphore); return pBufMatch; // Return the Matched Buffer! }
FF_ERROR FF_FormatPartition(FF_IOMAN *pIoman, FF_T_UINT32 ulPartitionNumber, FF_T_UINT32 ulClusterSize) { FF_BUFFER *pBuffer; FF_T_SINT8 scPartitionCount; FF_T_UINT32 maxClusters, f16MaxClusters, f32MaxClusters; FF_T_UINT32 fatSize = 32; // Default to a fat32 format. FF_PARTITION_ENTRY partitionGeom; FF_T_UINT32 ulBPRLba; ///< The LBA of the boot partition record. FF_T_UINT32 fat32Size, fat16Size, newFat32Size, newFat16Size, finalFatSize; FF_T_UINT32 sectorsPerCluster = ulClusterSize / pIoman->BlkSize; FF_T_UINT32 ulReservedSectors, ulTotalSectors; FF_T_UINT32 ul32DataSectors, ul16DataSectors; FF_T_UINT32 i; FF_T_UINT32 ulClusterBeginLBA; FF_ERROR Error = FF_ERR_NONE; // Get Partition Metrics, and pass on to FF_Format() function pBuffer = FF_GetBuffer(pIoman, 0, FF_MODE_READ); { if(!pBuffer) { return FF_ERR_DEVICE_DRIVER_FAILED | FF_FORMATPARTITION; } scPartitionCount = FF_PartitionCount(pBuffer->pBuffer); if(!scPartitionCount) { // Get Partition Geom from volume boot record. ulBPRLba = 0; partitionGeom.ulStartLBA = FF_getShort(pBuffer->pBuffer, FF_FAT_RESERVED_SECTORS); // Get offset to start of where we can actually put the FAT table. ulReservedSectors = partitionGeom.ulStartLBA; partitionGeom.ulLength = (FF_T_UINT32) FF_getShort(pBuffer->pBuffer, FF_FAT_16_TOTAL_SECTORS); if(partitionGeom.ulLength == 0) { // 32-bit entry was used. partitionGeom.ulLength = FF_getLong(pBuffer->pBuffer, FF_FAT_32_TOTAL_SECTORS); } ulTotalSectors = partitionGeom.ulLength; partitionGeom.ulLength -= partitionGeom.ulStartLBA; // Remove the reserved sectors from the count. } else { // Get partition Geom from the partition table entry. } // Calculate the max possiblenumber of clusters based on clustersize. maxClusters = partitionGeom.ulLength / sectorsPerCluster; // Determine the size of a FAT table required to support this. fat32Size = (maxClusters * 4) / pIoman->BlkSize; // Potential size in sectors of a fat32 table. if((maxClusters * 4) % pIoman->BlkSize) { fat32Size++; } fat32Size *= 2; // Officially there are 2 copies of the FAT. fat16Size = (maxClusters * 2) / pIoman->BlkSize; // Potential size in bytes of a fat16 table. if((maxClusters * 2) % pIoman->BlkSize) { fat16Size++; } fat16Size *= 2; // A real number of sectors to be available is therefore ~~ ul16DataSectors = partitionGeom.ulLength - fat16Size; ul32DataSectors = partitionGeom.ulLength - fat32Size; f16MaxClusters = ul16DataSectors / sectorsPerCluster; f32MaxClusters = ul32DataSectors / sectorsPerCluster; newFat16Size = (f16MaxClusters * 2) / pIoman->BlkSize; if((f16MaxClusters * 2) % pIoman->BlkSize) { newFat16Size++; } newFat32Size = (f32MaxClusters * 4) / pIoman->BlkSize; if((f32MaxClusters * 4) % pIoman->BlkSize) { newFat32Size++; } // Now determine if this should be fat16/32 format? if(f16MaxClusters < 65525) { fatSize = 16; finalFatSize = newFat16Size; } else { fatSize = 32; finalFatSize = newFat32Size; } FF_ReleaseBuffer(pIoman, pBuffer); for(i = 0; i < finalFatSize*2; i++) { // Ensure the FAT table is clear. if(i == 0) { pBuffer = FF_GetBuffer(pIoman, partitionGeom.ulStartLBA, FF_MODE_WR_ONLY); if(!pBuffer) { return FF_ERR_DEVICE_DRIVER_FAILED; } memset(pBuffer->pBuffer, 0, pIoman->BlkSize); } else { FF_BlockWrite(pIoman, partitionGeom.ulStartLBA+i, 1, pBuffer->pBuffer, FF_FALSE); } } switch(fatSize) { case 16: { FF_putShort(pBuffer->pBuffer, 0, 0xFFF8); // First FAT entry. FF_putShort(pBuffer->pBuffer, 2, 0xFFFF); // RESERVED alloc. break; } case 32: { FF_putLong(pBuffer->pBuffer, 0, 0x0FFFFFF8); // FAT32 FAT sig. FF_putLong(pBuffer->pBuffer, 4, 0xFFFFFFFF); // RESERVED alloc. FF_putLong(pBuffer->pBuffer, 8, 0x0FFFFFFF); // Root dir allocation. break; } default: break; } FF_ReleaseBuffer(pIoman, pBuffer); // Clear and initialise the root dir. ulClusterBeginLBA = partitionGeom.ulStartLBA + (finalFatSize*2); for(i = 0; i < sectorsPerCluster; i++) { if(i == 0) { pBuffer = FF_GetBuffer(pIoman, ulClusterBeginLBA, FF_MODE_WR_ONLY); memset(pBuffer->pBuffer, 0, pIoman->BlkSize); } else { FF_BlockWrite(pIoman, ulClusterBeginLBA+i, 1, pBuffer->pBuffer, FF_FALSE); } } FF_ReleaseBuffer(pIoman, pBuffer); // Correctly modify the second FAT item again. pBuffer = FF_GetBuffer(pIoman, partitionGeom.ulStartLBA + finalFatSize, FF_MODE_WRITE); { switch(fatSize) { case 16: { FF_putShort(pBuffer->pBuffer, 0, 0xFFF8); FF_putShort(pBuffer->pBuffer, 2, 0xFFFF); break; } case 32: FF_putLong(pBuffer->pBuffer, 0, 0x0FFFFFF8); FF_putLong(pBuffer->pBuffer, 4, 0xFFFFFFFF); FF_putLong(pBuffer->pBuffer, 8, 0x0FFFFFFF); // Root dir allocation. } } FF_ReleaseBuffer(pIoman, pBuffer); // Modify the fields in the VBR/PBR for correct mounting. pBuffer = FF_GetBuffer(pIoman, ulBPRLba, FF_MODE_WRITE); // Modify the FAT descriptions. { // -- First section Common vars to Fat12/16 and 32. memset(pBuffer->pBuffer, 0, pIoman->BlkSize); // Clear the boot record. FF_putChar(pBuffer->pBuffer, 0, 0xEB); // Place the Jump to bootstrap x86 instruction. FF_putChar(pBuffer->pBuffer, 1, 0x3C); // Even though we won't populate the bootstrap code. FF_putChar(pBuffer->pBuffer, 2, 0x90); // Some devices look for this as a signature. memcpy(((FF_T_UINT8 *)pBuffer->pBuffer+3), "FULLFAT2", 8); // Place the FullFAT OEM code. FF_putShort(pBuffer->pBuffer, 11, pIoman->BlkSize); FF_putChar(pBuffer->pBuffer, 13, (FF_T_UINT8) sectorsPerCluster); FF_putShort(pBuffer->pBuffer, FF_FAT_RESERVED_SECTORS, (FF_T_UINT16)partitionGeom.ulStartLBA); // Number of reserved sectors. (1 for fat12/16, 32 for f32). FF_putShort(pBuffer->pBuffer, FF_FAT_NUMBER_OF_FATS, 2); // Always 2 copies. //FF_putShort(pBuffer->pBuffer, 19, 0); // Number of sectors in partition if size < 32mb. FF_putChar(pBuffer->pBuffer, 21, 0xF8); // Media type -- HDD. FF_putShort(pBuffer->pBuffer, 510, 0xAA55); // MBR sig. FF_putLong(pBuffer->pBuffer, 32, partitionGeom.ulLength+partitionGeom.ulStartLBA); // Total sectors of this partition. if(fatSize == 32) { FF_putShort(pBuffer->pBuffer, 36, (FF_T_UINT16)finalFatSize); // Number of sectors per fat. FF_putShort(pBuffer->pBuffer, 44, 2); // Root dir cluster (2). FF_putShort(pBuffer->pBuffer, 48, 1); // FSINFO sector at LBA1. FF_putShort(pBuffer->pBuffer, 50, 6); // 0 for no backup boot sector. FF_putChar(pBuffer->pBuffer, 66, 0x29); // Indicate extended signature is present. memcpy(((FF_T_UINT8 *)pBuffer->pBuffer+71), "FullFAT2-V", 10); // Volume name. memcpy(((FF_T_UINT8 *)pBuffer->pBuffer+81), "FAT32 ", 8); // Put backup boot sector. FF_BlockWrite(pIoman, 6, 1, pBuffer->pBuffer, FF_FALSE); } else { FF_putChar(pBuffer->pBuffer, 38, 0x28); // Signal this contains an extended signature. memcpy(((FF_T_UINT8 *)pBuffer->pBuffer+43), "FullFAT2-V", 10); // Volume name. memcpy(((FF_T_UINT8 *)pBuffer->pBuffer+54), "FAT16 ", 8); FF_putShort(pBuffer->pBuffer, FF_FAT_16_SECTORS_PER_FAT, (FF_T_UINT16) finalFatSize); FF_putShort(pBuffer->pBuffer, 17, 512); // Number of Dir entries. (FAT32 0). //FF_putShort(pBuffer->pBuffer, FF_FAT_ROOT_ENTRY_COUNT, } } } FF_ReleaseBuffer(pIoman, pBuffer); if(fatSize == 32) { // Finally if FAT32, create an FSINFO sector. pBuffer = FF_GetBuffer(pIoman, 1, FF_MODE_WRITE); { memset(pBuffer->pBuffer, 0, pIoman->BlkSize); FF_putLong(pBuffer->pBuffer, 0, 0x41615252); // FSINFO sect magic number. FF_putLong(pBuffer->pBuffer, 484, 0x61417272); // FSINFO second sig. // Calculate total sectors, -1 for the root dir allocation. (Free sectors count). FF_putLong(pBuffer->pBuffer, 488, ((ulTotalSectors - (ulReservedSectors + (2*finalFatSize))) / sectorsPerCluster)-1); FF_putLong(pBuffer->pBuffer, 492, 2); // Hint for next free cluster. FF_putShort(pBuffer->pBuffer, 510, 0xAA55); } FF_ReleaseBuffer(pIoman, pBuffer); } FF_FlushCache(pIoman); return Error; }
/** * @public * @brief Writes data to a File. * * @param pFile FILE Pointer. * @param ElementSize Size of an Element of Data to be copied. (in bytes). * @param Count Number of Elements of Data to be copied. (ElementSize * Count must not exceed ((2^31)-1) bytes. (2GB). For best performance, multiples of 512 bytes or Cluster sizes are best. * @param buffer Byte-wise buffer containing the data to be written. * * @return **/ FF_T_SINT32 FF_Write(FF_FILE *pFile, FF_T_UINT32 ElementSize, FF_T_UINT32 Count, FF_T_UINT8 *buffer) { FF_T_UINT32 nBytes = ElementSize * Count; FF_T_UINT32 nBytesWritten = 0; FF_T_UINT32 nBytesToWrite; FF_IOMAN *pIoman; FF_BUFFER *pBuffer; FF_T_UINT32 nRelBlockPos; FF_T_UINT32 nItemLBA; FF_T_SINT32 slRetVal = 0; FF_T_UINT16 sSectors; FF_T_UINT32 nRelClusterPos; FF_T_UINT32 nBytesPerCluster, nClusterDiff, nClusters; FF_ERROR Error; if(!pFile) { return FF_ERR_NULL_POINTER; } if(!(pFile->Mode & FF_MODE_WRITE)) { return FF_ERR_FILE_NOT_OPENED_IN_WRITE_MODE; } // Make sure a write is after the append point. if((pFile->Mode & FF_MODE_APPEND)) { if(pFile->FilePointer < pFile->Filesize) { FF_Seek(pFile, 0, FF_SEEK_END); } } pIoman = pFile->pIoman; nBytesPerCluster = (pIoman->pPartition->SectorsPerCluster * pIoman->BlkSize); // Extend File for atleast nBytes! // Handle file-space allocation Error = FF_ExtendFile(pFile, pFile->FilePointer + nBytes); if(Error) { return Error; } nRelBlockPos = FF_getMinorBlockEntry(pIoman, pFile->FilePointer, 1); // Get the position within a block. nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster; if(nClusterDiff) { if(pFile->CurrentCluster != FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) { pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error); if(Error) { return Error; } pFile->CurrentCluster += nClusterDiff; } } nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster); nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1); if((nRelBlockPos + nBytes) < pIoman->BlkSize) { // Bytes to read are within a block and less than a block size. pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_WRITE); { if(!pBuffer) { return FF_ERR_DEVICE_DRIVER_FAILED; } memcpy((pBuffer->pBuffer + nRelBlockPos), buffer, nBytes); } FF_ReleaseBuffer(pIoman, pBuffer); pFile->FilePointer += nBytes; nBytesWritten = nBytes; //return nBytes; // Return the number of bytes read. } else { //---------- Write (memcpy) to a Sector Boundary if(nRelBlockPos != 0) { // Not on a sector boundary, at this point the LBA is known. nBytesToWrite = pIoman->BlkSize - nRelBlockPos; pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_WRITE); { if(!pBuffer) { return FF_ERR_DEVICE_DRIVER_FAILED; } // Here we copy to the sector boudary. memcpy((pBuffer->pBuffer + nRelBlockPos), buffer, nBytesToWrite); } FF_ReleaseBuffer(pIoman, pBuffer); nBytes -= nBytesToWrite; nBytesWritten += nBytesToWrite; pFile->FilePointer += nBytesToWrite; buffer += nBytesToWrite; } //---------- Write to a Cluster Boundary nRelClusterPos = FF_getClusterPosition(pIoman, pFile->FilePointer, 1); if(nRelClusterPos != 0 && nBytes >= nBytesPerCluster) { // Need to get to cluster boundary nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster; if(nClusterDiff) { if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) { pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error); if(Error) { return Error; } pFile->CurrentCluster += nClusterDiff; } } nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster); nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1); sSectors = (FF_T_UINT16) (pIoman->pPartition->SectorsPerCluster - (nRelClusterPos / pIoman->BlkSize)); slRetVal = FF_BlockWrite(pFile->pIoman, nItemLBA, sSectors, buffer); if(slRetVal < 0) { return slRetVal; } nBytesToWrite = sSectors * pIoman->BlkSize; nBytes -= nBytesToWrite; buffer += nBytesToWrite; nBytesWritten += nBytesToWrite; pFile->FilePointer += nBytesToWrite; } //---------- Write Clusters if(nBytes >= nBytesPerCluster) { //----- Thanks to Christopher Clark of DigiPen Institute of Technology in Redmond, US adding this traversal check. nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster; if(nClusterDiff) { if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) { pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error); if(Error) { return Error; } pFile->CurrentCluster += nClusterDiff; } } //----- End of Contributor fix. nClusters = (nBytes / nBytesPerCluster); slRetVal = FF_WriteClusters(pFile, nClusters, buffer); if(slRetVal < 0) { return slRetVal; } nBytesToWrite = (nBytesPerCluster * nClusters); pFile->FilePointer += nBytesToWrite; nBytes -= nBytesToWrite; buffer += nBytesToWrite; nBytesWritten += nBytesToWrite; } //---------- Write Remaining Blocks if(nBytes >= pIoman->BlkSize) { sSectors = (FF_T_UINT16) (nBytes / pIoman->BlkSize); nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster; if(nClusterDiff) { if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) { pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error); if(Error) { return Error; } pFile->CurrentCluster += nClusterDiff; } } nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster); nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1); slRetVal = FF_BlockWrite(pFile->pIoman, nItemLBA, sSectors, buffer); if(slRetVal < 0) { return slRetVal; } nBytesToWrite = sSectors * pIoman->BlkSize; pFile->FilePointer += nBytesToWrite; nBytes -= nBytesToWrite; buffer += nBytesToWrite; nBytesWritten += nBytesToWrite; } //---------- Write (memcpy) Remaining Bytes if(nBytes > 0) { nClusterDiff = FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1) - pFile->CurrentCluster; if(nClusterDiff) { if(pFile->CurrentCluster < FF_getClusterChainNumber(pFile->pIoman, pFile->FilePointer, 1)) { pFile->AddrCurrentCluster = FF_TraverseFAT(pIoman, pFile->AddrCurrentCluster, nClusterDiff, &Error); if(Error) { return Error; } pFile->CurrentCluster += nClusterDiff; } } nItemLBA = FF_Cluster2LBA(pIoman, pFile->AddrCurrentCluster); nItemLBA = FF_getRealLBA(pIoman, nItemLBA + FF_getMajorBlockNumber(pIoman, pFile->FilePointer, 1)) + FF_getMinorBlockNumber(pIoman, pFile->FilePointer, 1); pBuffer = FF_GetBuffer(pIoman, nItemLBA, FF_MODE_WRITE); { if(!pBuffer) { return FF_ERR_DEVICE_DRIVER_FAILED; } memcpy(pBuffer->pBuffer, buffer, nBytes); } FF_ReleaseBuffer(pIoman, pBuffer); nBytesToWrite = nBytes; pFile->FilePointer += nBytesToWrite; nBytes -= nBytesToWrite; buffer += nBytesToWrite; nBytesWritten += nBytesToWrite; } } if(pFile->FilePointer > pFile->Filesize) { pFile->Filesize = pFile->FilePointer; } return nBytesWritten; }