#include //#include #define MIN(a,b) ((a) < (b) ? (a) : (b)) #include #include "settings.h" #include "dev/eeprom.h" #include #include #include #include #include "contiki.h" #ifndef SETTINGS_TOP_ADDR #define SETTINGS_TOP_ADDR (E2END-4) //!< Defaults to end of EEPROM, minus 4 bytes for avrdude erase count #endif #ifndef SETTINGS_MAX_SIZE #define SETTINGS_MAX_SIZE (1024) //!< Defaults to 1KB #endif //#pragma mark - //#pragma mark Private Functions typedef struct { uint8_t size_extra; uint8_t size_low; uint8_t size_check; settings_key_t key; } item_header_t; inline static bool settings_is_item_valid_(eeprom_addr_t item_addr) { item_header_t header = {}; if(item_addr==EEPROM_NULL) return false; // if((SETTINGS_TOP_ADDR-item_addr)>=SETTINGS_MAX_SIZE-3) // return false; eeprom_read( item_addr+1-sizeof(header), (unsigned char*)&header, sizeof(header) ); if((uint8_t)header.size_check!=(uint8_t)~header.size_low) return false; // TODO: Check length as well return true; } inline static settings_key_t settings_get_key_(eeprom_addr_t item_addr) { item_header_t header; eeprom_read( item_addr+1-sizeof(header), (unsigned char*)&header, sizeof(header) ); if((uint8_t)header.size_check!=(uint8_t)~header.size_low) return SETTINGS_INVALID_KEY; return header.key; } inline static size_t settings_get_value_length_(eeprom_addr_t item_addr) { item_header_t header; size_t ret = 0; eeprom_read( item_addr+1-sizeof(header), (unsigned char*)&header, sizeof(header) ); if((uint8_t)header.size_check!=(uint8_t)~header.size_low) goto bail; ret = header.size_low; if(ret&(1<<7)) { ret = ((ret&~(1<<7))<<8) | header.size_extra; } bail: return ret; } inline static eeprom_addr_t settings_get_value_addr_(eeprom_addr_t item_addr) { size_t len = settings_get_value_length_(item_addr); if(len>128) return item_addr+1-sizeof(item_header_t)-len; return item_addr+1-sizeof(item_header_t)+1-len; } inline static eeprom_addr_t settings_next_item_(eeprom_addr_t item_addr) { return settings_get_value_addr_(item_addr)-1; } //#pragma mark - //#pragma mark Public Functions bool settings_check(settings_key_t key,uint8_t index) { bool ret = false; eeprom_addr_t current_item = SETTINGS_TOP_ADDR; for(current_item=SETTINGS_TOP_ADDR;settings_is_item_valid_(current_item);current_item=settings_next_item_(current_item)) { if(settings_get_key_(current_item)==key) { if(!index) { ret = true; break; } else { // Nope, keep looking index--; } } } return ret; } settings_status_t settings_get(settings_key_t key,uint8_t index,unsigned char* value,size_t* value_size) { settings_status_t ret = SETTINGS_STATUS_NOT_FOUND; eeprom_addr_t current_item = SETTINGS_TOP_ADDR; for(current_item=SETTINGS_TOP_ADDR;settings_is_item_valid_(current_item);current_item=settings_next_item_(current_item)) { if(settings_get_key_(current_item)==key) { if(!index) { // We found it! *value_size = MIN(*value_size,settings_get_value_length_(current_item)); eeprom_read( settings_get_value_addr_(current_item), value, *value_size ); ret = SETTINGS_STATUS_OK; break; } else { // Nope, keep looking index--; } } } return ret; } settings_status_t settings_add(settings_key_t key,const unsigned char* value,size_t value_size) { settings_status_t ret = SETTINGS_STATUS_FAILURE; eeprom_addr_t current_item = SETTINGS_TOP_ADDR; item_header_t header; // Find end of list for(current_item=SETTINGS_TOP_ADDR;settings_is_item_valid_(current_item);current_item=settings_next_item_(current_item)); if(current_item==EEPROM_NULL) goto bail; // TODO: size check! header.key = key; if(value_size<0x80) { // If the value size is less than 128, then // we can get away with only using one byte // as the size. header.size_low = value_size; } else if(value_size<=SETTINGS_MAX_VALUE_SIZE) { // If the value size of larger than 128, // then we need to use two bytes. Store // the most significant 7 bits in the first // size byte (with MSB set) and store the // least significant bits in the second // byte (with LSB clear) header.size_low = (value_size>>7) | 0x80; header.size_extra = value_size & ~0x80; } else { // Value size way too big! goto bail; } header.size_check = ~header.size_low; // Write the header first eeprom_write( current_item+1-sizeof(header), (unsigned char*)&header, sizeof(header) ); // Sanity check, remove once confident if(settings_get_value_length_(current_item)!=value_size) { goto bail; } // Now write the data eeprom_write( settings_get_value_addr_(current_item), (unsigned char*)value, value_size ); ret = SETTINGS_STATUS_OK; bail: return ret; } settings_status_t settings_set(settings_key_t key,const unsigned char* value,size_t value_size) { settings_status_t ret = SETTINGS_STATUS_FAILURE; eeprom_addr_t current_item = SETTINGS_TOP_ADDR; for(current_item=SETTINGS_TOP_ADDR;settings_is_item_valid_(current_item);current_item=settings_next_item_(current_item)) { if(settings_get_key_(current_item)==key) { break; } } if((current_item==EEPROM_NULL) || !settings_is_item_valid_(current_item)) { ret = settings_add(key,value,value_size); goto bail; } if(value_size!=settings_get_value_length_(current_item)) { // Requires the settings store to be shifted. Currently unimplemented. goto bail; } // Now write the data eeprom_write( settings_get_value_addr_(current_item), (unsigned char*)value, value_size ); ret = SETTINGS_STATUS_OK; bail: return ret; } settings_status_t settings_delete(settings_key_t key,uint8_t index) { // Requires the settings store to be shifted. Currently unimplemented. // TODO: Writeme! return SETTINGS_STATUS_UNIMPLEMENTED; } void settings_wipe(void) { size_t i = SETTINGS_TOP_ADDR-SETTINGS_MAX_SIZE; for(;i<=SETTINGS_TOP_ADDR;i++) { eeprom_write_byte((uint8_t*)i,0xFF); wdt_reset(); } }