Merge branch 'issue/37-crc-flags-config-regs' into dev
This commit is contained in:
		@@ -82,6 +82,8 @@ static inline void adc_pt1000_disable_adc(void)
 | 
			
		||||
	ADC_PT1000_PERIPH->CR2 &= ~ADC_CR2_ADON;
 | 
			
		||||
	DMA2_Stream0->CR = 0;
 | 
			
		||||
 | 
			
		||||
	safety_controller_set_crc_monitor(ERR_CRC_MON_MEAS_ADC, SAFETY_CRC_MON_MEAS_ADC_PW);
 | 
			
		||||
 | 
			
		||||
	safety_controller_report_error_with_key(ERR_FLAG_MEAS_ADC_OFF, MEAS_ADC_SAFETY_FLAG_KEY);
 | 
			
		||||
	safety_controller_enable_timing_mon(ERR_TIMING_MEAS_ADC, false);
 | 
			
		||||
	rcc_manager_disable_clock(&RCC->APB2ENR, BITMASK_TO_BITNO(RCC_APB2ENR_ADC3EN));
 | 
			
		||||
@@ -181,6 +183,8 @@ void adc_pt1000_setup_meas(void)
 | 
			
		||||
	streaming_flag_ptr = NULL;
 | 
			
		||||
	adc_watchdog_counter = 0UL;
 | 
			
		||||
	stream_buffer = NULL;
 | 
			
		||||
 | 
			
		||||
	safety_controller_set_crc_monitor(ERR_CRC_MON_MEAS_ADC, SAFETY_CRC_MON_MEAS_ADC_PW);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void adc_pt1000_set_moving_average_filter_param(float alpha)
 | 
			
		||||
 
 | 
			
		||||
@@ -58,6 +58,8 @@ enum safety_flag {
 | 
			
		||||
	ERR_FLAG_OVERTEMP = (1<<18),
 | 
			
		||||
	ERR_FLAG_FLASH_CRC_CODE = (1<<19),
 | 
			
		||||
	ERR_FLAG_FLASH_CRC_DATA = (1<<20),
 | 
			
		||||
	ERR_FLAG_CFG_CRC_MEAS_ADC = (1<<21),
 | 
			
		||||
	ERR_FLAG_CFG_CRC_SAFETY_ADC = (1<<22),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -73,6 +75,12 @@ enum timing_monitor {
 | 
			
		||||
	ERR_TIMING_MAIN_LOOP = (1<<3),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum crc_monitor {
 | 
			
		||||
	ERR_CRC_MON_MEAS_ADC = 0,
 | 
			
		||||
	ERR_CRC_MON_SAFETY_ADC,
 | 
			
		||||
	N_ERR_CRC_MON
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Enum Type representing analog value monitors.
 | 
			
		||||
 *
 | 
			
		||||
@@ -146,6 +154,16 @@ enum analog_value_monitor {
 | 
			
		||||
 */
 | 
			
		||||
#define SAFETY_CONTROLLER_ADC_DELAY_MS 250
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Password for resetting ERR_CRC_MON_MEAS_ADC
 | 
			
		||||
 */
 | 
			
		||||
#define SAFETY_CRC_MON_MEAS_ADC_PW 0xD96254A2
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Password for resetting ERR_CRC_MON_SAFETY_ADC
 | 
			
		||||
 */
 | 
			
		||||
#define SAFETY_CRC_MON_SAFETY_ADC_PW 0xA8DF2368
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Default persistence of safety flags. These values are loaded into the safety tables on startup
 | 
			
		||||
 */
 | 
			
		||||
@@ -169,7 +187,9 @@ enum analog_value_monitor {
 | 
			
		||||
					ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_AMON_SUPPLY_VOLT, false), \
 | 
			
		||||
					ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_OVERTEMP, false), \
 | 
			
		||||
					ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_FLASH_CRC_CODE, true), \
 | 
			
		||||
					ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_FLASH_CRC_DATA, true)
 | 
			
		||||
					ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_FLASH_CRC_DATA, true), \
 | 
			
		||||
					ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_CFG_CRC_MEAS_ADC, true), \
 | 
			
		||||
					ERR_FLAG_PERSIST_ENTRY(ERR_FLAG_CFG_CRC_SAFETY_ADC, true), \
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Default config weights of safety flags. These values are loaded into the safety tables on startup
 | 
			
		||||
 */
 | 
			
		||||
