sysparam fixes, tests, spi flash refactoring (#299)
Original work by @ourairquality * Sysparam threadsafe and SPI access * Sysparam test cases * Fix for negative int8 * Sysparam getting bool without memory allocation. Bool tests. * SPI flash refactoring. * Extract common spiflash.c into core. * Use spiflash.c in sysparam. * Use memcpy in spiflash.c insted of hand-written version. * Tests for spiflash.c
This commit is contained in:
		
							parent
							
								
									07ca0d2e9e
								
							
						
					
					
						commit
						a91ec6eb61
					
				
					 10 changed files with 724 additions and 406 deletions
				
			
		|  | @ -36,4 +36,6 @@ typedef struct { | |||
|     uint32_t status_mask; | ||||
| } sdk_flashchip_t; | ||||
| 
 | ||||
| extern sdk_flashchip_t sdk_flashchip; | ||||
| 
 | ||||
| #endif /* _FLASHCHIP_H */ | ||||
|  |  | |||
|  | @ -21,14 +21,14 @@ | |||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #ifndef __ESP_SPIFFS_FLASH_H__ | ||||
| #define __ESP_SPIFFS_FLASH_H__ | ||||
| #ifndef __SPIFLASH_H__ | ||||
| #define __SPIFLASH_H__ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
| #include "common_macros.h" | ||||
| 
 | ||||
| #define ESP_SPIFFS_FLASH_OK        0 | ||||
| #define ESP_SPIFFS_FLASH_ERROR     1 | ||||
| #define SPI_FLASH_SECTOR_SIZE      4096 | ||||
| 
 | ||||
| /**
 | ||||
|  * Read data from SPI flash. | ||||
|  | @ -37,9 +37,9 @@ | |||
|  * @param buf Buffer to read to. Doesn't have to be aligned. | ||||
|  * @param size Size of data to read. Buffer size must be >= than data size. | ||||
|  * | ||||
|  * @return ESP_SPIFFS_FLASH_OK or ESP_SPIFFS_FLASH_ERROR | ||||
|  * @return true if success, otherwise false | ||||
|  */ | ||||
| uint32_t IRAM esp_spiffs_flash_read(uint32_t addr, uint8_t *buf, uint32_t size); | ||||
| bool IRAM spiflash_read(uint32_t addr, uint8_t *buf, uint32_t size); | ||||
| 
 | ||||
| /**
 | ||||
|  * Write data to SPI flash. | ||||
|  | @ -48,17 +48,17 @@ uint32_t IRAM esp_spiffs_flash_read(uint32_t addr, uint8_t *buf, uint32_t size); | |||
|  * @param buf Buffer of data to write to flash. Doesn't have to be aligned. | ||||
|  * @param size Size of data to write. Buffer size must be >= than data size. | ||||
|  * | ||||
|  * @return ESP_SPIFFS_FLASH_OK or ESP_SPIFFS_FLASH_ERROR | ||||
|  * @return true if success, otherwise false | ||||
|  */ | ||||
| uint32_t IRAM esp_spiffs_flash_write(uint32_t addr, uint8_t *buf, uint32_t size); | ||||
| bool IRAM spiflash_write(uint32_t addr, uint8_t *buf, uint32_t size); | ||||
| 
 | ||||
| /**
 | ||||
|  * Erase a sector. | ||||
|  * | ||||
|  * @param addr Address of sector to erase. Must be sector aligned. | ||||
|  * | ||||
|  * @return ESP_SPIFFS_FLASH_OK or ESP_SPIFFS_FLASH_ERROR | ||||
|  * @return true if success, otherwise false | ||||
|  */ | ||||
| uint32_t IRAM esp_spiffs_flash_erase_sector(uint32_t addr); | ||||
| bool IRAM spiflash_erase_sector(uint32_t addr); | ||||
| 
 | ||||
| #endif  // __ESP_SPIFFS_FLASH_H__
 | ||||
| #endif  // __SPIFLASH_H__
 | ||||
|  | @ -119,7 +119,7 @@ sysparam_status_t sysparam_init(uint32_t base_addr, uint32_t top_addr); | |||
|  *  you reformat the area currently being used, you will also need to call | ||||
|  *  sysparam_init() again afterward before you will be able to continue using | ||||
|  *  it. | ||||
|  */  | ||||
|  */ | ||||
| sysparam_status_t sysparam_create_area(uint32_t base_addr, uint16_t num_sectors, bool force); | ||||
| 
 | ||||
| /** Get the start address and size of the currently active sysparam area
 | ||||
|  | @ -180,24 +180,20 @@ sysparam_status_t sysparam_compact(); | |||
|  */ | ||||
| sysparam_status_t sysparam_get_data(const char *key, uint8_t **destptr, size_t *actual_length, bool *is_binary); | ||||
| 
 | ||||
| /** Get the value associated with a key (static buffers only)
 | ||||
| /** Get the value associated with a key (static value buffer)
 | ||||
|  * | ||||
|  *  This performs the same function as sysparam_get_data() but without | ||||
|  *  performing any memory allocations.  It can thus be used before the heap has | ||||
|  *  been configured or in other cases where using the heap would be a problem | ||||
|  *  (i.e. in an OOM handler, etc).  It requires that the caller pass in a | ||||
|  *  suitably sized buffer for the value to be read (if the supplied buffer is | ||||
|  *  not large enough, the returned value will be truncated and the full | ||||
|  *  required length will be returned in `actual_length`). | ||||
|  * | ||||
|  *  NOTE: In addition to being large enough for the value, the supplied buffer | ||||
|  *  must also be at least as large as the length of the key being requested. | ||||
|  *  If it is not, an error will be returned. | ||||
|  *  allocating memory for the result value.  It can thus be used before the heap | ||||
|  *  has been configured or in other cases where using the heap would be a | ||||
|  *  problem (i.e. in an OOM handler, etc).  It requires that the caller pass in | ||||
|  *  a suitably sized buffer for the value to be read (if the supplied buffer is | ||||
|  *  not large enough, the returned value will be truncated and the full required | ||||
|  *  length will be returned in `actual_length`). | ||||
|  * | ||||
|  *  @param[in]  key            Key name (zero-terminated string) | ||||
|  *  @param[in]  buffer         Pointer to a buffer to hold the returned value | ||||
|  *  @param[in]  buffer_size    Length of the supplied buffer in bytes | ||||
|  *  @param[out] actual_length  pointer to a location to hold the actual length | ||||
|  *  @param[in]  dest           Pointer to a buffer to hold the returned value. | ||||
|  *  @param[in]  dest_size      Length of the supplied buffer in bytes. | ||||
|  *  @param[out] actual_length  Pointer to a location to hold the actual length | ||||
|  *                             of the data which was associated with the key | ||||
|  *                             (may be NULL). | ||||
|  *  @param[out] is_binary      Pointer to a bool to hold whether the returned | ||||
|  | @ -210,10 +206,10 @@ sysparam_status_t sysparam_get_data(const char *key, uint8_t **destptr, size_t * | |||
|  *  @retval ::SYSPARAM_ERR_CORRUPT  Sysparam region has bad/corrupted data | ||||
|  *  @retval ::SYSPARAM_ERR_IO       I/O error reading/writing flash | ||||
|  */ | ||||
| sysparam_status_t sysparam_get_data_static(const char *key, uint8_t *buffer, size_t buffer_size, size_t *actual_length, bool *is_binary); | ||||
| sysparam_status_t sysparam_get_data_static(const char *key, uint8_t *dest, size_t dest_size, size_t *actual_length, bool *is_binary); | ||||
| 
 | ||||
| /** Get the string value associated with a key
 | ||||
|  *  | ||||
|  * | ||||
|  *  This routine can be used if you know that the value in a key will (or at | ||||
|  *  least should) be a string.  It will return a zero-terminated char buffer | ||||
|  *  containing the value retrieved. | ||||
|  | @ -240,9 +236,10 @@ sysparam_status_t sysparam_get_data_static(const char *key, uint8_t *buffer, siz | |||
| sysparam_status_t sysparam_get_string(const char *key, char **destptr); | ||||
| 
 | ||||
| /** Get the int32_t value associated with a key
 | ||||
|  *  | ||||
|  * | ||||
|  *  This routine can be used if you know that the value in a key will (or at | ||||
|  *  least should) be an int32_t value. | ||||
|  *  least should) be an int32_t value. This is done without allocating any | ||||
|  *  memory. | ||||
|  * | ||||
|  *  Note: If the status result is anything other than ::SYSPARAM_OK, the value | ||||
|  *  in `result` is not changed.  This means it is possible to set a default | ||||
|  | @ -266,7 +263,8 @@ sysparam_status_t sysparam_get_int32(const char *key, int32_t *result); | |||
| /** Get the int8_t value associated with a key
 | ||||
|  * | ||||
|  *  This routine can be used if you know that the value in a key will (or at | ||||
|  *  least should) be a uint8_t binary value. | ||||
|  *  least should) be a uint8_t binary value. This is done without allocating any | ||||
|  *  memory. | ||||
|  * | ||||
|  *  Note: If the status result is anything other than ::SYSPARAM_OK, the value | ||||
|  *  in `result` is not changed.  This means it is possible to set a default | ||||
|  | @ -288,7 +286,7 @@ sysparam_status_t sysparam_get_int32(const char *key, int32_t *result); | |||
| sysparam_status_t sysparam_get_int8(const char *key, int8_t *result); | ||||
| 
 | ||||
| /** Get the boolean value associated with a key
 | ||||
|  *  | ||||
|  * | ||||
|  *  This routine can be used if you know that the value in a key will (or at | ||||
|  *  least should) be a boolean setting.  It will read the specified value as a | ||||
|  *  text string and attempt to parse it as a boolean value. | ||||
|  | @ -320,7 +318,7 @@ sysparam_status_t sysparam_get_bool(const char *key, bool *result); | |||
|  * | ||||
|  *  The supplied value can be any data, up to 255 bytes in length.  If `value` | ||||
|  *  is NULL or `value_len` is 0, this is treated as a request to delete any | ||||
|  *  current entry matching `key`. | ||||
|  *  current entry matching `key`. This is done without allocating any memory. | ||||
|  * | ||||
|  *  If `binary` is true, the data will be considered binary (unprintable) data, | ||||
|  *  and this will be annotated in the saved entry.  This does not affect the | ||||
|  | @ -368,7 +366,8 @@ sysparam_status_t sysparam_set_string(const char *key, const char *value); | |||
| /** Set a key's value as a number
 | ||||
|  * | ||||
|  *  Write an int32_t binary value to the specified key. This does the inverse of | ||||
|  *  the sysparam_get_int32() function. | ||||
|  *  the sysparam_get_int32() function. This is done without allocating any | ||||
|  *  memory. | ||||
|  * | ||||
|  *  @param[in] key        Key name (zero-terminated string) | ||||
|  *  @param[in] value      Value to set | ||||
|  | @ -386,10 +385,8 @@ sysparam_status_t sysparam_set_int32(const char *key, int32_t value); | |||
| /** Set a key's value as a number
 | ||||
|  * | ||||
|  *  Write an int8_t binary value to the specified key. This does the inverse of | ||||
|  *  the sysparam_get_int8() function. | ||||
|  * | ||||
|  *  Note that if the key already contains a value which parses to the same | ||||
|  *  boolean (true/false) value, it is left unchanged. | ||||
|  *  the sysparam_get_int8() function. This is done without allocating any | ||||
|  *  memory. | ||||
|  * | ||||
|  *  @param[in] key        Key name (zero-terminated string) | ||||
|  *  @param[in] value      Value to set | ||||
|  |  | |||
|  | @ -21,12 +21,13 @@ | |||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| #include "esp_spiffs_flash.h" | ||||
| #include "flashchip.h" | ||||
| #include "espressif/spi_flash.h" | ||||
| #include "FreeRTOS.h" | ||||
| #include "esp/rom.h" | ||||
| #include "esp/spi_regs.h" | ||||
| #include "include/spiflash.h" | ||||
| 
 | ||||
| #include "include/flashchip.h" | ||||
| #include "include/esp/rom.h" | ||||
| #include "include/esp/spi_regs.h" | ||||
| 
 | ||||
| #include <FreeRTOS.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -52,25 +53,6 @@ | |||
| #define SPI_READ_MAX_SIZE   60 | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Copy unaligned data to 4-byte aligned destination buffer. | ||||
|  * | ||||
|  * @param words Number of 4-byte words to write. | ||||
|  * | ||||
|  * @see unaligned_memcpy.S | ||||
|  */ | ||||
| void memcpy_unaligned_src(volatile uint32_t *dst, uint8_t *src, uint8_t words); | ||||
| 
 | ||||
| /**
 | ||||
|  * Copy 4-byte aligned source data to unaligned destination buffer. | ||||
|  * | ||||
|  * @param bytes Number of byte to copy to dst. | ||||
|  * | ||||
|  * @see unaligned_memcpy.S | ||||
|  */ | ||||
| void memcpy_unaligned_dst(uint8_t *dst, volatile uint32_t *src, uint8_t bytes); | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Low level SPI flash write. Write block of data up to 64 bytes. | ||||
|  */ | ||||
|  | @ -86,7 +68,9 @@ static inline void IRAM spi_write_data(sdk_flashchip_t *chip, uint32_t addr, | |||
| 
 | ||||
|     SPI(0).ADDR = (addr & 0x00FFFFFF) | (size << 24); | ||||
| 
 | ||||
|     memcpy_unaligned_src(SPI(0).W, buf, words); | ||||
|     memcpy((void*)SPI(0).W, buf, words<<2); | ||||
| 
 | ||||
|     __asm__ volatile("memw"); | ||||
| 
 | ||||
|     SPI_write_enable(chip); | ||||
| 
 | ||||
|  | @ -97,16 +81,16 @@ static inline void IRAM spi_write_data(sdk_flashchip_t *chip, uint32_t addr, | |||
| /**
 | ||||
|  * Write a page of flash. Data block should not cross page boundary. | ||||
|  */ | ||||
| static uint32_t IRAM spi_write_page(sdk_flashchip_t *flashchip, uint32_t dest_addr, | ||||
| static bool IRAM spi_write_page(sdk_flashchip_t *flashchip, uint32_t dest_addr, | ||||
|     uint8_t *buf, uint32_t size) | ||||
| { | ||||
|     // check if block to write doesn't cross page boundary
 | ||||
|     if (flashchip->page_size < size + (dest_addr % flashchip->page_size)) { | ||||
|         return ESP_SPIFFS_FLASH_ERROR; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (size < 1) { | ||||
|         return ESP_SPIFFS_FLASH_OK; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     while (size >= SPI_WRITE_MAX_SIZE) { | ||||
|  | @ -117,58 +101,58 @@ static uint32_t IRAM spi_write_page(sdk_flashchip_t *flashchip, uint32_t dest_ad | |||
|         buf += SPI_WRITE_MAX_SIZE; | ||||
| 
 | ||||
|         if (size < 1) { | ||||
|             return ESP_SPIFFS_FLASH_OK; | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     spi_write_data(flashchip, dest_addr, buf, size); | ||||
| 
 | ||||
|     return ESP_SPIFFS_FLASH_OK; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Split block of data into pages and write pages. | ||||
|  */ | ||||
| static uint32_t IRAM spi_write(uint32_t addr, uint8_t *dst, uint32_t size) | ||||
| static bool IRAM spi_write(uint32_t addr, uint8_t *dst, uint32_t size) | ||||
| { | ||||
|     if (sdk_flashchip.chip_size < (addr + size)) { | ||||
|         return ESP_SPIFFS_FLASH_ERROR; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     uint32_t write_bytes_to_page = sdk_flashchip.page_size - | ||||
|         (addr % sdk_flashchip.page_size);  // TODO: place for optimization
 | ||||
| 
 | ||||
|     if (size < write_bytes_to_page) { | ||||
|         if (spi_write_page(&sdk_flashchip, addr, dst, size)) { | ||||
|             return ESP_SPIFFS_FLASH_ERROR; | ||||
|         if (!spi_write_page(&sdk_flashchip, addr, dst, size)) { | ||||
|             return false; | ||||
|         } | ||||
|     } else { | ||||
|         if (spi_write_page(&sdk_flashchip, addr, dst, write_bytes_to_page)) { | ||||
|             return ESP_SPIFFS_FLASH_ERROR; | ||||
|         if (!spi_write_page(&sdk_flashchip, addr, dst, write_bytes_to_page)) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         uint32_t offset = write_bytes_to_page; | ||||
|         uint32_t pages_to_write = (size - offset) / sdk_flashchip.page_size; | ||||
|         for (uint8_t i = 0; i != pages_to_write; i++) { | ||||
|             if (spi_write_page(&sdk_flashchip, addr + offset, | ||||
|             if (!spi_write_page(&sdk_flashchip, addr + offset, | ||||
|                         dst + offset, sdk_flashchip.page_size)) { | ||||
|                 return ESP_SPIFFS_FLASH_ERROR; | ||||
|                 return false; | ||||
|             } | ||||
|             offset += sdk_flashchip.page_size; | ||||
|         } | ||||
| 
 | ||||
|         if (spi_write_page(&sdk_flashchip, addr + offset, | ||||
|         if (!spi_write_page(&sdk_flashchip, addr + offset, | ||||
|                     dst + offset, size - offset)) { | ||||
|             return ESP_SPIFFS_FLASH_ERROR; | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return ESP_SPIFFS_FLASH_OK; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| uint32_t IRAM esp_spiffs_flash_write(uint32_t addr, uint8_t *buf, uint32_t size) | ||||
| bool IRAM spiflash_write(uint32_t addr, uint8_t *buf, uint32_t size) | ||||
| { | ||||
|     uint32_t result = ESP_SPIFFS_FLASH_ERROR; | ||||
|     bool result = false; | ||||
| 
 | ||||
|     if (buf) { | ||||
|         vPortEnterCritical(); | ||||
|  | @ -197,21 +181,23 @@ static inline void IRAM read_block(sdk_flashchip_t *chip, uint32_t addr, | |||
| 
 | ||||
|     while (SPI(0).CMD) {}; | ||||
| 
 | ||||
|     memcpy_unaligned_dst(buf, SPI(0).W, size); | ||||
|     __asm__ volatile("memw"); | ||||
| 
 | ||||
|     memcpy(buf, (const void*)SPI(0).W, size); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Read SPI flash data. Data region doesn't need to be page aligned. | ||||
|  */ | ||||
| static inline uint32_t IRAM read_data(sdk_flashchip_t *flashchip, uint32_t addr, | ||||
| static inline bool IRAM read_data(sdk_flashchip_t *flashchip, uint32_t addr, | ||||
|         uint8_t *dst, uint32_t size) | ||||
| { | ||||
|     if (size < 1) { | ||||
|         return ESP_SPIFFS_FLASH_OK; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     if ((addr + size) > flashchip->chip_size) { | ||||
|         return ESP_SPIFFS_FLASH_ERROR; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     while (size >= SPI_READ_MAX_SIZE) { | ||||
|  | @ -225,12 +211,12 @@ static inline uint32_t IRAM read_data(sdk_flashchip_t *flashchip, uint32_t addr, | |||
|         read_block(flashchip, addr, dst, size); | ||||
|     } | ||||
| 
 | ||||
|     return ESP_SPIFFS_FLASH_OK; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| uint32_t IRAM esp_spiffs_flash_read(uint32_t dest_addr, uint8_t *buf, uint32_t size) | ||||
| bool IRAM spiflash_read(uint32_t dest_addr, uint8_t *buf, uint32_t size) | ||||
| { | ||||
|     uint32_t result = ESP_SPIFFS_FLASH_ERROR; | ||||
|     bool result = false; | ||||
| 
 | ||||
|     if (buf) { | ||||
|         vPortEnterCritical(); | ||||
|  | @ -245,14 +231,14 @@ uint32_t IRAM esp_spiffs_flash_read(uint32_t dest_addr, uint8_t *buf, uint32_t s | |||
|     return result; | ||||
| } | ||||
| 
 | ||||
| uint32_t IRAM esp_spiffs_flash_erase_sector(uint32_t addr) | ||||
| bool IRAM spiflash_erase_sector(uint32_t addr) | ||||
| { | ||||
|     if ((addr + sdk_flashchip.sector_size) > sdk_flashchip.chip_size) { | ||||
|         return ESP_SPIFFS_FLASH_ERROR; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (addr & 0xFFF) { | ||||
|         return ESP_SPIFFS_FLASH_ERROR; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     vPortEnterCritical(); | ||||
|  | @ -269,5 +255,5 @@ uint32_t IRAM esp_spiffs_flash_erase_sector(uint32_t addr) | |||
|     Cache_Read_Enable(0, 0, 1); | ||||
|     vPortExitCritical(); | ||||
| 
 | ||||
|     return ESP_SPIFFS_FLASH_OK; | ||||
|     return true; | ||||
| } | ||||
							
								
								
									
										371
									
								
								core/sysparam.c
									
										
									
									
									
								
							
							
						
						
									
										371
									
								
								core/sysparam.c
									
										
									
									
									
								
							|  | @ -8,14 +8,12 @@ | |||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <sysparam.h> | ||||
| #include <espressif/spi_flash.h> | ||||
| #include "spiflash.h" | ||||
| #include "flashchip.h" | ||||
| #include <common_macros.h> | ||||
| #include "FreeRTOS.h" | ||||
| #include "semphr.h" | ||||
| 
 | ||||
| //TODO: make this properly threadsafe
 | ||||
| //TODO: reduce stack usage
 | ||||
| 
 | ||||
| /* The "magic" value that indicates the start of a sysparam region in flash.
 | ||||
|  */ | ||||
| #define SYSPARAM_MAGIC 0x70524f45 // "EORp" in little-endian
 | ||||
|  | @ -33,11 +31,14 @@ | |||
|  */ | ||||
| #define SCAN_BUFFER_SIZE 8 // words
 | ||||
| 
 | ||||
| /* The size of the temporary buffer used for reading back and verifying data
 | ||||
|  * written to flash.  Making this larger will make the write-and-verify | ||||
|  * operation slightly faster, but will use more heap during writes | ||||
| /* The size in words of the buffer used for reading keys when searching for a
 | ||||
|  * match, for reading payloads to check if the value has changed, and reading | ||||
|  * back from the flash to verify writes. Will work well if big enough for | ||||
|  * commonly used keys, and must be at least one word. Stack allocated so not too | ||||
|  * large! | ||||
|  */ | ||||
| #define VERIFY_BUF_SIZE 64 | ||||
| #define BOUNCE_BUFFER_WORDS 3 | ||||
| #define BOUNCE_BUFFER_SIZE (BOUNCE_BUFFER_WORDS * sizeof(uint32_t)) | ||||
| 
 | ||||
| /* Size of region/entry headers.  These should not normally need tweaking (and
 | ||||
|  * will probably require some code changes if they are tweaked). | ||||
|  | @ -76,14 +77,16 @@ | |||
| /******************************* Useful Macros *******************************/ | ||||
| 
 | ||||
| #define ROUND_TO_WORD_BOUNDARY(x) (((x) + 3) & 0xfffffffc) | ||||
| #define ENTRY_SIZE(payload_len) (ENTRY_HEADER_SIZE + ROUND_TO_WORD_BOUNDARY(payload_len)) | ||||
| #define ENTRY_SIZE(payload_len) (ENTRY_HEADER_SIZE + payload_len) | ||||
| 
 | ||||
| #define max(x, y) ((x) > (y) ? (x) : (y)) | ||||
| #define min(x, y) ((x) < (y) ? (x) : (y)) | ||||
| 
 | ||||
| #define debug(level, format, ...) if (SYSPARAM_DEBUG >= (level)) { printf("%s" format "\n", "sysparam: ", ## __VA_ARGS__); } | ||||
| 
 | ||||
| #define CHECK_FLASH_OP(x) do { int __x = (x); if ((__x) != SPI_FLASH_RESULT_OK) { debug(1, "FLASH ERR: %d", __x); return SYSPARAM_ERR_IO; } } while (0); | ||||
| #define CHECK_FLASH_OP(x) do { bool __x = (x); if (!(__x)) { \ | ||||
|         debug(1, "FLASH ERR: %d", __x); return SYSPARAM_ERR_IO; \ | ||||
|     } } while (0); | ||||
| 
 | ||||
| /********************* Internal datatypes and structures *********************/ | ||||
| 
 | ||||
|  | @ -119,50 +122,28 @@ static struct { | |||
| 
 | ||||
| /***************************** Internal routines *****************************/ | ||||
| 
 | ||||
| static inline IRAM sysparam_status_t _do_write(uint32_t addr, const void *data, size_t data_size) { | ||||
|     CHECK_FLASH_OP(sdk_spi_flash_write(addr, (void*) data, data_size)); | ||||
|     return SYSPARAM_OK; | ||||
| } | ||||
| static sysparam_status_t _write_and_verify(uint32_t addr, const void *data, size_t data_size) { | ||||
|     uint8_t bounce[BOUNCE_BUFFER_SIZE]; | ||||
| 
 | ||||
| static inline IRAM sysparam_status_t _do_verify(uint32_t addr, const void *data, void *buffer, size_t len) { | ||||
|     CHECK_FLASH_OP(sdk_spi_flash_read(addr, buffer, len)); | ||||
|     if (memcmp(data, buffer, len)) { | ||||
|         return SYSPARAM_ERR_IO; | ||||
|     for (int i = 0; i < data_size; i += BOUNCE_BUFFER_SIZE) { | ||||
|         size_t count = min(data_size - i, BOUNCE_BUFFER_SIZE); | ||||
|         memcpy(bounce, data + i, count); | ||||
|         CHECK_FLASH_OP(spiflash_write(addr + i, bounce, count)); | ||||
|         CHECK_FLASH_OP(spiflash_read(addr + i, bounce, count)); | ||||
|         if (memcmp(data + i, bounce, count) != 0) { | ||||
|             debug(1, "Flash write (@ 0x%08x) verify failed!", addr); | ||||
|             return SYSPARAM_ERR_IO; | ||||
|         } | ||||
|     } | ||||
|     return SYSPARAM_OK; | ||||
| } | ||||
| 
 | ||||
| /*FIXME: Eventually, this should probably be implemented down at the SPI flash library layer, where it can just compare bytes/words straight from the SPI hardware buffer instead of allocating a whole separate temp buffer, reading chunks into that, and then doing a memcmp.. */ | ||||
| static IRAM sysparam_status_t _write_and_verify(uint32_t addr, const void *data, size_t data_size) { | ||||
|     int i; | ||||
|     size_t count; | ||||
|     sysparam_status_t status = SYSPARAM_OK; | ||||
|     uint8_t *verify_buf = malloc(VERIFY_BUF_SIZE); | ||||
| 
 | ||||
|     if (!verify_buf) return SYSPARAM_ERR_NOMEM; | ||||
|     do { | ||||
|         status = _do_write(addr, data, data_size); | ||||
|         if (status != SYSPARAM_OK) break; | ||||
|         for (i = 0; i < data_size; i += VERIFY_BUF_SIZE) { | ||||
|             count = min(data_size - i, VERIFY_BUF_SIZE); | ||||
|             status = _do_verify(addr + i, data + i, verify_buf, count); | ||||
|             if (status != SYSPARAM_OK) { | ||||
|                 debug(1, "Flash write (@ 0x%08x) verify failed!", addr); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } while (false); | ||||
|     free(verify_buf); | ||||
|     return status; | ||||
| } | ||||
| 
 | ||||
| /** Erase the sectors of a region */ | ||||
| static sysparam_status_t _format_region(uint32_t addr, uint16_t num_sectors) { | ||||
|     uint16_t sector = addr / sdk_flashchip.sector_size; | ||||
|     int i; | ||||
| 
 | ||||
|     for (i = 0; i < num_sectors; i++) { | ||||
|         CHECK_FLASH_OP(sdk_spi_flash_erase_sector(sector + i)); | ||||
|         CHECK_FLASH_OP(spiflash_erase_sector(addr + (i * SPI_FLASH_SECTOR_SIZE))); | ||||
|     } | ||||
|     return SYSPARAM_OK; | ||||
| } | ||||
|  | @ -212,7 +193,7 @@ static sysparam_status_t init_write_context(struct sysparam_context *ctx) { | |||
|     memset(ctx, 0, sizeof(*ctx)); | ||||
|     ctx->addr = _sysparam_info.end_addr; | ||||
|     debug(3, "read entry header @ 0x%08x", ctx->addr); | ||||
|     CHECK_FLASH_OP(sdk_spi_flash_read(ctx->addr, (void*) &ctx->entry, ENTRY_HEADER_SIZE)); | ||||
|     CHECK_FLASH_OP(spiflash_read(ctx->addr, (void*) &ctx->entry, ENTRY_HEADER_SIZE)); | ||||
|     return SYSPARAM_OK; | ||||
| } | ||||
| 
 | ||||
|  | @ -238,7 +219,10 @@ static sysparam_status_t _find_entry(struct sysparam_context *ctx, uint16_t matc | |||
|                 // workaround is to make sure that the next write operation
 | ||||
|                 // will always start with a compaction, which will leave off
 | ||||
|                 // the invalid data at the end and fix the issue going forward.
 | ||||
|                 debug(1, "Encountered entry with invalid length (0x%04x) @ 0x%08x (region end is 0x%08x).  Truncating entries.", ctx->entry.len, ctx->addr, _sysparam_info.end_addr); | ||||
|                 debug(1, "Encountered entry with invalid length (0x%04x) @ 0x%08x (region end is 0x%08x).  Truncating entries.", | ||||
|                         ctx->entry.len, | ||||
|                         ctx->addr, _sysparam_info.end_addr); | ||||
| 
 | ||||
|                 _sysparam_info.force_compact = true; | ||||
|                 break; | ||||
|             } | ||||
|  | @ -251,7 +235,7 @@ static sysparam_status_t _find_entry(struct sysparam_context *ctx, uint16_t matc | |||
|         } | ||||
| 
 | ||||
|         debug(3, "read entry header @ 0x%08x", ctx->addr); | ||||
|         CHECK_FLASH_OP(sdk_spi_flash_read(ctx->addr, (void*) &ctx->entry, ENTRY_HEADER_SIZE)); | ||||
|         CHECK_FLASH_OP(spiflash_read(ctx->addr, (void*) &ctx->entry, ENTRY_HEADER_SIZE)); | ||||
|         debug(3, "  idflags = 0x%04x", ctx->entry.idflags); | ||||
|         if (ctx->entry.idflags == 0xffff) { | ||||
|             // 0xffff is never a valid id field, so this means we've hit the
 | ||||
|  | @ -295,17 +279,39 @@ static sysparam_status_t _find_entry(struct sysparam_context *ctx, uint16_t matc | |||
| } | ||||
| 
 | ||||
| /** Read the payload from the current entry pointed to by `ctx` */ | ||||
| 
 | ||||
| static inline sysparam_status_t _read_payload(struct sysparam_context *ctx, uint8_t *buffer, size_t buffer_size) { | ||||
|     debug(3, "read payload (%d) @ 0x%08x", min(buffer_size, ctx->entry.len), ctx->addr); | ||||
|     CHECK_FLASH_OP(sdk_spi_flash_read(ctx->addr + ENTRY_HEADER_SIZE, (void*) buffer, min(buffer_size, ctx->entry.len))); | ||||
|     uint32_t addr = ctx->addr + ENTRY_HEADER_SIZE; | ||||
|     size_t size = min(buffer_size, ctx->entry.len); | ||||
|     debug(3, "read payload (%d) @ 0x%08x", size, addr); | ||||
| 
 | ||||
|     CHECK_FLASH_OP(spiflash_read(addr, buffer, buffer_size)); | ||||
| 
 | ||||
|     return SYSPARAM_OK; | ||||
| } | ||||
| 
 | ||||
| static inline sysparam_status_t _compare_payload(struct sysparam_context *ctx, uint8_t *value, size_t size) { | ||||
|     debug(3, "compare payload (%d) @ 0x%08x", size, ctx->addr); | ||||
|     if (ctx->entry.len != size) return SYSPARAM_NOTFOUND; | ||||
|     uint32_t bounce[BOUNCE_BUFFER_WORDS]; | ||||
|     uint32_t addr = ctx->addr + ENTRY_HEADER_SIZE; | ||||
|     int i; | ||||
|     for (i = 0; i < size; i += BOUNCE_BUFFER_SIZE) { | ||||
|         int len = min(size - i, BOUNCE_BUFFER_SIZE); | ||||
|         CHECK_FLASH_OP(spiflash_read(addr + i, (void*)bounce, len)); | ||||
|         if (memcmp(value + i, bounce, len)) { | ||||
|             // Mismatch.
 | ||||
|             return SYSPARAM_NOTFOUND; | ||||
|         } | ||||
|     } | ||||
|     return SYSPARAM_OK; | ||||
| } | ||||
| 
 | ||||
| /** Find the entry corresponding to the specified key name */ | ||||
| static sysparam_status_t _find_key(struct sysparam_context *ctx, const char *key, uint16_t key_len, uint8_t *buffer) { | ||||
| static sysparam_status_t _find_key(struct sysparam_context *ctx, const char *key, uint16_t key_len) { | ||||
|     sysparam_status_t status; | ||||
| 
 | ||||
|     debug(3, "find key: %s", key ? key : "(null)"); | ||||
|     debug(3, "find key len %d: %s", key_len, key ? key : "(null)"); | ||||
|     while (true) { | ||||
|         // Find the next key entry
 | ||||
|         status = _find_entry(ctx, ENTRY_ID_ANY, false); | ||||
|  | @ -316,12 +322,12 @@ static sysparam_status_t _find_key(struct sysparam_context *ctx, const char *key | |||
|             break; | ||||
|         } | ||||
|         if (ctx->entry.len == key_len) { | ||||
|             status = _read_payload(ctx, buffer, key_len); | ||||
|             if (status < 0) return status; | ||||
|             if (!memcmp(key, buffer, key_len)) { | ||||
|             status = _compare_payload(ctx, (uint8_t *)key, key_len); | ||||
|             if (status == SYSPARAM_OK) { | ||||
|                 // We have a match
 | ||||
|                 break; | ||||
|             } | ||||
|             if (status != SYSPARAM_NOTFOUND) return status; | ||||
|             debug(3, "entry payload does not match"); | ||||
|         } else { | ||||
|             debug(3, "key length (%d) does not match (%d)", ctx->entry.len, key_len); | ||||
|  | @ -394,13 +400,11 @@ static inline sysparam_status_t _delete_entry(uint32_t addr) { | |||
| 
 | ||||
|     debug(2, "Deleting entry @ 0x%08x", addr); | ||||
|     debug(3, "read entry header @ 0x%08x", addr); | ||||
|     CHECK_FLASH_OP(sdk_spi_flash_read(addr, (void*) &entry, ENTRY_HEADER_SIZE)); | ||||
|     CHECK_FLASH_OP(spiflash_read(addr, (uint8_t*) &entry, ENTRY_HEADER_SIZE)); | ||||
|     // Set the ID to zero to mark it as "deleted"
 | ||||
|     entry.idflags &= ~ENTRY_FLAG_ALIVE; | ||||
|     debug(3, "write entry header @ 0x%08x", addr); | ||||
|     CHECK_FLASH_OP(sdk_spi_flash_write(addr, (void*) &entry, ENTRY_HEADER_SIZE)); | ||||
| 
 | ||||
|     return SYSPARAM_OK; | ||||
|     return _write_and_verify(addr, &entry, ENTRY_HEADER_SIZE); | ||||
| } | ||||
| 
 | ||||
| /** Compact the current region, removing all deleted/unused entries, and write
 | ||||
|  | @ -424,7 +428,11 @@ static sysparam_status_t _compact_params(struct sysparam_context *ctx, int *key_ | |||
|     uint16_t binary_flag; | ||||
|     uint16_t num_sectors = _sysparam_info.region_size / sdk_flashchip.sector_size; | ||||
| 
 | ||||
|     debug(1, "compacting region (current size %d, expect to recover %d%s bytes)...", _sysparam_info.end_addr - _sysparam_info.cur_base, ctx ? ctx->compactable : 0, (ctx && ctx->unused_keys > 0) ? "+ (unused keys present)" : ""); | ||||
|     debug(1, "compacting region (current size %d, expect to recover %d%s bytes)...", | ||||
|             _sysparam_info.end_addr - _sysparam_info.cur_base, | ||||
|             ctx ? ctx->compactable : 0, | ||||
|             (ctx && ctx->unused_keys > 0) ? "+ (unused keys present)" : ""); | ||||
| 
 | ||||
|     status = _format_region(new_base, num_sectors); | ||||
|     if (status < 0) return status; | ||||
|     status = sysparam_iter_start(&iter); | ||||
|  | @ -505,7 +513,7 @@ sysparam_status_t sysparam_init(uint32_t base_addr, uint32_t top_addr) { | |||
|         top_addr = base_addr + sdk_flashchip.sector_size; | ||||
|     } | ||||
|     for (addr0 = base_addr; addr0 < top_addr; addr0 += sdk_flashchip.sector_size) { | ||||
|         CHECK_FLASH_OP(sdk_spi_flash_read(addr0, (void*) &header0, REGION_HEADER_SIZE)); | ||||
|         CHECK_FLASH_OP(spiflash_read(addr0, (void*) &header0, REGION_HEADER_SIZE)); | ||||
|         if (header0.magic == SYSPARAM_MAGIC) { | ||||
|             // Found a starting point...
 | ||||
|             break; | ||||
|  | @ -523,7 +531,7 @@ sysparam_status_t sysparam_init(uint32_t base_addr, uint32_t top_addr) { | |||
|     } else { | ||||
|         addr1 = addr0 + num_sectors * sdk_flashchip.sector_size; | ||||
|     } | ||||
|     CHECK_FLASH_OP(sdk_spi_flash_read(addr1, (void*) &header1, REGION_HEADER_SIZE)); | ||||
|     CHECK_FLASH_OP(spiflash_read(addr1, (uint8_t*) &header1, REGION_HEADER_SIZE)); | ||||
| 
 | ||||
|     if (header1.magic == SYSPARAM_MAGIC) { | ||||
|         // Yay! Found the other one.  Sanity-check it..
 | ||||
|  | @ -600,7 +608,7 @@ sysparam_status_t sysparam_create_area(uint32_t base_addr, uint16_t num_sectors, | |||
|         // we're not going to be clobbering something else important.
 | ||||
|         for (addr = base_addr; addr < base_addr + region_size * 2; addr += SCAN_BUFFER_SIZE) { | ||||
|             debug(3, "read %d words @ 0x%08x", SCAN_BUFFER_SIZE, addr); | ||||
|             CHECK_FLASH_OP(sdk_spi_flash_read(addr, buffer, SCAN_BUFFER_SIZE * 4)); | ||||
|             CHECK_FLASH_OP(spiflash_read(addr, (uint8_t*)buffer, SCAN_BUFFER_SIZE * 4)); | ||||
|             for (i = 0; i < SCAN_BUFFER_SIZE; i++) { | ||||
|                 if (buffer[i] != 0xffffffff) { | ||||
|                     // Uh oh, not empty.
 | ||||
|  | @ -655,70 +663,79 @@ sysparam_status_t sysparam_get_data(const char *key, uint8_t **destptr, size_t * | |||
|     sysparam_status_t status; | ||||
|     size_t key_len = strlen(key); | ||||
|     uint8_t *buffer; | ||||
|     uint8_t *newbuf; | ||||
|     | ||||
|     if (!_sysparam_info.cur_base) return SYSPARAM_ERR_NOINIT; | ||||
| 
 | ||||
|     buffer = malloc(key_len + 2); | ||||
|     if (!buffer) return SYSPARAM_ERR_NOMEM; | ||||
|     do { | ||||
|         _init_context(&ctx); | ||||
|         status = _find_key(&ctx, key, key_len, buffer); | ||||
|         if (status != SYSPARAM_OK) break; | ||||
|     xSemaphoreTake(_sysparam_info.sem, portMAX_DELAY); | ||||
| 
 | ||||
|         // Find the associated value
 | ||||
|         status = _find_value(&ctx, ctx.entry.idflags); | ||||
|         if (status != SYSPARAM_OK) break; | ||||
| 
 | ||||
|         newbuf = realloc(buffer, ctx.entry.len + 1); | ||||
|         if (!newbuf) { | ||||
|             status = SYSPARAM_ERR_NOMEM; | ||||
|             break; | ||||
|         } | ||||
|         buffer = newbuf; | ||||
|         status = _read_payload(&ctx, buffer, ctx.entry.len); | ||||
|         if (status != SYSPARAM_OK) break; | ||||
| 
 | ||||
|         // Zero-terminate the result, just in case (doesn't hurt anything for
 | ||||
|         // non-string data, and can avoid nasty mistakes if the caller wants to
 | ||||
|         // interpret the result as a string).
 | ||||
|         buffer[ctx.entry.len] = 0; | ||||
| 
 | ||||
|         *destptr = buffer; | ||||
|         if (actual_length) *actual_length = ctx.entry.len; | ||||
|         if (is_binary) *is_binary = (bool)(ctx.entry.idflags & ENTRY_FLAG_BINARY); | ||||
|         return SYSPARAM_OK; | ||||
|     } while (false); | ||||
| 
 | ||||
|     free(buffer); | ||||
|     if (actual_length) *actual_length = 0; | ||||
| 
 | ||||
|     if (!_sysparam_info.cur_base) { | ||||
|         status = SYSPARAM_ERR_NOINIT; | ||||
|         goto done; | ||||
|     } | ||||
| 
 | ||||
|     _init_context(&ctx); | ||||
|     status = _find_key(&ctx, key, key_len); | ||||
|     if (status != SYSPARAM_OK) goto done; | ||||
| 
 | ||||
|     // Find the associated value
 | ||||
|     status = _find_value(&ctx, ctx.entry.idflags); | ||||
|     if (status != SYSPARAM_OK) goto done; | ||||
| 
 | ||||
|     buffer = malloc(ctx.entry.len + 1); | ||||
|     if (!buffer) { | ||||
|         status = SYSPARAM_ERR_NOMEM; | ||||
|         goto done; | ||||
|     } | ||||
| 
 | ||||
|     status = _read_payload(&ctx, buffer, ctx.entry.len); | ||||
|     if (status != SYSPARAM_OK) { | ||||
|         free(buffer); | ||||
|         goto done; | ||||
|     } | ||||
| 
 | ||||
|     // Zero-terminate the result, just in case (doesn't hurt anything for
 | ||||
|     // non-string data, and can avoid nasty mistakes if the caller wants to
 | ||||
|     // interpret the result as a string).
 | ||||
|     buffer[ctx.entry.len] = 0; | ||||
| 
 | ||||
|     *destptr = buffer; | ||||
|     if (actual_length) *actual_length = ctx.entry.len; | ||||
|     if (is_binary) *is_binary = (bool)(ctx.entry.idflags & ENTRY_FLAG_BINARY); | ||||
|     status = SYSPARAM_OK; | ||||
| 
 | ||||
|  done: | ||||
|     xSemaphoreGive(_sysparam_info.sem); | ||||
|     return status; | ||||
| } | ||||
| 
 | ||||
| sysparam_status_t sysparam_get_data_static(const char *key, uint8_t *buffer, size_t buffer_size, size_t *actual_length, bool *is_binary) { | ||||
| sysparam_status_t sysparam_get_data_static(const char *key, uint8_t *dest, size_t dest_size, size_t *actual_length, bool *is_binary) { | ||||
|     struct sysparam_context ctx; | ||||
|     sysparam_status_t status = SYSPARAM_OK; | ||||
|     size_t key_len = strlen(key); | ||||
| 
 | ||||
|     if (!_sysparam_info.cur_base) return SYSPARAM_ERR_NOINIT; | ||||
| 
 | ||||
|     // Supplied buffer must be at least as large as the key, or 2 bytes,
 | ||||
|     // whichever is larger.
 | ||||
|     if (buffer_size < max(key_len, 2)) return SYSPARAM_ERR_NOMEM; | ||||
|     xSemaphoreTake(_sysparam_info.sem, portMAX_DELAY); | ||||
| 
 | ||||
|     if (actual_length) *actual_length = 0; | ||||
| 
 | ||||
|     if (!_sysparam_info.cur_base) { | ||||
|         status = SYSPARAM_ERR_NOINIT; | ||||
|         goto done; | ||||
|     } | ||||
| 
 | ||||
|     _init_context(&ctx); | ||||
|     status = _find_key(&ctx, key, key_len, buffer); | ||||
|     if (status != SYSPARAM_OK) return status; | ||||
|     status = _find_key(&ctx, key, key_len); | ||||
|     if (status != SYSPARAM_OK) goto done; | ||||
|     status = _find_value(&ctx, ctx.entry.idflags); | ||||
|     if (status != SYSPARAM_OK) return status; | ||||
|     status = _read_payload(&ctx, buffer, buffer_size); | ||||
|     if (status != SYSPARAM_OK) return status; | ||||
|     if (status != SYSPARAM_OK) goto done; | ||||
|     status = _read_payload(&ctx, dest, dest_size); | ||||
|     if (status != SYSPARAM_OK) goto done; | ||||
| 
 | ||||
|     if (actual_length) *actual_length = ctx.entry.len; | ||||
|     if (is_binary) *is_binary = (bool)(ctx.entry.idflags & ENTRY_FLAG_BINARY); | ||||
|     return SYSPARAM_OK; | ||||
| 
 | ||||
|  done: | ||||
|     xSemaphoreGive(_sysparam_info.sem); | ||||
|     return status; | ||||
| } | ||||
| 
 | ||||
| sysparam_status_t sysparam_get_string(const char *key, char **destptr) { | ||||
|  | @ -741,63 +758,78 @@ sysparam_status_t sysparam_get_string(const char *key, char **destptr) { | |||
| } | ||||
| 
 | ||||
| sysparam_status_t sysparam_get_int32(const char *key, int32_t *result) { | ||||
|     char *buffer; | ||||
|     char *endptr; | ||||
|     int32_t value; | ||||
|     size_t actual_length; | ||||
|     bool is_binary; | ||||
|     sysparam_status_t status; | ||||
| 
 | ||||
|     status = sysparam_get_string(key, &buffer); | ||||
|     status = sysparam_get_data_static(key, (uint8_t *)&value, sizeof(int32_t), | ||||
|                                       &actual_length, &is_binary); | ||||
|     if (status != SYSPARAM_OK) return status; | ||||
|     value = strtol(buffer, &endptr, 0); | ||||
|     if (*endptr) { | ||||
|         // There was extra crap at the end of the string.
 | ||||
|         free(buffer); | ||||
|     if (!is_binary || actual_length != sizeof(int32_t)) | ||||
|         return SYSPARAM_PARSEFAILED; | ||||
|     } | ||||
| 
 | ||||
|     *result = value; | ||||
|     free(buffer); | ||||
|     return SYSPARAM_OK; | ||||
|     return status; | ||||
| } | ||||
| 
 | ||||
| sysparam_status_t sysparam_get_int8(const char *key, int8_t *result) { | ||||
|     int32_t value; | ||||
|     int8_t value; | ||||
|     size_t actual_length; | ||||
|     bool is_binary; | ||||
|     sysparam_status_t status; | ||||
| 
 | ||||
|     status = sysparam_get_int32(key, &value); | ||||
|     if (status == SYSPARAM_OK) { | ||||
|         *result = value; | ||||
|     } | ||||
|     status = sysparam_get_data_static(key, (uint8_t *)&value, sizeof(int8_t), | ||||
|                                       &actual_length, &is_binary); | ||||
|     if (status != SYSPARAM_OK) return status; | ||||
|     if (!is_binary || actual_length != sizeof(int8_t)) | ||||
|         return SYSPARAM_PARSEFAILED; | ||||
|     *result = value; | ||||
|     return status; | ||||
| } | ||||
| 
 | ||||
| sysparam_status_t sysparam_get_bool(const char *key, bool *result) { | ||||
|     char *buffer; | ||||
|     const size_t buf_size = 8; | ||||
|     char buf[buf_size + 1];  // extra byte for zero termination
 | ||||
|     size_t data_len = 0; | ||||
|     bool binary = false; | ||||
|     sysparam_status_t status; | ||||
| 
 | ||||
|     status = sysparam_get_string(key, &buffer); | ||||
|     status = sysparam_get_data_static(key, (uint8_t*)buf, | ||||
|             buf_size, &data_len, &binary); | ||||
| 
 | ||||
|     if (status != SYSPARAM_OK) return status; | ||||
|     do { | ||||
|         if (!strcasecmp(buffer, "y")    || | ||||
|             !strcasecmp(buffer, "yes")  || | ||||
|             !strcasecmp(buffer, "t")    || | ||||
|             !strcasecmp(buffer, "true") || | ||||
|             !strcmp(buffer, "1")) { | ||||
|         if (binary) { | ||||
|             if (data_len == 1) {  // int8 value
 | ||||
|                 *result = (int8_t)(*buf) ? true : false; | ||||
|             } else if (data_len == 4) {  // int32 value
 | ||||
|                 *result = (int32_t)(*buf) ? true : false; | ||||
|             } else { | ||||
|                 status = SYSPARAM_PARSEFAILED; | ||||
|             } | ||||
|             break; | ||||
|         } | ||||
|         buf[data_len] = 0; | ||||
| 
 | ||||
|         if (!strcasecmp(buf, "y")    || | ||||
|             !strcasecmp(buf, "yes")  || | ||||
|             !strcasecmp(buf, "t")    || | ||||
|             !strcasecmp(buf, "true") || | ||||
|             !strcmp(buf, "1")) { | ||||
|                 *result = true; | ||||
|                 break; | ||||
|         } | ||||
|         if (!strcasecmp(buffer, "n")     || | ||||
|             !strcasecmp(buffer, "no")    || | ||||
|             !strcasecmp(buffer, "f")     || | ||||
|             !strcasecmp(buffer, "false") || | ||||
|             !strcmp(buffer, "0")) { | ||||
|         if (!strcasecmp(buf, "n")     || | ||||
|             !strcasecmp(buf, "no")    || | ||||
|             !strcasecmp(buf, "f")     || | ||||
|             !strcasecmp(buf, "false") || | ||||
|             !strcmp(buf, "0")) { | ||||
|                 *result = false; | ||||
|                 break; | ||||
|         } | ||||
|         status = SYSPARAM_PARSEFAILED; | ||||
|     } while (0); | ||||
| 
 | ||||
|     free(buffer); | ||||
|     return status; | ||||
| } | ||||
| 
 | ||||
|  | @ -806,48 +838,30 @@ sysparam_status_t sysparam_set_data(const char *key, const uint8_t *value, size_ | |||
|     struct sysparam_context write_ctx; | ||||
|     sysparam_status_t status = SYSPARAM_OK; | ||||
|     uint16_t key_len = strlen(key); | ||||
|     uint8_t *buffer; | ||||
|     uint8_t *newbuf; | ||||
|     size_t free_space; | ||||
|     size_t needed_space; | ||||
|     bool free_value = false; | ||||
|     int key_id = -1; | ||||
|     uint32_t old_value_addr = 0; | ||||
|     uint16_t binary_flag; | ||||
|     | ||||
|     if (!_sysparam_info.cur_base) return SYSPARAM_ERR_NOINIT; | ||||
| 
 | ||||
|     if (!key_len) return SYSPARAM_ERR_BADVALUE; | ||||
|     if (key_len > MAX_KEY_LEN) return SYSPARAM_ERR_BADVALUE; | ||||
|     if (value_len > MAX_VALUE_LEN) return SYSPARAM_ERR_BADVALUE; | ||||
| 
 | ||||
|     xSemaphoreTake(_sysparam_info.sem, portMAX_DELAY); | ||||
| 
 | ||||
|     if (!value) value_len = 0; | ||||
| 
 | ||||
|     debug(1, "updating value for '%s' (%d bytes)", key, value_len); | ||||
|     if (value_len && ((intptr_t)value & 0x3)) { | ||||
|         // The passed value isn't word-aligned.  This will be a problem later
 | ||||
|         // when calling `sdk_spi_flash_write`, so make a word-aligned copy.
 | ||||
|         buffer = malloc(value_len); | ||||
|         if (!buffer) { | ||||
|             status = SYSPARAM_ERR_NOMEM; | ||||
|             goto done; | ||||
|         } | ||||
|         memcpy(buffer, value, value_len); | ||||
|         value = buffer; | ||||
|         free_value = true; | ||||
|     } | ||||
|     // Create a working buffer for `_find_key` to use.
 | ||||
|     buffer = malloc(key_len); | ||||
|     if (!buffer) { | ||||
|         if (free_value) free((void *)value); | ||||
|         status = SYSPARAM_ERR_NOMEM; | ||||
| 
 | ||||
|     xSemaphoreTake(_sysparam_info.sem, portMAX_DELAY); | ||||
| 
 | ||||
|     if (!_sysparam_info.cur_base) { | ||||
|         status = SYSPARAM_ERR_NOINIT; | ||||
|         goto done; | ||||
|     } | ||||
| 
 | ||||
|     do { | ||||
|         _init_context(&ctx); | ||||
|         status = _find_key(&ctx, key, key_len, buffer); | ||||
|         status = _find_key(&ctx, key, key_len); | ||||
|         if (status == SYSPARAM_OK) { | ||||
|             // Key already exists, see if there's a current value.
 | ||||
|             key_id = ctx.entry.idflags & ENTRY_MASK_ID; | ||||
|  | @ -862,24 +876,17 @@ sysparam_status_t sysparam_set_data(const char *key, const uint8_t *value, size_ | |||
| 
 | ||||
|         if (value_len) { | ||||
|             if (old_value_addr) { | ||||
|                 if ((ctx.entry.idflags & ENTRY_FLAG_BINARY) == binary_flag && ctx.entry.len == value_len) { | ||||
|                 if ((ctx.entry.idflags & ENTRY_FLAG_BINARY) == binary_flag && | ||||
|                     ctx.entry.len == value_len) { | ||||
|                     // Are we trying to write the same value that's already there?
 | ||||
|                     if (value_len > key_len) { | ||||
|                         newbuf = realloc(buffer, value_len); | ||||
|                         if (!newbuf) { | ||||
|                             status = SYSPARAM_ERR_NOMEM; | ||||
|                             break; | ||||
|                         } | ||||
|                         buffer = newbuf; | ||||
|                     } | ||||
|                     status = _read_payload(&ctx, buffer, value_len); | ||||
|                     if (status < 0) break; | ||||
|                     if (!memcmp(buffer, value, value_len)) { | ||||
|                     status = _compare_payload(&ctx, (uint8_t *)value, value_len); | ||||
|                     if (status == SYSPARAM_OK) { | ||||
|                         // Yup, it's a match! No need to do anything further,
 | ||||
|                         // just leave the current value as-is.
 | ||||
|                         status = SYSPARAM_OK; | ||||
|                         break; | ||||
|                     } | ||||
|                     if (status != SYSPARAM_NOTFOUND) goto done; | ||||
|                 } | ||||
| 
 | ||||
|                 // Since we will be deleting the old value (if any) make sure
 | ||||
|  | @ -981,9 +988,6 @@ sysparam_status_t sysparam_set_data(const char *key, const uint8_t *value, size_ | |||
|         debug(1, "New addr is 0x%08x (%d bytes remaining)", _sysparam_info.end_addr, _sysparam_info.cur_base + _sysparam_info.region_size - _sysparam_info.end_addr); | ||||
|     } while (false); | ||||
| 
 | ||||
|     if (free_value) free((void *)value); | ||||
|     free(buffer); | ||||
| 
 | ||||
|  done: | ||||
|     xSemaphoreGive(_sysparam_info.sem); | ||||
| 
 | ||||
|  | @ -995,15 +999,11 @@ sysparam_status_t sysparam_set_string(const char *key, const char *value) { | |||
| } | ||||
| 
 | ||||
| sysparam_status_t sysparam_set_int32(const char *key, int32_t value) { | ||||
|     uint8_t buffer[12]; | ||||
|     int len; | ||||
|      | ||||
|     len = snprintf((char *)buffer, 12, "%d", value); | ||||
|     return sysparam_set_data(key, buffer, len, false); | ||||
|     return sysparam_set_data(key, (const uint8_t *)&value, sizeof(value), true); | ||||
| } | ||||
| 
 | ||||
| sysparam_status_t sysparam_set_int8(const char *key, int8_t value) { | ||||
|     return sysparam_set_int32(key, value); | ||||
|     return sysparam_set_data(key, (const uint8_t *)&value, sizeof(value), true); | ||||
| } | ||||
| 
 | ||||
| sysparam_status_t sysparam_set_bool(const char *key, bool value) { | ||||
|  | @ -1043,7 +1043,6 @@ sysparam_status_t sysparam_iter_start(sysparam_iter_t *iter) { | |||
| } | ||||
| 
 | ||||
| sysparam_status_t sysparam_iter_next(sysparam_iter_t *iter) { | ||||
|     uint8_t buffer[2]; | ||||
|     sysparam_status_t status; | ||||
|     size_t required_len; | ||||
|     struct sysparam_context *ctx = iter->ctx; | ||||
|  | @ -1052,7 +1051,7 @@ sysparam_status_t sysparam_iter_next(sysparam_iter_t *iter) { | |||
|     char *newbuf; | ||||
| 
 | ||||
|     while (true) { | ||||
|         status = _find_key(ctx, NULL, 0, buffer); | ||||
|         status = _find_key(ctx, NULL, 0); | ||||
|         if (status != SYSPARAM_OK) return status; | ||||
|         memcpy(&value_ctx, ctx, sizeof(value_ctx)); | ||||
| 
 | ||||
|  | @ -1060,7 +1059,7 @@ sysparam_status_t sysparam_iter_next(sysparam_iter_t *iter) { | |||
|         if (status < 0) return status; | ||||
|         if (status == SYSPARAM_NOTFOUND) continue; | ||||
| 
 | ||||
|         key_space = ROUND_TO_WORD_BOUNDARY(ctx->entry.len + 1); | ||||
|         key_space = ctx->entry.len + 1; | ||||
|         required_len = key_space + value_ctx.entry.len + 1; | ||||
|         if (required_len > iter->bufsize) { | ||||
|             newbuf = realloc(iter->key, required_len); | ||||
|  |  | |||
|  | @ -7,11 +7,10 @@ | |||
|  */ | ||||
| #include "esp_spiffs.h" | ||||
| #include "spiffs.h" | ||||
| #include <espressif/spi_flash.h> | ||||
| #include <spiflash.h> | ||||
| #include <stdbool.h> | ||||
| #include <esp/uart.h> | ||||
| #include <fcntl.h> | ||||
| #include "esp_spiffs_flash.h" | ||||
| 
 | ||||
| spiffs fs; | ||||
| 
 | ||||
|  | @ -34,7 +33,7 @@ static fs_buf_t cache_buf = {0}; | |||
| 
 | ||||
| static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst) | ||||
| { | ||||
|     if (esp_spiffs_flash_read(addr, dst, size) == ESP_SPIFFS_FLASH_ERROR) { | ||||
|     if (!spiflash_read(addr, dst, size)) { | ||||
|         return SPIFFS_ERR_INTERNAL; | ||||
|     } | ||||
| 
 | ||||
|  | @ -43,7 +42,7 @@ static s32_t esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst) | |||
| 
 | ||||
| static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) | ||||
| { | ||||
|     if (esp_spiffs_flash_write(addr, src, size) == ESP_SPIFFS_FLASH_ERROR) { | ||||
|     if (!spiflash_write(addr, src, size)) { | ||||
|         return SPIFFS_ERR_INTERNAL; | ||||
|     } | ||||
| 
 | ||||
|  | @ -52,11 +51,10 @@ static s32_t esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) | |||
| 
 | ||||
| static s32_t esp_spiffs_erase(u32_t addr, u32_t size) | ||||
| { | ||||
|     uint32_t sectors = size / SPI_FLASH_SEC_SIZE; | ||||
|     uint32_t sectors = size / SPI_FLASH_SECTOR_SIZE; | ||||
| 
 | ||||
|     for (uint32_t i = 0; i < sectors; i++) { | ||||
|         if (esp_spiffs_flash_erase_sector(addr + (SPI_FLASH_SEC_SIZE * i)) | ||||
|                 == ESP_SPIFFS_FLASH_ERROR) { | ||||
|         if (!spiflash_erase_sector(addr + (SPI_FLASH_SECTOR_SIZE * i))) { | ||||
|             return SPIFFS_ERR_INTERNAL; | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -1,112 +0,0 @@ | |||
| /** | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2016 sheinz (https://github.com/sheinz) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
|         .text | ||||
|         .section .iram1.text, "x" | ||||
|         .literal_position | ||||
| 
 | ||||
| /** | ||||
|  * Copy unaligned data to 4-byte aligned buffer. | ||||
|  */ | ||||
|         .align  4
 | ||||
|         .global memcpy_unaligned_src
 | ||||
|         .type   memcpy_unaligned_src, @function
 | ||||
| memcpy_unaligned_src: | ||||
| /* a2: dst, a3: src, a4: size */ | ||||
|         ssa8l       a3 | ||||
|         srli        a3, a3, 2 | ||||
|         slli        a3, a3, 2 | ||||
|         beqz        a4, u_src_end | ||||
|         l32i        a6, a3, 0 | ||||
| u_src_loop: | ||||
|         l32i        a7, a3, 4 | ||||
|         src         a8, a7, a6 | ||||
|         memw | ||||
|         s32i        a8, a2, 0 | ||||
|         mov         a6, a7 | ||||
|         addi        a3, a3, 4 | ||||
|         addi        a2, a2, 4 | ||||
|         addi        a4, a4, -1 | ||||
|         bnez        a4, u_src_loop | ||||
| u_src_end: | ||||
|         movi a2, 0 | ||||
|         ret.n | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Copy data from 4-byte aligned source to unaligned destination buffer. | ||||
|  */ | ||||
|         .align  4
 | ||||
|         .global memcpy_unaligned_dst
 | ||||
|         .type   memcpy_unaligned_dst, @function
 | ||||
| memcpy_unaligned_dst: | ||||
| /* a2: dst, a3: src, a4: size */ | ||||
|         beqz.n       a4, u_dst_end | ||||
|         extui        a5, a4, 0, 2 | ||||
|         beqz.n       a5, aligned_dst_loop | ||||
| u_dst_loop: | ||||
|         /* Load data word */ | ||||
|         memw | ||||
|         l32i.n       a5, a3, 0 | ||||
| 
 | ||||
|         /* Save byte number 0 */ | ||||
|         s8i          a5, a2, 0 | ||||
|         addi.n       a4, a4, -1 | ||||
|         beqz         a4, u_dst_end | ||||
|         addi.n       a2, a2, 1 | ||||
| 
 | ||||
|         /* Shift and save byte number 1 */ | ||||
|         srli         a5, a5, 8 | ||||
|         s8i          a5, a2, 0 | ||||
|         addi.n       a4, a4, -1 | ||||
|         beqz         a4, u_dst_end | ||||
|         addi.n       a2, a2, 1 | ||||
| 
 | ||||
|         /* Shift and save byte number 2 */ | ||||
|         srli         a5, a5, 8 | ||||
|         s8i          a5, a2, 0 | ||||
|         addi.n       a4, a4, -1 | ||||
|         beqz         a4, u_dst_end | ||||
|         addi.n       a2, a2, 1 | ||||
| 
 | ||||
|         /* Shift and save byte number 3 */ | ||||
|         srli         a5, a5, 8 | ||||
|         s8i          a5, a2, 0 | ||||
|         addi.n       a4, a4, -1 | ||||
|         addi.n       a2, a2, 1 | ||||
| 
 | ||||
|         /* Next word */ | ||||
|         addi.n       a3, a3, 4 | ||||
|         bnez.n       a4, u_dst_loop | ||||
|         ret.n | ||||
| aligned_dst_loop: | ||||
|         memw | ||||
|         l32i        a5, a3, 0 | ||||
|         s32i        a5, a2, 0 | ||||
|         addi.n      a3, a3, 4 | ||||
|         addi.n      a2, a2, 4 | ||||
|         addi.n      a4, a4, -4 | ||||
|         bnez.n      a4, aligned_dst_loop | ||||
| u_dst_end:   ret.n | ||||
| 
 | ||||
|  | @ -1,6 +1,6 @@ | |||
| /* 
 | ||||
| /*
 | ||||
|  * copyright (c) Espressif System 2010 | ||||
|  *  | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #ifndef __SPI_FLASH_H__ | ||||
|  | @ -46,12 +46,6 @@ sdk_SpiFlashOpResult sdk_spi_flash_write(uint32_t des_addr, uint32_t *src, uint3 | |||
| */ | ||||
| sdk_SpiFlashOpResult sdk_spi_flash_read(uint32_t src_addr, uint32_t *des, uint32_t size); | ||||
| 
 | ||||
| /* SDK uses this structure internally to account for flash size.
 | ||||
| 
 | ||||
|    See flashchip.h for more info. | ||||
| */ | ||||
| extern sdk_flashchip_t sdk_flashchip; | ||||
| 
 | ||||
| #ifdef	__cplusplus | ||||
| } | ||||
| #endif | ||||
|  |  | |||
							
								
								
									
										403
									
								
								tests/cases/07_sysparam.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										403
									
								
								tests/cases/07_sysparam.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,403 @@ | |||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include <FreeRTOS.h> | ||||
| #include <task.h> | ||||
| 
 | ||||
| #include <sysparam.h> | ||||
| 
 | ||||
| #include <testcase.h> | ||||
| 
 | ||||
| // #define DEBUG
 | ||||
| 
 | ||||
| #ifdef DEBUG | ||||
| #include <stdio.h> | ||||
| #define debug(fmt, ...) printf("%s" fmt, "test: ", ## __VA_ARGS__); | ||||
| #else | ||||
| #define debug(fmt, ...) | ||||
| #endif | ||||
| 
 | ||||
| DEFINE_SOLO_TESTCASE(07_sysparam_basic_test); | ||||
| DEFINE_SOLO_TESTCASE(07_sysparam_load_test); | ||||
| DEFINE_SOLO_TESTCASE(07_sysparam_bool_test); | ||||
| 
 | ||||
| #define TEST_ITERATIONS         10 | ||||
| #define KEY_BUF_SIZE            32 | ||||
| #define TEST_STRING_BUF_SIZE    64 | ||||
| #define NUMBER_OF_TEST_DATA     20 | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint32_t start_key_index; | ||||
|     uint32_t key_index; | ||||
| } test_data_t; | ||||
| 
 | ||||
| typedef enum { | ||||
|     VALUE_STRING = 0, | ||||
|     VALUE_INT32, | ||||
|     VALUE_INT8, | ||||
|     VALUE_BOOL, | ||||
|     VALUE_ENUM_END | ||||
| } value_type_t; | ||||
| 
 | ||||
| 
 | ||||
| static uint32_t get_current_time() | ||||
| { | ||||
|      return timer_get_count(FRC2) / 5000;  // to get roughly 1ms resolution
 | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Recreate sysparam area | ||||
|  */ | ||||
| static inline void init_sysparam() | ||||
| { | ||||
|     sysparam_status_t status; | ||||
|     uint32_t base_addr, num_sectors; | ||||
| 
 | ||||
|     status = sysparam_get_info(&base_addr, &num_sectors); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
| 
 | ||||
|     status = sysparam_create_area(base_addr, num_sectors, /*force=*/true); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
| 
 | ||||
|     status = sysparam_init(base_addr, 0); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
| 
 | ||||
|     debug("sysparam initialized at addr=%x, sectors=%d\n", | ||||
|             base_addr, num_sectors); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Initialize test data with random seed. | ||||
|  */ | ||||
| static void test_data_init(test_data_t *data) | ||||
| { | ||||
|     debug("test_data_init\n"); | ||||
|     data->start_key_index = data->key_index = rand() % 100; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Reset test data to the initial seed. | ||||
|  */ | ||||
| static void test_data_reset(test_data_t *data) | ||||
| { | ||||
|     debug("test_data_reset\n"); | ||||
|     data->key_index = data->start_key_index; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get key string for the current data. | ||||
|  */ | ||||
| static void test_data_get_key(test_data_t *data, char *key_buf) | ||||
| { | ||||
|     sprintf(key_buf, "key_%d", data->key_index); | ||||
|     debug("test_data_get_key: key=%s\n", key_buf); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Generate test string for the current data. | ||||
|  */ | ||||
| static void test_data_get_string(test_data_t *data, char *str_buf) | ||||
| { | ||||
|     srand(data->key_index); | ||||
|     for (int i = 0; i < TEST_STRING_BUF_SIZE - 1; ++i) { | ||||
|         str_buf[i] = '0' + rand() % 74; // generate a char 0-9,a-z,A-Z and other
 | ||||
|     } | ||||
|     str_buf[TEST_STRING_BUF_SIZE-1] = 0;  // terminate string with zero
 | ||||
|     debug("test_data_get_string: str=%s\n", str_buf); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Generate test int32 value for the current data. | ||||
|  */ | ||||
| static int32_t test_data_get_int32(test_data_t *data) | ||||
| { | ||||
|     srand(data->key_index); | ||||
|     int32_t v = rand(); | ||||
|     debug("test_data_get_int32: value=%d\n", v); | ||||
|     return v; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Generate test int8 value for the current data. | ||||
|  */ | ||||
| int8_t test_data_get_int8(test_data_t *data) | ||||
| { | ||||
|     srand(data->key_index); | ||||
|     int8_t v = rand() % 256; | ||||
|     debug("test_data_get_int8: value=%d\n", v); | ||||
|     return v; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Generate test bool value for the current data. | ||||
|  */ | ||||
| bool test_data_get_bool(test_data_t *data) | ||||
| { | ||||
|     srand(data->key_index); | ||||
|     bool v = rand() % 2; | ||||
|     debug("test_data_get_bool, value=%s\n", v ? "true" : "false"); | ||||
|     return v; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Get type of the current data. | ||||
|  */ | ||||
| value_type_t test_data_get_type(test_data_t *data) | ||||
| { | ||||
|     srand(data->key_index); | ||||
|     value_type_t t = rand() % VALUE_ENUM_END; | ||||
|     debug("test_data_get_type: type=%d\n", t); | ||||
|     return t; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Generate next data. | ||||
|  */ | ||||
| void test_data_next(test_data_t *data) | ||||
| { | ||||
|     data->key_index++; | ||||
|     debug("test_data_next: key_index=%d\n", data->key_index); | ||||
| } | ||||
| 
 | ||||
| static void write_test_values(test_data_t *data) | ||||
| { | ||||
|     sysparam_status_t status = SYSPARAM_ERR_BADVALUE; | ||||
|     char key_buf[KEY_BUF_SIZE]; | ||||
|     char str_buf[TEST_STRING_BUF_SIZE]; | ||||
| 
 | ||||
|     for (int i = 0; i < NUMBER_OF_TEST_DATA; ++i) { | ||||
|         test_data_get_key(data, key_buf); | ||||
|         switch (test_data_get_type(data)) { | ||||
|             case VALUE_STRING: | ||||
|                 test_data_get_string(data, str_buf); | ||||
|                 status = sysparam_set_string(key_buf, str_buf); | ||||
|                 break; | ||||
|             case VALUE_INT32: | ||||
|                 status = sysparam_set_int32(key_buf, test_data_get_int32(data)); | ||||
|                 break; | ||||
|             case VALUE_INT8: | ||||
|                 status = sysparam_set_int8(key_buf, test_data_get_int8(data)); | ||||
|                 break; | ||||
|             case VALUE_BOOL: | ||||
|                 status = sysparam_set_bool(key_buf, test_data_get_bool(data)); | ||||
|                 break; | ||||
|             case VALUE_ENUM_END: | ||||
|             default: | ||||
|                 break; | ||||
|         } | ||||
| 
 | ||||
|         TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
| 
 | ||||
|         test_data_next(data); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void verify_test_values(test_data_t *data) | ||||
| { | ||||
|     sysparam_status_t status = SYSPARAM_ERR_BADVALUE; | ||||
|     char key_buf[KEY_BUF_SIZE]; | ||||
|     char expected_str_buf[TEST_STRING_BUF_SIZE]; | ||||
|     char *actual_str; | ||||
|     int32_t actual_int32; | ||||
|     int8_t actual_int8; | ||||
|     bool actual_bool; | ||||
| 
 | ||||
|     for (int i = 0; i < NUMBER_OF_TEST_DATA; ++i) { | ||||
|         test_data_get_key(data, key_buf); | ||||
|         switch (test_data_get_type(data)) { | ||||
|             case VALUE_STRING: | ||||
|                 test_data_get_string(data, expected_str_buf); | ||||
|                 status = sysparam_get_string(key_buf, &actual_str); | ||||
|                 TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|                 TEST_ASSERT_EQUAL_STRING(expected_str_buf, actual_str); | ||||
|                 free(actual_str); | ||||
|                 break; | ||||
|             case VALUE_INT32: | ||||
|                 status = sysparam_get_int32(key_buf, &actual_int32); | ||||
|                 TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|                 TEST_ASSERT_EQUAL_INT(test_data_get_int32(data), actual_int32); | ||||
|                 break; | ||||
|             case VALUE_INT8: | ||||
|                 status = sysparam_get_int8(key_buf, &actual_int8); | ||||
|                 TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|                 TEST_ASSERT_EQUAL_INT(test_data_get_int8(data), actual_int8); | ||||
|                 break; | ||||
|             case VALUE_BOOL: | ||||
|                 status = sysparam_get_bool(key_buf, &actual_bool); | ||||
|                 TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|                 TEST_ASSERT_TRUE(test_data_get_bool(data) == actual_bool); | ||||
|                 break; | ||||
|             case VALUE_ENUM_END: | ||||
|             default: | ||||
|                 break; | ||||
|         } | ||||
| 
 | ||||
|         test_data_next(data); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void clear_test_values(test_data_t *data) | ||||
| { | ||||
|     char key_buf[KEY_BUF_SIZE]; | ||||
|     sysparam_status_t status = SYSPARAM_ERR_BADVALUE; | ||||
| 
 | ||||
|     for (int i = 0; i < NUMBER_OF_TEST_DATA; ++i) { | ||||
|         test_data_get_key(data, key_buf); | ||||
|         status = sysparam_set_data(key_buf, NULL, 0, false); | ||||
|         TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
| 
 | ||||
|         test_data_next(data); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static void a_07_sysparam_load_test(void) | ||||
| { | ||||
|     test_data_t test_data; | ||||
|     init_sysparam(); | ||||
|     uint32_t start_time = get_current_time(); | ||||
|     uint32_t free_heap_at_start = xPortGetFreeHeapSize(); | ||||
| 
 | ||||
|     for (int i = 0; i < TEST_ITERATIONS; ++i) { | ||||
|         test_data_init(&test_data); | ||||
|         write_test_values(&test_data); | ||||
|         test_data_reset(&test_data); | ||||
|         verify_test_values(&test_data); | ||||
|         test_data_reset(&test_data); | ||||
|         clear_test_values(&test_data); | ||||
|     } | ||||
| 
 | ||||
|     TEST_ASSERT_EQUAL_INT_MESSAGE(free_heap_at_start, xPortGetFreeHeapSize(), | ||||
|             "Free heap size is less than at test start. Possible memory leak."); | ||||
| 
 | ||||
|     printf("Test took %d ms\n", get_current_time() - start_time); | ||||
| 
 | ||||
|     TEST_PASS(); | ||||
| } | ||||
| 
 | ||||
| static void a_07_sysparam_basic_test(void) | ||||
| { | ||||
|     sysparam_status_t status; | ||||
|     int32_t int32_val = 0; | ||||
|     int8_t int8_val = 0; | ||||
|     char *str; | ||||
|     bool bool_val; | ||||
| 
 | ||||
|     init_sysparam(); | ||||
| 
 | ||||
|     status = sysparam_set_int32("int_1", -123); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|     status = sysparam_get_int32("int_1", &int32_val); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|     TEST_ASSERT_EQUAL_INT(-123, int32_val); | ||||
| 
 | ||||
|     status = sysparam_set_int8("int_2", -34); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|     status = sysparam_get_int8("int_2", &int8_val); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|     TEST_ASSERT_EQUAL_INT(-34, int8_val); | ||||
| 
 | ||||
|     status = sysparam_set_string("str_1", "test string"); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|     status = sysparam_get_string("str_1", &str); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|     TEST_ASSERT_EQUAL_STRING("test string", str); | ||||
|     free(str); | ||||
| 
 | ||||
|     status = sysparam_set_bool("bool_true", true); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|     status = sysparam_get_bool("bool_true", &bool_val); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|     TEST_ASSERT_TRUE(bool_val); | ||||
| 
 | ||||
|     status = sysparam_set_bool("bool_false", false); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|     status = sysparam_get_bool("bool_false", &bool_val); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|     TEST_ASSERT_FALSE(bool_val); | ||||
| 
 | ||||
|     TEST_PASS(); | ||||
| } | ||||
| 
 | ||||
| typedef struct { | ||||
|     const char *key; | ||||
|     const char *str; | ||||
|     bool value; | ||||
| } bool_test_data_t; | ||||
| 
 | ||||
| const static bool_test_data_t bool_data[] = { | ||||
|     {"str_true", "true", true}, | ||||
|     {"str_True", "True", true}, | ||||
|     {"str_TRUE", "TRUE", true}, | ||||
|     {"str_t", "t", true}, | ||||
|     {"str_T", "T", true}, | ||||
|     {"str_y", "y", true}, | ||||
|     {"str_Y", "Y", true}, | ||||
|     {"str_yes", "yes", true}, | ||||
|     {"str_Yes", "Yes", true}, | ||||
|     {"str_YES", "YES", true}, | ||||
|     {"str_1", "1", true}, | ||||
| 
 | ||||
|     {"str_false", "false", false}, | ||||
|     {"str_False", "False", false}, | ||||
|     {"str_FALSE", "FALSE", false}, | ||||
|     {"str_f", "f", false}, | ||||
|     {"str_F", "F", false}, | ||||
|     {"str_n", "n", false}, | ||||
|     {"str_N", "N", false}, | ||||
|     {"str_no", "no", false}, | ||||
|     {"str_No", "No", false}, | ||||
|     {"str_NO", "NO", false}, | ||||
|     {"str_0", "0", false}, | ||||
| }; | ||||
| 
 | ||||
| static void a_07_sysparam_bool_test(void) | ||||
| { | ||||
|     sysparam_status_t status; | ||||
|     bool value; | ||||
| 
 | ||||
|     init_sysparam(); | ||||
| 
 | ||||
|     for (int i = 0; i < sizeof(bool_data) / sizeof(bool_data[0]); ++i) { | ||||
|         status = sysparam_set_string(bool_data[i].key, bool_data[i].str); | ||||
|         TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|     } | ||||
| 
 | ||||
|     status = sysparam_set_int8("int8_0", 0); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
| 
 | ||||
|     status = sysparam_set_int8("int8_1", 1); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
| 
 | ||||
|     status = sysparam_set_int32("int32_0", 0); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
| 
 | ||||
|     status = sysparam_set_int32("int32_1", 1); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
| 
 | ||||
|     for (int i = 0; i < sizeof(bool_data) / sizeof(bool_data[0]); ++i) { | ||||
|         debug("Getting bool key=%s\n", bool_data[i].key); | ||||
|         status = sysparam_get_bool(bool_data[i].key, &value); | ||||
|         TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|         TEST_ASSERT_TRUE(bool_data[i].value == value); | ||||
|     } | ||||
| 
 | ||||
|     status = sysparam_get_bool("int8_0", &value); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|     TEST_ASSERT_FALSE(value); | ||||
| 
 | ||||
|     status = sysparam_get_bool("int8_1", &value); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|     TEST_ASSERT_TRUE(value); | ||||
| 
 | ||||
|     status = sysparam_get_bool("int32_0", &value); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|     TEST_ASSERT_FALSE(value); | ||||
| 
 | ||||
|     status = sysparam_get_bool("int32_1", &value); | ||||
|     TEST_ASSERT_EQUAL_INT(SYSPARAM_OK, status); | ||||
|     TEST_ASSERT_TRUE(value); | ||||
| 
 | ||||
|     TEST_PASS(); | ||||
| } | ||||
							
								
								
									
										51
									
								
								tests/cases/08_spiflash.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								tests/cases/08_spiflash.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,51 @@ | |||
| #include <string.h> | ||||
| #include <espressif/esp_common.h> | ||||
| #include <esp/uart.h> | ||||
| #include <esp/timer.h> | ||||
| #include <FreeRTOS.h> | ||||
| #include <task.h> | ||||
| #include <esp8266.h> | ||||
| #include <stdio.h> | ||||
| #include <testcase.h> | ||||
| 
 | ||||
| #include <spiflash.h> | ||||
| 
 | ||||
| DEFINE_SOLO_TESTCASE(08_spiflash_unaligned) | ||||
| 
 | ||||
| /**
 | ||||
|  * Test unaligned access to spi flash. | ||||
|  */ | ||||
| static void a_08_spiflash_unaligned(void) | ||||
| { | ||||
|     const int test_addr = 0x100000 - (4096 * 8); | ||||
|     const char test_str[] = "test_string"; | ||||
|     const int buf_size = 256; | ||||
|     uint8_t buf[buf_size]; | ||||
| 
 | ||||
|     TEST_ASSERT_TRUE(spiflash_erase_sector(test_addr)); | ||||
|     TEST_ASSERT_TRUE(spiflash_erase_sector(test_addr + 4096)); | ||||
| 
 | ||||
|     TEST_ASSERT_TRUE( | ||||
|             spiflash_write(test_addr, (uint8_t*)test_str, sizeof(test_str))); | ||||
| 
 | ||||
|     TEST_ASSERT_TRUE(spiflash_read(test_addr, buf, buf_size)); | ||||
| 
 | ||||
|     TEST_ASSERT_EQUAL_STRING(test_str, buf); | ||||
| 
 | ||||
|     TEST_ASSERT_TRUE( | ||||
|             spiflash_write(test_addr + 31, (uint8_t*)test_str, sizeof(test_str))); | ||||
|     TEST_ASSERT_TRUE(spiflash_read(test_addr + 31, buf, buf_size)); | ||||
|     TEST_ASSERT_EQUAL_STRING(test_str, buf); | ||||
| 
 | ||||
|     TEST_ASSERT_TRUE( | ||||
|             spiflash_write(test_addr + 101, (uint8_t*)test_str, sizeof(test_str))); | ||||
|     TEST_ASSERT_TRUE(spiflash_read(test_addr + 101, buf + 1, buf_size - 1)); | ||||
|     TEST_ASSERT_EQUAL_STRING(test_str, buf + 1); | ||||
| 
 | ||||
|     TEST_ASSERT_TRUE( | ||||
|             spiflash_write(test_addr + 201, (uint8_t*)test_str + 1, sizeof(test_str) - 1)); | ||||
|     TEST_ASSERT_TRUE(spiflash_read(test_addr + 201, buf + 1, buf_size - 1)); | ||||
|     TEST_ASSERT_EQUAL_STRING(test_str + 1, buf + 1); | ||||
| 
 | ||||
|     TEST_PASS(); | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue