drv_flashfs.c
Go to the documentation of this file.
1 /*
2  drv_flashfs.c : Flash memory support for STM32F103
3 
4  Adapted from https://github.com/cleanflight/cleanflight/blob/master/src/main/io/flashfs.c
5 
6  This file is part of BreezySTM32.
7 
8  BreezySTM32 is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12 
13  BreezySTM32 is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with BreezySTM32. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
36 #include <stdint.h>
37 #include <stdbool.h>
38 #include <string.h>
39 
40 #include "drv_m25p16.h"
41 #include "drv_flashfs.h"
42 
44 
45 /* The position of our head and tail in the circular flash write buffer.
46  *
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.
49  *
50  * When the circular buffer is empty, head == tail
51  */
52 static uint8_t bufferHead = 0, bufferTail = 0;
53 
54 // The position of the buffer's tail in the overall flash address space:
55 static uint32_t tailAddress = 0;
56 
57 static void flashfsClearBuffer()
58 {
59  bufferTail = bufferHead = 0;
60 }
61 
62 static bool flashfsBufferIsEmpty()
63 {
64  return bufferTail == bufferHead;
65 }
66 
67 static void flashfsSetTailAddress(uint32_t address)
68 {
69  tailAddress = address;
70 }
71 
73 {
75 
77 
79 }
80 
85 void flashfsEraseRange(uint32_t start, uint32_t end)
86 {
88 
89  if (geometry->sectorSize <= 0)
90  return;
91 
92  // Round the start down to a sector boundary
93  int startSector = start / geometry->sectorSize;
94 
95  // And the end upward
96  int endSector = end / geometry->sectorSize;
97  int endRemainder = end % geometry->sectorSize;
98 
99  if (endRemainder > 0) {
100  endSector++;
101  }
102 
103  for (int i = startSector; i < endSector; i++) {
104  m25p16_eraseSector(i * geometry->sectorSize);
105  }
106 }
107 
112 {
113  return m25p16_isReady();
114 }
115 
116 uint32_t flashfsGetSize()
117 {
118  return m25p16_getGeometry()->totalSize;
119 }
120 
121 static uint32_t flashfsTransmitBufferUsed()
122 {
123  if (bufferHead >= bufferTail)
124  return bufferHead - bufferTail;
125 
126  return FLASHFS_WRITE_BUFFER_SIZE - bufferTail + bufferHead;
127 }
128 
133 {
135 }
136 
141 {
143 }
144 
146 {
147  return m25p16_getGeometry();
148 }
149 
169 static uint32_t flashfsWriteBuffers(uint8_t const **buffers, uint32_t *bufferSizes, int bufferCount, bool sync)
170 {
171  uint32_t bytesTotal = 0;
172 
173  int i;
174 
175  for (i = 0; i < bufferCount; i++) {
176  bytesTotal += bufferSizes[i];
177  }
178 
179  if (!sync && !m25p16_isReady()) {
180  return 0;
181  }
182 
183  uint32_t bytesTotalRemaining = bytesTotal;
184 
185  while (bytesTotalRemaining > 0) {
186  uint32_t bytesTotalThisIteration;
187  uint32_t bytesRemainThisIteration;
188 
189  /*
190  * Each page needs to be saved in a separate program operation, so
191  * if we would cross a page boundary, only write up to the boundary in this iteration:
192  */
193  if (tailAddress % M25P16_PAGESIZE + bytesTotalRemaining > M25P16_PAGESIZE) {
194  bytesTotalThisIteration = M25P16_PAGESIZE - tailAddress % M25P16_PAGESIZE;
195  } else {
196  bytesTotalThisIteration = bytesTotalRemaining;
197  }
198 
199  // Are we at EOF already? Abort.
200  if (flashfsIsEOF()) {
201  // May as well throw away any buffered data
203 
204  break;
205  }
206 
208 
209  bytesRemainThisIteration = bytesTotalThisIteration;
210 
211  for (i = 0; i < bufferCount; i++) {
212  if (bufferSizes[i] > 0) {
213  // Is buffer larger than our write limit? Write our limit out of it
214  if (bufferSizes[i] >= bytesRemainThisIteration) {
215  m25p16_pageProgramContinue(buffers[i], bytesRemainThisIteration);
216 
217  buffers[i] += bytesRemainThisIteration;
218  bufferSizes[i] -= bytesRemainThisIteration;
219 
220  bytesRemainThisIteration = 0;
221  break;
222  } else {
223  // We'll still have more to write after finishing this buffer off
224  m25p16_pageProgramContinue(buffers[i], bufferSizes[i]);
225 
226  bytesRemainThisIteration -= bufferSizes[i];
227 
228  buffers[i] += bufferSizes[i];
229  bufferSizes[i] = 0;
230  }
231  }
232  }
233 
235 
236  bytesTotalRemaining -= bytesTotalThisIteration;
237 
238  // Advance the cursor in the file system to match the bytes we wrote
239  flashfsSetTailAddress(tailAddress + bytesTotalThisIteration);
240 
241  /*
242  * We'll have to wait for that write to complete before we can issue the next one, so if
243  * the user requested asynchronous writes, break now.
244  */
245  if (!sync)
246  break;
247  }
248 
249  return bytesTotal - bytesTotalRemaining;
250 }
251 
252 /*
253  * Since the buffered data might wrap around the end of the circular buffer, we can have two segments of data to write,
254  * an initial portion and a possible wrapped portion.
255  *
256  * This routine will fill the details of those buffers into the provided arrays, which must be at least 2 elements long.
257  */
258 static void flashfsGetDirtyDataBuffers(uint8_t const *buffers[], uint32_t bufferSizes[])
259 {
260  buffers[0] = flashWriteBuffer + bufferTail;
261  buffers[1] = flashWriteBuffer + 0;
262 
263  if (bufferHead >= bufferTail) {
264  bufferSizes[0] = bufferHead - bufferTail;
265  bufferSizes[1] = 0;
266  } else {
267  bufferSizes[0] = FLASHFS_WRITE_BUFFER_SIZE - bufferTail;
268  bufferSizes[1] = bufferHead;
269  }
270 }
271 
276 {
277  uint8_t const * buffers[2];
278  uint32_t bufferSizes[2];
279 
280  // Dirty data in the buffers contributes to the offset
281 
282  flashfsGetDirtyDataBuffers(buffers, bufferSizes);
283 
284  return tailAddress + bufferSizes[0] + bufferSizes[1];
285 }
286 
290 static void flashfsAdvanceTailInBuffer(uint32_t delta)
291 {
292  bufferTail += delta;
293 
294  // Wrap tail around the end of the buffer
297  }
298 
299  if (flashfsBufferIsEmpty()) {
300  flashfsClearBuffer(); // Bring buffer pointers back to the start to be tidier
301  }
302 }
303 
311 {
312  if (flashfsBufferIsEmpty()) {
313  return true; // Nothing to flush
314  }
315 
316  uint8_t const * buffers[2];
317  uint32_t bufferSizes[2];
318  uint32_t bytesWritten;
319 
320  flashfsGetDirtyDataBuffers(buffers, bufferSizes);
321  bytesWritten = flashfsWriteBuffers(buffers, bufferSizes, 2, false);
322  flashfsAdvanceTailInBuffer(bytesWritten);
323 
324  return flashfsBufferIsEmpty();
325 }
326 
334 {
335  if (flashfsBufferIsEmpty()) {
336  return; // Nothing to flush
337  }
338 
339  uint8_t const * buffers[2];
340  uint32_t bufferSizes[2];
341 
342  flashfsGetDirtyDataBuffers(buffers, bufferSizes);
343  flashfsWriteBuffers(buffers, bufferSizes, 2, true);
344 
345  // We've written our entire buffer now:
347 }
348 
349 void flashfsSeekAbs(uint32_t offset)
350 {
352 
353  flashfsSetTailAddress(offset);
354 }
355 
356 void flashfsSeekRel(int32_t offset)
357 {
359 
361 }
362 
366 void flashfsWriteByte(uint8_t byte)
367 {
368  flashWriteBuffer[bufferHead++] = byte;
369 
371  bufferHead = 0;
372  }
373 
376  }
377 }
378 
385 void flashfsWrite(const uint8_t *data, unsigned int len, bool sync)
386 {
387  uint8_t const * buffers[3];
388  uint32_t bufferSizes[3];
389 
390  // There could be two dirty buffers to write out already:
391  flashfsGetDirtyDataBuffers(buffers, bufferSizes);
392 
393  // Plus the buffer the user supplied:
394  buffers[2] = data;
395  bufferSizes[2] = len;
396 
397  /*
398  * Would writing this data to our buffer cause our buffer to reach the flush threshold? If so try to write through
399  * to the flash now
400  */
401  if (bufferSizes[0] + bufferSizes[1] + bufferSizes[2] >= FLASHFS_WRITE_BUFFER_AUTO_FLUSH_LEN) {
402  uint32_t bytesWritten;
403 
404  // Attempt to write all three buffers through to the flash asynchronously
405  bytesWritten = flashfsWriteBuffers(buffers, bufferSizes, 3, false);
406 
407  if (bufferSizes[0] == 0 && bufferSizes[1] == 0) {
408  // We wrote all the data that was previously buffered
410 
411  if (bufferSizes[2] == 0) {
412  // And we wrote all the data the user supplied! Job done!
413  return;
414  }
415  } else {
416  // We only wrote a portion of the old data, so advance the tail to remove the bytes we did write from the buffer
417  flashfsAdvanceTailInBuffer(bytesWritten);
418  }
419 
420  // Is the remainder of the data to be written too big to fit in the buffers?
421  if (bufferSizes[0] + bufferSizes[1] + bufferSizes[2] > FLASHFS_WRITE_BUFFER_USABLE) {
422  if (sync) {
423  // Write it through synchronously
424  flashfsWriteBuffers(buffers, bufferSizes, 3, true);
426  } else {
427  /*
428  * Silently drop the data the user asked to write (i.e. no-op) since we can't buffer it and they
429  * requested async.
430  */
431  }
432 
433  return;
434  }
435 
436  // Fall through and add the remainder of the incoming data to our buffer
437  data = buffers[2];
438  len = bufferSizes[2];
439  }
440 
441  // Buffer up the data the user supplied instead of writing it right away
442 
443  // First write the portion before we wrap around the end of the circular buffer
444  unsigned int bufferBytesBeforeWrap = FLASHFS_WRITE_BUFFER_SIZE - bufferHead;
445 
446  unsigned int firstPortion = len < bufferBytesBeforeWrap ? len : bufferBytesBeforeWrap;
447 
448  memcpy(flashWriteBuffer + bufferHead, data, firstPortion);
449 
450  bufferHead += firstPortion;
451 
452  data += firstPortion;
453  len -= firstPortion;
454 
455  // If we wrap the head around, write the remainder to the start of the buffer (if any)
456  if (bufferHead == FLASHFS_WRITE_BUFFER_SIZE) {
457  memcpy(flashWriteBuffer + 0, data, len);
458 
459  bufferHead = len;
460  }
461 }
462 
468 int flashfsReadAbs(uint32_t address, uint8_t *buffer, unsigned int len)
469 {
470  int bytesRead;
471 
472  // Did caller try to read past the end of the volume?
473  if (address + len > flashfsGetSize()) {
474  // Truncate their request
475  len = flashfsGetSize() - address;
476  }
477 
478  // Since the read could overlap data in our dirty buffers, force a sync to clear those first
480 
481  bytesRead = m25p16_readBytes(address, buffer, len);
482 
483  return bytesRead;
484 }
485 
490 {
491  /* Find the start of the free space on the device by examining the beginning of blocks with a binary search,
492  * looking for ones that appear to be erased. We can achieve this with good accuracy because an erased block
493  * is all bits set to 1, which pretty much never appears in reasonable size substrings of blackbox logs.
494  *
495  * To do better we might write a volume header instead, which would mark how much free space remains. But keeping
496  * a header up to date while logging would incur more writes to the flash, which would consume precious write
497  * bandwidth and block more often.
498  */
499 
500  enum {
501  /* We can choose whatever power of 2 size we like, which determines how much wastage of free space we'll have
502  * at the end of the last written data. But smaller blocksizes will require more searching.
503  */
504  FREE_BLOCK_SIZE = 2048,
505 
506  /* We don't expect valid data to ever contain this many consecutive uint32_t's of all 1 bits: */
507  FREE_BLOCK_TEST_SIZE_INTS = 4, // i.e. 16 bytes
508  FREE_BLOCK_TEST_SIZE_BYTES = FREE_BLOCK_TEST_SIZE_INTS * sizeof(uint32_t),
509  };
510 
511  union {
512  uint8_t bytes[FREE_BLOCK_TEST_SIZE_BYTES];
513  uint32_t ints[FREE_BLOCK_TEST_SIZE_INTS];
514  } testBuffer;
515 
516  int left = 0; // Smallest block index in the search region
517  int right = flashfsGetSize() / FREE_BLOCK_SIZE; // One past the largest block index in the search region
518  int mid;
519  int result = right;
520  int i;
521  bool blockErased;
522 
523  while (left < right) {
524  mid = (left + right) / 2;
525 
526  if (m25p16_readBytes(mid * FREE_BLOCK_SIZE, testBuffer.bytes, FREE_BLOCK_TEST_SIZE_BYTES) < FREE_BLOCK_TEST_SIZE_BYTES) {
527  // Unexpected timeout from flash, so bail early (reporting the device fuller than it really is)
528  break;
529  }
530 
531  // Checking the buffer 4 bytes at a time like this is probably faster than byte-by-byte, but I didn't benchmark it :)
532  blockErased = true;
533  for (i = 0; i < FREE_BLOCK_TEST_SIZE_INTS; i++) {
534  if (testBuffer.ints[i] != 0xFFFFFFFF) {
535  blockErased = false;
536  break;
537  }
538  }
539 
540  if (blockErased) {
541  /* This erased block might be the leftmost erased block in the volume, but we'll need to continue the
542  * search leftwards to find out:
543  */
544  result = mid;
545 
546  right = mid;
547  } else {
548  left = mid + 1;
549  }
550  }
551 
552  return result * FREE_BLOCK_SIZE;
553 }
554 
558 bool flashfsIsEOF() {
559  return tailAddress >= flashfsGetSize();
560 }
561 
566 {
567  // If we have a flash chip present at all
568  if (flashfsGetSize() > 0) {
569  // Start the file pointer off at the beginning of free space so caller can start writing immediately
571  }
572 }
static uint8_t flashWriteBuffer[FLASHFS_WRITE_BUFFER_SIZE]
Definition: drv_flashfs.c:43
static uint32_t flashfsTransmitBufferUsed()
Definition: drv_flashfs.c:121
uint32_t totalSize
Definition: drv_flash.h:34
uint32_t flashfsGetWriteBufferSize()
Definition: drv_flashfs.c:132
uint32_t flashfsGetOffset()
Definition: drv_flashfs.c:275
#define FLASHFS_WRITE_BUFFER_AUTO_FLUSH_LEN
Definition: drv_flashfs.h:32
uint32_t flashfsGetWriteBufferFreeSpace()
Definition: drv_flashfs.c:140
static uint8_t bufferTail
Definition: drv_flashfs.c:52
bool flashfsIsReady()
Definition: drv_flashfs.c:111
int flashfsIdentifyStartOfFreeSpace()
Definition: drv_flashfs.c:489
static uint8_t buffer[BMP280_DATA_FRAME_SIZE]
Definition: drv_bmp280.c:61
int flashfsReadAbs(uint32_t address, uint8_t *buffer, unsigned int len)
Definition: drv_flashfs.c:468
int m25p16_readBytes(uint32_t address, uint8_t *buffer, int length)
Definition: drv_m25p16.c:307
const flashGeometry_t * m25p16_getGeometry()
Definition: drv_m25p16.c:330
void flashfsEraseCompletely()
Definition: drv_flashfs.c:72
void flashfsWriteByte(uint8_t byte)
Definition: drv_flashfs.c:366
static uint8_t bufferHead
Definition: drv_flashfs.c:52
static uint32_t tailAddress
Definition: drv_flashfs.c:55
void m25p16_pageProgramContinue(const uint8_t *data, int length)
Definition: drv_m25p16.c:265
void m25p16_eraseCompletely()
Definition: drv_m25p16.c:243
static void flashfsGetDirtyDataBuffers(uint8_t const *buffers[], uint32_t bufferSizes[])
Definition: drv_flashfs.c:258
static flashGeometry_t geometry
Definition: drv_m25p16.c:81
static volatile uint8_t bytes
Definition: drv_i2c.c:97
void flashfsFlushSync()
Definition: drv_flashfs.c:333
void flashfsSeekRel(int32_t offset)
Definition: drv_flashfs.c:356
static void flashfsSetTailAddress(uint32_t address)
Definition: drv_flashfs.c:67
#define M25P16_PAGESIZE
Definition: drv_m25p16.h:27
void flashfsInit()
Definition: drv_flashfs.c:565
static bool flashfsBufferIsEmpty()
Definition: drv_flashfs.c:62
bool flashfsIsEOF()
Definition: drv_flashfs.c:558
static void flashfsAdvanceTailInBuffer(uint32_t delta)
Definition: drv_flashfs.c:290
static uint32_t flashfsWriteBuffers(uint8_t const **buffers, uint32_t *bufferSizes, int bufferCount, bool sync)
Definition: drv_flashfs.c:169
#define FLASHFS_WRITE_BUFFER_SIZE
Definition: drv_flashfs.h:28
void flashfsSeekAbs(uint32_t offset)
Definition: drv_flashfs.c:349
void flashfsEraseRange(uint32_t start, uint32_t end)
Definition: drv_flashfs.c:85
bool m25p16_isReady()
Definition: drv_m25p16.c:128
uint32_t sectorSize
Definition: drv_flash.h:32
void m25p16_pageProgramBegin(uint32_t address)
Definition: drv_m25p16.c:252
#define FLASHFS_WRITE_BUFFER_USABLE
Definition: drv_flashfs.h:29
static void flashfsClearBuffer()
Definition: drv_flashfs.c:57
const flashGeometry_t * flashfsGetGeometry()
Definition: drv_flashfs.c:145
void m25p16_pageProgramFinish()
Definition: drv_m25p16.c:270
bool flashfsFlushAsync()
Definition: drv_flashfs.c:310
void flashfsWrite(const uint8_t *data, unsigned int len, bool sync)
Definition: drv_flashfs.c:385
uint32_t flashfsGetSize()
Definition: drv_flashfs.c:116
void m25p16_eraseSector(uint32_t address)
Definition: drv_m25p16.c:228


rosflight_firmware
Author(s): Daniel Koch , James Jackson
autogenerated on Thu Apr 15 2021 05:07:46