@@ -194,5 +214,7 @@ enum analog_value_monitor {
 | 
			
		||||
			ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_OVERTEMP, SAFETY_FLAG_CONFIG_WEIGHT_PID), \
 | 
			
		||||
			ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_FLASH_CRC_CODE, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
 | 
			
		||||
			ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_FLASH_CRC_DATA, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
 | 
			
		||||
			ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_CFG_CRC_MEAS_ADC, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
 | 
			
		||||
			ERR_FLAG_WEIGHT_ENTRY(ERR_FLAG_CFG_CRC_SAFETY_ADC, SAFETY_FLAG_CONFIG_WEIGHT_NONE), \
 | 
			
		||||
 | 
			
		||||
#endif /* __SAFETY_CONFIG_H__ */
 | 
			
		||||
 
 | 
			
		||||
@@ -274,6 +274,14 @@ float safety_controller_get_overtemp_limit(void);
 | 
			
		||||
 */
 | 
			
		||||
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
 | 
			
		||||
 * @param password Password
 | 
			
		||||
 * @return 0 if successful
 | 
			
		||||
 */
 | 
			
		||||
int safety_controller_set_crc_monitor(enum crc_monitor mon, uint32_t password);
 | 
			
		||||
 | 
			
		||||
#endif /* __SAFETY_CONTROLLER_H__ */
 | 
			
		||||
 | 
			
		||||
/** @} */
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@
 | 
			
		||||
#include <helper-macros/helper-macros.h>
 | 
			
		||||
#include <stm-periph/rcc-manager.h>
 | 
			
		||||
#include <reflow-controller/hw-version-detect.h>
 | 
			
		||||
#include <reflow-controller/safety/safety-controller.h>
 | 
			
		||||
 | 
			
		||||
static const uint8_t safety_adc_channels[SAFETY_ADC_NUM_OF_CHANNELS] = {SAFETY_ADC_CHANNELS};
 | 
			
		||||
static volatile uint8_t safety_adc_conversion_complete;
 | 
			
		||||
@@ -86,7 +87,9 @@ void safety_adc_init(void)
 | 
			
		||||
	NVIC_EnableIRQ(DMA2_Stream4_IRQn);
 | 
			
		||||
 | 
			
		||||
	/* Enable ADC */
 | 
			
		||||
	SAFETY_ADC_ADC_PERIPHERAL->CR1 |= ADC_CR1_SCAN;
 | 
			
		||||
	SAFETY_ADC_ADC_PERIPHERAL->CR2 = ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS;
 | 
			
		||||
	safety_controller_set_crc_monitor(ERR_CRC_MON_SAFETY_ADC, SAFETY_CRC_MON_SAFETY_ADC_PW);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -95,9 +98,11 @@ void safety_adc_deinit(void)
 | 
			
		||||
	SAFETY_ADC_ADC_PERIPHERAL->CR1 = 0UL;
 | 
			
		||||
	SAFETY_ADC_ADC_PERIPHERAL->CR2 = 0UL;
 | 
			
		||||
	SAFETY_ADC_ADC_PERIPHERAL->SMPR1  = 0UL;
 | 
			
		||||
 | 
			
		||||
	rcc_manager_disable_clock(&RCC->APB1ENR, BITMASK_TO_BITNO(RCC_APB2ENR_ADC2EN));
 | 
			
		||||
	DMA2_Stream4->CR = 0;
 | 
			
		||||
	rcc_manager_disable_clock(&RCC->AHB1ENR, BITMASK_TO_BITNO(RCC_AHB1ENR_DMA2EN));
 | 
			
		||||
	safety_controller_set_crc_monitor(ERR_CRC_MON_SAFETY_ADC, SAFETY_CRC_MON_SAFETY_ADC_PW);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float safety_adc_convert_channel(enum safety_adc_meas_channel channel, uint16_t analog_value)
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,7 @@
 | 
			
		||||
