uint16_t DataloggerFile_WriteAtomic(DataloggerFile *dlgFile, uint8_t *data, uint16_t dataLen) { // Check if there is enough free space in the buffer for an atomic write if (dataLen > dlgFile->bufferFree) { return 0; } if (dlgFile->requestClose) { return 0; } // If the buffer is clear and the file is ready, write directly to the file if (dlgFile->bufferFree == dlgFile->bufferSize && dlgFile->file->state != FILE_Uninitialized && dlgFile->file->state != FILE_Creating) { fs_length_t writeLength = FS_WriteFile(dlgFile->file, data, dataLen); data += writeLength; dataLen -= writeLength; DBG_SPAM_printf("DLGFile: write->card %u bytes", writeLength); } while (dataLen > 0) { fs_length_t writeLength; // Determine maximum contigious write length if (dlgFile->readPos > dlgFile->writePos) { writeLength = dlgFile->bufferFree; } else { writeLength = dlgFile->bufferSize - dlgFile->writePos; } // Cap write length at data length if (dataLen < writeLength) { writeLength = dataLen; } // Write data and update buffer variables memcpy(dlgFile->buffer + dlgFile->writePos, data, writeLength); dlgFile->writePos += writeLength; if (dlgFile->writePos >= dlgFile->bufferSize) { dlgFile->writePos = 0; } dlgFile->bufferFree -= writeLength; // Update remaining data dataLen -= writeLength; data += writeLength; DBG_SPAM_printf("DLGFile: write->buffer %u bytes, bufFree = %u", writeLength, dlgFile->bufferFree); } return 1; }
fs_result_t DataloggerFile_Tasks(DataloggerFile *dlgFile) { // Check if there is data to write if (dlgFile->file->state != FILE_Uninitialized && dlgFile->file->state != FILE_Creating) { while (dlgFile->bufferFree != dlgFile->bufferSize) { fs_length_t writeLength; if (dlgFile->writePos > dlgFile->readPos) { writeLength = dlgFile->writePos - dlgFile->readPos; } else { writeLength = dlgFile->bufferSize - dlgFile->readPos; } writeLength = FS_WriteFile(dlgFile->file, dlgFile->buffer + dlgFile->readPos, writeLength); if (writeLength > 0) { dlgFile->readPos += writeLength; if (dlgFile->readPos >= dlgFile->bufferSize) { dlgFile->readPos = 0; } dlgFile->bufferFree += writeLength; DBG_SPAM_printf("DLGFile: buffer->card %u bytes, bufFree = %u", writeLength, dlgFile->bufferFree); } else { // No data written (no filesystem file buffer left), we're done break; } } } // Check if we want to close the file if (dlgFile->requestClose && !dlgFile->file->requestClose && dlgFile->bufferFree == dlgFile->bufferSize) { DBG_DATA_printf("DLGFile: Request file close"); FS_RequestFileClose(dlgFile->file); } if (T1CON == 0x00) { DBG_ERR_printf("T1CON = 0"); T1CON = 0x8002; } // Do filesystem tasks if (dlgFile->file->state != FILE_Uninitialized && dlgFile->file->state != FILE_Creating) { return FS_FileTasks(dlgFile->file); } else { return FS_UNREADY; } }
void FAT32_Tasks(FAT32FileOpt *file) { if (file->currentOperation == FILE_OP_None) { // Check if there is a queued command - and if so, start it if (file->nextOperation == FILE_OP_WritingDataIdle) { SD_DMA_MBW_Start(file->fs->card, file->currentLBA); file->currentOperation = FILE_OP_WritingDataIdle; DBG_SPAM_printf("file->currentOperation going to FILE_OP_WritingDataIdle (queued)"); } else if (file->nextOperation == FILE_OP_WritingDirectoryTable) { SD_DMA_MBW_Start(file->fs->card, file->directoryTableLBA); SD_DMA_MBW_SendBlock(2); file->currentOperation = FILE_OP_WritingDirectoryTable; DBG_SPAM_printf("file->currentOperation going to FILE_OP_WritingDirectoryTable (queued)"); } else if (file->nextOperation == FILE_OP_WritingFAT) { SD_DMA_MBW_Start(file->fs->card, file->currentFATLBA); SD_DMA_MBW_SendBlock(2); file->currentOperation = FILE_OP_WritingFAT; DBG_SPAM_printf("file->currentOperation going to FILE_OP_WritingFAT (queued)"); } else if (file->nextOperation == FILE_OP_WritingFSInformation) { SD_DMA_MBW_Start(file->fs->card, file->fs->FS_info_LBA); SD_DMA_MBW_SendBlock(2); file->currentOperation = FILE_OP_WritingFSInformation; DBG_SPAM_printf("file->currentOperation going to FILE_OP_WritingFSInformation (queued)"); } // Check if we passed the allocation boundaries if (file->currentCluster > file->currentClusterEnd) { // Check if more bookkeeping needs to be done if (!file->currentFATAllocated) { FAT32_AllocateFATBlock(file); FAT32_FillCurrentFATBlock(file); SD_DMA_MBW_Start(file->fs->card, file->currentFATLBA); SD_DMA_MBW_SendBlock(2); file->currentOperation = FILE_OP_WritingFAT; DBG_SPAM_printf("file->currentOperation going to FILE_OP_WritingFAT (end of allocation)"); } else if (file->currentFATDirty) { FAT32_FillCurrentFATBlock(file); SD_DMA_MBW_Start(file->fs->card, file->currentFATLBA); SD_DMA_MBW_SendBlock(2); file->currentOperation = FILE_OP_WritingFAT; DBG_SPAM_printf("file->currentOperation going to FILE_OP_WritingFAT (end of allocation)"); } else if (file->directoryTableDirty) { FAT32_FillDirectoryTableBlock(file); SD_DMA_MBW_Start(file->fs->card, file->directoryTableLBA); SD_DMA_MBW_SendBlock(2); file->currentOperation = FILE_OP_WritingDirectoryTable; DBG_SPAM_printf("file->currentOperation going to FILE_OP_WritingDirectoryTable (end of allocation)"); } else if (file->fs->fsInfoDirty) { FAT32_FillFSInformationSector(file); SD_DMA_MBW_Start(file->fs->card, file->fs->FS_info_LBA); SD_DMA_MBW_SendBlock(2); file->currentOperation = FILE_OP_WritingFSInformation; DBG_SPAM_printf("file->currentOperation going to FILE_OP_WritingFSInformation (end of allocation)"); } else { FAT32_SwitchNextBlock(file); DBG_DATA_printf("Switching to next block"); } } else { SD_DMA_MBW_Start(file->fs->card, file->currentLBA); file->currentOperation = FILE_OP_WritingDataIdle; } } else if (file->currentOperation == FILE_OP_WritingDataIdle) { // Check the current SD DMA state if (SD_DMA_MBW_GetIdle()) { // Check is we have passed the allocation boundaries if (file->currentCluster > file->currentClusterEnd) { SD_DMA_MBW_End(); file->currentOperation = FILE_OP_MultipleBlockWriteTerminate; file->nextOperation = FILE_OP_None; DBG_SPAM_printf("file->currentOperation going to FILE_OP_MultipleBlockWriteTerminate (allocation exceeded)"); } else { // Check is there is a new block ready to send if (file->dataBufferNumFilled > 0) { DBG_SPAM_printf("Writing data block at LBA=0x%08lx, cluster=0x%08lx, allocated end=0x%08lx, buffer=%u", file->currentLBA, file->currentCluster, file->currentClusterEnd, file->dataBufferWrite); // Send the new block SD_DMA_MBW_SendBlock(file->dataBufferWrite); file->currentOperation = FILE_OP_WritingData; } else { // No data available, check if we have bookkeeping to do // TODO Terminate block write mid-write and do bookkeeping } } } } else if (file->currentOperation == FILE_OP_WritingData) { uint8_t status = SD_DMA_MBW_GetBlockStatus(); if (status != 0x00) { // Current block is done, advance the data buffers DBG_SPAM_printf("SD_DMA_MBW_GetBlockStatus returned 0x%02x", status); file->dataBufferWrite++; if (file->dataBufferWrite >= FAT32_NUM_DATA_BUFFERS) { file->dataBufferWrite = 0; } file->dataBufferNumFilled--; file->currentLBA++; file->currentClusterLBAOffset++; if (file->currentClusterLBAOffset >= file->fs->sectorsPerCluster) { file->currentCluster++; file->currentClusterLBAOffset = 0; } file->position += file->fs->bytesPerSector; file->currentOperation = FILE_OP_WritingDataIdle; } } else if (file->currentOperation == FILE_OP_WritingDirectoryTable || file->currentOperation == FILE_OP_WritingFAT || file->currentOperation == FILE_OP_WritingFSInformation) { uint8_t status = SD_DMA_MBW_GetBlockStatus(); if (status != 0x00) { // Block complete, terminate command SD_DMA_MBW_End(); if (file->currentOperation == FILE_OP_WritingDirectoryTable) { file->directoryTableDirty = 0; } else if (file->currentOperation == FILE_OP_WritingFAT) { file->currentFATDirty = 0; } else if (file->currentOperation == FILE_OP_WritingFSInformation) { file->fs->fsInfoDirty = 0; } file->currentOperation = FILE_OP_MultipleBlockWriteTerminate; file->nextOperation = FILE_OP_None; DBG_SPAM_printf("file->currentOperation going to FILE_OP_MultipleBlockWriteTerminate (operation complete)"); } else { DBG_SPAM_printf("SD_DMA_MBW_GetBlockStatus returned 0x%02x", status); } } else if (file->currentOperation == FILE_OP_MultipleBlockWriteTerminate) { uint8_t status = SD_DMA_MBW_GetBlockStatus(); if (status != 0x00) { file->currentOperation = FILE_OP_None; DBG_SPAM_printf("file->currentOperation going to FILE_OP_None"); } else { DBG_SPAM_printf("SD_DMA_MBW_GetBlockStatus returned 0x%02x", status); } } }
/** * Creates a file (creates the directory entry for a file) for optimized write. * After this function returns successfully, it should be possible to do optimized * writes on the file starting at the beginning. * No checks are done against duplicate file name, and no clusters are allocated. * File space is allocated during write operations. * * @param[in] fs Filesystem structure. * @param[in] dir Directory structure. * @param[out] fileOpt Output optimized file structure. * @param[in] name File name, up to 8 characters. * @param[in] ext File extension, up to 3 characters. * @retval 1 Success * @retval -1 Error creating file * @retval -2 SD Card Error * @retval -128 General error */ int8_t FAT32_CreateFileOpt(FAT32FS *fs, FAT32Directory *dir, FAT32FileOpt *file, char *name, char *ext) { uint32_t currentCluster = dir->directoryTableAvailableCluster; fs_addr_t currentLBA = dir->directoryTableAvailableLBA; uint8_t clusterOffset = dir->directoryTableAvailableClusterOffset; // block number within the cluster fs_length_t iii = 0; DBG_DATA_printf("Starting cluster = 0x%08lx", currentCluster); // Search for an empty file while (1) { if (BLOCK_SIZE != SD_SPI_ReadSingleBlock(fs->card, currentLBA, file->directoryTableBlockData)) { DBG_printf("Error creating file (SD Read error on LBA = 0x%08lx).", currentLBA); return -2; // Sanity check: Not reading the correct number of bytes } DBG_DATA_printf("Read Directory Table LBA = 0x%08lx", currentLBA) // Search Directory Table for desired entry for (iii = 0; iii < fs->bytesPerSector; iii += 32) { DBG_SPAM_printf("Searching record '%8.8s.%3.3s'.", file->directoryTableBlockData + iii, file->directoryTableBlockData + iii + 8); if (file->directoryTableBlockData[iii] == 0x00 || file->directoryTableBlockData[iii] == 0xe5) { // Available entry DBG_printf("Available entry found."); // Update directory structure with new end dir->directoryTableAvailableCluster = currentCluster; dir->directoryTableAvailableClusterOffset = clusterOffset; dir->directoryTableAvailableLBA = currentLBA; FATCreateDirectoryTableEntry(file->directoryTableBlockData+iii, name, ext); // Fill out file structure strncpy((char *) file->name, (char*)file->directoryTableBlockData+iii, 8); strncpy((char *) file->ext, (char*)file->directoryTableBlockData+iii+8, 3); file->fs = fs; file->directoryTableLBA = currentLBA; file->directoryTableBlockOffset = iii; file->directoryTableDirty = 0; file->startCluster = 0; file->size = 0; file->previousFATBlockData = file->previousFATData; file->currentFATBlockData = file->currentFATData; file->previousFATBlockOffset = 0; file->previousFATDirty = 0; file->previousFileSize = 0; file->currentCluster = 0; file->currentLBA = 0; file->position = 0; file->currentOperation = FILE_OP_None; file->nextOperation = FILE_OP_WritingDataIdle; file->dataBuffer[0] = SD_DMA_GetBuffer(0); if (file->dataBuffer[0] == NULL) { DBG_ERR_printf("Error allocating DMA buffer 0"); return -128; } file->dataBuffer[1] = SD_DMA_GetBuffer(1); if (file->dataBuffer[1] == NULL) { DBG_ERR_printf("Error allocating DMA buffer 1"); return -128; } file->fsBuffer = SD_DMA_GetBuffer(2); if (file->fs == NULL) { DBG_ERR_printf("Error allocating DMA FS buffer"); return -128; } file->dataBufferWrite = 0; file->dataBufferFill = 0; file->dataBufferNumFilled = 0; file->dataBufferPos = 0; file->overflowBufferBegin = 0; file->overflowBufferEnd = 0; file->overflowBufferSize = 0; FAT32_InitializeFileFAT(file); SD_SPI_WriteSingleBlock(fs->card, file->directoryTableLBA, file->directoryTableBlockData); file->directoryTableDirty = 0; SD_SPI_WriteSingleBlock(fs->card, file->currentFATLBA, file->currentFATBlockData); file->currentFATAllocated = 0; FAT32_FillFSInformationSector(file); SD_SPI_WriteSingleBlock(fs->card, fs->FS_info_LBA, file->fsBuffer); file->fs->fsInfoDirty = 0; return 1; } } // Advance to next block clusterOffset++; if (clusterOffset >= fs->sectorsPerCluster) { // End of cluster currentCluster = getNextCluster(fs, currentCluster); DBG_DATA_printf("Next cluster = 0x%08lx", currentCluster); // Sanity check: ensure Directory Table cluster is valid if (currentCluster >= 0xF0000000) { DBG_printf("Error creating file (searched past end of Directory Table Cluster)."); return -1; } currentLBA = GetClusterLBA(fs, currentCluster); clusterOffset = 0; } else { // Advance to next block within cluster currentLBA++; } } }