/* Reflow Oven Controller * * Copyright (C) 2020 Mario Hüttel * * 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 . */ #include #include #include #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; 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); header.err_memory_offset = header.config_overrides_offset + SAFETY_MEMORY_CONFIG_OVERRIDE_COUNT; header.err_memory_end = header.err_memory_offset; header.magic = SAFETY_MEMORY_MAGIC; header.magic_i = ~SAFETY_MEMORY_MAGIC; 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; } int safety_memory_reinit(enum safety_memory_state *found_state) { 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: break; default: *found_state = SAFETY_MEMORY_INIT_CORRUPTED; break; } /* Check if memory header has to be written */ if (*found_state != SAFETY_MEMORY_INIT_VALID_MEMORY) { safety_memory_write_new_header(); /* If yes, generate new CRC checksum */ res = safety_memory_gen_crc(); if (res) ret = -100; else ret = 0; } else { ret = 0; } return ret; } int safety_memory_init(enum safety_memory_state *found_state) { crc_unit_init(); backup_ram_init(true); return safety_memory_reinit(found_state); } int safety_memory_get_boot_status(struct safety_memory_boot_status *status); int safety_memory_get_error_entry_count(uint32_t *count); int safety_memory_check(void) { int res; res = safety_memory_check_crc(); 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);