#include <stm-periph/rcc-manager.h>
 | 
			
		||||
#include <reflow-controller/temp-converter.h>
 | 
			
		||||
#include <reflow-controller/adc-meas.h>
 | 
			
		||||
#include <reflow-controller/periph-config/safety-adc-hwcfg.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Macro that checks if a given @ref error_flag is persistent
 | 
			
		||||
@@ -162,6 +163,28 @@ struct flash_crcs {
 | 
			
		||||
	uint32_t end_magic;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct crc_monitor_register {
 | 
			
		||||
	const volatile void *reg_addr;
 | 
			
		||||
	uint32_t mask;
 | 
			
		||||
	uint8_t size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define CRC_MON_REGISTER_ENTRY(_addr, _mask, _size) {.reg_addr = &(_addr), .mask = (_mask), .size = (_size)}
 | 
			
		||||
 | 
			
		||||
struct crc_mon {
 | 
			
		||||
	/**
 | 
			
		||||
	 * @brief Array of registers to monitor. Terminated by NULL sentinel!
 | 
			
		||||
	 */
 | 
			
		||||
	const struct crc_monitor_register *registers;
 | 
			
		||||
	const enum crc_monitor monitor;
 | 
			
		||||
	const uint32_t pw;
 | 
			
		||||
	const enum safety_flag flag_to_set;
 | 
			
		||||
	uint32_t expected_crc;
 | 
			
		||||
	uint32_t expected_crc_inv;
 | 
			
		||||
	uint32_t last_crc;
 | 
			
		||||
	bool active;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief All safety error flags.
 | 
			
		||||
 */
 | 
			
		||||
@@ -187,6 +210,8 @@ static volatile struct error_flag IN_SECTION(.ccm.data) flags[] = {
 | 
			
		||||
	ERR_FLAG_ENTRY(ERR_FLAG_OVERTEMP),
 | 
			
		||||
	ERR_FLAG_ENTRY(ERR_FLAG_FLASH_CRC_CODE),
 | 
			
		||||
	ERR_FLAG_ENTRY(ERR_FLAG_FLASH_CRC_DATA),
 | 
			
		||||
	ERR_FLAG_ENTRY(ERR_FLAG_CFG_CRC_MEAS_ADC),
 | 
			
		||||
	ERR_FLAG_ENTRY(ERR_FLAG_CFG_CRC_SAFETY_ADC),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -259,6 +284,64 @@ static uint32_t IN_SECTION(.ccm.bss) flag_weight_crc;
 | 
			
		||||
 */
 | 
			
		||||
static struct overtemp_config IN_SECTION(.ccm.bss) safety_controller_overtemp_config;
 | 
			
		||||
 | 
			
		||||
static const struct crc_monitor_register meas_adc_crc_regs[] = {
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->CR1, 0xFFFFFFFF, 4),
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->CR2, ADC_CR2_ADON | ADC_CR2_CONT | ADC_CR2_ALIGN |
 | 
			
		||||
			       ADC_CR2_DMA | ADC_CR2_DDS | ADC_CR2_EOCS | ADC_CR2_EXTEN | ADC_CR2_EXTSEL, 4),
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->SMPR1, 0xFFFFFFFF, 4),
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->SMPR2, 0xFFFFFFFF, 4),
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->HTR, ADC_HTR_HT, 4),
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->LTR, ADC_LTR_LT, 4),
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->SQR1, ADC_SQR1_L |
 | 
			
		||||
			       ADC_SQR1_SQ16 | ADC_SQR1_SQ15 | ADC_SQR1_SQ14 | ADC_SQR1_SQ13, 4),
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->SQR2,
 | 
			
		||||
			       ADC_SQR2_SQ12 | ADC_SQR2_SQ11 | ADC_SQR2_SQ10 | ADC_SQR2_SQ9 |
 | 
			
		||||
			       ADC_SQR2_SQ8 | ADC_SQR2_SQ7, 4),
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(ADC_PT1000_PERIPH->SQR3, ADC_SQR3_SQ6 | ADC_SQR3_SQ5 | ADC_SQR3_SQ4 |
 | 
			
		||||
			       ADC_SQR3_SQ3| ADC_SQR3_SQ2 | ADC_SQR3_SQ1, 4),
 | 
			
		||||
	{NULL, 0, 0}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct crc_monitor_register safety_adc_crc_regs[] = {
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(SAFETY_ADC_ADC_PERIPHERAL->CR1, 0xFFFFFFFF, 4),
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(SAFETY_ADC_ADC_PERIPHERAL->CR2, ADC_CR2_ADON | ADC_CR2_CONT | ADC_CR2_ALIGN |
 | 
			
		||||
			       ADC_CR2_DMA | ADC_CR2_DDS | ADC_CR2_EOCS | ADC_CR2_EXTEN | ADC_CR2_EXTSEL, 4),
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(SAFETY_ADC_ADC_PERIPHERAL->SMPR1, 0xFFFFFFFF, 4),
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(SAFETY_ADC_ADC_PERIPHERAL->SMPR2, 0xFFFFFFFF, 4),
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(SAFETY_ADC_ADC_PERIPHERAL->SQR1, ADC_SQR1_L |
 | 
			
		||||
			       ADC_SQR1_SQ16 | ADC_SQR1_SQ15 | ADC_SQR1_SQ14 | ADC_SQR1_SQ13, 4),
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(SAFETY_ADC_ADC_PERIPHERAL->SQR2,
 | 
			
		||||
			       ADC_SQR2_SQ12 | ADC_SQR2_SQ11 | ADC_SQR2_SQ10 | ADC_SQR2_SQ9 |
 | 
			
		||||
			       ADC_SQR2_SQ8 | ADC_SQR2_SQ7, 4),
 | 
			
		||||
	CRC_MON_REGISTER_ENTRY(SAFETY_ADC_ADC_PERIPHERAL->SQR3, ADC_SQR3_SQ6 | ADC_SQR3_SQ5 | ADC_SQR3_SQ4 |
 | 
			
		||||
			       ADC_SQR3_SQ3| ADC_SQR3_SQ2 | ADC_SQR3_SQ1, 4),
 | 
			
		||||
	{NULL, 0, 0}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct crc_mon IN_SECTION(.ccm.data) crc_monitors[] =
 | 
			
		||||
{
 | 
			
		||||
	{
 | 
			
		||||
		.registers = meas_adc_crc_regs,
 | 
			
		||||
		.monitor = ERR_CRC_MON_MEAS_ADC,
 | 
			
		||||
		.pw = SAFETY_CRC_MON_MEAS_ADC_PW,
 | 
			
		||||
		.flag_to_set = ERR_FLAG_CFG_CRC_MEAS_ADC,
 | 
			
		||||
		.expected_crc = 0UL,
 | 
			
		||||
		.expected_crc_inv = ~0UL,
 | 
			
		||||
		.last_crc = 0UL,
 | 
			
		||||
		.active = false,
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		.registers = safety_adc_crc_regs,
 | 
			
		||||
		.monitor = ERR_CRC_MON_SAFETY_ADC,
 | 
			
		||||
		.pw = SAFETY_CRC_MON_SAFETY_ADC_PW,
 | 
			
		||||
		.flag_to_set = ERR_FLAG_CFG_CRC_SAFETY_ADC,
 | 
			
		||||
		.expected_crc = 0UL,
 | 
			
		||||
		.expected_crc_inv = ~0UL,
 | 
			
		||||
		.last_crc = 0UL,
 | 
			
		||||
		.active = false,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Configure the overtemperature flag's settings
 | 
			
		||||
 * @param over_temperature Temperature to set the limit to.
 | 
			
		||||
@@ -389,6 +472,62 @@ static volatile struct error_flag *find_error_flag(enum safety_flag flag)
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int crc_monitor_calculate_crc(const struct crc_monitor_register *registers, uint32_t *crc_out)
 | 
			
		||||
{
 | 
			
		||||
	const struct crc_monitor_register *reg;
 | 
			
		||||
	uint32_t i;
 | 
			
		||||
	uint32_t reg_val;
 | 
			
		||||
 | 
			
		||||
	if (!registers || !crc_out)
 | 
			
		||||
		return -1000;
 | 
			
		||||
 | 
			
		||||
	crc_unit_reset();
 | 
			
		||||
	for (i = 0; registers[i].reg_addr != NULL; i++) {
 | 
			
		||||
		reg = ®isters[i];
 | 
			
		||||
		switch (reg->size) {
 | 
			
		||||
		case 1:
 | 
			
		||||
			reg_val = *((volatile uint8_t *)reg->reg_addr);
 | 
			
		||||
			break;
 | 
			
		||||
		case 2:
 | 
			
		||||
			reg_val = *((volatile uint16_t *)reg->reg_addr);
 | 
			
		||||
			break;
 | 
			
		||||
		case 4: /* FALLTHRU */
 | 
			
		||||
		default:
 | 
			
		||||
			reg_val = *((volatile uint32_t *)reg->reg_addr);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		reg_val &= reg->mask;
 | 
			
		||||
		crc_unit_input(reg_val);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*crc_out = crc_unit_get_crc();
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int safety_controller_check_crc_monitors(void)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t i;
 | 
			
		||||
	struct crc_mon *mon;
 | 
			
		||||
	uint32_t crc;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < COUNT_OF(crc_monitors); i++) {
 | 
			
		||||
		mon = &crc_monitors[i];
 | 
			
		||||
		if (!mon->active)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (crc_monitor_calculate_crc(mon->registers, &crc))
 | 
			
		||||
			return -1;
 | 
			
		||||
		if (mon->expected_crc != crc || ~mon->expected_crc_inv != crc) {
 | 
			
		||||
			safety_controller_report_error(mon->flag_to_set);
 | 
			
		||||
		}
 | 
			
		||||
		mon->last_crc = crc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief This function copies the safety weigths from flash ro RAM and computes the CRC
 | 
			
		||||
 */
 | 
			
		||||
@@ -608,6 +747,8 @@ static void safety_controller_process_monitor_checks(void)
 | 
			
		||||
		safety_controller_report_error(ERR_FLAG_OVERTEMP);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	(void)safety_controller_check_crc_monitors();
 | 
			
		||||
 | 
			
		||||
	safety_controller_process_active_timing_mons();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1406,4 +1547,31 @@ int safety_controller_trigger_flash_crc_check()
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int safety_controller_set_crc_monitor(enum crc_monitor mon, uint32_t password)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t i;
 | 
			
		||||
	struct crc_mon *monitor;
 | 
			
		||||
	uint32_t crc;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < COUNT_OF(crc_monitors); i++) {
 | 
			
		||||
		monitor = &crc_monitors[i];
 | 
			
		||||
		if (monitor->monitor != mon)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		monitor->active = true;
 | 
			
		||||
 | 
			
		||||
		if (password != monitor->pw)
 | 
			
		||||
			return -1002;
 | 
			
		||||
 | 
			
		||||
		(void)crc_monitor_calculate_crc(monitor->registers, &crc);
 | 
			
		||||
		monitor->expected_crc = crc;
 | 
			
		||||
		monitor->expected_crc_inv = ~crc;
 | 
			
		||||
		monitor->last_crc = crc;
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -1001;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** @} */
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user