SP8EBC-ParaTNC/src/kiss_protocol/kiss_did.c

449 wiersze
12 KiB
C

/*
* kiss_did.c
*
* Implementation of all arrays defining data source for DIDs and function
* which generate raw binary data to be returned to a tester
*
* Created on: Jun 27, 2023
* Author: mateusz
*/
#include <etc/kiss_did_configuration.h>
#include <etc/kiss_configuation.h>
#include <string.h>
#ifdef STM32F10X_MD_VL
#include <stm32f10x.h>
#define SRAM1_SIZE_MAX (0x00002000UL) /*!< maximum SRAM1 size (up to 96 KBytes) */
#endif
#ifdef STM32L471xx
#include <stm32l4xx.h>
#endif
#define KISS_DID_SIZE_MAPPING_INT8 1
#define KISS_DID_SIZE_MAPPING_INT16 2
#define KISS_DID_SIZE_MAPPING_INT32 3
//!< Dummy variable used only as end of definition marker in tables
char did_dummy_data;
//!< Structure to define a DID which return numeric data
typedef struct kiss_did_numeric_definition_t {
uint16_t identifier;
void* first_data;
uint8_t first_data_size;
void* second_data;
uint8_t second_data_size;
void* third_data;
uint8_t third_data_size;
}kiss_did_numeric_definition_t;
//!< Definition of all DIDs with numeric data
const static kiss_did_numeric_definition_t kiss_did_def[] = {
DIDS_STRING(DID_NUMERIC_STRING_DEFINITION_EXPANDER)
DIDS_FLOAT(DID_NUMERIC_FLOAT_DEFINITION_EXPANDER)
DIDS_NUMERIC(DID_NUMERIC_DEFINITION_EXPANDER)
};
//!< Mapping between a result of sizeof operator and a value of sizebyte
const static uint8_t kiss_did_sizeof_to_sizebyte_mapping[5] = {
0,// nothing
KISS_DID_SIZE_MAPPING_INT8, // int8_t -> 1
KISS_DID_SIZE_MAPPING_INT16, // int16_t -> 2
0, // nothing
KISS_DID_SIZE_MAPPING_INT32 // int32_t -> 3
};
/**
* Checks how many variables will be returned by this DID
* @param definition
* @return
*/
static uint8_t kiss_did_how_much_data(kiss_did_numeric_definition_t * definition) {
int out = 0;
if (definition != 0) {
if (definition->first_data != &DID_EMPTY) {
out++;
if (definition->second_data != &DID_EMPTY) {
out++;
if (definition->third_data != &DID_EMPTY) {
out++;
}
else {
// zero data size as DID_EMPTY is only a dummy placeholder
definition->third_data_size = 0;
}
}
else {
// zero data size as DID_EMPTY is only a dummy placeholder
definition->third_data_size = 0;
definition->second_data_size = 0;
}
}
}
return out;
}
/**
* Checks if this DID defines data with float
* @param definition
* @return
*/
static int kiss_did_validate_is_float(kiss_did_numeric_definition_t * definition) {
int out = 0;
if (definition != 0) {
if (kiss_did_how_much_data(definition) >= 1) {
if (definition->first_data_size == 0 && definition->second_data_size == 0 && definition->third_data_size == 0) {
out = 1;
}
}
}
return out;
}
/**
* Check if this DID return string data
* @param definition
* @return
*/
static int kiss_did_validate_is_string(kiss_did_numeric_definition_t * definition) {
int out = 0;
if (definition != 0) {
if (kiss_did_how_much_data(definition) == 3) {
if (definition->second_data == (void*)0xDEADBEEFu && definition->third_data == (void*)0xDEADBEEFu) {
out = 1;
}
}
}
return out;
}
/**
* Checks if DID configuration is anything valid
* @param definition
* @return
*/
static int kiss_did_validate(kiss_did_numeric_definition_t * definition, uint8_t * amount) {
int out = 0;
uint8_t amount_of_data = 0;
// check if pointer is valid
if (definition != 0) {
// check how many variables will be returned by this DID
amount_of_data = kiss_did_how_much_data(definition);
// if first DID is defined
if (amount_of_data > 0) {
// check if DID data size an address is correct
if ((definition->first_data_size == sizeof(int8_t) ||
definition->first_data_size == sizeof(int16_t) ||
definition->first_data_size == sizeof(int32_t)) &&
(uint32_t)definition->first_data > SRAM_BASE &&
(uint32_t)definition->first_data < SRAM_BASE + SRAM1_SIZE_MAX) {
// valid
out = 1;
// if second did is also defined
if (amount_of_data > 1) {
// check if DID data size is correct
if ((definition->first_data_size == sizeof(int8_t) ||
definition->first_data_size == sizeof(int16_t) ||
definition->first_data_size == sizeof(int32_t)) &&
(uint32_t)definition->second_data > SRAM_BASE &&
(uint32_t)definition->second_data < SRAM_BASE + SRAM1_SIZE_MAX) {
// valid
out = 1;
// if third DID source is also defined
if (amount_of_data > 2) {
// check third DID source data size
if ((definition->first_data_size == sizeof(int8_t) ||
definition->first_data_size == sizeof(int16_t) ||
definition->first_data_size == sizeof(int32_t)) &&
(uint32_t)definition->third_data > SRAM_BASE &&
(uint32_t)definition->third_data < SRAM_BASE + SRAM1_SIZE_MAX) {
// valid
out = 1;
}
else {
out = 0;
}
}
else {
; // third data source doesn't need to be defined
}
}
else {
// not valid, zero output and do nothing more
out = 0;
}
}
else {
; // second did source doesn't need to be defined
// so do nothing here
}
}
else {
out = 0;
}
}
else {
; // at least one DID must be defined
// keep out set to zero
}
}
// special case for float DIDs
if (out == 0) {
if (kiss_did_validate_is_float(definition) != 0) {
out = 1;
}
}
if (amount != 0) {
*amount = amount_of_data;
}
return out;
}
/**
* Creates a response for DID request into specified buffer. Please take into account
* that this function cannot use full size of this buffer. The exact content of the
* response is: FEND, NONSTANDARD, RESPONSE_LN, KISS_READ_DID_RESP, DID LSB, DID MSB,
* data, FEND.
* So in practice payload data may use only buffer_ln - 7 bytes of data. By default
* an output buffer used to return a response has 20 bytes in size
*
* @param identifier
* @param output_buffer pointer to buffer where response will be generated into
* @param buffer_ln a size of this buffer, please be aware of note about available buffer!
* @return Zero if DID hasn't been found in definitions, otherwise total data lenght
*/
uint8_t kiss_did_response(uint16_t identifier, uint8_t * output_buffer, uint16_t buffer_ln) {
uint8_t out = 0xFF;
// iterator to go through DID definition
int i = 0;
/**
* Explanation how size_byte works. DID can return three kinds of data:
* a string, integer data (one, two or three) with different width,
* float (also one, two or three). size_byte is used to distinguish
* between these three formats. It works like that
*
* 0y - If most significant bit is set to zero, size_byte will
* signalize string DID as ASCII characters from basic ASIC
* table are from range 0 to 127
* 10 - If most significant bit is set to one AND next significant
* bit is set to zero this DID returns integer data. In such
* case three groups of two bits controls a size of data according
* to 'kiss_did_sizeof_to_sizebyte_mapping'. If a group of two bits
* is set to 0 it means that DID consist less than 3 variables
* 11 - This means that a DID return float data. One did cannot mix
* integer and floating point data and float has always 32 bit wide.
* The rest of size_bute doesn't specify any size. Less significant
* part consist number how many variables are returned.
*/
// first byte of a DID response which defines size of each field
uint8_t size_byte = 0u;
uint8_t number_of_data_source = 0u;
// data of a did found in configuration
kiss_did_numeric_definition_t found = {.identifier = 0xFFFFu};
// iterate through DID definition and check if this id exist there
do {
// check if current position is the DID we're looking for
if (kiss_did_def[i].identifier == identifier) {
// if yes copy this data and exit the loop
found = kiss_did_def[i];
break;
}
else {
// if no, go to the next DID definition
i++;
}
} while (kiss_did_def[i].identifier != 0xFFFFU);
// check is valid
const int is_valid = kiss_did_validate(&found, &number_of_data_source);
// check if this is string did
const int is_string = kiss_did_validate_is_string(&found);
// check if this is float number did
const int is_float = kiss_did_validate_is_float(&found);
// if something has been found and it is valid
if (found.identifier != 0xFFFFu && is_valid == 1 && is_float == 0) {
// put DID itself at the begining of a response
output_buffer[0] = (identifier & 0xFF);
output_buffer[1] = (identifier & 0xFF00) >> 8;
// set the sign bit in size_byte to distinguish that from ASCII did.
// ASCII characters are from range 0 - 127 so they never have that
// set to one
size_byte |= 0x80u;
// append a size of first data source
size_byte |= kiss_did_sizeof_to_sizebyte_mapping[found.first_data_size];
// append a size of second data source
size_byte |= (kiss_did_sizeof_to_sizebyte_mapping[found.second_data_size] << 2);
// append a size of third data source
size_byte |= (kiss_did_sizeof_to_sizebyte_mapping[found.third_data_size] << 4);
output_buffer[2] = size_byte;
// move after DID value and size_byte
output_buffer += 3;
//append first data source
memcpy(output_buffer, found.first_data, found.first_data_size);
//move forward a poiner to response buffer
output_buffer += found.first_data_size;
out += found.first_data_size;
if (number_of_data_source > 1) {
//append second data source
memcpy(output_buffer, found.second_data, found.second_data_size);
//move forward a poiner to response buffer
output_buffer += found.second_data_size;
out += found.second_data_size;
}
if (number_of_data_source > 2) {
//append third data source
memcpy(output_buffer, found.third_data, found.third_data_size);
//move forward a poitner to response buffer
output_buffer += found.third_data_size;
out += found.third_data_size;
}
// also include size_byte in this calculation
out++;
// include DID value itself
out += 2;
}
else if (found.identifier != 0xFFFFu && is_valid == 1 && is_float == 1) {
// put DID itself at the begining of a response
output_buffer[0] = (identifier & 0xFF);
output_buffer[1] = (identifier & 0xFF00) >> 8;
// set two most significant bits to one to signalize that it is
size_byte |= 0xC0u;
size_byte |= number_of_data_source;
output_buffer[2] = size_byte;
// move after DID value and size_byte
output_buffer += 3;
//append first data source
memcpy(output_buffer, found.first_data, sizeof(float));
//move forward a poiner to response buffer
output_buffer += sizeof(float);
out += found.first_data_size;
if (number_of_data_source > 1) {
//append second data source
memcpy(output_buffer, found.second_data, sizeof(float));
//move forward a poiner to response buffer
output_buffer += sizeof(float);
out += sizeof(float);
}
if (number_of_data_source > 2) {
//append third data source
memcpy(output_buffer, found.third_data, sizeof(float));
//move forward a poitner to response buffer
output_buffer += sizeof(float);
out += sizeof(float);
}
// also include size_byte in this calculation
out++;
// include DID value itself
out += 2;
}
else if (found.identifier != 0xFFFFu && is_string == 1) {
// put DID itself at the begining of a response
output_buffer[0] = (identifier & 0xFF);
output_buffer[1] = (identifier & 0xFF00) >> 8;
// if this is a string DID
const void * str = (void *)found.first_data;
const size_t str_len = found.first_data_size;
memset(output_buffer, 0x00, buffer_ln - 2);
if (str_len - 2 > buffer_ln) {
memcpy(output_buffer, str, buffer_ln - 2);
out = buffer_ln - 2;
}
else {
memcpy(output_buffer, str, str_len);
out = str_len;
}
// include DID number itself
out += 2;
}
else {
out = 0;
}
return out;
}