Fix unused param, add PERIPH_DRIVER flag for F4, tidied up F1 and F3 in prep.
[betaflight.git] / src / main / io / flashfs.c
blob5e346d1b82c71920f498db045e9875de65cffc79
1 /*
2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
18 /**
19 * This provides a stream interface to a flash chip if one is present.
21 * On statup, call flashfsInit() after initialising the flash chip in order to init the filesystem. This will
22 * result in the file pointer being pointed at the first free block found, or at the end of the device if the
23 * flash chip is full.
25 * Note that bits can only be set to 0 when writing, not back to 1 from 0. You must erase sectors in order
26 * to bring bits back to 1 again.
28 * In future, we can add support for multiple different flash chips by adding a flash device driver vtable
29 * and make calls through that, at the moment flashfs just calls m25p16_* routines explicitly.
32 #include <stdint.h>
33 #include <stdbool.h>
34 #include <string.h>
36 #include "drivers/flash.h"
37 #include "drivers/flash_m25p16.h"
39 #include "io/flashfs.h"
41 static uint8_t flashWriteBuffer[FLASHFS_WRITE_BUFFER_SIZE];
43 /* The position of our head and tail in the circular flash write buffer.
45 * The head is the index that a byte would be inserted into on writing, while the tail is the index of the
46 * oldest byte that has yet to be written to flash.
48 * When the circular buffer is empty, head == tail
50 static uint8_t bufferHead = 0, bufferTail = 0;
52 // The position of the buffer's tail in the overall flash address space:
53 static uint32_t tailAddress = 0;
55 static void flashfsClearBuffer()
57 bufferTail = bufferHead = 0;
60 static bool flashfsBufferIsEmpty()
62 return bufferTail == bufferHead;
65 static void flashfsSetTailAddress(uint32_t address)
67 tailAddress = address;
70 void flashfsEraseCompletely()
72 m25p16_eraseCompletely();
74 flashfsClearBuffer();
76 flashfsSetTailAddress(0);
79 /**
80 * Start and end must lie on sector boundaries, or they will be rounded out to sector boundaries such that
81 * all the bytes in the range [start...end) are erased.
83 void flashfsEraseRange(uint32_t start, uint32_t end)
85 const flashGeometry_t *geometry = m25p16_getGeometry();
87 if (geometry->sectorSize <= 0)
88 return;
90 // Round the start down to a sector boundary
91 int startSector = start / geometry->sectorSize;
93 // And the end upward
94 int endSector = end / geometry->sectorSize;
95 int endRemainder = end % geometry->sectorSize;
97 if (endRemainder > 0) {
98 endSector++;
101 for (int i = startSector; i < endSector; i++) {
102 m25p16_eraseSector(i * geometry->sectorSize);
107 * Return true if the flash is not currently occupied with an operation.
109 bool flashfsIsReady()
111 return m25p16_isReady();
114 uint32_t flashfsGetSize()
116 return m25p16_getGeometry()->totalSize;
119 static uint32_t flashfsTransmitBufferUsed()
121 if (bufferHead >= bufferTail)
122 return bufferHead - bufferTail;
124 return FLASHFS_WRITE_BUFFER_SIZE - bufferTail + bufferHead;
128 * Get the size of the largest single write that flashfs could ever accept without blocking or data loss.
130 uint32_t flashfsGetWriteBufferSize()
132 return FLASHFS_WRITE_BUFFER_USABLE;
136 * Get the number of bytes that can currently be written to flashfs without any blocking or data loss.
138 uint32_t flashfsGetWriteBufferFreeSpace()
140 return flashfsGetWriteBufferSize() - flashfsTransmitBufferUsed();
143 const flashGeometry_t* flashfsGetGeometry()
145 return m25p16_getGeometry();
149 * Write the given buffers to flash sequentially at the current tail address, advancing the tail address after
150 * each write.
152 * In synchronous mode, waits for the flash to become ready before writing so that every byte requested can be written.
154 * In asynchronous mode, if the flash is busy, then the write is aborted and the routine returns immediately.
155 * In this case the returned number of bytes written will be less than the total amount requested.
157 * Modifies the supplied buffer pointers and sizes to reflect how many bytes remain in each of them.
159 * bufferCount: the number of buffers provided
160 * buffers: an array of pointers to the beginning of buffers
161 * bufferSizes: an array of the sizes of those buffers
162 * sync: true if we should wait for the device to be idle before writes, otherwise if the device is busy the
163 * write will be aborted and this routine will return immediately.
165 * Returns the number of bytes written
167 static uint32_t flashfsWriteBuffers(uint8_t const **buffers, uint32_t *bufferSizes, int bufferCount, bool sync)
169 uint32_t bytesTotal = 0;
171 int i;
173 for (i = 0; i < bufferCount; i++) {
174 bytesTotal += bufferSizes[i];
177 if (!sync && !m25p16_isReady()) {
178 return 0;
181 uint32_t bytesTotalRemaining = bytesTotal;
183 while (bytesTotalRemaining > 0) {
184 uint32_t bytesTotalThisIteration;
185 uint32_t bytesRemainThisIteration;
188 * Each page needs to be saved in a separate program operation, so
189 * if we would cross a page boundary, only write up to the boundary in this iteration:
191 if (tailAddress % M25P16_PAGESIZE + bytesTotalRemaining > M25P16_PAGESIZE) {
192 bytesTotalThisIteration = M25P16_PAGESIZE - tailAddress % M25P16_PAGESIZE;
193 } else {
194 bytesTotalThisIteration = bytesTotalRemaining;
197 // Are we at EOF already? Abort.
198 if (flashfsIsEOF()) {
199 // May as well throw away any buffered data
200 flashfsClearBuffer();
202 break;
205 m25p16_pageProgramBegin(tailAddress);
207 bytesRemainThisIteration = bytesTotalThisIteration;
209 for (i = 0; i < bufferCount; i++) {
210 if (bufferSizes[i] > 0) {
211 // Is buffer larger than our write limit? Write our limit out of it
212 if (bufferSizes[i] >= bytesRemainThisIteration) {
213 m25p16_pageProgramContinue(buffers[i], bytesRemainThisIteration);
215 buffers[i] += bytesRemainThisIteration;
216 bufferSizes[i] -= bytesRemainThisIteration;
218 bytesRemainThisIteration = 0;
219 break;
220 } else {
221 // We'll still have more to write after finishing this buffer off
222 m25p16_pageProgramContinue(buffers[i], bufferSizes[i]);
224 bytesRemainThisIteration -= bufferSizes[i];
226 buffers[i] += bufferSizes[i];
227 bufferSizes[i] = 0;
232 m25p16_pageProgramFinish();
234 bytesTotalRemaining -= bytesTotalThisIteration;
236 // Advance the cursor in the file system to match the bytes we wrote
237 flashfsSetTailAddress(tailAddress + bytesTotalThisIteration);
240 * We'll have to wait for that write to complete before we can issue the next one, so if
241 * the user requested asynchronous writes, break now.
243 if (!sync)
244 break;
247 return bytesTotal - bytesTotalRemaining;
251 * Since the buffered data might wrap around the end of the circular buffer, we can have two segments of data to write,
252 * an initial portion and a possible wrapped portion.
254 * This routine will fill the details of those buffers into the provided arrays, which must be at least 2 elements long.
256 static void flashfsGetDirtyDataBuffers(uint8_t const *buffers[], uint32_t bufferSizes[])
258 buffers[0] = flashWriteBuffer + bufferTail;
259 buffers[1] = flashWriteBuffer + 0;
261 if (bufferHead >= bufferTail) {
262 bufferSizes[0] = bufferHead - bufferTail;
263 bufferSizes[1] = 0;
264 } else {
265 bufferSizes[0] = FLASHFS_WRITE_BUFFER_SIZE - bufferTail;
266 bufferSizes[1] = bufferHead;
271 * Get the current offset of the file pointer within the volume.
273 uint32_t flashfsGetOffset()
275 uint8_t const * buffers[2];
276 uint32_t bufferSizes[2];
278 // Dirty data in the buffers contributes to the offset
280 flashfsGetDirtyDataBuffers(buffers, bufferSizes);
282 return tailAddress + bufferSizes[0] + bufferSizes[1];
286 * Called after bytes have been written from the buffer to advance the position of the tail by the given amount.
288 static void flashfsAdvanceTailInBuffer(uint32_t delta)
290 bufferTail += delta;
292 // Wrap tail around the end of the buffer
293 if (bufferTail >= FLASHFS_WRITE_BUFFER_SIZE) {
294 bufferTail -= FLASHFS_WRITE_BUFFER_SIZE;
297 if (flashfsBufferIsEmpty()) {
298 flashfsClearBuffer(); // Bring buffer pointers back to the start to be tidier
303 * If the flash is ready to accept writes, flush the buffer to it.
305 * Returns true if all data in the buffer has been flushed to the device, or false if
306 * there is still data to be written (call flush again later).
308 bool flashfsFlushAsync()
310 if (flashfsBufferIsEmpty()) {
311 return true; // Nothing to flush
314 uint8_t const * buffers[2];
315 uint32_t bufferSizes[2];
316 uint32_t bytesWritten;
318 flashfsGetDirtyDataBuffers(buffers, bufferSizes);
319 bytesWritten = flashfsWriteBuffers(buffers, bufferSizes, 2, false);
320 flashfsAdvanceTailInBuffer(bytesWritten);
322 return flashfsBufferIsEmpty();
326 * Wait for the flash to become ready and begin flushing any buffered data to flash.
328 * The flash will still be busy some time after this sync completes, but space will
329 * be freed up to accept more writes in the write buffer.
331 void flashfsFlushSync()
333 if (flashfsBufferIsEmpty()) {
334 return; // Nothing to flush
337 uint8_t const * buffers[2];
338 uint32_t bufferSizes[2];
340 flashfsGetDirtyDataBuffers(buffers, bufferSizes);
341 flashfsWriteBuffers(buffers, bufferSizes, 2, true);
343 // We've written our entire buffer now:
344 flashfsClearBuffer();
347 void flashfsSeekAbs(uint32_t offset)
349 flashfsFlushSync();
351 flashfsSetTailAddress(offset);
354 void flashfsSeekRel(int32_t offset)
356 flashfsFlushSync();
358 flashfsSetTailAddress(tailAddress + offset);
362 * Write the given byte asynchronously to the flash. If the buffer overflows, data is silently discarded.
364 void flashfsWriteByte(uint8_t byte)
366 flashWriteBuffer[bufferHead++] = byte;
368 if (bufferHead >= FLASHFS_WRITE_BUFFER_SIZE) {
369 bufferHead = 0;
372 if (flashfsTransmitBufferUsed() >= FLASHFS_WRITE_BUFFER_AUTO_FLUSH_LEN) {
373 flashfsFlushAsync();
378 * Write the given buffer to the flash either synchronously or asynchronously depending on the 'sync' parameter.
380 * If writing asynchronously, data will be silently discarded if the buffer overflows.
381 * If writing synchronously, the routine will block waiting for the flash to become ready so will never drop data.
383 void flashfsWrite(const uint8_t *data, unsigned int len, bool sync)
385 uint8_t const * buffers[3];
386 uint32_t bufferSizes[3];
388 // There could be two dirty buffers to write out already:
389 flashfsGetDirtyDataBuffers(buffers, bufferSizes);
391 // Plus the buffer the user supplied:
392 buffers[2] = data;
393 bufferSizes[2] = len;
396 * Would writing this data to our buffer cause our buffer to reach the flush threshold? If so try to write through
397 * to the flash now
399 if (bufferSizes[0] + bufferSizes[1] + bufferSizes[2] >= FLASHFS_WRITE_BUFFER_AUTO_FLUSH_LEN) {
400 uint32_t bytesWritten;
402 // Attempt to write all three buffers through to the flash asynchronously
403 bytesWritten = flashfsWriteBuffers(buffers, bufferSizes, 3, false);
405 if (bufferSizes[0] == 0 && bufferSizes[1] == 0) {
406 // We wrote all the data that was previously buffered
407 flashfsClearBuffer();
409 if (bufferSizes[2] == 0) {
410 // And we wrote all the data the user supplied! Job done!
411 return;
413 } else {
414 // We only wrote a portion of the old data, so advance the tail to remove the bytes we did write from the buffer
415 flashfsAdvanceTailInBuffer(bytesWritten);
418 // Is the remainder of the data to be written too big to fit in the buffers?
419 if (bufferSizes[0] + bufferSizes[1] + bufferSizes[2] > FLASHFS_WRITE_BUFFER_USABLE) {
420 if (sync) {
421 // Write it through synchronously
422 flashfsWriteBuffers(buffers, bufferSizes, 3, true);
423 flashfsClearBuffer();
424 } else {
426 * Silently drop the data the user asked to write (i.e. no-op) since we can't buffer it and they
427 * requested async.
431 return;
434 // Fall through and add the remainder of the incoming data to our buffer
435 data = buffers[2];
436 len = bufferSizes[2];
439 // Buffer up the data the user supplied instead of writing it right away
441 // First write the portion before we wrap around the end of the circular buffer
442 unsigned int bufferBytesBeforeWrap = FLASHFS_WRITE_BUFFER_SIZE - bufferHead;
444 unsigned int firstPortion = len < bufferBytesBeforeWrap ? len : bufferBytesBeforeWrap;
446 memcpy(flashWriteBuffer + bufferHead, data, firstPortion);
448 bufferHead += firstPortion;
450 data += firstPortion;
451 len -= firstPortion;
453 // If we wrap the head around, write the remainder to the start of the buffer (if any)
454 if (bufferHead == FLASHFS_WRITE_BUFFER_SIZE) {
455 memcpy(flashWriteBuffer + 0, data, len);
457 bufferHead = len;
462 * Read `len` bytes from the given address into the supplied buffer.
464 * Returns the number of bytes actually read which may be less than that requested.
466 int flashfsReadAbs(uint32_t address, uint8_t *buffer, unsigned int len)
468 int bytesRead;
470 // Did caller try to read past the end of the volume?
471 if (address + len > flashfsGetSize()) {
472 // Truncate their request
473 len = flashfsGetSize() - address;
476 // Since the read could overlap data in our dirty buffers, force a sync to clear those first
477 flashfsFlushSync();
479 bytesRead = m25p16_readBytes(address, buffer, len);
481 return bytesRead;
485 * Find the offset of the start of the free space on the device (or the size of the device if it is full).
487 int flashfsIdentifyStartOfFreeSpace()
489 /* Find the start of the free space on the device by examining the beginning of blocks with a binary search,
490 * looking for ones that appear to be erased. We can achieve this with good accuracy because an erased block
491 * is all bits set to 1, which pretty much never appears in reasonable size substrings of blackbox logs.
493 * To do better we might write a volume header instead, which would mark how much free space remains. But keeping
494 * a header up to date while logging would incur more writes to the flash, which would consume precious write
495 * bandwidth and block more often.
498 enum {
499 /* We can choose whatever power of 2 size we like, which determines how much wastage of free space we'll have
500 * at the end of the last written data. But smaller blocksizes will require more searching.
502 FREE_BLOCK_SIZE = 2048,
504 /* We don't expect valid data to ever contain this many consecutive uint32_t's of all 1 bits: */
505 FREE_BLOCK_TEST_SIZE_INTS = 4, // i.e. 16 bytes
506 FREE_BLOCK_TEST_SIZE_BYTES = FREE_BLOCK_TEST_SIZE_INTS * sizeof(uint32_t)
509 union {
510 uint8_t bytes[FREE_BLOCK_TEST_SIZE_BYTES];
511 uint32_t ints[FREE_BLOCK_TEST_SIZE_INTS];
512 } testBuffer;
514 int left = 0; // Smallest block index in the search region
515 int right = flashfsGetSize() / FREE_BLOCK_SIZE; // One past the largest block index in the search region
516 int mid;
517 int result = right;
518 int i;
519 bool blockErased;
521 while (left < right) {
522 mid = (left + right) / 2;
524 if (m25p16_readBytes(mid * FREE_BLOCK_SIZE, testBuffer.bytes, FREE_BLOCK_TEST_SIZE_BYTES) < FREE_BLOCK_TEST_SIZE_BYTES) {
525 // Unexpected timeout from flash, so bail early (reporting the device fuller than it really is)
526 break;
529 // Checking the buffer 4 bytes at a time like this is probably faster than byte-by-byte, but I didn't benchmark it :)
530 blockErased = true;
531 for (i = 0; i < FREE_BLOCK_TEST_SIZE_INTS; i++) {
532 if (testBuffer.ints[i] != 0xFFFFFFFF) {
533 blockErased = false;
534 break;
538 if (blockErased) {
539 /* This erased block might be the leftmost erased block in the volume, but we'll need to continue the
540 * search leftwards to find out:
542 result = mid;
544 right = mid;
545 } else {
546 left = mid + 1;
550 return result * FREE_BLOCK_SIZE;
554 * Returns true if the file pointer is at the end of the device.
556 bool flashfsIsEOF() {
557 return tailAddress >= flashfsGetSize();
561 * Call after initializing the flash chip in order to set up the filesystem.
563 void flashfsInit()
565 // If we have a flash chip present at all
566 if (flashfsGetSize() > 0) {
567 // Start the file pointer off at the beginning of free space so caller can start writing immediately
568 flashfsSeekAbs(flashfsIdentifyStartOfFreeSpace());