Compare commits
No commits in common. "6570d217c778c7fbe034473757928f6fc6a2357d" and "ad3de6e6b746434f7c8b632b5b786ca217433058" have entirely different histories.
6570d217c7
...
ad3de6e6b7
@ -6,7 +6,7 @@ import pathlib
|
||||
|
||||
license_header = """/* Reflow Oven Controller
|
||||
*
|
||||
* Copyright (C) 2022 Mario Hüttel <mario.huettel@gmx.net>
|
||||
* Copyright (C) 2020 Mario Hüttel <mario.huettel@gmx.net>
|
||||
*
|
||||
* This file is part of the Reflow Oven Controller Project.
|
||||
*
|
||||
@ -37,7 +37,7 @@ cpath = os.path.join(project_dir, sys.argv[1]+'.c')
|
||||
hfile = sys.argv[1]+'.h'
|
||||
hpath = os.path.join(module_include_dir, hfile)
|
||||
|
||||
h_define = '_'+hfile.replace('.', '_').replace('-', '_').replace('/', '_').upper()+'_'
|
||||
h_define = '__'+hfile.replace('.', '_').replace('-', '_').replace('/', '_').upper()+'__'
|
||||
|
||||
if os.path.exists(cpath) or os.path.exists(hpath):
|
||||
print("File already exists! Abort!")
|
||||
|
@ -1,37 +0,0 @@
|
||||
/* Reflow Oven Controller
|
||||
*
|
||||
* Copyright (C) 2022 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/>.
|
||||
*/
|
||||
|
||||
#ifndef _SAFETY_FLASH_CRC_STRUCT_H_
|
||||
#define _SAFETY_FLASH_CRC_STRUCT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct flash_crcs {
|
||||
uint32_t start_magic;
|
||||
uint32_t crc_section_text;
|
||||
uint32_t crc_section_data;
|
||||
uint32_t crc_section_ccm_data;
|
||||
uint32_t crc_section_vectors;
|
||||
uint32_t end_magic;
|
||||
};
|
||||
|
||||
extern const struct flash_crcs crcs_in_flash;
|
||||
|
||||
#endif /* _SAFETY_FLASH_CRC_STRUCT_H_ */
|
@ -1,56 +0,0 @@
|
||||
/* Reflow Oven Controller
|
||||
*
|
||||
* Copyright (C) 2022 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/>.
|
||||
*/
|
||||
|
||||
#ifndef _SAFETY_FLASH_CRC_H_
|
||||
#define _SAFETY_FLASH_CRC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum flash_crc_section {
|
||||
FLASH_CRC_VECTOR = 0,
|
||||
FLASH_CRC_TEXT,
|
||||
FLASH_CRC_DATA,
|
||||
FLASH_CRC_CCMDATA,
|
||||
N_FLASH_CRC,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Perform a CRC check of the flash memory and set appropriate flags
|
||||
* @return negative if internal error occured. Otherwise (independent from CRC check result) 0.
|
||||
* @note This function requires the safety controller (and CRC unit) to be set up before calling!
|
||||
*/
|
||||
int flash_crc_trigger_check(void);
|
||||
|
||||
/**
|
||||
* @brief Calculate CRC over flash section
|
||||
* @param sec Section to calculate CRC over
|
||||
* @param[out] crc_result Calculated CRC
|
||||
* @return negative if internal error occured. Otherwise (independent from CRC check result) 0.
|
||||
*/
|
||||
int flash_crc_calc_section(enum flash_crc_section sec, uint32_t *crc_result);
|
||||
|
||||
/**
|
||||
* @brief Get expected CRC value of a section
|
||||
* @param sec Section
|
||||
* @return Expected CRC
|
||||
*/
|
||||
uint32_t flash_crc_get_expected_crc(enum flash_crc_section sec);
|
||||
|
||||
#endif /* _SAFETY_FLASH_CRC_H_ */
|
@ -267,6 +267,13 @@ int safety_controller_set_overtemp_limit(float over_temperature);
|
||||
*/
|
||||
float safety_controller_get_overtemp_limit(void);
|
||||
|
||||
/**
|
||||
* @brief Perform a CRC check of the flash memory and set appropriate flags
|
||||
* @return negative if internal error occured. Otherwise (independent from CRC check result) 0.
|
||||
* @note This function requires the safety controller to be set up before!
|
||||
*/
|
||||
int safety_controller_trigger_flash_crc_check(void);
|
||||
|
||||
/**
|
||||
* @brief Recalculate the CRC of a given CRC Monitor. This has to be done once the supervised registers update
|
||||
* @param mon Monitor to recalculate
|
||||
|
@ -1,31 +0,0 @@
|
||||
/* Reflow Oven Controller
|
||||
*
|
||||
* Copyright (C) 2022 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/flash-crc-struct.h>
|
||||
#include <helper-macros/helper-macros.h>
|
||||
|
||||
const struct flash_crcs IN_SECTION(.flashcrc) crcs_in_flash = {
|
||||
.start_magic = 0xA8BE53F9UL,
|
||||
.crc_section_ccm_data = 0UL,
|
||||
.crc_section_text = 0UL,
|
||||
.crc_section_data = 0UL,
|
||||
.crc_section_vectors = 0UL,
|
||||
.end_magic = 0xFFA582FFUL,
|
||||
};
|
@ -1,152 +0,0 @@
|
||||
/* Reflow Oven Controller
|
||||
*
|
||||
* Copyright (C) 2022 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/flash-crc.h>
|
||||
#include <reflow-controller/safety/flash-crc-struct.h>
|
||||
#include <reflow-controller/safety/safety-controller.h>
|
||||
#include <stm-periph/crc-unit.h>
|
||||
|
||||
extern const uint32_t __ld_vectors_start;
|
||||
extern const uint32_t __ld_vectors_end;
|
||||
extern const uint32_t __ld_text_start;
|
||||
extern const uint32_t __ld_text_end;
|
||||
|
||||
extern const uint32_t __ld_sdata_ccm;
|
||||
extern const uint32_t __ld_edata_ccm;
|
||||
extern const uint32_t __ld_load_ccm_data;
|
||||
|
||||
extern const uint32_t __ld_sdata;
|
||||
extern const uint32_t __ld_edata;
|
||||
extern const uint32_t __ld_load_data;
|
||||
|
||||
|
||||
int flash_crc_trigger_check(void)
|
||||
{
|
||||
int ret = -1;
|
||||
int res;
|
||||
int any_err = 0;
|
||||
uint32_t crc;
|
||||
|
||||
/* Perform CRC check over vector table */
|
||||
res = flash_crc_calc_section(FLASH_CRC_VECTOR, &crc);
|
||||
if (res || crc != flash_crc_get_expected_crc(FLASH_CRC_VECTOR))
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
|
||||
any_err |= res;
|
||||
|
||||
/* Perform CRC check over text section */
|
||||
res = flash_crc_calc_section(FLASH_CRC_TEXT, &crc);
|
||||
if (res || crc != flash_crc_get_expected_crc(FLASH_CRC_TEXT))
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
|
||||
any_err |= res;
|
||||
|
||||
/* Perform CRC check over data section */
|
||||
res = flash_crc_calc_section(FLASH_CRC_DATA, &crc);
|
||||
if (res || crc != flash_crc_get_expected_crc(FLASH_CRC_DATA))
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
|
||||
any_err |= res;
|
||||
|
||||
/* Perform CRC check over ccm data section */
|
||||
res = flash_crc_calc_section(FLASH_CRC_CCMDATA, &crc);
|
||||
if (res || crc != flash_crc_get_expected_crc(FLASH_CRC_CCMDATA))
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
|
||||
any_err |= res;
|
||||
|
||||
if (!any_err)
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int flash_crc_calc_section(enum flash_crc_section sec, uint32_t *crc_result)
|
||||
{
|
||||
uint32_t len;
|
||||
const uint32_t *startptr;
|
||||
const uint32_t *endptr;
|
||||
const uint32_t *load_addr = NULL;
|
||||
|
||||
if (!crc_result)
|
||||
return -1002;
|
||||
|
||||
switch (sec) {
|
||||
case FLASH_CRC_VECTOR:
|
||||
startptr = &__ld_vectors_start;
|
||||
endptr = &__ld_vectors_end;
|
||||
break;
|
||||
case FLASH_CRC_TEXT:
|
||||
startptr = &__ld_text_start;
|
||||
endptr = &__ld_text_end;
|
||||
break;
|
||||
case FLASH_CRC_DATA:
|
||||
startptr = &__ld_sdata;
|
||||
endptr = &__ld_edata;
|
||||
load_addr = &__ld_load_data;
|
||||
break;
|
||||
case FLASH_CRC_CCMDATA:
|
||||
startptr = &__ld_sdata_ccm;
|
||||
endptr = &__ld_edata_ccm;
|
||||
load_addr = &__ld_load_ccm_data;
|
||||
break;
|
||||
default:
|
||||
return -1001;
|
||||
}
|
||||
|
||||
len = (uint32_t)((void *)endptr - (void *)startptr);
|
||||
if (!load_addr)
|
||||
load_addr = startptr;
|
||||
|
||||
/* Not a multiple of 32bit words long. Cannot calculate CRC in this case! */
|
||||
if (len % 4)
|
||||
return -1;
|
||||
|
||||
/* Calculate word count */
|
||||
len /= 4;
|
||||
|
||||
/* Reset CRC and calculate over data range */
|
||||
crc_unit_reset();
|
||||
crc_unit_input_array(load_addr, len);
|
||||
*crc_result = crc_unit_get_crc();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t flash_crc_get_expected_crc(enum flash_crc_section sec)
|
||||
{
|
||||
uint32_t crc;
|
||||
|
||||
switch (sec) {
|
||||
case FLASH_CRC_VECTOR:
|
||||
crc = crcs_in_flash.crc_section_vectors;
|
||||
break;
|
||||
case FLASH_CRC_TEXT:
|
||||
crc = crcs_in_flash.crc_section_text;
|
||||
break;
|
||||
case FLASH_CRC_DATA:
|
||||
crc = crcs_in_flash.crc_section_data;
|
||||
break;
|
||||
case FLASH_CRC_CCMDATA:
|
||||
crc = crcs_in_flash.crc_section_ccm_data;
|
||||
break;
|
||||
default:
|
||||
crc = 0xFFFFFFFFul;
|
||||
break;
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
@ -43,7 +43,6 @@
|
||||
#include <stm-periph/rcc-manager.h>
|
||||
#include <reflow-controller/temp-converter.h>
|
||||
#include <reflow-controller/adc-meas.h>
|
||||
#include <reflow-controller/safety/flash-crc.h>
|
||||
#include <reflow-controller/periph-config/safety-adc-hwcfg.h>
|
||||
|
||||
/**
|
||||
@ -155,6 +154,15 @@ struct overtemp_config {
|
||||
uint32_t crc;
|
||||
};
|
||||
|
||||
struct flash_crcs {
|
||||
uint32_t start_magic;
|
||||
uint32_t crc_section_text;
|
||||
uint32_t crc_section_data;
|
||||
uint32_t crc_section_ccm_data;
|
||||
uint32_t crc_section_vectors;
|
||||
uint32_t end_magic;
|
||||
};
|
||||
|
||||
struct crc_monitor_register {
|
||||
const volatile void *reg_addr;
|
||||
uint32_t mask;
|
||||
@ -906,7 +914,7 @@ void safety_controller_init(void)
|
||||
/* This is usually done by the safety memory already. But, since this module also uses the CRC... */
|
||||
crc_unit_init();
|
||||
|
||||
flash_crc_trigger_check();
|
||||
safety_controller_trigger_flash_crc_check();
|
||||
stack_check_init_corruption_detect_area();
|
||||
|
||||
hw_rev = get_pcb_hardware_version();
|
||||
@ -1446,6 +1454,94 @@ float safety_controller_get_overtemp_limit(void)
|
||||
return safety_controller_overtemp_config.overtemp_deg_celsius;
|
||||
}
|
||||
|
||||
extern const uint32_t __ld_vectors_start;
|
||||
extern const uint32_t __ld_vectors_end;
|
||||
extern const uint32_t __ld_text_start;
|
||||
extern const uint32_t __ld_text_end;
|
||||
|
||||
extern const uint32_t __ld_sdata_ccm;
|
||||
extern const uint32_t __ld_edata_ccm;
|
||||
extern const uint32_t __ld_load_ccm_data;
|
||||
|
||||
extern const uint32_t __ld_sdata;
|
||||
extern const uint32_t __ld_edata;
|
||||
extern const uint32_t __ld_load_data;
|
||||
|
||||
int safety_controller_trigger_flash_crc_check(void)
|
||||
{
|
||||
/* This structs needs to be volatile!!
|
||||
* This prevents the compiler form optimizing out the reads to the crcs which will be patched in later by
|
||||
* a separate python script!
|
||||
*/
|
||||
static volatile const struct flash_crcs IN_SECTION(.flashcrc) crcs_in_flash = {
|
||||
.start_magic = 0xA8BE53F9UL,
|
||||
.crc_section_ccm_data = 0UL,
|
||||
.crc_section_text = 0UL,
|
||||
.crc_section_data = 0UL,
|
||||
.crc_section_vectors = 0UL,
|
||||
.end_magic = 0xFFA582FFUL,
|
||||
};
|
||||
|
||||
int ret = -1;
|
||||
uint32_t len;
|
||||
uint32_t crc;
|
||||
|
||||
/* Perform CRC check over vector table */
|
||||
len = (uint32_t)((void *)&__ld_vectors_end - (void *)&__ld_vectors_start);
|
||||
if (len % 4) {
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
|
||||
} else {
|
||||
len /= 4;
|
||||
crc_unit_reset();
|
||||
crc_unit_input_array(&__ld_vectors_start, len);
|
||||
crc = crc_unit_get_crc();
|
||||
if (crc != crcs_in_flash.crc_section_vectors)
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
|
||||
}
|
||||
|
||||
/* Perform CRC check over text section */
|
||||
len = (uint32_t)((void *)&__ld_text_end - (void *)&__ld_text_start);
|
||||
if (len % 4) {
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
|
||||
} else {
|
||||
len /= 4;
|
||||
crc_unit_reset();
|
||||
crc_unit_input_array(&__ld_text_start, len);
|
||||
crc = crc_unit_get_crc();
|
||||
if (crc != crcs_in_flash.crc_section_text)
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_CODE);
|
||||
}
|
||||
|
||||
/* Perform CRC check over data section */
|
||||
len = (uint32_t)((void *)&__ld_edata - (void *)&__ld_sdata);
|
||||
if (len % 4) {
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
|
||||
} else {
|
||||
len /= 4;
|
||||
crc_unit_reset();
|
||||
crc_unit_input_array(&__ld_load_data, len);
|
||||
crc = crc_unit_get_crc();
|
||||
if (crc != crcs_in_flash.crc_section_data)
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
|
||||
}
|
||||
|
||||
/* Perform CRC check over ccm data section */
|
||||
len = (uint32_t)((void *)&__ld_edata_ccm - (void *)&__ld_sdata_ccm);
|
||||
if (len % 4) {
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
|
||||
} else {
|
||||
len /= 4;
|
||||
crc_unit_reset();
|
||||
crc_unit_input_array(&__ld_load_ccm_data, len);
|
||||
crc = crc_unit_get_crc();
|
||||
if (crc != crcs_in_flash.crc_section_ccm_data)
|
||||
safety_controller_report_error(ERR_FLAG_FLASH_CRC_DATA);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int safety_controller_set_crc_monitor(enum crc_monitor mon, uint32_t password)
|
||||
{
|
||||
uint32_t i;
|
||||
|
@ -48,7 +48,6 @@
|
||||
#include <stm-periph/option-bytes.h>
|
||||
#include <reflow-controller/ui/gui.h>
|
||||
#include <reflow-controller/ui/shell-uart.h>
|
||||
#include <reflow-controller/safety/flash-crc.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
@ -1021,53 +1020,6 @@ shellmatta_retCode_t shell_cmd_set_baud(const shellmatta_handle_t handle, const
|
||||
return SHELLMATTA_OK;
|
||||
}
|
||||
|
||||
static void shell_print_crc_line(const shellmatta_handle_t handle, const char *name,
|
||||
uint32_t crc, uint32_t crc_expected)
|
||||
{
|
||||
shellmatta_printf(handle, "%-15s0x%08x 0x%08x [%s]\r\n",
|
||||
name,
|
||||
crc,
|
||||
crc_expected,
|
||||
crc != crc_expected ? "\e[1;31mERR\e[m" : "\e[32mOK\e[m");
|
||||
}
|
||||
|
||||
shellmatta_retCode_t shell_cmd_flash_crc(const shellmatta_handle_t handle, const char *args, uint32_t len)
|
||||
{
|
||||
(void)args;
|
||||
(void)len;
|
||||
uint32_t crc = 0;
|
||||
uint32_t crc_expected = 0;
|
||||
int res;
|
||||
|
||||
shellmatta_printf(handle, " Calculated Expected State\r\n\r\n");
|
||||
|
||||
res = flash_crc_calc_section(FLASH_CRC_VECTOR, &crc);
|
||||
crc_expected = flash_crc_get_expected_crc(FLASH_CRC_VECTOR);
|
||||
if (res)
|
||||
shellmatta_printf(handle, "Error during calculation!\r\n");
|
||||
shell_print_crc_line(handle, "Vector CRC:", crc, crc_expected);
|
||||
|
||||
res = flash_crc_calc_section(FLASH_CRC_TEXT, &crc);
|
||||
crc_expected = flash_crc_get_expected_crc(FLASH_CRC_TEXT);
|
||||
if (res)
|
||||
shellmatta_printf(handle, "Error during calculation!\r\n");
|
||||
shell_print_crc_line(handle, "Code CRC:", crc, crc_expected);
|
||||
|
||||
res = flash_crc_calc_section(FLASH_CRC_DATA, &crc);
|
||||
crc_expected = flash_crc_get_expected_crc(FLASH_CRC_DATA);
|
||||
if (res)
|
||||
shellmatta_printf(handle, "Error during calculation!\r\n");
|
||||
shell_print_crc_line(handle, "Data CRC:", crc, crc_expected);
|
||||
|
||||
res = flash_crc_calc_section(FLASH_CRC_CCMDATA, &crc);
|
||||
crc_expected = flash_crc_get_expected_crc(FLASH_CRC_CCMDATA);
|
||||
if (res)
|
||||
shellmatta_printf(handle, "Error during calculation!\r\n");
|
||||
shell_print_crc_line(handle, "CCM Data CRC:", crc, crc_expected);
|
||||
|
||||
return SHELLMATTA_OK;
|
||||
}
|
||||
|
||||
//typedef struct shellmatta_cmd
|
||||
//{
|
||||
// char *cmd; /**< command name */
|
||||
@ -1077,7 +1029,7 @@ shellmatta_retCode_t shell_cmd_flash_crc(const shellmatta_handle_t handle, const
|
||||
// shellmatta_cmdFct_t cmdFct; /**< pointer to the cmd callack function */
|
||||
// struct shellmatta_cmd *next; /**< pointer to next command or NULL */
|
||||
//} shellmatta_cmd_t;
|
||||
static shellmatta_cmd_t cmd[26] = {
|
||||
static shellmatta_cmd_t cmd[25] = {
|
||||
{
|
||||
.cmd = "version",
|
||||
.cmdAlias = "ver",
|
||||
@ -1277,14 +1229,6 @@ static shellmatta_cmd_t cmd[26] = {
|
||||
.helpText = "Set a new temporary baudrate for the UART",
|
||||
.usageText = "baudrate <new baud>",
|
||||
.cmdFct = shell_cmd_set_baud,
|
||||
.next = &cmd[25],
|
||||
},
|
||||
{
|
||||
.cmd = "flashcrc",
|
||||
.cmdAlias = "fcrc",
|
||||
.helpText = "Calculate the Flash CRCs",
|
||||
.usageText = "flashcrc",
|
||||
.cmdFct = shell_cmd_flash_crc,
|
||||
.next = NULL,
|
||||
},
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user