2020-09-04 22:55:34 +02:00
|
|
|
/* Reflow Oven Controller
|
|
|
|
*
|
|
|
|
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
|
|
|
|
*
|
|
|
|
* This file is part of the Reflow Oven Controller Project.
|
|
|
|
*
|
|
|
|
* The reflow oven controller is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* The Reflow Oven Control Firmware is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with the reflow oven controller project.
|
|
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <reflow-controller/safety/safety-memory.h>
|
2020-09-05 15:15:31 +02:00
|
|
|
#include <stm-periph/crc-unit.h>
|
|
|
|
#include <stm-periph/backup-ram.h>
|
2020-09-04 22:55:34 +02:00
|
|
|
|
2020-09-05 15:15:31 +02:00
|
|
|
#define wordsize_of(x) ((sizeof(x) / 4U) / ((sizeof(x) % 4U) ? 0U : 1U))
|
|
|
|
|
|
|
|
static enum safety_memory_state safety_memory_get_header(struct safety_memory_header *header)
|
|
|
|
{
|
|
|
|
|
|
|
|
int res;
|
|
|
|
enum safety_memory_state ret;
|
|
|
|
|
|
|
|
if (!header)
|
|
|
|
return SAFETY_MEMORY_INIT_CORRUPTED;
|
|
|
|
|
|
|
|
res = backup_ram_get_data(0UL, (uint32_t *)header, wordsize_of(struct safety_memory_header));
|
|
|
|
if (res)
|
|
|
|
return SAFETY_MEMORY_INIT_CORRUPTED;
|
|
|
|
|
|
|
|
/* Check magics */
|
|
|
|
if (header->magic != SAFETY_MEMORY_MAGIC || header->magic_i != (uint32_t)(~SAFETY_MEMORY_MAGIC)) {
|
|
|
|
/* Magics invalid */
|
|
|
|
ret = SAFETY_MEMORY_INIT_FRESH;
|
|
|
|
goto return_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = 0;
|
|
|
|
if (header->boot_status_offset < wordsize_of(struct safety_memory_header))
|
|
|
|
res++;
|
|
|
|
if (header->config_overrides_offset < header->boot_status_offset + wordsize_of(struct safety_memory_boot_status))
|
|
|
|
res++;
|
|
|
|
if (header->config_overrides_len > SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT)
|
|
|
|
res++;
|
|
|
|
if (header->err_memory_offset < header->config_overrides_offset + header->config_overrides_len)
|
|
|
|
res++;
|
|
|
|
if (header->err_memory_end >= backup_ram_get_size_in_words() || header->err_memory_end < header->err_memory_offset)
|
|
|
|
res++;
|
|
|
|
|
|
|
|
if (res) {
|
|
|
|
/* Error detected: Write new header */
|
|
|
|
ret = SAFETY_MEMORY_INIT_CORRUPTED;
|
|
|
|
} else {
|
|
|
|
ret = SAFETY_MEMORY_INIT_VALID_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
return_val:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void safety_memory_write_new_header(void)
|
|
|
|
{
|
|
|
|
struct safety_memory_header header;
|
|
|
|
|
2020-09-05 15:56:52 +02:00
|
|
|
header.boot_status_offset = wordsize_of(struct safety_memory_header);
|
|
|
|
header.config_overrides_len = SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT;
|
|
|
|
header.config_overrides_offset = header.boot_status_offset + wordsize_of(struct safety_memory_boot_status);
|
2020-09-05 15:15:31 +02:00
|
|
|
header.err_memory_offset = header.config_overrides_offset + SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT;
|
|
|
|
header.err_memory_end = header.err_memory_offset;
|
2020-09-05 15:56:52 +02:00
|
|
|
header.magic = SAFETY_MEMORY_MAGIC;
|
|
|
|
header.magic_i = ~SAFETY_MEMORY_MAGIC;
|
2020-09-05 15:15:31 +02:00
|
|
|
|
|
|
|
backup_ram_wipe();
|
|
|
|
backup_ram_write_data(0UL, (uint32_t *)&header, wordsize_of(header));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int safety_memory_check_crc()
|
|
|
|
{
|
|
|
|
struct safety_memory_header header;
|
|
|
|
enum safety_memory_state state = safety_memory_get_header(&header);
|
|
|
|
uint32_t crc_offset;
|
|
|
|
uint32_t data;
|
|
|
|
uint32_t addr;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (state != SAFETY_MEMORY_INIT_VALID_MEMORY)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
crc_offset = header.err_memory_end;
|
|
|
|
|
|
|
|
crc_unit_reset();
|
|
|
|
|
|
|
|
for (addr = 0; addr < crc_offset; addr++) {
|
|
|
|
res = backup_ram_get_data(addr, &data, 1UL);
|
|
|
|
if (res)
|
|
|
|
return -2000;
|
|
|
|
crc_unit_input(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
res = backup_ram_get_data(crc_offset, &data, 1UL);
|
|
|
|
if (res)
|
|
|
|
return -2001;
|
|
|
|
|
|
|
|
if (crc_unit_get_crc() != data)
|
|
|
|
return -3000;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int safety_memory_gen_crc()
|
|
|
|
{
|
|
|
|
struct safety_memory_header header;
|
|
|
|
uint32_t word_addr;
|
|
|
|
uint32_t data;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
crc_unit_reset();
|
|
|
|
for (word_addr = 0; word_addr < header.err_memory_end; word_addr++) {
|
|
|
|
res = backup_ram_get_data(word_addr, &data, 1);
|
|
|
|
if (res)
|
|
|
|
return -2;
|
|
|
|
crc_unit_input(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write CRC */
|
|
|
|
data = crc_unit_get_crc();
|
|
|
|
res = backup_ram_write_data(header.err_memory_end, &data, 1UL);
|
|
|
|
if (res)
|
|
|
|
return -3;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-09-05 15:56:52 +02:00
|
|
|
int safety_memory_reinit(enum safety_memory_state *found_state)
|
2020-09-05 15:15:31 +02:00
|
|
|
{
|
|
|
|
struct safety_memory_header header;
|
|
|
|
int res;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (!found_state)
|
|
|
|
return -1001;
|
|
|
|
|
|
|
|
*found_state = safety_memory_get_header(&header);
|
|
|
|
|
|
|
|
switch (*found_state) {
|
|
|
|
case SAFETY_MEMORY_INIT_VALID_MEMORY:
|
|
|
|
/* Valid memory detected. Check CRC */
|
|
|
|
res = safety_memory_check_crc();
|
|
|
|
if (res)
|
|
|
|
*found_state = SAFETY_MEMORY_INIT_CORRUPTED;
|
|
|
|
break;
|
|
|
|
case SAFETY_MEMORY_INIT_FRESH:
|
|
|
|
break;
|
|
|
|
case SAFETY_MEMORY_INIT_CORRUPTED:
|
2020-09-05 15:56:52 +02:00
|
|
|
break;
|
2020-09-05 15:15:31 +02:00
|
|
|
default:
|
|
|
|
*found_state = SAFETY_MEMORY_INIT_CORRUPTED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-09-05 15:56:52 +02:00
|
|
|
/* Check if memory header has to be written */
|
2020-09-05 15:15:31 +02:00
|
|
|
if (*found_state != SAFETY_MEMORY_INIT_VALID_MEMORY) {
|
2020-09-05 15:56:52 +02:00
|
|
|
safety_memory_write_new_header();
|
2020-09-05 15:15:31 +02:00
|
|
|
/* If yes, generate new CRC checksum */
|
|
|
|
res = safety_memory_gen_crc();
|
|
|
|
if (res)
|
|
|
|
ret = -100;
|
|
|
|
else
|
|
|
|
ret = 0;
|
2020-09-05 15:56:52 +02:00
|
|
|
} else {
|
|
|
|
ret = 0;
|
2020-09-05 15:15:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-09-05 15:56:52 +02:00
|
|
|
int safety_memory_init(enum safety_memory_state *found_state)
|
|
|
|
{
|
|
|
|
|
|
|
|
crc_unit_init();
|
|
|
|
backup_ram_init(true);
|
|
|
|
|
|
|
|
return safety_memory_reinit(found_state);
|
|
|
|
}
|
|
|
|
|
2020-09-05 16:32:31 +02:00
|
|
|
int safety_memory_get_boot_status(struct safety_memory_boot_status *status)
|
|
|
|
{
|
|
|
|
struct safety_memory_header header;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (!status)
|
|
|
|
return -1001;
|
|
|
|
|
|
|
|
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
|
|
|
|
return -2000;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (safety_memory_check_crc())
|
|
|
|
return -2001;
|
|
|
|
|
|
|
|
res = backup_ram_get_data(header.boot_status_offset, (uint32_t *)status, wordsize_of(*status));
|
|
|
|
if (res)
|
|
|
|
return -3000;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int safety_memory_set_boot_status(const struct safety_memory_boot_status *status)
|
|
|
|
{
|
|
|
|
struct safety_memory_header header;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (!status)
|
|
|
|
return -1001;
|
|
|
|
|
|
|
|
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
|
|
|
|
return -2000;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (safety_memory_check_crc())
|
|
|
|
return -2001;
|
|
|
|
|
|
|
|
res = backup_ram_write_data(header.boot_status_offset, (uint32_t *)status, wordsize_of(*status));
|
|
|
|
res |= safety_memory_gen_crc();
|
|
|
|
if (res)
|
|
|
|
return -3000;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2020-09-05 15:15:31 +02:00
|
|
|
|
2020-09-05 17:37:56 +02:00
|
|
|
static int safety_memory_check_error_entries()
|
|
|
|
{
|
|
|
|
struct safety_memory_header header;
|
|
|
|
uint32_t addr;
|
|
|
|
uint32_t data;
|
|
|
|
int ret = 0;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (safety_memory_get_header(&header) != SAFETY_MEMORY_INIT_VALID_MEMORY) {
|
|
|
|
return -2000;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (addr = header.err_memory_offset; addr < header.err_memory_end; addr++) {
|
|
|
|
res = backup_ram_get_data(addr, &data, 1UL);
|
|
|
|
if (res)
|
|
|
|
return -100;
|
|
|
|
|
|
|
|
/* Valid flag entry */
|
|
|
|
if ((data & 0xFF) == 0x51)
|
|
|
|
continue;
|
|
|
|
if (data == SAFETY_MEMORY_NOP_ENTRY)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ret--;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-09-05 15:15:31 +02:00
|
|
|
int safety_memory_get_error_entry_count(uint32_t *count);
|
|
|
|
|
|
|
|
int safety_memory_check(void)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
|
|
|
|
res = safety_memory_check_crc();
|
2020-09-05 17:37:56 +02:00
|
|
|
if (!res) {
|
|
|
|
res |= safety_memory_check_error_entries();
|
|
|
|
}
|
|
|
|
|
2020-09-05 15:15:31 +02:00
|
|
|
return -!!res;
|
|
|
|
}
|
|
|
|
|
|
|
|
int safety_memory_get_error_entry(uint32_t idx, struct error_memory_entry *entry);
|
|
|
|
|
|
|
|
int safety_memory_insert_error_entry(struct error_memory_entry *entry);
|
|
|
|
|
|
|
|
int safety_memory_insert_config_override(struct config_override *config_override);
|
|
|
|
|
|
|
|
int safety_memory_get_config_override_count(uint32_t *count);
|
|
|
|
|
|
|
|
int safety_memory_get_config_override(uint32_t idx, struct config_override *config_override);
|