Files
templates/c/flash-storage/Src/flash_storage.c

64 lines
9.3 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "flash_storage.h"
#include "flash_storage_config.h"
#include <stddef.h>
#include <string.h>
#define NO_ADDRESS 0U
#define V2_PAYLOAD 512U
#define V1_PAYLOAD 256U
typedef struct { uint32_t magic;uint16_t version,header_size;uint32_t sequence,length,crc32,reserved;uint8_t payload[V2_PAYLOAD];uint32_t pad,commit; } record_v2_t;
typedef struct { uint32_t magic;uint16_t version,header_size;uint32_t sequence,length,crc32,reserved;uint8_t payload[V1_PAYLOAD];uint32_t pad,commit; } record_v1_t;
typedef char v2_size_check[(sizeof(record_v2_t)==FLASH_STORAGE_RECORD_SIZE)?1:-1];
typedef char v1_size_check[(sizeof(record_v1_t)==FLASH_STORAGE_V1_RECORD_SIZE)?1:-1];
static uint32_t crc_update(uint32_t crc,const uint8_t *p,size_t n)
{ size_t i;uint8_t bit;for(i=0;i<n;++i){crc^=p[i];for(bit=0;bit<8U;++bit)crc=(crc>>1U)^((0U-(crc&1U))&0xEDB88320UL);}return crc; }
static uint32_t fields_crc(uint16_t v,uint32_t seq,uint32_t len,const uint8_t *p)
{ uint32_t c=0xFFFFFFFFUL;c=crc_update(c,(uint8_t*)&v,sizeof(v));c=crc_update(c,(uint8_t*)&seq,sizeof(seq));c=crc_update(c,(uint8_t*)&len,sizeof(len));c=crc_update(c,p,len);return c^0xFFFFFFFFUL; }
static int newer(uint32_t a,uint32_t b){return (int32_t)(a-b)>0;}
static uint32_t count_v2(const flash_storage_t *s){return ((s->layout.bank_size*(100U-s->layout.reserve_percent))/100U)/FLASH_STORAGE_RECORD_SIZE;}
static int read_at(flash_storage_t*s,uint32_t a,void*p,size_t n){return s->ops->read(s->platform_context,a,p,n)==FLASH_STORAGE_OK;}
static int erased(flash_storage_t*s,uint32_t a,size_t n)
{ uint32_t w;size_t i;for(i=0;i<n;i+=4U){if(!read_at(s,a+(uint32_t)i,&w,4U)||w!=0xFFFFFFFFUL)return 0;}return 1; }
static int valid_v2(const record_v2_t*r){return r->commit==FLASH_STORAGE_COMMIT&&r->magic==FLASH_STORAGE_MAGIC&&r->version==2U&&r->header_size==offsetof(record_v2_t,payload)&&r->length&&r->length<=V2_PAYLOAD&&r->crc32==fields_crc(r->version,r->sequence,r->length,r->payload);}
static int valid_v1(const record_v1_t*r){return r->commit==FLASH_STORAGE_COMMIT&&r->magic==FLASH_STORAGE_MAGIC&&r->version==1U&&r->header_size==offsetof(record_v1_t,payload)&&r->length&&r->length<=V1_PAYLOAD&&r->crc32==fields_crc(r->version,r->sequence,r->length,r->payload);}
static flash_storage_status_t scan(flash_storage_t*s)
{ uint32_t b,i,a,active=0U,n2=count_v2(s),n1=(s->layout.bank_size*90U/100U)/FLASH_STORAGE_V1_RECORD_SIZE;int have=0;record_v2_t *r2=(record_v2_t *)s->workspace;record_v1_t *r1=(record_v1_t *)s->workspace;
s->latest_address=NO_ADDRESS;s->latest_sequence=0;s->latest_version=0;s->next_address=NO_ADDRESS;s->valid_records=0;s->programmed_slots=0;
for(b=0;b<2U;++b){for(i=0;i<n2;++i){a=s->layout.bank_address[b]+i*FLASH_STORAGE_RECORD_SIZE;if(!erased(s,a,FLASH_STORAGE_RECORD_SIZE))++s->programmed_slots;if(read_at(s,a,r2,sizeof(*r2))&&valid_v2(r2)){++s->valid_records;if(!have||newer(r2->sequence,s->latest_sequence)){have=1;s->latest_address=a;s->latest_sequence=r2->sequence;s->latest_version=2U;active=b;}}}
/* Старую сетку сканируем отдельно: CRC/version исключают ложное совпадение с v2. */
for(i=0;i<n1;++i){a=s->layout.bank_address[b]+i*FLASH_STORAGE_V1_RECORD_SIZE;if(read_at(s,a,r1,sizeof(*r1))&&valid_v1(r1)){++s->valid_records;if(!have||newer(r1->sequence,s->latest_sequence)){have=1;s->latest_address=a;s->latest_sequence=r1->sequence;s->latest_version=1U;active=b;}}}}
if(have){for(i=0;i<n2;++i){a=s->layout.bank_address[active]+i*FLASH_STORAGE_RECORD_SIZE;if(a>s->latest_address&&erased(s,a,FLASH_STORAGE_RECORD_SIZE)){s->next_address=a;break;}}}
else for(b=0;b<2U&&!s->next_address;++b)for(i=0;i<n2;++i){a=s->layout.bank_address[b]+i*FLASH_STORAGE_RECORD_SIZE;if(erased(s,a,FLASH_STORAGE_RECORD_SIZE)){s->next_address=a;break;}}
return FLASH_STORAGE_OK; }
static flash_storage_status_t validate_layout(const flash_storage_ops_t*o,const flash_storage_layout_t*l)
{ if(!o||!l||!o->read||!o->program||!o->erase||!l->bank_address[0]||!l->bank_address[1]||l->bank_address[0]==l->bank_address[1]||l->bank_size<FLASH_STORAGE_RECORD_SIZE||l->reserve_percent<10U||l->reserve_percent>=100U||!l->program_alignment||!l->erase_alignment||FLASH_STORAGE_RECORD_SIZE%l->program_alignment||l->bank_address[0]%l->erase_alignment||l->bank_address[1]%l->erase_alignment||l->bank_size%l->erase_alignment)return FLASH_STORAGE_E_LAYOUT;return FLASH_STORAGE_OK; }
flash_storage_status_t flash_storage_init(flash_storage_t*s,const flash_storage_ops_t*o,void*c,const flash_storage_layout_t*l)
{ flash_storage_status_t st;if(!s)return FLASH_STORAGE_E_ARGUMENT;st=validate_layout(o,l);if(st)return st;memset(s,0,sizeof(*s));s->ops=o;s->platform_context=c;s->layout=*l;st=scan(s);if(!st)s->initialized=1U;return st; }
flash_storage_status_t flash_storage_read_latest(flash_storage_t*s,void*d,size_t cap,size_t*len,uint32_t*seq)
{ record_v2_t *r;if(!s||!s->initialized||!d)return FLASH_STORAGE_E_ARGUMENT;r=(record_v2_t *)s->workspace;if(!s->latest_address)return FLASH_STORAGE_E_NOT_FOUND;if(!read_at(s,s->latest_address,r,sizeof(*r)))return FLASH_STORAGE_E_IO;if(s->latest_version==2U){if(!valid_v2(r))return FLASH_STORAGE_E_CORRUPT;}else{record_v1_t*r1=(record_v1_t*)r;if(!valid_v1(r1))return FLASH_STORAGE_E_CORRUPT;}if(cap<r->length)return FLASH_STORAGE_E_BOUNDS;memcpy(d,r->payload,r->length);if(len)*len=r->length;if(seq)*seq=r->sequence;return FLASH_STORAGE_OK; }
flash_storage_status_t flash_storage_write(flash_storage_t*s,const void*d,size_t len)
{ record_v2_t *r;uint32_t now=0,target,bank;flash_storage_status_t st;if(!s||!s->initialized||!d||!len||len>V2_PAYLOAD)return FLASH_STORAGE_E_ARGUMENT;r=(record_v2_t *)s->workspace;if(s->busy)return FLASH_STORAGE_E_BUSY;if(s->latest_address&&read_at(s,s->latest_address,r,sizeof(*r))&&r->length==len&&!memcmp(r->payload,d,len))return FLASH_STORAGE_OK;if(s->ops->tick_ms)now=s->ops->tick_ms(s->platform_context);if(s->wrote_this_boot&&s->ops->tick_ms&&(uint32_t)(now-s->last_write_tick)<s->layout.minimum_write_ms)return FLASH_STORAGE_E_BUSY;s->busy=1U;target=s->next_address;
if(!target){bank=(s->latest_address>=s->layout.bank_address[1])?0U:1U;st=s->ops->erase(s->platform_context,s->layout.bank_address[bank],s->layout.bank_size);if(st){s->busy=0;return FLASH_STORAGE_E_IO;}target=s->layout.bank_address[bank];}
memset(r,0xFF,sizeof(*r));r->magic=FLASH_STORAGE_MAGIC;r->version=2U;r->header_size=(uint16_t)offsetof(record_v2_t,payload);r->sequence=s->latest_sequence+1U;r->length=(uint32_t)len;memcpy(r->payload,d,len);r->crc32=fields_crc(r->version,r->sequence,r->length,r->payload);r->commit=FLASH_STORAGE_COMMIT;
/* commit записывается последним: оборванная запись никогда не вытеснит старую. */
st=s->ops->program(s->platform_context,target,r,offsetof(record_v2_t,commit));if(!st)st=s->ops->program(s->platform_context,target+offsetof(record_v2_t,commit),&r->commit,4U);s->busy=0;scan(s);if(st)return FLASH_STORAGE_E_IO;if(s->latest_address!=target)return FLASH_STORAGE_E_CORRUPT;s->wrote_this_boot=1U;s->last_write_tick=now;return FLASH_STORAGE_OK; }
flash_storage_status_t flash_storage_format(flash_storage_t*s)
{ flash_storage_status_t st;if(!s||!s->initialized)return FLASH_STORAGE_E_ARGUMENT;if(s->busy)return FLASH_STORAGE_E_BUSY;s->busy=1;st=s->ops->erase(s->platform_context,s->layout.bank_address[0],s->layout.bank_size);if(!st)st=s->ops->erase(s->platform_context,s->layout.bank_address[1],s->layout.bank_size);s->busy=0;scan(s);s->wrote_this_boot=0;return st?FLASH_STORAGE_E_IO:FLASH_STORAGE_OK; }
flash_storage_status_t flash_storage_get_info(flash_storage_t*s,flash_storage_info_t*i)
{ uint32_t usable;if(!s||!s->initialized||!i)return FLASH_STORAGE_E_ARGUMENT;usable=count_v2(s)*FLASH_STORAGE_RECORD_SIZE;i->sequence=s->latest_sequence;i->valid_records=s->valid_records;i->programmed_slots=s->programmed_slots;i->allocated_bytes=2U*s->layout.bank_size;i->reserved_bytes=2U*(s->layout.bank_size-usable);return FLASH_STORAGE_OK; }
flash_storage_status_t eeprom_store_init(eeprom_store_t*e,const flash_storage_ops_t*o,void*c,const flash_storage_layout_t*l)
{ size_t n=0;flash_storage_status_t s;if(!e)return FLASH_STORAGE_E_ARGUMENT;memset(e,0,sizeof(*e));memset(e->image,0xFF,sizeof(e->image));s=flash_storage_init(&e->journal,o,c,l);if(s)return s;s=flash_storage_read_latest(&e->journal,e->image,sizeof(e->image),&n,NULL);if(s==FLASH_STORAGE_E_NOT_FOUND){e->initialized=1;return FLASH_STORAGE_OK;}if(s||!(n==128U||n==256U||n==512U)){memset(e->image,0xFF,sizeof(e->image));e->initialized=1;return FLASH_STORAGE_OK;}e->initialized=1;return FLASH_STORAGE_OK; }
flash_storage_status_t eeprom_store_read(eeprom_store_t*e,size_t off,void*d,size_t n)
{ if(!e||!e->initialized||!d||!n||off>sizeof(e->image)||n>sizeof(e->image)-off)return FLASH_STORAGE_E_BOUNDS;memcpy(d,e->image+off,n);return FLASH_STORAGE_OK; }
flash_storage_status_t eeprom_store_write(eeprom_store_t*e,size_t off,const void*d,size_t n)
{ flash_storage_status_t s;if(!e||!e->initialized||!d||!n||off>512U||n>512U-off)return FLASH_STORAGE_E_BOUNDS;memcpy(e->next_image,e->image,512U);memcpy(e->next_image+off,d,n);s=flash_storage_write(&e->journal,e->next_image,512U);if(!s)memcpy(e->image,e->next_image,512U);return s; }
flash_storage_status_t eeprom_store_format(eeprom_store_t*e)
{ flash_storage_status_t s;if(!e)return FLASH_STORAGE_E_ARGUMENT;s=flash_storage_format(&e->journal);if(!s){memset(e->image,0xFF,512U);e->initialized=1;}return s; }