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/>.
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
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.
35 #if defined(USE_FLASHFS)
37 #include "drivers/flash.h"
39 #include "io/flashfs.h"
41 static flashPartition_t
*flashPartition
;
43 static uint8_t flashWriteBuffer
[FLASHFS_WRITE_BUFFER_SIZE
];
45 /* The position of our head and tail in the circular flash write buffer.
47 * The head is the index that a byte would be inserted into on writing, while the tail is the index of the
48 * oldest byte that has yet to be written to flash.
50 * When the circular buffer is empty, head == tail
52 static uint8_t bufferHead
= 0, bufferTail
= 0;
54 // The position of the buffer's tail in the overall flash address space:
55 static uint32_t tailAddress
= 0;
57 static void flashfsClearBuffer(void)
59 bufferTail
= bufferHead
= 0;
62 static bool flashfsBufferIsEmpty(void)
64 return bufferTail
== bufferHead
;
67 static void flashfsSetTailAddress(uint32_t address
)
69 tailAddress
= address
;
72 void flashfsEraseCompletely(void)
74 flashPartitionErase(flashPartition
);
76 flashfsSetTailAddress(0);
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
= flashGetGeometry();
87 if (geometry
->sectorSize
<= 0)
90 // Round the start down to a sector boundary
91 int startSector
= start
/ geometry
->sectorSize
;
94 int endSector
= end
/ geometry
->sectorSize
;
95 int endRemainder
= end
% geometry
->sectorSize
;
97 if (endRemainder
> 0) {
101 for (int i
= startSector
; i
< endSector
; i
++) {
102 flashEraseSector(i
* geometry
->sectorSize
);
107 * Return true if the flash is not currently occupied with an operation.
109 bool flashfsIsReady(void)
111 return !!flashPartition
;
114 uint32_t flashfsGetSize(void)
116 return flashPartitionSize(flashPartition
);
119 static uint32_t flashfsTransmitBufferUsed(void)
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(void)
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(void)
140 return flashfsGetWriteBufferSize() - flashfsTransmitBufferUsed();
144 * Write the given buffers to flash sequentially at the current tail address, advancing the tail address after
147 * In synchronous mode, waits for the flash to become ready before writing so that every byte requested can be written.
149 * In asynchronous mode, if the flash is busy, then the write is aborted and the routine returns immediately.
150 * In this case the returned number of bytes written will be less than the total amount requested.
152 * Modifies the supplied buffer pointers and sizes to reflect how many bytes remain in each of them.
154 * bufferCount: the number of buffers provided
155 * buffers: an array of pointers to the beginning of buffers
156 * bufferSizes: an array of the sizes of those buffers
157 * sync: true if we should wait for the device to be idle before writes, otherwise if the device is busy the
158 * write will be aborted and this routine will return immediately.
160 * Returns the number of bytes written
162 static uint32_t flashfsWriteBuffers(uint8_t const **buffers
, uint32_t *bufferSizes
, int bufferCount
, bool sync
)
164 const flashGeometry_t
*geometry
= flashGetGeometry();
166 uint32_t bytesTotal
= 0;
170 for (i
= 0; i
< bufferCount
; i
++) {
171 bytesTotal
+= bufferSizes
[i
];
174 if (!sync
&& !flashIsReady()) {
178 uint32_t bytesTotalRemaining
= bytesTotal
;
180 while (bytesTotalRemaining
> 0) {
181 uint32_t bytesTotalThisIteration
;
182 uint32_t bytesRemainThisIteration
;
183 uint32_t currentFlashAddress
= tailAddress
;
186 * Each page needs to be saved in a separate program operation, so
187 * if we would cross a page boundary, only write up to the boundary in this iteration:
189 if (tailAddress
% geometry
->pageSize
+ bytesTotalRemaining
> geometry
->pageSize
) {
190 bytesTotalThisIteration
= geometry
->pageSize
- tailAddress
% geometry
->pageSize
;
192 bytesTotalThisIteration
= bytesTotalRemaining
;
195 // Are we at EOF already? Abort.
196 if (flashfsIsEOF()) {
197 // May as well throw away any buffered data
198 flashfsClearBuffer();
203 bytesRemainThisIteration
= bytesTotalThisIteration
;
205 for (i
= 0; i
< bufferCount
; i
++) {
206 if (bufferSizes
[i
] > 0) {
207 // Is buffer larger than our write limit? Write our limit out of it
208 if (bufferSizes
[i
] >= bytesRemainThisIteration
) {
209 currentFlashAddress
= flashPageProgram(currentFlashAddress
, buffers
[i
], bytesRemainThisIteration
);
211 buffers
[i
] += bytesRemainThisIteration
;
212 bufferSizes
[i
] -= bytesRemainThisIteration
;
214 bytesRemainThisIteration
= 0;
217 // We'll still have more to write after finishing this buffer off
218 currentFlashAddress
= flashPageProgram(currentFlashAddress
, buffers
[i
], bufferSizes
[i
]);
220 bytesRemainThisIteration
-= bufferSizes
[i
];
222 buffers
[i
] += bufferSizes
[i
];
228 bytesTotalRemaining
-= bytesTotalThisIteration
;
230 // Advance the cursor in the file system to match the bytes we wrote
231 flashfsSetTailAddress(tailAddress
+ bytesTotalThisIteration
);
234 * We'll have to wait for that write to complete before we can issue the next one, so if
235 * the user requested asynchronous writes, break now.
241 return bytesTotal
- bytesTotalRemaining
;
245 * Since the buffered data might wrap around the end of the circular buffer, we can have two segments of data to write,
246 * an initial portion and a possible wrapped portion.
248 * This routine will fill the details of those buffers into the provided arrays, which must be at least 2 elements long.
250 static void flashfsGetDirtyDataBuffers(uint8_t const *buffers
[], uint32_t bufferSizes
[])
252 buffers
[0] = flashWriteBuffer
+ bufferTail
;
253 buffers
[1] = flashWriteBuffer
+ 0;
255 if (bufferHead
>= bufferTail
) {
256 bufferSizes
[0] = bufferHead
- bufferTail
;
259 bufferSizes
[0] = FLASHFS_WRITE_BUFFER_SIZE
- bufferTail
;
260 bufferSizes
[1] = bufferHead
;
265 * Get the current offset of the file pointer within the volume.
267 uint32_t flashfsGetOffset(void)
269 uint8_t const * buffers
[2];
270 uint32_t bufferSizes
[2];
272 // Dirty data in the buffers contributes to the offset
274 flashfsGetDirtyDataBuffers(buffers
, bufferSizes
);
276 return tailAddress
+ bufferSizes
[0] + bufferSizes
[1];
280 * Called after bytes have been written from the buffer to advance the position of the tail by the given amount.
282 static void flashfsAdvanceTailInBuffer(uint32_t delta
)
286 // Wrap tail around the end of the buffer
287 if (bufferTail
>= FLASHFS_WRITE_BUFFER_SIZE
) {
288 bufferTail
-= FLASHFS_WRITE_BUFFER_SIZE
;
291 if (flashfsBufferIsEmpty()) {
292 flashfsClearBuffer(); // Bring buffer pointers back to the start to be tidier
297 * If the flash is ready to accept writes, flush the buffer to it.
299 * Returns true if all data in the buffer has been flushed to the device, or false if
300 * there is still data to be written (call flush again later).
302 bool flashfsFlushAsync(void)
304 if (flashfsBufferIsEmpty()) {
305 return true; // Nothing to flush
308 uint8_t const * buffers
[2];
309 uint32_t bufferSizes
[2];
310 uint32_t bytesWritten
;
312 flashfsGetDirtyDataBuffers(buffers
, bufferSizes
);
313 bytesWritten
= flashfsWriteBuffers(buffers
, bufferSizes
, 2, false);
314 flashfsAdvanceTailInBuffer(bytesWritten
);
316 return flashfsBufferIsEmpty();
320 * Wait for the flash to become ready and begin flushing any buffered data to flash.
322 * The flash will still be busy some time after this sync completes, but space will
323 * be freed up to accept more writes in the write buffer.
325 void flashfsFlushSync(void)
327 if (flashfsBufferIsEmpty()) {
328 return; // Nothing to flush
331 uint8_t const * buffers
[2];
332 uint32_t bufferSizes
[2];
334 flashfsGetDirtyDataBuffers(buffers
, bufferSizes
);
335 flashfsWriteBuffers(buffers
, bufferSizes
, 2, true);
337 // We've written our entire buffer now:
338 flashfsClearBuffer();
341 void flashfsSeekAbs(uint32_t offset
)
345 flashfsSetTailAddress(offset
);
348 void flashfsSeekRel(int32_t offset
)
352 flashfsSetTailAddress(tailAddress
+ offset
);
356 * Write the given byte asynchronously to the flash. If the buffer overflows, data is silently discarded.
358 void flashfsWriteByte(uint8_t byte
)
360 flashWriteBuffer
[bufferHead
++] = byte
;
362 if (bufferHead
>= FLASHFS_WRITE_BUFFER_SIZE
) {
366 if (flashfsTransmitBufferUsed() >= FLASHFS_WRITE_BUFFER_AUTO_FLUSH_LEN
) {
372 * Write the given buffer to the flash either synchronously or asynchronously depending on the 'sync' parameter.
374 * If writing asynchronously, data will be silently discarded if the buffer overflows.
375 * If writing synchronously, the routine will block waiting for the flash to become ready so will never drop data.
377 void flashfsWrite(const uint8_t *data
, unsigned int len
, bool sync
)
379 uint8_t const * buffers
[3];
380 uint32_t bufferSizes
[3];
382 // There could be two dirty buffers to write out already:
383 flashfsGetDirtyDataBuffers(buffers
, bufferSizes
);
385 // Plus the buffer the user supplied:
387 bufferSizes
[2] = len
;
390 * Would writing this data to our buffer cause our buffer to reach the flush threshold? If so try to write through
393 if (bufferSizes
[0] + bufferSizes
[1] + bufferSizes
[2] >= FLASHFS_WRITE_BUFFER_AUTO_FLUSH_LEN
) {
394 uint32_t bytesWritten
;
396 // Attempt to write all three buffers through to the flash asynchronously
397 bytesWritten
= flashfsWriteBuffers(buffers
, bufferSizes
, 3, false);
399 if (bufferSizes
[0] == 0 && bufferSizes
[1] == 0) {
400 // We wrote all the data that was previously buffered
401 flashfsClearBuffer();
403 if (bufferSizes
[2] == 0) {
404 // And we wrote all the data the user supplied! Job done!
408 // We only wrote a portion of the old data, so advance the tail to remove the bytes we did write from the buffer
409 flashfsAdvanceTailInBuffer(bytesWritten
);
412 // Is the remainder of the data to be written too big to fit in the buffers?
413 if (bufferSizes
[0] + bufferSizes
[1] + bufferSizes
[2] > FLASHFS_WRITE_BUFFER_USABLE
) {
415 // Write it through synchronously
416 flashfsWriteBuffers(buffers
, bufferSizes
, 3, true);
417 flashfsClearBuffer();
420 * Silently drop the data the user asked to write (i.e. no-op) since we can't buffer it and they
428 // Fall through and add the remainder of the incoming data to our buffer
430 len
= bufferSizes
[2];
433 // Buffer up the data the user supplied instead of writing it right away
435 // First write the portion before we wrap around the end of the circular buffer
436 unsigned int bufferBytesBeforeWrap
= FLASHFS_WRITE_BUFFER_SIZE
- bufferHead
;
438 unsigned int firstPortion
= len
< bufferBytesBeforeWrap
? len
: bufferBytesBeforeWrap
;
440 memcpy(flashWriteBuffer
+ bufferHead
, data
, firstPortion
);
442 bufferHead
+= firstPortion
;
444 data
+= firstPortion
;
447 // If we wrap the head around, write the remainder to the start of the buffer (if any)
448 if (bufferHead
== FLASHFS_WRITE_BUFFER_SIZE
) {
449 memcpy(flashWriteBuffer
+ 0, data
, len
);
456 * Read `len` bytes from the given address into the supplied buffer.
458 * Returns the number of bytes actually read which may be less than that requested.
460 int flashfsReadAbs(uint32_t address
, uint8_t *buffer
, unsigned int len
)
464 // Did caller try to read past the end of the volume?
465 if (address
+ len
> flashfsGetSize()) {
466 // Truncate their request
467 len
= flashfsGetSize() - address
;
470 // Since the read could overlap data in our dirty buffers, force a sync to clear those first
473 bytesRead
= flashReadBytes(address
, buffer
, len
);
479 * Find the offset of the start of the free space on the device (or the size of the device if it is full).
481 int flashfsIdentifyStartOfFreeSpace(void)
483 /* Find the start of the free space on the device by examining the beginning of blocks with a binary search,
484 * looking for ones that appear to be erased. We can achieve this with good accuracy because an erased block
485 * is all bits set to 1, which pretty much never appears in reasonable size substrings of blackbox logs.
487 * To do better we might write a volume header instead, which would mark how much free space remains. But keeping
488 * a header up to date while logging would incur more writes to the flash, which would consume precious write
489 * bandwidth and block more often.
493 /* We can choose whatever power of 2 size we like, which determines how much wastage of free space we'll have
494 * at the end of the last written data. But smaller blocksizes will require more searching.
496 FREE_BLOCK_SIZE
= 2048,
498 /* We don't expect valid data to ever contain this many consecutive uint32_t's of all 1 bits: */
499 FREE_BLOCK_TEST_SIZE_INTS
= 4, // i.e. 16 bytes
500 FREE_BLOCK_TEST_SIZE_BYTES
= FREE_BLOCK_TEST_SIZE_INTS
* sizeof(uint32_t),
504 uint8_t bytes
[FREE_BLOCK_TEST_SIZE_BYTES
];
505 uint32_t ints
[FREE_BLOCK_TEST_SIZE_INTS
];
508 int left
= 0; // Smallest block index in the search region
509 int right
= flashfsGetSize() / FREE_BLOCK_SIZE
; // One past the largest block index in the search region
515 while (left
< right
) {
516 mid
= (left
+ right
) / 2;
518 if (flashReadBytes(mid
* FREE_BLOCK_SIZE
, testBuffer
.bytes
, FREE_BLOCK_TEST_SIZE_BYTES
) < FREE_BLOCK_TEST_SIZE_BYTES
) {
519 // Unexpected timeout from flash, so bail early (reporting the device fuller than it really is)
523 // Checking the buffer 4 bytes at a time like this is probably faster than byte-by-byte, but I didn't benchmark it :)
525 for (i
= 0; i
< FREE_BLOCK_TEST_SIZE_INTS
; i
++) {
526 if (testBuffer
.ints
[i
] != 0xFFFFFFFF) {
533 /* This erased block might be the leftmost erased block in the volume, but we'll need to continue the
534 * search leftwards to find out:
544 return result
* FREE_BLOCK_SIZE
;
548 * Returns true if the file pointer is at the end of the device.
550 bool flashfsIsEOF(void)
552 return tailAddress
>= flashfsGetSize();
556 * Call after initializing the flash chip in order to set up the filesystem.
558 void flashfsInit(void)
560 flashPartition
= flashPartitionFindByType(FLASH_PARTITION_TYPE_FLASHFS
);
562 if (flashPartition
) {
563 // Start the file pointer off at the beginning of free space so caller can start writing immediately
564 flashfsSeekAbs(flashfsIdentifyStartOfFreeSpace());