Fix missing 'platform.h' includes in compilation units, and make them stay away.
[betaflight.git] / src / main / io / flashfs.c
blobdbc2abe6bf67fe10adcb51f47924a36a27617ab5
1 /*
2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
8 * any later version.
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 /**
22 * This provides a stream interface to a flash chip if one is present.
24 * On statup, call flashfsInit() after initialising the flash chip in order to init the filesystem. This will
25 * result in the file pointer being pointed at the first free block found, or at the end of the device if the
26 * flash chip is full.
28 * Note that bits can only be set to 0 when writing, not back to 1 from 0. You must erase sectors in order
29 * to bring bits back to 1 again.
31 * In future, we can add support for multiple different flash chips by adding a flash device driver vtable
32 * and make calls through that, at the moment flashfs just calls m25p16_* routines explicitly.
35 #include <stdint.h>
36 #include <stdbool.h>
37 #include <string.h>
39 #include "platform.h"
41 #include "drivers/flash.h"
43 #include "io/flashfs.h"
45 static uint8_t flashWriteBuffer[FLASHFS_WRITE_BUFFER_SIZE];
47 /* The position of our head and tail in the circular flash write buffer.
49 * The head is the index that a byte would be inserted into on writing, while the tail is the index of the
50 * oldest byte that has yet to be written to flash.
52 * When the circular buffer is empty, head == tail
54 static uint8_t bufferHead = 0, bufferTail = 0;
56 // The position of the buffer's tail in the overall flash address space:
57 static uint32_t tailAddress = 0;
59 static void flashfsClearBuffer(void)
61 bufferTail = bufferHead = 0;
64 static bool flashfsBufferIsEmpty(void)
66 return bufferTail == bufferHead;
69 static void flashfsSetTailAddress(uint32_t address)
71 tailAddress = address;
74 void flashfsEraseCompletely(void)
76 flashEraseCompletely();
78 flashfsClearBuffer();
80 flashfsSetTailAddress(0);
83 /**
84 * Start and end must lie on sector boundaries, or they will be rounded out to sector boundaries such that
85 * all the bytes in the range [start...end) are erased.
87 void flashfsEraseRange(uint32_t start, uint32_t end)
89 const flashGeometry_t *geometry = flashGetGeometry();
91 if (geometry->sectorSize <= 0)
92 return;
94 // Round the start down to a sector boundary
95 int startSector = start / geometry->sectorSize;
97 // And the end upward
98 int endSector = end / geometry->sectorSize;
99 int endRemainder = end % geometry->sectorSize;
101 if (endRemainder > 0) {
102 endSector++;
105 for (int i = startSector; i < endSector; i++) {
106 flashEraseSector(i * geometry->sectorSize);
111 * Return true if the flash is not currently occupied with an operation.
113 bool flashfsIsReady(void)
115 // Check for flash chip existence first, then check if ready.
117 return (flashfsIsSupported() && flashIsReady());
120 bool flashfsIsSupported(void)
122 return flashfsGetSize() > 0;
125 uint32_t flashfsGetSize(void)
127 return flashGetGeometry()->totalSize;
130 static uint32_t flashfsTransmitBufferUsed(void)
132 if (bufferHead >= bufferTail)
133 return bufferHead - bufferTail;
135 return FLASHFS_WRITE_BUFFER_SIZE - bufferTail + bufferHead;
139 * Get the size of the largest single write that flashfs could ever accept without blocking or data loss.
141 uint32_t flashfsGetWriteBufferSize(void)
143 return FLASHFS_WRITE_BUFFER_USABLE;
147 * Get the number of bytes that can currently be written to flashfs without any blocking or data loss.
149 uint32_t flashfsGetWriteBufferFreeSpace(void)
151 return flashfsGetWriteBufferSize() - flashfsTransmitBufferUsed();
154 const flashGeometry_t* flashfsGetGeometry(void)
156 return flashGetGeometry();
160 * Write the given buffers to flash sequentially at the current tail address, advancing the tail address after
161 * each write.
163 * In synchronous mode, waits for the flash to become ready before writing so that every byte requested can be written.
165 * In asynchronous mode, if the flash is busy, then the write is aborted and the routine returns immediately.
166 * In this case the returned number of bytes written will be less than the total amount requested.
168 * Modifies the supplied buffer pointers and sizes to reflect how many bytes remain in each of them.
170 * bufferCount: the number of buffers provided
171 * buffers: an array of pointers to the beginning of buffers
172 * bufferSizes: an array of the sizes of those buffers
173 * sync: true if we should wait for the device to be idle before writes, otherwise if the device is busy the
174 * write will be aborted and this routine will return immediately.
176 * Returns the number of bytes written
178 static uint32_t flashfsWriteBuffers(uint8_t const **buffers, uint32_t *bufferSizes, int bufferCount, bool sync)
180 uint32_t bytesTotal = 0;
182 int i;
184 for (i = 0; i < bufferCount; i++) {
185 bytesTotal += bufferSizes[i];
188 if (!sync && !flashIsReady()) {
189 return 0;
192 uint32_t bytesTotalRemaining = bytesTotal;
194 uint16_t pageSize = flashfsGetGeometry()->pageSize;
196 while (bytesTotalRemaining > 0) {
197 uint32_t bytesTotalThisIteration;
198 uint32_t bytesRemainThisIteration;
201 * Each page needs to be saved in a separate program operation, so
202 * if we would cross a page boundary, only write up to the boundary in this iteration:
204 if (tailAddress % pageSize + bytesTotalRemaining > pageSize) {
205 bytesTotalThisIteration = pageSize - tailAddress % pageSize;
206 } else {
207 bytesTotalThisIteration = bytesTotalRemaining;
210 // Are we at EOF already? Abort.
211 if (flashfsIsEOF()) {
212 // May as well throw away any buffered data
213 flashfsClearBuffer();
215 break;
218 flashPageProgramBegin(tailAddress);
220 bytesRemainThisIteration = bytesTotalThisIteration;
222 for (i = 0; i < bufferCount; i++) {
223 if (bufferSizes[i] > 0) {
224 // Is buffer larger than our write limit? Write our limit out of it
225 if (bufferSizes[i] >= bytesRemainThisIteration) {
226 flashPageProgramContinue(buffers[i], bytesRemainThisIteration);
228 buffers[i] += bytesRemainThisIteration;
229 bufferSizes[i] -= bytesRemainThisIteration;
231 bytesRemainThisIteration = 0;
232 break;
233 } else {
234 // We'll still have more to write after finishing this buffer off
235 flashPageProgramContinue(buffers[i], bufferSizes[i]);
237 bytesRemainThisIteration -= bufferSizes[i];
239 buffers[i] += bufferSizes[i];
240 bufferSizes[i] = 0;
245 flashPageProgramFinish();
247 bytesTotalRemaining -= bytesTotalThisIteration;
249 // Advance the cursor in the file system to match the bytes we wrote
250 flashfsSetTailAddress(tailAddress + bytesTotalThisIteration);
253 * We'll have to wait for that write to complete before we can issue the next one, so if
254 * the user requested asynchronous writes, break now.
256 if (!sync)
257 break;
260 return bytesTotal - bytesTotalRemaining;
264 * Since the buffered data might wrap around the end of the circular buffer, we can have two segments of data to write,
265 * an initial portion and a possible wrapped portion.
267 * This routine will fill the details of those buffers into the provided arrays, which must be at least 2 elements long.
269 static void flashfsGetDirtyDataBuffers(uint8_t const *buffers[], uint32_t bufferSizes[])
271 buffers[0] = flashWriteBuffer + bufferTail;
272 buffers[1] = flashWriteBuffer + 0;
274 if (bufferHead >= bufferTail) {
275 bufferSizes[0] = bufferHead - bufferTail;
276 bufferSizes[1] = 0;
277 } else {
278 bufferSizes[0] = FLASHFS_WRITE_BUFFER_SIZE - bufferTail;
279 bufferSizes[1] = bufferHead;
284 * Get the current offset of the file pointer within the volume.
286 uint32_t flashfsGetOffset(void)
288 uint8_t const * buffers[2];
289 uint32_t bufferSizes[2];
291 // Dirty data in the buffers contributes to the offset
293 flashfsGetDirtyDataBuffers(buffers, bufferSizes);
295 return tailAddress + bufferSizes[0] + bufferSizes[1];
299 * Called after bytes have been written from the buffer to advance the position of the tail by the given amount.
301 static void flashfsAdvanceTailInBuffer(uint32_t delta)
303 bufferTail += delta;
305 // Wrap tail around the end of the buffer
306 if (bufferTail >= FLASHFS_WRITE_BUFFER_SIZE) {
307 bufferTail -= FLASHFS_WRITE_BUFFER_SIZE;
310 if (flashfsBufferIsEmpty()) {
311 flashfsClearBuffer(); // Bring buffer pointers back to the start to be tidier
316 * If the flash is ready to accept writes, flush the buffer to it.
318 * Returns true if all data in the buffer has been flushed to the device, or false if
319 * there is still data to be written (call flush again later).
321 bool flashfsFlushAsync(void)
323 if (flashfsBufferIsEmpty()) {
324 return true; // Nothing to flush
327 uint8_t const * buffers[2];
328 uint32_t bufferSizes[2];
329 uint32_t bytesWritten;
331 flashfsGetDirtyDataBuffers(buffers, bufferSizes);
332 bytesWritten = flashfsWriteBuffers(buffers, bufferSizes, 2, false);
333 flashfsAdvanceTailInBuffer(bytesWritten);
335 return flashfsBufferIsEmpty();
339 * Wait for the flash to become ready and begin flushing any buffered data to flash.
341 * The flash will still be busy some time after this sync completes, but space will
342 * be freed up to accept more writes in the write buffer.
344 void flashfsFlushSync(void)
346 if (flashfsBufferIsEmpty()) {
347 return; // Nothing to flush
350 uint8_t const * buffers[2];
351 uint32_t bufferSizes[2];
353 flashfsGetDirtyDataBuffers(buffers, bufferSizes);
354 flashfsWriteBuffers(buffers, bufferSizes, 2, true);
356 // We've written our entire buffer now:
357 flashfsClearBuffer();
360 void flashfsSeekAbs(uint32_t offset)
362 flashfsFlushSync();
364 flashfsSetTailAddress(offset);
367 void flashfsSeekRel(int32_t offset)
369 flashfsFlushSync();
371 flashfsSetTailAddress(tailAddress + offset);
375 * Write the given byte asynchronously to the flash. If the buffer overflows, data is silently discarded.
377 void flashfsWriteByte(uint8_t byte)
379 flashWriteBuffer[bufferHead++] = byte;
381 if (bufferHead >= FLASHFS_WRITE_BUFFER_SIZE) {
382 bufferHead = 0;
385 if (flashfsTransmitBufferUsed() >= FLASHFS_WRITE_BUFFER_AUTO_FLUSH_LEN) {
386 flashfsFlushAsync();
391 * Write the given buffer to the flash either synchronously or asynchronously depending on the 'sync' parameter.
393 * If writing asynchronously, data will be silently discarded if the buffer overflows.
394 * If writing synchronously, the routine will block waiting for the flash to become ready so will never drop data.
396 void flashfsWrite(const uint8_t *data, unsigned int len, bool sync)
398 uint8_t const * buffers[3];
399 uint32_t bufferSizes[3];
401 // There could be two dirty buffers to write out already:
402 flashfsGetDirtyDataBuffers(buffers, bufferSizes);
404 // Plus the buffer the user supplied:
405 buffers[2] = data;
406 bufferSizes[2] = len;
409 * Would writing this data to our buffer cause our buffer to reach the flush threshold? If so try to write through
410 * to the flash now
412 if (bufferSizes[0] + bufferSizes[1] + bufferSizes[2] >= FLASHFS_WRITE_BUFFER_AUTO_FLUSH_LEN) {
413 uint32_t bytesWritten;
415 // Attempt to write all three buffers through to the flash asynchronously
416 bytesWritten = flashfsWriteBuffers(buffers, bufferSizes, 3, false);
418 if (bufferSizes[0] == 0 && bufferSizes[1] == 0) {
419 // We wrote all the data that was previously buffered
420 flashfsClearBuffer();
422 if (bufferSizes[2] == 0) {
423 // And we wrote all the data the user supplied! Job done!
424 return;
426 } else {
427 // We only wrote a portion of the old data, so advance the tail to remove the bytes we did write from the buffer
428 flashfsAdvanceTailInBuffer(bytesWritten);
431 // Is the remainder of the data to be written too big to fit in the buffers?
432 if (bufferSizes[0] + bufferSizes[1] + bufferSizes[2] > FLASHFS_WRITE_BUFFER_USABLE) {
433 if (sync) {
434 // Write it through synchronously
435 flashfsWriteBuffers(buffers, bufferSizes, 3, true);
436 flashfsClearBuffer();
437 } else {
439 * Silently drop the data the user asked to write (i.e. no-op) since we can't buffer it and they
440 * requested async.
444 return;
447 // Fall through and add the remainder of the incoming data to our buffer
448 data = buffers[2];
449 len = bufferSizes[2];
452 // Buffer up the data the user supplied instead of writing it right away
454 // First write the portion before we wrap around the end of the circular buffer
455 unsigned int bufferBytesBeforeWrap = FLASHFS_WRITE_BUFFER_SIZE - bufferHead;
457 unsigned int firstPortion = len < bufferBytesBeforeWrap ? len : bufferBytesBeforeWrap;
459 memcpy(flashWriteBuffer + bufferHead, data, firstPortion);
461 bufferHead += firstPortion;
463 data += firstPortion;
464 len -= firstPortion;
466 // If we wrap the head around, write the remainder to the start of the buffer (if any)
467 if (bufferHead == FLASHFS_WRITE_BUFFER_SIZE) {
468 memcpy(flashWriteBuffer + 0, data, len);
470 bufferHead = len;
475 * Read `len` bytes from the given address into the supplied buffer.
477 * Returns the number of bytes actually read which may be less than that requested.
479 int flashfsReadAbs(uint32_t address, uint8_t *buffer, unsigned int len)
481 int bytesRead;
483 // Did caller try to read past the end of the volume?
484 if (address + len > flashfsGetSize()) {
485 // Truncate their request
486 len = flashfsGetSize() - address;
489 // Since the read could overlap data in our dirty buffers, force a sync to clear those first
490 flashfsFlushSync();
492 bytesRead = flashReadBytes(address, buffer, len);
494 return bytesRead;
498 * Find the offset of the start of the free space on the device (or the size of the device if it is full).
500 int flashfsIdentifyStartOfFreeSpace(void)
502 /* Find the start of the free space on the device by examining the beginning of blocks with a binary search,
503 * looking for ones that appear to be erased. We can achieve this with good accuracy because an erased block
504 * is all bits set to 1, which pretty much never appears in reasonable size substrings of blackbox logs.
506 * To do better we might write a volume header instead, which would mark how much free space remains. But keeping
507 * a header up to date while logging would incur more writes to the flash, which would consume precious write
508 * bandwidth and block more often.
511 enum {
512 /* We can choose whatever power of 2 size we like, which determines how much wastage of free space we'll have
513 * at the end of the last written data. But smaller blocksizes will require more searching.
515 FREE_BLOCK_SIZE = 2048, // XXX This can't be smaller than page size for underlying flash device.
517 /* We don't expect valid data to ever contain this many consecutive uint32_t's of all 1 bits: */
518 FREE_BLOCK_TEST_SIZE_INTS = 4, // i.e. 16 bytes
519 FREE_BLOCK_TEST_SIZE_BYTES = FREE_BLOCK_TEST_SIZE_INTS * sizeof(uint32_t)
522 STATIC_ASSERT(FREE_BLOCK_SIZE >= FLASH_MAX_PAGE_SIZE, FREE_BLOCK_SIZE_too_small);
524 union {
525 uint8_t bytes[FREE_BLOCK_TEST_SIZE_BYTES];
526 uint32_t ints[FREE_BLOCK_TEST_SIZE_INTS];
527 } testBuffer;
529 int left = 0; // Smallest block index in the search region
530 int right = flashfsGetSize() / FREE_BLOCK_SIZE; // One past the largest block index in the search region
531 int mid;
532 int result = right;
533 int i;
534 bool blockErased;
536 while (left < right) {
537 mid = (left + right) / 2;
539 if (flashReadBytes(mid * FREE_BLOCK_SIZE, testBuffer.bytes, FREE_BLOCK_TEST_SIZE_BYTES) < FREE_BLOCK_TEST_SIZE_BYTES) {
540 // Unexpected timeout from flash, so bail early (reporting the device fuller than it really is)
541 break;
544 // Checking the buffer 4 bytes at a time like this is probably faster than byte-by-byte, but I didn't benchmark it :)
545 blockErased = true;
546 for (i = 0; i < FREE_BLOCK_TEST_SIZE_INTS; i++) {
547 if (testBuffer.ints[i] != 0xFFFFFFFF) {
548 blockErased = false;
549 break;
553 if (blockErased) {
554 /* This erased block might be the leftmost erased block in the volume, but we'll need to continue the
555 * search leftwards to find out:
557 result = mid;
559 right = mid;
560 } else {
561 left = mid + 1;
565 return result * FREE_BLOCK_SIZE;
569 * Returns true if the file pointer is at the end of the device.
571 bool flashfsIsEOF(void)
573 return tailAddress >= flashfsGetSize();
576 void flashfsClose(void)
578 switch(flashfsGetGeometry()->flashType) {
579 case FLASH_TYPE_NOR:
580 break;
582 case FLASH_TYPE_NAND:
583 flashFlush();
585 // Advance tailAddress to next page boundary.
586 uint32_t pageSize = flashfsGetGeometry()->pageSize;
587 flashfsSetTailAddress((tailAddress + pageSize - 1) & ~(pageSize - 1));
589 break;
594 * Call after initializing the flash chip in order to set up the filesystem.
596 void flashfsInit(void)
598 // If we have a flash chip present at all
599 if (flashfsGetSize() > 0) {
600 // Start the file pointer off at the beginning of free space so caller can start writing immediately
601 flashfsSeekAbs(flashfsIdentifyStartOfFreeSpace());