save failed integration state
This commit is contained in:
1084
gomspace/libparam_client/src/bindings/python/pyparam.c
Normal file
1084
gomspace/libparam_client/src/bindings/python/pyparam.c
Normal file
File diff suppressed because it is too large
Load Diff
418
gomspace/libparam_client/src/pp/cmd/master_cmd.c
Normal file
418
gomspace/libparam_client/src/pp/cmd/master_cmd.c
Normal file
@ -0,0 +1,418 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/param/pp/spi/spi.h>
|
||||
#include <gs/param/pp/i2c/i2c.h>
|
||||
#include <gs/util/gosh/command.h>
|
||||
#include <gs/util/stdio.h>
|
||||
#include <gs/util/string.h>
|
||||
#include <gs/util/log.h>
|
||||
|
||||
static bool use_checksum;
|
||||
|
||||
static gs_pp_t gs_pp;
|
||||
|
||||
static int cmd_spi_init(gs_command_context_t *ctx)
|
||||
{
|
||||
uint8_t slave;
|
||||
gs_error_t error = gs_string_to_uint8(ctx->argv[1], &slave);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
bool big_endian = false;
|
||||
if (ctx->argc > 2) {
|
||||
error = gs_string_to_bool(ctx->argv[2], &big_endian);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
error = gs_pp_spi_init(&gs_pp, slave, big_endian);
|
||||
if (error) {
|
||||
memset(&gs_pp, 0, sizeof(gs_pp));
|
||||
return error;
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static int cmd_i2c_init(gs_command_context_t *ctx)
|
||||
{
|
||||
uint8_t bus;
|
||||
gs_error_t error = gs_string_to_uint8(ctx->argv[1], &bus);
|
||||
if (error) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
uint8_t addr;
|
||||
error = gs_string_to_uint8(ctx->argv[2], &addr);
|
||||
if (error) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
bool big_endian = false;
|
||||
if (ctx->argc > 3) {
|
||||
error = gs_string_to_bool(ctx->argv[3], &big_endian);
|
||||
if (error) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
error = gs_pp_i2c_init(&gs_pp, bus, addr, big_endian);
|
||||
if (error) {
|
||||
memset(&gs_pp, 0, sizeof(gs_pp));
|
||||
return error;
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static int cmd_checksum(gs_command_context_t *ctx)
|
||||
{
|
||||
if (ctx->argc > 1) {
|
||||
if (gs_string_to_bool(ctx->argv[1], &use_checksum)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
printf("Use CHECKSUM: %d\r\n", use_checksum);
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
// get_xxxx <table> <addr>
|
||||
static int cmd_parse(gs_command_context_t *ctx,
|
||||
uint32_t * table, uint32_t * addr)
|
||||
{
|
||||
if (ctx->argc < 3) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
gs_error_t error = gs_string_to_uint32(ctx->argv[1], table);
|
||||
if (error || (*table > 7)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
error = gs_string_to_uint32(ctx->argv[2], addr);
|
||||
if (error || (*addr > 255)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static int cmd_get_parse(gs_command_context_t * ctx,
|
||||
uint32_t * table, uint32_t * addr, uint32_t * count)
|
||||
{
|
||||
int res = cmd_parse(ctx, table, addr);
|
||||
if (res == GS_OK) {
|
||||
*count = 1;
|
||||
if (ctx->argc > 3) {
|
||||
gs_error_t error = gs_string_to_uint32(ctx->argv[3], count);
|
||||
if (error || (*count > 100)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#define CMD_GET(_ctx, _type, _func, _format) \
|
||||
{ \
|
||||
uint32_t table; \
|
||||
uint32_t addr; \
|
||||
uint32_t count; \
|
||||
int res = cmd_get_parse(_ctx, &table, &addr, &count); \
|
||||
if (res == GS_OK) { \
|
||||
_type value[count]; \
|
||||
gs_error_t error = _func(&gs_pp, table, addr, value, count, use_checksum ? GS_PP_FLAG_CHECKSUM : 0); \
|
||||
if (error) { \
|
||||
return error; \
|
||||
} \
|
||||
printf("value(s): "); \
|
||||
for (unsigned int i = 0; i < count; ++i) { \
|
||||
printf("%" _format " ", value[i]); \
|
||||
} \
|
||||
printf("\r\n"); \
|
||||
} \
|
||||
return res; \
|
||||
}
|
||||
|
||||
static int cmd_get_int8(gs_command_context_t * ctx)
|
||||
{
|
||||
CMD_GET(ctx, int8_t, gs_pp_get_int8, PRId8);
|
||||
}
|
||||
|
||||
static int cmd_get_int16(gs_command_context_t * ctx)
|
||||
{
|
||||
CMD_GET(ctx, int16_t, gs_pp_get_int16, PRId16);
|
||||
}
|
||||
|
||||
static int cmd_get_int32(gs_command_context_t * ctx)
|
||||
{
|
||||
CMD_GET(ctx, int32_t, gs_pp_get_int32, PRId32);
|
||||
}
|
||||
|
||||
static int cmd_get_uint8(gs_command_context_t * ctx)
|
||||
{
|
||||
CMD_GET(ctx, uint8_t, gs_pp_get_uint8, PRIu8);
|
||||
}
|
||||
|
||||
static int cmd_get_uint16(gs_command_context_t * ctx)
|
||||
{
|
||||
CMD_GET(ctx, uint16_t, gs_pp_get_uint16, PRIu16);
|
||||
}
|
||||
|
||||
static int cmd_get_uint32(gs_command_context_t * ctx)
|
||||
{
|
||||
CMD_GET(ctx, uint32_t, gs_pp_get_uint32, PRIu32);
|
||||
}
|
||||
|
||||
static int cmd_get_float(gs_command_context_t * ctx)
|
||||
{
|
||||
CMD_GET(ctx, float, gs_pp_get_float, "f");
|
||||
}
|
||||
|
||||
#define CMD_SET(_ctx, _type, _func, _convert) \
|
||||
{ \
|
||||
const unsigned int MAX_VALUES = 20; \
|
||||
uint32_t table; \
|
||||
uint32_t addr; \
|
||||
int res = cmd_parse(_ctx, &table, &addr); \
|
||||
if (res == GS_OK) { \
|
||||
_type value[MAX_VALUES]; \
|
||||
unsigned int count = 0; \
|
||||
for (int i = 3; (i < _ctx->argc) && (count < MAX_VALUES); ++i, ++count) { \
|
||||
res = _convert(_ctx->argv[i], &value[count]); \
|
||||
if (res) { \
|
||||
return GS_ERROR_DATA; \
|
||||
} \
|
||||
} \
|
||||
res = _func(&gs_pp, table, addr, value, count, use_checksum ? GS_PP_FLAG_CHECKSUM : 0); \
|
||||
} \
|
||||
return res; \
|
||||
}
|
||||
|
||||
static int cmd_set_int8(gs_command_context_t * ctx)
|
||||
{
|
||||
CMD_SET(ctx, int8_t, gs_pp_set_int8, gs_string_to_int8);
|
||||
}
|
||||
|
||||
static int cmd_set_int16(gs_command_context_t * ctx)
|
||||
{
|
||||
CMD_SET(ctx, int16_t, gs_pp_set_int16, gs_string_to_int16);
|
||||
}
|
||||
|
||||
static int cmd_set_int32(gs_command_context_t * ctx)
|
||||
{
|
||||
CMD_SET(ctx, int32_t, gs_pp_set_int32, gs_string_to_int32);
|
||||
}
|
||||
|
||||
static int cmd_set_uint8(gs_command_context_t * ctx)
|
||||
{
|
||||
CMD_SET(ctx, uint8_t, gs_pp_set_uint8, gs_string_to_uint8);
|
||||
}
|
||||
|
||||
static int cmd_set_uint16(gs_command_context_t * ctx)
|
||||
{
|
||||
CMD_SET(ctx, uint16_t, gs_pp_set_uint16, gs_string_to_uint16);
|
||||
}
|
||||
|
||||
static int cmd_set_uint32(gs_command_context_t * ctx)
|
||||
{
|
||||
CMD_SET(ctx, uint32_t, gs_pp_set_uint32, gs_string_to_uint32);
|
||||
}
|
||||
|
||||
static int cmd_set_float(gs_command_context_t * ctx)
|
||||
{
|
||||
CMD_SET(ctx, float, gs_pp_set_float, gs_string_to_float);
|
||||
}
|
||||
|
||||
static int cmd_get_table_lock(gs_command_context_t * ctx)
|
||||
{
|
||||
uint32_t table;
|
||||
gs_error_t res = gs_string_to_uint32(ctx->argv[1], &table);
|
||||
if (res == GS_OK) {
|
||||
if (table <= 7) {
|
||||
bool lock;
|
||||
res = gs_pp_get_table_lock(&gs_pp, table, &lock, GS_PP_FLAG_CHECKSUM);
|
||||
if (res == GS_OK) {
|
||||
const char locked[] = "locked\n";
|
||||
const char unlocked[] = "unlocked\n";
|
||||
const char * lock_state = (lock) ? locked : unlocked;
|
||||
printf("Table %s", lock_state);
|
||||
}
|
||||
} else {
|
||||
res = GS_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int cmd_set_table_lock(gs_command_context_t * ctx)
|
||||
{
|
||||
uint32_t table;
|
||||
gs_error_t res = gs_string_to_uint32(ctx->argv[1], &table);
|
||||
if (res == GS_OK) {
|
||||
if (table <= 7) {
|
||||
uint32_t lock;
|
||||
res = gs_string_to_uint32(ctx->argv[2], &lock);
|
||||
if (res == GS_OK) {
|
||||
if(lock <= 1) {
|
||||
res = gs_pp_set_table_lock(&gs_pp, table, (bool *)&lock, GS_PP_FLAG_CHECKSUM);
|
||||
} else {
|
||||
res = GS_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = GS_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static const gs_command_t gs_param_cmd_master_pp_sub[] = {
|
||||
{
|
||||
.name = "spi_init",
|
||||
.help = "Initialize/setup SPI device",
|
||||
.usage = "<slave> [big_endian]",
|
||||
.handler = cmd_spi_init,
|
||||
.mandatory_args = 1,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "i2c_init",
|
||||
.help = "Initialize/setup I2C device",
|
||||
.usage = "<device> <addr> [big_endian]",
|
||||
.handler = cmd_i2c_init,
|
||||
.mandatory_args = 2,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "checksum",
|
||||
.help = "Enable/disable checksum",
|
||||
.usage = "[0|1]",
|
||||
.handler = cmd_checksum,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "get_table_lock",
|
||||
.help = "Get table lock (0 = unlocked, 1 = locked)",
|
||||
.usage = "<table>",
|
||||
.mandatory_args = 1,
|
||||
.handler = cmd_get_table_lock,
|
||||
},{
|
||||
.name = "set_table_lock",
|
||||
.help = "Set table lock (0 = unlocked, 1 = locked)",
|
||||
.usage = "<table> <lock/unlock>",
|
||||
.mandatory_args = 2,
|
||||
.handler = cmd_set_table_lock,
|
||||
},{
|
||||
.name = "get_int8",
|
||||
.help = "Get int8",
|
||||
.usage = "<table> <addr> [count]",
|
||||
.handler = cmd_get_int8,
|
||||
.mandatory_args = 2,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "get_int16",
|
||||
.help = "Get int16",
|
||||
.usage = "<table> <addr> [count]",
|
||||
.handler = cmd_get_int16,
|
||||
.mandatory_args = 2,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "get_int32",
|
||||
.help = "Get int32",
|
||||
.usage = "<table> <addr> [count]",
|
||||
.handler = cmd_get_int32,
|
||||
.mandatory_args = 2,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "get_uint8",
|
||||
.help = "Get uint8",
|
||||
.usage = "<table> <addr> [count]",
|
||||
.handler = cmd_get_uint8,
|
||||
.mandatory_args = 2,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "get_uint16",
|
||||
.help = "Get uint16",
|
||||
.usage = "<table> <addr> [count]",
|
||||
.handler = cmd_get_uint16,
|
||||
.mandatory_args = 2,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "get_uint32",
|
||||
.help = "Get uint32",
|
||||
.usage = "<table> <addr> [count]",
|
||||
.handler = cmd_get_uint32,
|
||||
.mandatory_args = 2,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "get_float",
|
||||
.help = "Get float",
|
||||
.usage = "<table> <addr> [count]",
|
||||
.handler = cmd_get_float,
|
||||
.mandatory_args = 2,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "set_int8",
|
||||
.help = "Set int8",
|
||||
.usage = "<table> <addr> <data> [data] ...",
|
||||
.handler = cmd_set_int8,
|
||||
.mandatory_args = 3,
|
||||
.optional_args = 20,
|
||||
},{
|
||||
.name = "set_int16",
|
||||
.help = "Set int16",
|
||||
.usage = "<table> <addr> <data> [data] ...",
|
||||
.handler = cmd_set_int16,
|
||||
.mandatory_args = 3,
|
||||
.optional_args = 20,
|
||||
},{
|
||||
.name = "set_int32",
|
||||
.help = "Set int32",
|
||||
.usage = "<table> <addr> <data> [data] ...",
|
||||
.handler = cmd_set_int32,
|
||||
.mandatory_args = 3,
|
||||
.optional_args = 20,
|
||||
},{
|
||||
.name = "set_uint8",
|
||||
.help = "Set uint8",
|
||||
.usage = "<table> <addr> <data> [data] ...",
|
||||
.handler = cmd_set_uint8,
|
||||
.mandatory_args = 3,
|
||||
.optional_args = 20,
|
||||
},{
|
||||
.name = "set_uint16",
|
||||
.help = "Set uint16",
|
||||
.usage = "<table> <addr> <data> [data] ...",
|
||||
.handler = cmd_set_uint16,
|
||||
.mandatory_args = 3,
|
||||
.optional_args = 20,
|
||||
},{
|
||||
.name = "set_uint32",
|
||||
.help = "Set uint32",
|
||||
.usage = "<table> <addr> <data> [data] ...",
|
||||
.handler = cmd_set_uint32,
|
||||
.mandatory_args = 3,
|
||||
.optional_args = 20,
|
||||
},{
|
||||
.name = "set_float",
|
||||
.help = "Set float",
|
||||
.usage = "<table> <addr> <data> [data] ...",
|
||||
.handler = cmd_set_float,
|
||||
.mandatory_args = 3,
|
||||
.optional_args = 20,
|
||||
}
|
||||
};
|
||||
|
||||
static const gs_command_t GS_COMMAND_ROOT gs_param_cmd_master_pp[] = {
|
||||
{
|
||||
.name = "pp",
|
||||
.help = "Param Protocol interface",
|
||||
.chain = GS_COMMAND_INIT_CHAIN(gs_param_cmd_master_pp_sub),
|
||||
}
|
||||
};
|
||||
|
||||
gs_error_t gs_pp_register_commands(void)
|
||||
{
|
||||
return GS_COMMAND_REGISTER(gs_param_cmd_master_pp);
|
||||
}
|
129
gomspace/libparam_client/src/pp/i2c/i2c.c
Normal file
129
gomspace/libparam_client/src/pp/i2c/i2c.c
Normal file
@ -0,0 +1,129 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#define GS_PARAM_INTERNAL_USE 1
|
||||
|
||||
#include <gs/param/internal/pp/i2c/i2c.h>
|
||||
#include <gs/util/drivers/i2c/master.h>
|
||||
#include <gs/util/string.h>
|
||||
#include <gs/util/check.h>
|
||||
#include <alloca.h>
|
||||
|
||||
static gs_error_t gs_pp_i2c_write(gs_param_i2c_command_t cmd, gs_pp_t * pp, uint8_t table_id, uint16_t addr, void * value, size_t value_size, bool checksum)
|
||||
{
|
||||
GS_CHECK_RANGE(table_id <= 7);
|
||||
GS_CHECK_RANGE(addr <= 255);
|
||||
GS_CHECK_RANGE((value_size > 0) && (value_size <= 31));
|
||||
|
||||
gs_param_i2c_set_request_t * request;
|
||||
const size_t size = (sizeof(*request) + value_size + (checksum ? 1 : 0));
|
||||
request = alloca(size);
|
||||
|
||||
request->header.domain_command = GS_I2C_SLAVE_DISPATCH_HEADER(GS_I2C_SLAVE_DISPATCH_DOMAIN_PARAM, cmd);
|
||||
request->length_table = GS_PARAM_I2C_LENGTH_TABLE(value_size, table_id);
|
||||
request->addr = addr;
|
||||
memcpy(request->data, value, value_size);
|
||||
|
||||
if (checksum) {
|
||||
request->data[value_size] = gs_pp_checksum8(request, size - 1);
|
||||
}
|
||||
return gs_i2c_master_transaction(pp->pp.i2c.bus, pp->pp.i2c.addr, request, size, NULL, 0, 1000);
|
||||
|
||||
}
|
||||
|
||||
static gs_error_t gs_pp_i2c_read(gs_param_i2c_command_t cmd, gs_pp_t * pp, uint8_t table_id, uint16_t addr, void * value, size_t value_size, bool checksum)
|
||||
{
|
||||
GS_CHECK_RANGE(table_id <= 7);
|
||||
GS_CHECK_RANGE(addr <= 255);
|
||||
GS_CHECK_RANGE((value_size > 0) && (value_size <= 31));
|
||||
|
||||
gs_param_i2c_get_request_t request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.length_table = GS_PARAM_I2C_LENGTH_TABLE(value_size, table_id);
|
||||
request.addr = addr;
|
||||
size_t request_size;
|
||||
|
||||
uint8_t reply[value_size + sizeof(request.checksum)]; // + for checksum
|
||||
memset(reply, 0, sizeof(reply));
|
||||
size_t reply_size;
|
||||
|
||||
request.header.domain_command = GS_I2C_SLAVE_DISPATCH_HEADER(GS_I2C_SLAVE_DISPATCH_DOMAIN_PARAM, cmd);
|
||||
|
||||
if (checksum) {
|
||||
request.checksum = gs_pp_checksum8(&request, (sizeof(request) - sizeof(request.checksum)));
|
||||
request_size = sizeof(request);
|
||||
reply_size = sizeof(reply);
|
||||
} else {
|
||||
request_size = sizeof(request) - sizeof(request.checksum);
|
||||
reply_size = sizeof(reply) - sizeof(request.checksum);
|
||||
}
|
||||
|
||||
gs_error_t error = gs_i2c_master_transaction(pp->pp.i2c.bus, pp->pp.i2c.addr, &request, request_size, reply, reply_size, 1000);
|
||||
if (error == GS_OK) {
|
||||
if (checksum) {
|
||||
if (gs_pp_checksum8(reply, value_size) != reply[value_size]) {
|
||||
return GS_ERROR_DATA;
|
||||
}
|
||||
}
|
||||
memcpy(value, reply, value_size);
|
||||
}
|
||||
return error;
|
||||
|
||||
}
|
||||
|
||||
static gs_error_t gs_pp_i2c_get(gs_pp_t * pp, uint8_t table_id, uint16_t addr, void * value, size_t value_size, uint32_t flags)
|
||||
{
|
||||
gs_param_i2c_command_t cmd;
|
||||
bool checksum = false;
|
||||
if (flags & GS_PP_FLAG_CHECKSUM) {
|
||||
checksum = true;
|
||||
cmd = GS_PARAM_I2C_COMMAND_GET_WITH_CHECKSUM;
|
||||
} else {
|
||||
cmd = GS_PARAM_I2C_COMMAND_GET;
|
||||
}
|
||||
return gs_pp_i2c_read(cmd, pp, table_id, addr, value, value_size, checksum);
|
||||
}
|
||||
|
||||
static gs_error_t gs_pp_i2c_set(gs_pp_t * pp, uint8_t table_id, uint16_t addr, const void * value, size_t value_size, uint32_t flags)
|
||||
{
|
||||
gs_param_i2c_command_t cmd;
|
||||
bool checksum = false;
|
||||
if (flags & GS_PP_FLAG_CHECKSUM) {
|
||||
checksum = true;
|
||||
cmd = GS_PARAM_I2C_COMMAND_SET_WITH_CHECKSUM;
|
||||
} else {
|
||||
cmd = GS_PARAM_I2C_COMMAND_SET;
|
||||
}
|
||||
return gs_pp_i2c_write(cmd, pp, table_id, addr, (void *)value, value_size, checksum);
|
||||
}
|
||||
|
||||
static gs_error_t gs_pp_i2c_set_table_lock(gs_pp_t * pp, uint8_t table_id, const bool * value, uint32_t flags)
|
||||
{
|
||||
gs_param_i2c_command_t cmd = GS_PARAM_I2C_COMMAND_SET_LOCK_WITH_CHECKSUM;
|
||||
bool checksum = true;
|
||||
return gs_pp_i2c_write(cmd, pp, table_id, 0, (void *)value, 1, checksum);
|
||||
}
|
||||
|
||||
static gs_error_t gs_pp_i2c_get_table_lock(gs_pp_t * pp, uint8_t table_id, bool * value, uint32_t flags)
|
||||
{
|
||||
gs_param_i2c_command_t cmd = GS_PARAM_I2C_COMMAND_GET_LOCK_WITH_CHECKSUM;
|
||||
bool checksum = true;
|
||||
return gs_pp_i2c_read(cmd, pp, table_id, 0, value, 1, checksum);
|
||||
}
|
||||
|
||||
gs_error_t gs_pp_i2c_init(gs_pp_t * pp, uint8_t bus, uint8_t addr, bool big_endian)
|
||||
{
|
||||
GS_CHECK_HANDLE(pp != NULL);
|
||||
|
||||
memset(pp, 0, sizeof(*pp));
|
||||
|
||||
pp->get = gs_pp_i2c_get;
|
||||
pp->set = gs_pp_i2c_set;
|
||||
pp->set_table_lock = gs_pp_i2c_set_table_lock;
|
||||
pp->get_table_lock = gs_pp_i2c_get_table_lock;
|
||||
pp->big_endian = big_endian;
|
||||
|
||||
pp->pp.i2c.bus = bus;
|
||||
pp->pp.i2c.addr = addr;
|
||||
|
||||
return GS_OK;
|
||||
}
|
189
gomspace/libparam_client/src/pp/pp.c
Normal file
189
gomspace/libparam_client/src/pp/pp.c
Normal file
@ -0,0 +1,189 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/param/pp/pp.h>
|
||||
#include <gs/util/string.h>
|
||||
#include <gs/util/check.h>
|
||||
#include <gs/util/byteorder.h>
|
||||
#include <gs/util/endian.h>
|
||||
|
||||
static inline bool gs_pp_endian_convert(gs_pp_t * pp)
|
||||
{
|
||||
return (pp && (pp->big_endian != gs_endian_big()));
|
||||
}
|
||||
|
||||
static gs_error_t gs_pp_get(gs_pp_t * pp, uint8_t table_id, uint16_t addr, void * value, size_t value_size, uint32_t flags)
|
||||
{
|
||||
if (pp == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
if (pp->get == NULL) {
|
||||
return GS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
return pp->get(pp, table_id, addr, value, value_size, flags);
|
||||
}
|
||||
|
||||
static gs_error_t gs_pp_set(gs_pp_t * pp, uint8_t table_id, uint16_t addr, const void * value, size_t value_size, uint32_t flags)
|
||||
{
|
||||
if (pp == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
if (pp->set == NULL) {
|
||||
return GS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
return pp->set(pp, table_id, addr, value, value_size, flags);
|
||||
}
|
||||
|
||||
uint8_t gs_pp_checksum8(const void * data_in, size_t length)
|
||||
{
|
||||
const uint8_t * data = data_in;
|
||||
unsigned int checksum = 0;
|
||||
for (unsigned int i = 0; i < length; ++i) {
|
||||
checksum += *data++;
|
||||
}
|
||||
checksum &= 0xff;
|
||||
return checksum ? checksum : 1;
|
||||
}
|
||||
|
||||
gs_error_t gs_pp_get_table_lock(gs_pp_t * pp, uint8_t table_id, bool * value, uint32_t flags)
|
||||
{
|
||||
GS_CHECK_ARG(value != NULL);
|
||||
GS_CHECK_HANDLE(pp != NULL);
|
||||
if (pp->get_table_lock == NULL) {
|
||||
return GS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
return pp->get_table_lock(pp, table_id, value, flags);
|
||||
}
|
||||
|
||||
gs_error_t gs_pp_set_table_lock(gs_pp_t * pp, uint8_t table_id, const bool * value, uint32_t flags)
|
||||
{
|
||||
GS_CHECK_ARG(value != NULL);
|
||||
GS_CHECK_HANDLE(pp != NULL);
|
||||
if (pp->set_table_lock == NULL) {
|
||||
return GS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
return pp->set_table_lock(pp, table_id, value, flags);
|
||||
}
|
||||
|
||||
// int8_t
|
||||
gs_error_t gs_pp_get_int8(gs_pp_t * pp, uint8_t table_id, uint8_t addr, int8_t * value, size_t count, uint32_t flags)
|
||||
{
|
||||
return gs_pp_get_uint8(pp, table_id, addr, (uint8_t *) value, count, flags);
|
||||
}
|
||||
|
||||
gs_error_t gs_pp_set_int8(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const int8_t * value, size_t count, uint32_t flags)
|
||||
{
|
||||
return gs_pp_set_uint8(pp, table_id, addr, (const uint8_t *) value, count, flags);
|
||||
}
|
||||
|
||||
// uint8_t
|
||||
|
||||
gs_error_t gs_pp_get_uint8(gs_pp_t * pp, uint8_t table_id, uint8_t addr, uint8_t * value, size_t count, uint32_t flags)
|
||||
{
|
||||
GS_CHECK_ARG(value != NULL);
|
||||
GS_CHECK_ARG(count > 0);
|
||||
return gs_pp_get(pp, table_id, addr, value, (sizeof(*value) * count), flags);
|
||||
}
|
||||
|
||||
gs_error_t gs_pp_set_uint8(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const uint8_t * value, size_t count, uint32_t flags)
|
||||
{
|
||||
GS_CHECK_ARG(value != NULL);
|
||||
GS_CHECK_ARG(count > 0);
|
||||
return gs_pp_set(pp, table_id, addr, value, (sizeof(*value) * count), flags);
|
||||
}
|
||||
|
||||
// int16_t
|
||||
|
||||
gs_error_t gs_pp_get_int16(gs_pp_t * pp, uint8_t table_id, uint8_t addr, int16_t * value, size_t count, uint32_t flags)
|
||||
{
|
||||
return gs_pp_get_uint16(pp, table_id, addr, (uint16_t *) value, count, flags);
|
||||
}
|
||||
|
||||
gs_error_t gs_pp_set_int16(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const int16_t * value, size_t count, uint32_t flags)
|
||||
{
|
||||
return gs_pp_set_uint16(pp, table_id, addr, (const uint16_t *) value, count, flags);
|
||||
}
|
||||
|
||||
// uint16_t
|
||||
|
||||
gs_error_t gs_pp_get_uint16(gs_pp_t * pp, uint8_t table_id, uint8_t addr, uint16_t * value, size_t count, uint32_t flags)
|
||||
{
|
||||
GS_CHECK_ARG(value != NULL);
|
||||
GS_CHECK_ARG(count > 0);
|
||||
gs_error_t error = gs_pp_get(pp, table_id, addr, value, (sizeof(*value) * count), flags);
|
||||
if (gs_pp_endian_convert(pp)) {
|
||||
gs_bswap_16_array(value, value, count);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_pp_set_uint16(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const uint16_t * value, size_t count, uint32_t flags)
|
||||
{
|
||||
GS_CHECK_ARG(value != NULL);
|
||||
GS_CHECK_ARG(count > 0);
|
||||
uint16_t _converted[count];
|
||||
if (gs_pp_endian_convert(pp)) {
|
||||
gs_bswap_16_array(value, _converted, count);
|
||||
value = _converted;
|
||||
}
|
||||
return gs_pp_set(pp, table_id, addr, value, (sizeof(*value) * count), flags);
|
||||
}
|
||||
|
||||
// int32_t
|
||||
|
||||
gs_error_t gs_pp_get_int32(gs_pp_t * pp, uint8_t table_id, uint8_t addr, int32_t * value, size_t count, uint32_t flags)
|
||||
{
|
||||
return gs_pp_get_uint32(pp, table_id, addr, (uint32_t *) value, count, flags);
|
||||
}
|
||||
|
||||
gs_error_t gs_pp_set_int32(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const int32_t * value, size_t count, uint32_t flags)
|
||||
{
|
||||
return gs_pp_set_uint32(pp, table_id, addr, (const uint32_t *) value, count, flags);
|
||||
}
|
||||
|
||||
// uint32_t
|
||||
|
||||
gs_error_t gs_pp_get_uint32(gs_pp_t * pp, uint8_t table_id, uint8_t addr, uint32_t * value, size_t count, uint32_t flags)
|
||||
{
|
||||
GS_CHECK_ARG(value != NULL);
|
||||
GS_CHECK_ARG(count > 0);
|
||||
gs_error_t error = gs_pp_get(pp, table_id, addr, value, (sizeof(*value) * count), flags);
|
||||
if (gs_pp_endian_convert(pp)) {
|
||||
gs_bswap_32_array(value, value, count);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_pp_set_uint32(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const uint32_t * value, size_t count, uint32_t flags)
|
||||
{
|
||||
GS_CHECK_ARG(value != NULL);
|
||||
GS_CHECK_ARG(count > 0);
|
||||
uint32_t _converted[count];
|
||||
if (gs_pp_endian_convert(pp)) {
|
||||
gs_bswap_32_array(value, _converted, count);
|
||||
value = _converted;
|
||||
}
|
||||
return gs_pp_set(pp, table_id, addr, value, (sizeof(*value) * count), flags);
|
||||
}
|
||||
|
||||
gs_error_t gs_pp_get_float(gs_pp_t * pp, uint8_t table_id, uint8_t addr, float * value, size_t count, uint32_t flags)
|
||||
{
|
||||
GS_CHECK_ARG(value != NULL);
|
||||
GS_CHECK_ARG(count > 0);
|
||||
gs_error_t error = gs_pp_get(pp, table_id, addr, value, (sizeof(*value) * count), flags);
|
||||
if (gs_pp_endian_convert(pp)) {
|
||||
gs_bswap_float_array(value, value, count);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_pp_set_float(gs_pp_t * pp, uint8_t table_id, uint8_t addr, const float * value, size_t count, uint32_t flags)
|
||||
{
|
||||
GS_CHECK_ARG(value != NULL);
|
||||
GS_CHECK_ARG(count > 0);
|
||||
float _converted[count];
|
||||
if (gs_pp_endian_convert(pp)) {
|
||||
gs_bswap_float_array(value, _converted, count);
|
||||
value = _converted;
|
||||
}
|
||||
return gs_pp_set(pp, table_id, addr, value, (sizeof(*value) * count), flags);
|
||||
}
|
91
gomspace/libparam_client/src/pp/spi/spi.c
Normal file
91
gomspace/libparam_client/src/pp/spi/spi.c
Normal file
@ -0,0 +1,91 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#define GS_PARAM_INTERNAL_USE 1
|
||||
|
||||
#include <gs/param/internal/pp/spi/spi.h>
|
||||
#include <gs/util/drivers/spi/master.h>
|
||||
#include <gs/util/string.h>
|
||||
#include <gs/util/check.h>
|
||||
#include <alloca.h>
|
||||
|
||||
static gs_error_t gs_pp_spi_get(gs_pp_t * pp, uint8_t table_id, uint16_t addr, void * value, size_t value_size, uint32_t flags)
|
||||
{
|
||||
GS_CHECK_RANGE(table_id <= 7);
|
||||
GS_CHECK_RANGE(addr <= 255);
|
||||
|
||||
if (flags & GS_PP_FLAG_CHECKSUM) {
|
||||
gs_param_spi_get_with_checksum_t * request;
|
||||
const size_t size = (sizeof(*request) + value_size + 1); // +1 for CHECKSUM in returned data
|
||||
request = alloca(size);
|
||||
memset(request, 0, size);
|
||||
request->header.domain_command = GS_SPI_SLAVE_DISPATCH_HEADER(GS_SPI_SLAVE_DISPATCH_DOMAIN_PARAM, GS_PARAM_SPI_COMMAND_GET_WITH_CHECKSUM);
|
||||
request->length_table = GS_PARAM_SPI_LENGTH_TABLE(value_size, table_id);
|
||||
request->addr = addr;
|
||||
request->checksum = gs_pp_checksum8(request, (sizeof(*request) - sizeof(request->filler)));
|
||||
|
||||
gs_error_t error = gs_spi_master_transaction(pp->pp.spi.slave, request, request, size, 1000);
|
||||
if (error == GS_OK) {
|
||||
if (gs_pp_checksum8(request->data, value_size) != request->data[value_size]) {
|
||||
return GS_ERROR_DATA;
|
||||
}
|
||||
memcpy(value, request->data, value_size);
|
||||
}
|
||||
return error;
|
||||
|
||||
} else {
|
||||
gs_param_spi_get_t * request;
|
||||
const size_t size = (sizeof(*request) + value_size);
|
||||
request = alloca(size);
|
||||
memset(request, 0, size);
|
||||
request->header.domain_command = GS_SPI_SLAVE_DISPATCH_HEADER(GS_SPI_SLAVE_DISPATCH_DOMAIN_PARAM, GS_PARAM_SPI_COMMAND_GET);
|
||||
request->length_table = GS_PARAM_SPI_LENGTH_TABLE(value_size, table_id);
|
||||
request->addr = addr;
|
||||
|
||||
gs_error_t error = gs_spi_master_transaction(pp->pp.spi.slave, request, request, size, 1000);
|
||||
if (error == GS_OK) {
|
||||
memcpy(value, request->data, value_size);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
static gs_error_t gs_pp_spi_set(gs_pp_t * pp, uint8_t table_id, uint16_t addr, const void * value, size_t value_size, uint32_t flags)
|
||||
{
|
||||
GS_CHECK_RANGE(table_id <= 7);
|
||||
GS_CHECK_RANGE(addr <= 255);
|
||||
|
||||
gs_param_spi_set_t * request;
|
||||
const size_t size = (sizeof(*request) + value_size + ((flags & GS_PP_FLAG_CHECKSUM) ? 1 : 0));
|
||||
request = alloca(size);
|
||||
if (flags & GS_PP_FLAG_CHECKSUM) {
|
||||
request->header.domain_command = GS_SPI_SLAVE_DISPATCH_HEADER(GS_SPI_SLAVE_DISPATCH_DOMAIN_PARAM, GS_PARAM_SPI_COMMAND_SET_WITH_CHECKSUM);
|
||||
} else {
|
||||
request->header.domain_command = GS_SPI_SLAVE_DISPATCH_HEADER(GS_SPI_SLAVE_DISPATCH_DOMAIN_PARAM, GS_PARAM_SPI_COMMAND_SET);
|
||||
}
|
||||
request->length_table = GS_PARAM_SPI_LENGTH_TABLE(value_size, table_id);
|
||||
request->addr = addr;
|
||||
memcpy(request->data, value, value_size);
|
||||
|
||||
if (flags & GS_PP_FLAG_CHECKSUM) {
|
||||
request->data[value_size] = gs_pp_checksum8(request, size - 1);
|
||||
}
|
||||
|
||||
return gs_spi_master_transaction(pp->pp.spi.slave, request, NULL, size, 1000);
|
||||
}
|
||||
|
||||
gs_error_t gs_pp_spi_init(gs_pp_t * pp, uint8_t slave, bool big_endian)
|
||||
{
|
||||
GS_CHECK_HANDLE(pp != NULL);
|
||||
|
||||
memset(pp, 0, sizeof(*pp));
|
||||
|
||||
pp->get = gs_pp_spi_get;
|
||||
pp->set = gs_pp_spi_set;
|
||||
pp->set_table_lock = NULL; // Not implemented
|
||||
pp->get_table_lock = NULL; // Not implemented
|
||||
pp->big_endian = big_endian;
|
||||
|
||||
pp->pp.spi.slave = slave;
|
||||
|
||||
return GS_OK;
|
||||
}
|
354
gomspace/libparam_client/src/rparam/cmd/cmd_rparam.c
Normal file
354
gomspace/libparam_client/src/rparam/cmd/cmd_rparam.c
Normal file
@ -0,0 +1,354 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#define GS_PARAM_INTERNAL_USE 1
|
||||
|
||||
#include <gs/param/internal/rparam.h>
|
||||
#include <gs/param/internal/table.h>
|
||||
#include <gs/param/internal/serialize.h>
|
||||
#include <gs/param/internal/types.h>
|
||||
#include "../query.h"
|
||||
#include <gs/util/log.h>
|
||||
#include <gs/util/gosh/command.h>
|
||||
#include <gs/util/stdio.h>
|
||||
#include <gs/util/unistd.h>
|
||||
#include <gs/csp/csp.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_FILENAME 100
|
||||
|
||||
/** Remote param system setup */
|
||||
static gs_param_table_instance_t rparam_tinst;
|
||||
static gs_rparam_query_handle_t rquery = {.timeout_ms = 10000};
|
||||
static char * rparam_wd;
|
||||
|
||||
#define CHECK_TABLE() \
|
||||
if (rparam_tinst.rows == NULL) { \
|
||||
fprintf(ctx->out, "Run download or init to setup table\n"); \
|
||||
return GS_ERROR_NOT_FOUND; \
|
||||
}
|
||||
|
||||
static int cmd_rparam_list(gs_command_context_t *ctx)
|
||||
{
|
||||
CHECK_TABLE();
|
||||
return gs_param_list_to_stream(&rparam_tinst, true, 0, ctx->out);
|
||||
}
|
||||
|
||||
static void make_filename(char * fname, size_t fname_size)
|
||||
{
|
||||
char cwd[MAX_FILENAME + 1];
|
||||
const char * wd;
|
||||
if (gs_string_empty(rparam_wd) == false) {
|
||||
wd = rparam_wd;
|
||||
} else if (gs_getcwd(cwd, sizeof(cwd)) == GS_OK) {
|
||||
wd = cwd;
|
||||
} else {
|
||||
wd = NULL;
|
||||
}
|
||||
if (gs_string_empty(wd) == false) {
|
||||
snprintf(fname, fname_size, "%s/param-%d-%u.bin", wd, rquery.node, rquery.table_id);
|
||||
} else {
|
||||
fname[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int cmd_rparam_init_from_local_file(gs_command_context_t *ctx)
|
||||
{
|
||||
if (gs_string_to_uint8(ctx->argv[1], &rquery.node)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
if (ctx->argc > 2) {
|
||||
if (gs_string_to_uint8(ctx->argv[2], &rquery.table_id)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
char fname[100];
|
||||
make_filename(fname, sizeof(fname));
|
||||
return gs_rparam_load_table_spec(&rparam_tinst, fname, &rquery.checksum);
|
||||
}
|
||||
|
||||
static int cmd_rparam_init_from_remote_node(gs_command_context_t *ctx)
|
||||
{
|
||||
if (gs_string_to_uint8(ctx->argv[1], &rquery.node)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
if (ctx->argc > 2) {
|
||||
if (gs_string_to_uint8(ctx->argv[2], &rquery.table_id)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
char fname[100];
|
||||
make_filename(fname, sizeof(fname));
|
||||
return gs_rparam_download_table_spec(&rparam_tinst, fname, rquery.node, rquery.table_id, rquery.timeout_ms, &rquery.checksum);
|
||||
}
|
||||
|
||||
static int cmd_rparam_send(gs_command_context_t *ctx)
|
||||
{
|
||||
CHECK_TABLE();
|
||||
|
||||
gs_error_t error = gs_rparam_query_send(&rquery, &rparam_tinst);
|
||||
if (error == GS_OK) {
|
||||
if (rquery.action == RPARAM_GET) {
|
||||
const gs_param_table_row_t * last_print = NULL;
|
||||
for (unsigned int i = 0; i < rquery.length / 2; ++i) {
|
||||
const gs_param_table_row_t * row = gs_param_row_by_address(rquery.payload.addr[i], rparam_tinst.rows, rparam_tinst.row_count);
|
||||
if (row != last_print) { // work-around to avoid duplicate lines for elements within same array
|
||||
gs_param_list_single_to_stream(&rparam_tinst, row, true, 0, ctx->out);
|
||||
last_print = row;
|
||||
}
|
||||
}
|
||||
}
|
||||
gs_rparam_query_reset(&rquery);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static int cmd_rparam_get(gs_command_context_t *ctx)
|
||||
{
|
||||
CHECK_TABLE();
|
||||
|
||||
gs_rparam_query_set_quiet(&rquery, false);
|
||||
gs_error_t error = gs_rparam_query_get(&rquery, &rparam_tinst, ctx->argv[1]);
|
||||
if ((error == GS_OK) && rquery.auto_send) {
|
||||
error = cmd_rparam_send(ctx);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int cmd_rparam_getall(gs_command_context_t *ctx)
|
||||
{
|
||||
CHECK_TABLE();
|
||||
|
||||
fprintf(ctx->out, "Downloading table content for table %i from server %u\n", rquery.table_id, rquery.node);
|
||||
gs_error_t error = gs_rparam_get_full_table(&rparam_tinst, rquery.node, rquery.table_id, rquery.checksum, rquery.timeout_ms);
|
||||
if (error == GS_OK) {
|
||||
gs_param_list_to_stream(&rparam_tinst, true, 0, ctx->out);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static int cmd_rparam_set(gs_command_context_t *ctx)
|
||||
{
|
||||
CHECK_TABLE();
|
||||
|
||||
gs_error_t error = gs_rparam_query_set(&rquery, &rparam_tinst, ctx->argv[1], &ctx->argv[2], ctx->argc - 2);
|
||||
if ((error == GS_OK) && rquery.auto_send) {
|
||||
error = cmd_rparam_send(ctx);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int cmd_rparam_copy(gs_command_context_t *ctx)
|
||||
{
|
||||
uint8_t from;
|
||||
if (gs_string_to_uint8(ctx->argv[1], &from)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
uint8_t to;
|
||||
if (gs_string_to_uint8(ctx->argv[2], &to)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
return gs_rparam_copy(rquery.node, rquery.timeout_ms, from, to);
|
||||
}
|
||||
|
||||
static int cmd_rparam_load(gs_command_context_t *ctx)
|
||||
{
|
||||
uint8_t table_id;
|
||||
if (gs_string_to_uint8(ctx->argv[2], &table_id)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
uint8_t file_id;
|
||||
if (gs_string_to_uint8(ctx->argv[1], &file_id)) {
|
||||
// This may be a store - no way of validating
|
||||
return gs_rparam_load_from_store(rquery.node, rquery.timeout_ms, table_id, ctx->argv[1], NULL);
|
||||
}
|
||||
|
||||
return gs_rparam_load(rquery.node, rquery.timeout_ms, file_id, table_id);
|
||||
}
|
||||
|
||||
static int cmd_rparam_save(gs_command_context_t *ctx)
|
||||
{
|
||||
uint8_t table_id;
|
||||
if (gs_string_to_uint8(ctx->argv[1], &table_id)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
uint8_t file_id;
|
||||
if (gs_string_to_uint8(ctx->argv[2], &file_id)) {
|
||||
// This may be a store - no way of validating
|
||||
return gs_rparam_save_to_store(rquery.node, rquery.timeout_ms, table_id, ctx->argv[2], NULL);
|
||||
}
|
||||
|
||||
return gs_rparam_save(rquery.node, rquery.timeout_ms, table_id, file_id);
|
||||
}
|
||||
|
||||
static int cmd_rparam_reset(gs_command_context_t *ctx)
|
||||
{
|
||||
gs_rparam_query_reset(&rquery);
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static int cmd_rparam_set_autosend(gs_command_context_t *ctx)
|
||||
{
|
||||
if (ctx->argc > 1) {
|
||||
if (gs_string_to_bool(ctx->argv[1], &rquery.auto_send)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
fprintf(ctx->out, "auto send: %d\r\n", rquery.auto_send);
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static int cmd_set_wd(gs_command_context_t *ctx)
|
||||
{
|
||||
if (ctx->argc > 1) {
|
||||
if (rparam_wd == NULL) {
|
||||
rparam_wd = malloc(MAX_FILENAME + 1);
|
||||
if (rparam_wd == NULL) {
|
||||
return GS_ERROR_ALLOC;
|
||||
}
|
||||
}
|
||||
strcpy(rparam_wd, ctx->argv[1]);
|
||||
}
|
||||
fprintf(ctx->out, "working directory: %s\r\n", rparam_wd ? rparam_wd : "not set");
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static int cmd_rparam_set_timeout(gs_command_context_t *ctx)
|
||||
{
|
||||
if (ctx->argc > 1) {
|
||||
if (gs_string_to_uint32(ctx->argv[1], &rquery.timeout_ms)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
fprintf(ctx->out, "timeout: %"PRIu32" mS\r\n", rquery.timeout_ms);
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static int cmd_rparam_set_checksum(gs_command_context_t *ctx)
|
||||
{
|
||||
if (ctx->argc > 1) {
|
||||
if (strcasecmp(ctx->argv[1], "magic") == 0) {
|
||||
rquery.checksum = GS_RPARAM_MAGIC_CHECKSUM;
|
||||
} else if (gs_string_to_uint16(ctx->argv[1], &rquery.checksum)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
fprintf(ctx->out, "checksum: 0x%04x (magic: 0x%04x)\r\n", rquery.checksum, GS_RPARAM_MAGIC_CHECKSUM);
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static const gs_command_t rparam_commands[] = {
|
||||
{
|
||||
.name = "init",
|
||||
.help = "Set server and load table spec. from file",
|
||||
.usage = "<server> [table-id]",
|
||||
.handler = cmd_rparam_init_from_local_file,
|
||||
.mandatory_args = 1,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "download",
|
||||
.help = "Set server and download table spec.",
|
||||
.usage = "<server> [table-id]",
|
||||
.handler = cmd_rparam_init_from_remote_node,
|
||||
.mandatory_args = 1,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "getall",
|
||||
.help = "Download full table contents from server",
|
||||
.handler = cmd_rparam_getall,
|
||||
.mandatory_args = GS_COMMAND_NO_ARGS,
|
||||
},{
|
||||
.name = "list",
|
||||
.help = "Lists the table specification",
|
||||
.handler = cmd_rparam_list,
|
||||
.mandatory_args = GS_COMMAND_NO_ARGS,
|
||||
},{
|
||||
.name = "get",
|
||||
.help = "Add a 'get' to the current query transaction",
|
||||
.usage = "<name>",
|
||||
.handler = cmd_rparam_get,
|
||||
.mandatory_args = 1,
|
||||
},{
|
||||
.name = "set",
|
||||
.help = "Add a 'set' to the current query transaction",
|
||||
.usage = "<parameter> <value> [value] ...",
|
||||
.handler = cmd_rparam_set,
|
||||
.mandatory_args = 2,
|
||||
.optional_args = 100,
|
||||
},{
|
||||
.name = "copy",
|
||||
.usage = "<from table-id> <to table-id>",
|
||||
.help = "Copy table to table (version <= 3 only)",
|
||||
.handler = cmd_rparam_copy,
|
||||
.mandatory_args = 2,
|
||||
},{
|
||||
.name = "load",
|
||||
.usage = "<file-id|store> <table-id>",
|
||||
.help = "Load table",
|
||||
.handler = cmd_rparam_load,
|
||||
.mandatory_args = 2,
|
||||
},{
|
||||
.name = "save",
|
||||
.usage = "<table-id> <file-id|store>",
|
||||
.help = "Save table",
|
||||
.handler = cmd_rparam_save,
|
||||
.mandatory_args = 2,
|
||||
/* },{ */
|
||||
/* .name = "print", */
|
||||
/* .help = "Print the current query", */
|
||||
/* .handler = cmd_rparam_print, */
|
||||
/* .mandatory_args = GS_COMMAND_NO_ARGS, */
|
||||
},{
|
||||
.name = "reset",
|
||||
.help = "Reset the current query",
|
||||
.handler = cmd_rparam_reset,
|
||||
.mandatory_args = GS_COMMAND_NO_ARGS,
|
||||
},{
|
||||
.name = "send",
|
||||
.help = "Send the current query",
|
||||
.handler = cmd_rparam_send,
|
||||
.mandatory_args = GS_COMMAND_NO_ARGS,
|
||||
},{
|
||||
.name = "wd",
|
||||
.help = "Set working directory for init/download",
|
||||
.usage = "[path]",
|
||||
.handler = cmd_set_wd,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "timeout",
|
||||
.help = "Set timeout",
|
||||
.usage = "[timeout mS]",
|
||||
.handler = cmd_rparam_set_timeout,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "checksum",
|
||||
.help = "Set checksum",
|
||||
.usage = "[magic|<checksum>]",
|
||||
.handler = cmd_rparam_set_checksum,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "autosend",
|
||||
.usage = "[bool]",
|
||||
.help = "Enable/disable autosend for set and get queries",
|
||||
.handler = cmd_rparam_set_autosend,
|
||||
.optional_args = 1,
|
||||
}
|
||||
};
|
||||
|
||||
static const gs_command_t GS_COMMAND_ROOT rparam_root_command[] = {
|
||||
{
|
||||
.name = "rparam",
|
||||
.help = "Remote access to Parameter System",
|
||||
.chain = GS_COMMAND_INIT_CHAIN(rparam_commands),
|
||||
},
|
||||
};
|
||||
|
||||
gs_error_t gs_rparam_register_commands(void)
|
||||
{
|
||||
return GS_COMMAND_REGISTER(rparam_root_command);
|
||||
}
|
6
gomspace/libparam_client/src/rparam/deprecated_rparam.c
Normal file
6
gomspace/libparam_client/src/rparam/deprecated_rparam.c
Normal file
@ -0,0 +1,6 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <deprecated/param/rparam_client.h>
|
||||
|
||||
// Old deprecated API uses common data - making it not thread-safe.
|
||||
//gs_rparam_handle_t rparam_handle;
|
212
gomspace/libparam_client/src/rparam/query.c
Normal file
212
gomspace/libparam_client/src/rparam/query.c
Normal file
@ -0,0 +1,212 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#define GS_PARAM_INTERNAL_USE 1
|
||||
|
||||
#include "query.h"
|
||||
#include <gs/param/internal/rparam.h>
|
||||
#include <gs/param/internal/types.h>
|
||||
#include <gs/param/internal/table.h>
|
||||
#include "../serialize_local.h"
|
||||
#include <gs/csp/csp.h>
|
||||
#include <csp/csp_endian.h>
|
||||
|
||||
bool gs_rparam_query_is_set(gs_rparam_query_handle_t * handle)
|
||||
{
|
||||
return (handle->length > 0);
|
||||
}
|
||||
|
||||
void gs_rparam_query_reset(gs_rparam_query_handle_t * handle)
|
||||
{
|
||||
handle->action = RPARAM_GET;
|
||||
handle->length = 0;
|
||||
handle->get_size = 0;
|
||||
}
|
||||
|
||||
void gs_rparam_query_set_quiet(gs_rparam_query_handle_t * handle, bool quiet)
|
||||
{
|
||||
handle->quiet = quiet;
|
||||
}
|
||||
|
||||
gs_error_t gs_rparam_query_get(gs_rparam_query_handle_t * handle, gs_param_table_instance_t * tinst, const char* param_name)
|
||||
{
|
||||
if (tinst->rows == NULL)
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
|
||||
/* Ensure correct header */
|
||||
if (handle->action != RPARAM_GET) {
|
||||
gs_rparam_query_reset(handle);
|
||||
handle->action = RPARAM_GET;
|
||||
}
|
||||
|
||||
char shortname[GS_PARAM_MAX_NAME + 20];
|
||||
uint8_t array_index = 0;
|
||||
bool is_array;
|
||||
if (gs_param_parse_name_and_array_index(param_name, shortname, sizeof(shortname), &array_index, &is_array)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
const gs_param_table_row_t * param = gs_param_row_by_name(shortname, tinst->rows, tinst->row_count);
|
||||
if (param == NULL) {
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (array_index >= GS_PARAM_ARRAY_SIZE(param)) {
|
||||
return GS_ERROR_RANGE;
|
||||
}
|
||||
|
||||
unsigned int start;
|
||||
unsigned int end;
|
||||
if (is_array) {
|
||||
start = array_index;
|
||||
end = start + 1;
|
||||
} else {
|
||||
start = 0;
|
||||
end = GS_PARAM_ARRAY_SIZE(param);
|
||||
}
|
||||
for (unsigned int i = start; i < end; ++i) {
|
||||
|
||||
/* Size check */
|
||||
if (handle->get_size + param->size + sizeof(uint16_t) > GS_RPARAM_QUERY_MAX_PAYLOAD) {
|
||||
return GS_ERROR_OVERFLOW;
|
||||
}
|
||||
|
||||
/* Add to query */
|
||||
handle->payload.addr[handle->length/2] = param->addr + (param->size * i);
|
||||
handle->length += sizeof(uint16_t);
|
||||
handle->get_size += param->size + sizeof(uint16_t);
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_rparam_query_set(gs_rparam_query_handle_t * handle, gs_param_table_instance_t * tinst, const char* param_name, char * values[], uint8_t value_count)
|
||||
{
|
||||
/* Ensure correct header */
|
||||
if (handle->action != RPARAM_SET) {
|
||||
gs_rparam_query_reset(handle);
|
||||
handle->action = RPARAM_SET;
|
||||
}
|
||||
|
||||
char shortname[GS_PARAM_MAX_NAME + 20];
|
||||
uint8_t array_index = 0;
|
||||
if (gs_param_parse_name_and_array_index(param_name, shortname, sizeof(shortname), &array_index, NULL)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
const gs_param_table_row_t * param = gs_param_row_by_name(shortname, tinst->rows, tinst->row_count);
|
||||
if (param == NULL) {
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (array_index >= GS_PARAM_ARRAY_SIZE(param)) {
|
||||
return GS_ERROR_RANGE;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < value_count; i++) {
|
||||
|
||||
/* Parse input */
|
||||
uint8_t value[param->size];
|
||||
gs_error_t error = gs_param_from_string(param, values[i], value);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
unsigned int bytes = 0;
|
||||
error = gs_param_serialize_item(param, param->addr + (param->size * (array_index + i)), value, F_TO_BIG_ENDIAN,
|
||||
&handle->payload.packed[handle->length], GS_RPARAM_QUERY_MAX_PAYLOAD - handle->length, &bytes);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
handle->length += bytes;
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_rparam_query_send(gs_rparam_query_handle_t * handle, gs_param_table_instance_t * tinst)
|
||||
{
|
||||
if (tinst->rows == NULL) {
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (handle->length == 0) {
|
||||
return GS_ERROR_NO_DATA;
|
||||
}
|
||||
|
||||
gs_rparam_query_t * query;
|
||||
|
||||
/* Allocate outgoing buffer */
|
||||
csp_packet_t * packet = csp_buffer_get(RPARAM_QUERY_LENGTH(query, GS_RPARAM_QUERY_MAX_PAYLOAD));
|
||||
if (packet == NULL) {
|
||||
return GS_ERROR_NO_BUFFERS;
|
||||
}
|
||||
|
||||
csp_conn_t * conn = csp_connect(CSP_PRIO_HIGH, handle->node, GS_CSP_PORT_RPARAM, handle->timeout_ms, CSP_O_CRC32);
|
||||
if (conn == NULL) {
|
||||
csp_buffer_free(packet);
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
|
||||
query = (gs_rparam_query_t *) packet->data;
|
||||
|
||||
query->action = handle->action;
|
||||
query->table_id = handle->table_id;
|
||||
query->seq = 0;
|
||||
query->total = 0;
|
||||
query->length = csp_hton16(handle->length);
|
||||
query->checksum = csp_hton16(handle->checksum);
|
||||
|
||||
/* Copy payload to message */
|
||||
packet->length = RPARAM_QUERY_LENGTH(query, handle->length);
|
||||
memcpy(&query->payload, &handle->payload, handle->length);
|
||||
|
||||
/* Deal with endianness */
|
||||
if (handle->action == RPARAM_GET) {
|
||||
for (unsigned int i = 0; i < (handle->length/2); i++) {
|
||||
query->payload.addr[i] = csp_hton16(query->payload.addr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send packet */
|
||||
if (!csp_send(conn, packet, 0)) {
|
||||
csp_buffer_free(packet);
|
||||
csp_close(conn);
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
|
||||
/* Read reply */
|
||||
packet = csp_read(conn, handle->timeout_ms);
|
||||
if (packet == NULL) {
|
||||
csp_close(conn);
|
||||
return GS_ERROR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (packet->length <= 1) {
|
||||
gs_error_t error = GS_OK;
|
||||
if (packet->length == 1) {
|
||||
if (packet->data[0] == RPARAM_ERROR) {
|
||||
error = GS_ERROR_DATA;
|
||||
}
|
||||
} else {
|
||||
error = GS_ERROR_NO_DATA;
|
||||
}
|
||||
csp_buffer_free(packet);
|
||||
csp_close(conn);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* We have a reply */
|
||||
gs_rparam_query_t * reply = (gs_rparam_query_t *) packet->data;
|
||||
reply->length = csp_ntoh16(reply->length);
|
||||
|
||||
gs_error_t error;
|
||||
if (reply->action == RPARAM_REPLY) {
|
||||
error = gs_param_deserialize(tinst, reply->payload.packed, reply->length, F_FROM_BIG_ENDIAN);
|
||||
} else {
|
||||
error = GS_ERROR_TYPE;
|
||||
}
|
||||
|
||||
csp_buffer_free(packet);
|
||||
csp_close(conn);
|
||||
return error;
|
||||
}
|
110
gomspace/libparam_client/src/rparam/query.h
Normal file
110
gomspace/libparam_client/src/rparam/query.h
Normal file
@ -0,0 +1,110 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
|
||||
#include <gs/param/internal/rparam.h>
|
||||
|
||||
/**
|
||||
Rparam query handle.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
CSP node.
|
||||
*/
|
||||
uint8_t node;
|
||||
/**
|
||||
Remote table id.
|
||||
*/
|
||||
gs_param_table_id_t table_id;
|
||||
/**
|
||||
Remote table (definition) checksum.
|
||||
*/
|
||||
uint16_t checksum;
|
||||
/**
|
||||
Timeout (mS).
|
||||
*/
|
||||
uint32_t timeout_ms;
|
||||
/**
|
||||
If quite - no output to stdout.
|
||||
*/
|
||||
bool quiet;
|
||||
/**
|
||||
Auto send.
|
||||
*/
|
||||
bool auto_send;
|
||||
/**
|
||||
Type/action.
|
||||
*/
|
||||
int action;
|
||||
/**
|
||||
Size of current query.
|
||||
*/
|
||||
unsigned int length;
|
||||
/**
|
||||
Estimated total 'get' size.
|
||||
*/
|
||||
unsigned int get_size;
|
||||
/**
|
||||
Space for payload data.
|
||||
@note must be last in struct.
|
||||
*/
|
||||
union {
|
||||
uint16_t addr[0];
|
||||
uint8_t packed[GS_RPARAM_QUERY_MAX_PAYLOAD];
|
||||
} payload;
|
||||
} gs_rparam_query_handle_t;
|
||||
|
||||
/**
|
||||
Set whether rparam API should print to stdout or not.
|
||||
|
||||
@param[in] handle handle
|
||||
@param[in] quiet \a true print to stdout.
|
||||
*/
|
||||
void gs_rparam_query_set_quiet(gs_rparam_query_handle_t * handle, bool quiet);
|
||||
|
||||
/**
|
||||
Return true if any query has been set.
|
||||
|
||||
@param[in] handle handle
|
||||
@return \a true if any query has been set.
|
||||
*/
|
||||
bool gs_rparam_query_is_set(gs_rparam_query_handle_t * handle);
|
||||
|
||||
/**
|
||||
Add a 'get' query to the current query, after a succesfull rparam_query_send()
|
||||
the parameter value can be read using rparam_queury_get_value()
|
||||
|
||||
@param[in] handle handle
|
||||
@param[in] tinst table.
|
||||
@param[in] param_name name of the parameter.
|
||||
@return_gs_error_t
|
||||
@see rparam_query_send()
|
||||
*/
|
||||
gs_error_t gs_rparam_query_get(gs_rparam_query_handle_t * handle, gs_param_table_instance_t * tinst, const char* param_name);
|
||||
|
||||
/**
|
||||
Add a 'set' query to the current query. Use rparam_query_send() to execute the set query.
|
||||
|
||||
@param[in] handle handle
|
||||
@param[in] tinst table.
|
||||
@param[in] param_name name of the parameter to set
|
||||
@param[in] values array of values to set, multiple values can be set for array type parameters
|
||||
@param[in] value_count number of elements in \a values
|
||||
@return_gs_error_t
|
||||
@see rparam_query_send()
|
||||
*/
|
||||
gs_error_t gs_rparam_query_set(gs_rparam_query_handle_t * handle, gs_param_table_instance_t * tinst, const char* param_name, char * values[], uint8_t value_count);
|
||||
|
||||
/**
|
||||
Send the current query
|
||||
|
||||
@param[in] handle handle
|
||||
@param[in] tinst table.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_rparam_query_send(gs_rparam_query_handle_t * handle, gs_param_table_instance_t * tinst);
|
||||
|
||||
/**
|
||||
Reset/clear the current query
|
||||
@param[in] handle handle
|
||||
*/
|
||||
void gs_rparam_query_reset(gs_rparam_query_handle_t * handle);
|
474
gomspace/libparam_client/src/rparam/rparam.c
Normal file
474
gomspace/libparam_client/src/rparam/rparam.c
Normal file
@ -0,0 +1,474 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#define GS_PARAM_INTERNAL_USE 1
|
||||
|
||||
#include <gs/param/internal/rparam.h>
|
||||
#include <gs/param/internal/types.h>
|
||||
#include <gs/param/internal/table.h>
|
||||
#include "../serialize_local.h"
|
||||
#include <stdio.h>
|
||||
#include <alloca.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <gs/csp/csp.h>
|
||||
#include <csp/csp_endian.h>
|
||||
#include <gs/util/log.h>
|
||||
#include <gs/util/fletcher.h>
|
||||
#include <gs/util/check.h>
|
||||
|
||||
static gs_error_t gs_rparam_command(uint8_t node, uint32_t timeout_ms, uint8_t action, uint8_t table_id,
|
||||
const void * payload, size_t payload_size, char reply_ok)
|
||||
{
|
||||
gs_rparam_query_t * query = alloca(RPARAM_QUERY_LENGTH(query, payload_size));
|
||||
query->action = action;
|
||||
query->table_id = table_id;
|
||||
query->length = csp_hton16(payload_size);
|
||||
query->checksum = csp_hton16(GS_RPARAM_MAGIC_CHECKSUM); // Ignore checksum
|
||||
query->seq = 0;
|
||||
query->total = 0;
|
||||
memcpy(&query->payload, payload, payload_size);
|
||||
|
||||
/* Run single packet transaction */
|
||||
char reply = 0;
|
||||
if (csp_transaction2(CSP_PRIO_HIGH, node, GS_CSP_PORT_RPARAM, timeout_ms, query, RPARAM_QUERY_LENGTH(query, payload_size),
|
||||
&reply, 1, CSP_O_CRC32) <= 0) {
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
|
||||
return (reply == reply_ok) ? GS_OK : GS_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
gs_error_t gs_rparam_copy(uint8_t node, uint32_t timeout_ms, uint8_t from, uint8_t to)
|
||||
{
|
||||
gs_rparam_query_payload_t payload;
|
||||
payload.copy.from = from;
|
||||
payload.copy.to = to;
|
||||
return gs_rparam_command(node, timeout_ms, RPARAM_COPY, from, &payload, sizeof(payload.copy), RPARAM_COPY_OK);
|
||||
}
|
||||
|
||||
static gs_error_t gs_rparam_store(uint8_t node, uint32_t timeout_ms, uint8_t action, char reply_ok, uint8_t table_id,
|
||||
const char * table, const char * store, const char * slot)
|
||||
{
|
||||
gs_rparam_query_payload_store_t payload;
|
||||
memset(&payload, 0, sizeof(payload));
|
||||
if (table && (gs_string_empty(table) == false)) {
|
||||
GS_STRNCPY(payload.table, table);
|
||||
}
|
||||
if (store && (gs_string_empty(store) == false)) {
|
||||
GS_STRNCPY(payload.store, store);
|
||||
}
|
||||
if (slot && (gs_string_empty(slot) == false)) {
|
||||
GS_STRNCPY(payload.slot, slot);
|
||||
}
|
||||
return gs_rparam_command(node, timeout_ms, action, table_id, &payload, sizeof(payload), reply_ok);
|
||||
}
|
||||
|
||||
gs_error_t gs_rparam_save_to_store(uint8_t node, uint32_t timeout_ms, uint8_t table_id,
|
||||
const char * store, const char * slot)
|
||||
{
|
||||
return gs_rparam_store(node, timeout_ms, RPARAM_SAVE_TO_STORE, RPARAM_SAVE_OK, table_id, NULL, store, slot);
|
||||
}
|
||||
|
||||
gs_error_t gs_rparam_load_from_store(uint8_t node, uint32_t timeout_ms, uint8_t table_id,
|
||||
const char * store, const char * slot)
|
||||
{
|
||||
return gs_rparam_store(node, timeout_ms, RPARAM_LOAD_FROM_STORE, RPARAM_LOAD_OK, table_id, NULL, store, slot);
|
||||
}
|
||||
|
||||
gs_error_t gs_rparam_save(uint8_t node, uint32_t timeout_ms, uint8_t from, uint8_t to)
|
||||
{
|
||||
gs_rparam_query_payload_t payload;
|
||||
payload.copy.from = from;
|
||||
payload.copy.to = to;
|
||||
return gs_rparam_command(node, timeout_ms, RPARAM_SAVE, from, &payload, sizeof(payload.copy), RPARAM_SAVE_OK);
|
||||
}
|
||||
|
||||
gs_error_t gs_rparam_load(uint8_t node, uint32_t timeout_ms, uint8_t from, uint8_t to)
|
||||
{
|
||||
gs_rparam_query_payload_t payload;
|
||||
payload.copy.from = from;
|
||||
payload.copy.to = to;
|
||||
return gs_rparam_command(node, timeout_ms, RPARAM_LOAD, to, &payload, sizeof(payload.copy), RPARAM_LOAD_OK);
|
||||
}
|
||||
|
||||
static gs_error_t update_table(const char * func,
|
||||
gs_param_table_instance_t * tinst,
|
||||
gs_param_table_row_t * rows, unsigned int row_count,
|
||||
uint16_t checksum)
|
||||
{
|
||||
size_t memory_size = gs_param_calc_table_size(rows, row_count);
|
||||
if ((tinst->memory == NULL) || (tinst->memory_size < memory_size)) {
|
||||
// (re)allocate memory
|
||||
if (memory_size == 0) {
|
||||
return GS_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
memory_size = gs_max(1000U, memory_size);
|
||||
void * memory = calloc(1, memory_size);
|
||||
if (memory == NULL) {
|
||||
return GS_ERROR_ALLOC;
|
||||
}
|
||||
|
||||
free(tinst->memory);
|
||||
tinst->memory = memory;
|
||||
tinst->memory_size = memory_size;
|
||||
tinst->flags |= GS_PARAM_TABLE_F_ALLOC_MEMORY;
|
||||
}
|
||||
|
||||
free((void*)tinst->rows);
|
||||
tinst->rows = rows;
|
||||
tinst->row_count = row_count;
|
||||
tinst->checksum_be = tinst->checksum_le = 0;
|
||||
tinst->flags |= GS_PARAM_TABLE_F_ALLOC_ROWS;
|
||||
|
||||
if ((checksum != gs_param_table_checksum_le(tinst)) && (checksum != gs_param_table_checksum_be(tinst))) {
|
||||
log_error("%s: table specification has invalid checksum: %u - different from LE: %u and BE: %u",
|
||||
func, checksum, gs_param_table_checksum_le(tinst), gs_param_table_checksum_be(tinst));
|
||||
return GS_ERROR_DATA;
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_rparam_load_table_spec(gs_param_table_instance_t * tinst, const char* fname, uint16_t * return_checksum)
|
||||
{
|
||||
GS_CHECK_HANDLE(tinst != NULL);
|
||||
GS_CHECK_ARG(fname != NULL);
|
||||
|
||||
FILE * fd = fopen(fname, "r");
|
||||
if (fd == NULL) {
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
struct stat file_stat;
|
||||
if (fstat(fileno(fd), &file_stat) != 0) {
|
||||
log_error("%s: failed to stat file [%s]", __FUNCTION__, fname);
|
||||
fclose(fd);
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
|
||||
void * rows = calloc(file_stat.st_size, 1);
|
||||
if (rows == NULL) {
|
||||
fclose(fd);
|
||||
return GS_ERROR_ALLOC;
|
||||
}
|
||||
|
||||
uint16_t checksum = 0;
|
||||
size_t rs1 = fread(&checksum, 1, sizeof(checksum), fd);
|
||||
size_t rs2 = fread(rows, 1, file_stat.st_size, fd);
|
||||
fclose(fd);
|
||||
|
||||
const unsigned int single_row_size = sizeof(*tinst->rows);
|
||||
const unsigned int all_row_size = (file_stat.st_size - sizeof(checksum));
|
||||
const unsigned int row_count = (all_row_size) / single_row_size;
|
||||
if ((rs1 != sizeof(checksum)) || (rs2 != all_row_size) || (rs2 != (single_row_size * row_count))) {
|
||||
log_error("%s: incomplete/invalid read, expected %u + %u - read %u + %u, single row size: %u", __FUNCTION__,
|
||||
(unsigned int) sizeof(checksum), row_count,
|
||||
(unsigned int) rs1, (unsigned int) rs2, single_row_size);
|
||||
free(rows);
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
|
||||
gs_error_t error = update_table(__FUNCTION__, tinst, rows, row_count, checksum);
|
||||
|
||||
if (error == GS_OK) {
|
||||
if (return_checksum) {
|
||||
*return_checksum = checksum;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_rparam_download_table_spec(gs_param_table_instance_t * tinst,
|
||||
const char * fname,
|
||||
uint8_t node,
|
||||
gs_param_table_id_t table_id,
|
||||
uint32_t timeout_ms,
|
||||
uint16_t * return_checksum)
|
||||
{
|
||||
csp_conn_t * conn = csp_connect(CSP_PRIO_HIGH, node, GS_CSP_PORT_RPARAM, timeout_ms, CSP_O_CRC32);
|
||||
if (conn == NULL) {
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
|
||||
/* Allocate outgoing buffer */
|
||||
gs_rparam_query_t * query;
|
||||
csp_packet_t * packet = csp_buffer_get(RPARAM_QUERY_LENGTH(query, 0));
|
||||
if (packet == NULL) {
|
||||
csp_close(conn);
|
||||
return GS_ERROR_NO_BUFFERS;
|
||||
}
|
||||
|
||||
// setup request
|
||||
query = (gs_rparam_query_t *) packet->data;
|
||||
query->action = RPARAM_TABLE;
|
||||
query->table_id = table_id;
|
||||
query->length = 0;
|
||||
query->checksum = csp_hton16(GS_RPARAM_MAGIC_CHECKSUM); // Ignore checksum
|
||||
query->seq = 0;
|
||||
query->total = 0;
|
||||
|
||||
packet->length = RPARAM_QUERY_LENGTH(query, 0);
|
||||
if (!csp_send(conn, packet, 0)) {
|
||||
csp_buffer_free(packet);
|
||||
csp_close(conn);
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
|
||||
/* Receive remote parameter table, in host byte order
|
||||
* Note: This is necessary, because the SFP functions does not know the dataformat
|
||||
* and hence cannot be converted server-side. */
|
||||
void * dataout = NULL;
|
||||
int totalsize = 0;
|
||||
int result = csp_sfp_recv(conn, &dataout, &totalsize, timeout_ms);
|
||||
csp_close(conn);
|
||||
|
||||
if (result < 0) {
|
||||
free(dataout);
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
|
||||
gs_param_table_row_t * rows = dataout;
|
||||
const uint8_t row_count = totalsize / sizeof(*rows);
|
||||
|
||||
/* Calculate checksum on table (before converting endians!) */
|
||||
const uint16_t checksum = gs_fletcher16(rows, totalsize);
|
||||
|
||||
/* Autodetect Endians */
|
||||
int sum_first = 0;
|
||||
int sum_last = 0;
|
||||
for (unsigned int i = 0; i < row_count; i++) {
|
||||
sum_first += (rows[i].addr & 0xFF00) >> 8;
|
||||
sum_last += rows[i].addr & 0xFF;
|
||||
}
|
||||
|
||||
/* Correct endians */
|
||||
if (sum_first > sum_last) {
|
||||
for (unsigned int i = 0; i < row_count; i++) {
|
||||
rows[i].addr = (((rows[i].addr & 0xff00) >> 8) | ((rows[i].addr & 0x00ff) << 8));
|
||||
}
|
||||
}
|
||||
|
||||
gs_error_t error = update_table(__FUNCTION__, tinst, rows, row_count, checksum);
|
||||
if (error == GS_OK) {
|
||||
|
||||
if (return_checksum) {
|
||||
*return_checksum = checksum;
|
||||
}
|
||||
|
||||
// If filename provided, store table specification to file.
|
||||
if (gs_string_empty(fname) == false) {
|
||||
FILE * fd = fopen(fname, "w");
|
||||
if (fd == NULL) {
|
||||
log_error("%s: failed to open/create file: [%s]", __FUNCTION__, fname);
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
const size_t ws1_size = sizeof(checksum);
|
||||
const size_t ws1 = fwrite(&checksum, 1, ws1_size, fd);
|
||||
const size_t ws2 = fwrite(rows, 1, totalsize, fd);
|
||||
fclose(fd);
|
||||
if ((ws1 != ws1_size) || (ws2 != (size_t) totalsize)) {
|
||||
log_error("%s: failed to write %u + %d - wrote %u + %u", __FUNCTION__,
|
||||
(unsigned int) sizeof(checksum), totalsize, (unsigned int) ws1, (unsigned int) ws2);
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_rparam_get_full_table(gs_param_table_instance_t * tinst,
|
||||
uint8_t node,
|
||||
gs_param_table_id_t table_id,
|
||||
uint16_t checksum,
|
||||
uint32_t timeout_ms)
|
||||
{
|
||||
GS_CHECK_HANDLE(tinst != NULL);
|
||||
GS_CHECK_HANDLE(tinst->rows != NULL);
|
||||
GS_CHECK_HANDLE(tinst->memory != NULL);
|
||||
|
||||
unsigned int expected_bytes = 0;
|
||||
{
|
||||
unsigned int param_pos = 0;
|
||||
gs_error_t error = gs_param_serialize_full_table(tinst, ¶m_pos, GS_PARAM_SF_DRY_RUN, NULL, 10000, &expected_bytes);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
gs_rparam_query_t * query;
|
||||
csp_packet_t * request = csp_buffer_get(RPARAM_QUERY_LENGTH(query, 0));
|
||||
if (request == NULL) {
|
||||
return GS_ERROR_NO_BUFFERS;
|
||||
}
|
||||
|
||||
csp_conn_t * conn = csp_connect(CSP_PRIO_HIGH, node, GS_CSP_PORT_RPARAM, timeout_ms, CSP_O_CRC32);
|
||||
if (!conn) {
|
||||
csp_buffer_free(request);
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
|
||||
query = (gs_rparam_query_t *) request->data;
|
||||
query->action = RPARAM_GET;
|
||||
query->table_id = table_id;
|
||||
query->length = 0; // == get full table
|
||||
query->checksum = csp_hton16(checksum);
|
||||
query->seq = 0;
|
||||
query->total = 0;
|
||||
|
||||
request->length = RPARAM_QUERY_LENGTH(query, 0);
|
||||
if (!csp_send(conn, request, timeout_ms)) {
|
||||
csp_buffer_free(request);
|
||||
csp_close(conn);
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
|
||||
csp_packet_t * reply;
|
||||
gs_error_t error = GS_OK;
|
||||
unsigned int total_bytes = 0;
|
||||
while ((reply = csp_read(conn, timeout_ms)) != NULL) {
|
||||
|
||||
/* We have a reply */
|
||||
query = (void *) reply->data;
|
||||
const uint16_t qlength = csp_ntoh16(query->length);
|
||||
total_bytes += qlength;
|
||||
const uint16_t seq = csp_ntoh16(query->seq);
|
||||
const uint16_t total = csp_ntoh16(query->total);
|
||||
|
||||
if (query->action == RPARAM_REPLY) {
|
||||
error = gs_param_deserialize(tinst, query->payload.packed, qlength, F_FROM_BIG_ENDIAN);
|
||||
}
|
||||
csp_buffer_free(reply);
|
||||
|
||||
if (error || (seq >= total)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reply == NULL) {
|
||||
error = GS_ERROR_TIMEOUT;
|
||||
}
|
||||
|
||||
if ((error == GS_OK) && (expected_bytes != total_bytes)) {
|
||||
log_warning("%s: expected %u != received %u bytes", __FUNCTION__, expected_bytes, total_bytes);
|
||||
error = GS_ERROR_DATA;
|
||||
}
|
||||
|
||||
csp_close(conn);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_rparam_get(uint8_t node,
|
||||
gs_param_table_id_t table_id,
|
||||
uint16_t addr,
|
||||
gs_param_type_t type,
|
||||
uint16_t checksum,
|
||||
uint32_t timeout_ms,
|
||||
void * value,
|
||||
size_t value_size)
|
||||
{
|
||||
/* Calculate length */
|
||||
gs_rparam_query_t * query;
|
||||
const size_t query_size = RPARAM_QUERY_LENGTH(query, sizeof(query->payload.addr[0]));
|
||||
const size_t reply_payload_size = (value_size + sizeof(query->payload.addr[0]));
|
||||
const size_t reply_size = RPARAM_QUERY_LENGTH(query, reply_payload_size);
|
||||
|
||||
query = alloca(reply_size);
|
||||
query->action = RPARAM_GET;
|
||||
query->table_id = table_id;
|
||||
query->checksum = csp_hton16(checksum);
|
||||
query->seq = 0;
|
||||
query->total = 0;
|
||||
query->payload.addr[0] = csp_hton16(addr);
|
||||
query->length = csp_hton16(sizeof(query->payload.addr[0]));
|
||||
|
||||
/* Run single packet transaction */
|
||||
if (csp_transaction2(CSP_PRIO_HIGH, node, GS_CSP_PORT_RPARAM, timeout_ms, query, query_size, query, reply_size, CSP_O_CRC32) <= 0) {
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
|
||||
/* We have a reply */
|
||||
query->length = csp_ntoh16(query->length);
|
||||
|
||||
if (query->length != reply_payload_size) {
|
||||
log_warning("%s: Invalid reply size %u - expected %u", __FUNCTION__, query->length, (unsigned int) reply_payload_size);
|
||||
return GS_ERROR_DATA;
|
||||
}
|
||||
|
||||
/* Read address */
|
||||
query->payload.addr[0] = csp_betoh16(query->payload.addr[0]);
|
||||
if (query->payload.addr[0] != addr) {
|
||||
log_warning("%s: Invalid address in reply %u", __FUNCTION__, query->payload.addr[0]);
|
||||
return GS_ERROR_DATA;
|
||||
}
|
||||
|
||||
/* Read value */
|
||||
memcpy(value, &query->payload.packed[2], value_size);
|
||||
gs_param_betoh(type, value);
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_rparam_set(uint8_t node,
|
||||
gs_param_table_id_t table_id,
|
||||
uint16_t addr,
|
||||
gs_param_type_t type,
|
||||
uint16_t checksum,
|
||||
uint32_t timeout_ms,
|
||||
const void * value,
|
||||
size_t value_size)
|
||||
{
|
||||
/* Calculate length */
|
||||
gs_rparam_query_t * query;
|
||||
const size_t payload_size = (value_size + sizeof(query->payload.addr[0]));
|
||||
const size_t query_size = RPARAM_QUERY_LENGTH(query, payload_size);
|
||||
|
||||
query = alloca(query_size);
|
||||
query->action = RPARAM_SET;
|
||||
query->table_id = table_id;
|
||||
query->seq = 0;
|
||||
query->total = 0;
|
||||
query->checksum = csp_hton16(checksum);
|
||||
|
||||
/* Actual set query */
|
||||
unsigned int bytes = 0;
|
||||
gs_error_t error = gs_param_serialize_item_direct(type, value_size, addr, value, F_TO_BIG_ENDIAN, query->payload.packed, payload_size, &bytes);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Add to query */
|
||||
query->length = csp_hton16(bytes);
|
||||
|
||||
/* Run single packet transaction */
|
||||
if (csp_transaction2(CSP_PRIO_HIGH, node, GS_CSP_PORT_RPARAM, timeout_ms, query, query_size, query, 1, CSP_O_CRC32) <= 0) {
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
|
||||
/* We have a reply */
|
||||
query->length = csp_ntoh16(query->length);
|
||||
|
||||
if ((query->action != RPARAM_SET_OK) || (query->length != bytes)) {
|
||||
log_warning("%s: Invalid reply: size %u - expected %u, action %u - expected %u",
|
||||
__FUNCTION__, query->length, bytes, query->action, RPARAM_SET_OK);
|
||||
return GS_ERROR_DATA;
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_rparam_query_get_value(gs_param_table_instance_t * tinst, const char* param_name, uint16_t param_no, void* val_p, size_t val_size)
|
||||
{
|
||||
const gs_param_table_row_t * t = gs_param_row_by_name(param_name, tinst->rows, tinst->row_count);
|
||||
if (t == NULL) {
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (val_size < t->size) {
|
||||
return GS_ERROR_OVERFLOW;
|
||||
}
|
||||
|
||||
return gs_param_get(tinst, t->addr + (param_no * t->size), t->type, val_p, t->size, 0);
|
||||
}
|
353
gomspace/libparam_client/src/serialize.c
Normal file
353
gomspace/libparam_client/src/serialize.c
Normal file
@ -0,0 +1,353 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#define GS_PARAM_INTERNAL_USE 1
|
||||
|
||||
#include "serialize_local.h"
|
||||
#include <gs/param/table.h>
|
||||
#include <gs/param/internal/types.h>
|
||||
#include <gs/util/byteorder.h>
|
||||
#include <gs/util/endian.h>
|
||||
#include <alloca.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <param/param_types.h> // need PARM_X??? definitions
|
||||
|
||||
bool gs_param_betoh(gs_param_type_t type, void * item)
|
||||
{
|
||||
if (item) {
|
||||
switch (type) {
|
||||
case GS_PARAM_UINT16:
|
||||
case GS_PARAM_INT16:
|
||||
case PARAM_X16:
|
||||
{
|
||||
*(uint16_t *) item = util_betoh16(*(uint16_t *) item);
|
||||
return true;
|
||||
}
|
||||
case GS_PARAM_UINT32:
|
||||
case GS_PARAM_INT32:
|
||||
case PARAM_X32:
|
||||
{
|
||||
*(uint32_t *) item = util_betoh32(*(uint32_t *) item);
|
||||
return true;
|
||||
}
|
||||
case GS_PARAM_UINT64:
|
||||
case GS_PARAM_INT64:
|
||||
case PARAM_X64:
|
||||
{
|
||||
*(uint64_t *) item = util_betoh64(*(uint64_t *) item);
|
||||
return true;
|
||||
}
|
||||
case GS_PARAM_FLOAT:
|
||||
{
|
||||
*(float *) item = util_ntohflt(*(float *) item);
|
||||
return true;
|
||||
}
|
||||
case GS_PARAM_DOUBLE:
|
||||
{
|
||||
*(double *) item = util_ntohdbl(*(double *) item);
|
||||
return true;
|
||||
}
|
||||
|
||||
case GS_PARAM_UINT8:
|
||||
case GS_PARAM_INT8:
|
||||
case PARAM_X8:
|
||||
case GS_PARAM_STRING:
|
||||
case GS_PARAM_DATA:
|
||||
case GS_PARAM_BOOL:
|
||||
// no swap
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool gs_param_htobe(gs_param_type_t type, void * item)
|
||||
{
|
||||
if (item) {
|
||||
switch (type) {
|
||||
case PARAM_UINT16:
|
||||
case PARAM_INT16:
|
||||
case PARAM_X16:
|
||||
{
|
||||
*(uint16_t *) item = util_htobe16(*(uint16_t *) item);
|
||||
return true;
|
||||
}
|
||||
case PARAM_UINT32:
|
||||
case PARAM_INT32:
|
||||
case PARAM_X32:
|
||||
{
|
||||
*(uint32_t *) item = util_htobe32(*(uint32_t *) item);
|
||||
return true;
|
||||
}
|
||||
case PARAM_UINT64:
|
||||
case PARAM_INT64:
|
||||
case PARAM_X64:
|
||||
{
|
||||
*(uint64_t *) item = util_htobe64(*(uint64_t *) item);
|
||||
return true;
|
||||
}
|
||||
case PARAM_FLOAT:
|
||||
{
|
||||
*(float *) item = util_htonflt(*(float *) item);
|
||||
return true;
|
||||
}
|
||||
case PARAM_DOUBLE:
|
||||
{
|
||||
*(double *) item = util_htondbl(*(double *) item);
|
||||
return true;
|
||||
}
|
||||
|
||||
case PARAM_UINT8:
|
||||
case PARAM_INT8:
|
||||
case PARAM_X8:
|
||||
case PARAM_STRING:
|
||||
case PARAM_DATA:
|
||||
case PARAM_BOOL:
|
||||
// no swap
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool gs_param_require_endian_swap(gs_param_type_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case PARAM_UINT16:
|
||||
case PARAM_INT16:
|
||||
case PARAM_X16:
|
||||
case PARAM_UINT32:
|
||||
case PARAM_INT32:
|
||||
case PARAM_X32:
|
||||
case PARAM_UINT64:
|
||||
case PARAM_INT64:
|
||||
case PARAM_X64:
|
||||
case PARAM_FLOAT:
|
||||
case PARAM_DOUBLE:
|
||||
// swap
|
||||
break;
|
||||
|
||||
case PARAM_UINT8:
|
||||
case PARAM_INT8:
|
||||
case PARAM_X8:
|
||||
case PARAM_STRING:
|
||||
case PARAM_DATA:
|
||||
case PARAM_BOOL:
|
||||
// no swap
|
||||
return false;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static gs_error_t gs_param_serialize_array(gs_param_table_instance_t * tinst, const gs_param_table_row_t * param, uint32_t flags, uint8_t * buf, unsigned int buf_size, unsigned int * buf_pos)
|
||||
{
|
||||
const unsigned int param_size = GS_PARAM_SIZE(param);
|
||||
const unsigned int param_array_size = GS_PARAM_ARRAY_SIZE(param);
|
||||
const gs_param_type_t param_type = GS_PARAM_TYPE(param);
|
||||
|
||||
/* Calculate total parameter size (full array) */
|
||||
{
|
||||
unsigned int size = param_size * param_array_size;
|
||||
if ((flags & F_PACKED) == 0) {
|
||||
size += param_array_size * sizeof(uint16_t); // address
|
||||
}
|
||||
|
||||
/* Return if parameter array would exceed maxbuf */
|
||||
if (*buf_pos + size > buf_size) {
|
||||
return GS_ERROR_OVERFLOW;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t value[param_size];
|
||||
gs_error_t error = GS_OK;
|
||||
for (unsigned int j = 0; (j < param_array_size) && (error == GS_OK); j++) {
|
||||
const uint16_t addr = GS_PARAM_ADDR(param) + (param_size * j);
|
||||
error = gs_param_get(tinst, addr, param_type, value, param_size, 0);
|
||||
if (error == GS_OK) {
|
||||
error = gs_param_serialize_item(param, addr, value, flags, buf, buf_size, buf_pos);
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_param_serialize_item_direct(gs_param_type_t param_type, unsigned int param_size, uint16_t addr, const void * item, uint32_t flags, uint8_t * buf, unsigned int buf_size, unsigned int * buf_pos)
|
||||
{
|
||||
/* Check length */
|
||||
if ((((flags & F_PACKED) ? 0 : sizeof(addr)) + param_size + *buf_pos) > buf_size) {
|
||||
return GS_ERROR_OVERFLOW;
|
||||
}
|
||||
|
||||
/* Include address if not packed */
|
||||
if ((flags & F_PACKED) == 0) {
|
||||
|
||||
if (flags & F_TO_BIG_ENDIAN) {
|
||||
addr = util_htobe16(addr);
|
||||
}
|
||||
|
||||
if ((flags & F_DRY_RUN) == 0) {
|
||||
memcpy(&buf[*buf_pos], &addr, sizeof(addr));
|
||||
}
|
||||
|
||||
*buf_pos += sizeof(addr);
|
||||
}
|
||||
|
||||
if ((flags & F_DRY_RUN) == 0) {
|
||||
if (flags & F_TO_BIG_ENDIAN) {
|
||||
void * tmp = alloca(param_size); // this must be aligned
|
||||
memcpy(tmp, item, param_size);
|
||||
gs_param_htobe(param_type, tmp);
|
||||
item = tmp;
|
||||
}
|
||||
memcpy(&buf[*buf_pos], item, param_size);
|
||||
}
|
||||
|
||||
*buf_pos += param_size;
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_param_serialize_item(const gs_param_table_row_t * param, uint16_t addr, const void * item, uint32_t flags, uint8_t * buf, unsigned int buf_size, unsigned int * buf_pos)
|
||||
{
|
||||
const gs_param_type_t param_type = GS_PARAM_TYPE(param);
|
||||
const unsigned int param_size = GS_PARAM_SIZE(param);
|
||||
return gs_param_serialize_item_direct(param_type, param_size, addr, item, flags, buf, buf_size, buf_pos);
|
||||
}
|
||||
|
||||
gs_error_t gs_param_serialize_full_table(gs_param_table_instance_t * tinst, unsigned int * param_pos, uint32_t flags, uint8_t * buf, unsigned int buf_size, unsigned int * buf_pos)
|
||||
{
|
||||
if (flags & GS_PARAM_SF_DRY_RUN) {
|
||||
buf = NULL;
|
||||
buf_size = -1; // Max size
|
||||
}
|
||||
gs_error_t error = GS_OK;
|
||||
unsigned int i = *param_pos;
|
||||
for (; i < tinst->row_count; i++) {
|
||||
const gs_param_table_row_t * param = &tinst->rows[i];
|
||||
error = gs_param_serialize_array(tinst, param, flags, buf, buf_size, buf_pos);
|
||||
if (error) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*param_pos = i;
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_param_serialize_list(gs_param_table_instance_t * tinst,
|
||||
const uint16_t addr[], unsigned int addr_count,
|
||||
unsigned int * param_pos, uint32_t flags,
|
||||
uint8_t * buf, unsigned int buf_size, unsigned int * buf_pos)
|
||||
{
|
||||
if (tinst == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
gs_error_t error = GS_OK;
|
||||
unsigned int i = *param_pos;
|
||||
for (; i < addr_count; ++i) {
|
||||
const gs_param_table_row_t * param = gs_param_row_by_address(addr[i], tinst->rows, tinst->row_count);
|
||||
if (param == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const gs_param_type_t param_type = GS_PARAM_TYPE(param);
|
||||
const unsigned int param_size = GS_PARAM_SIZE(param);
|
||||
uint8_t value[param_size];
|
||||
error = gs_param_get(tinst, addr[i], param_type, value, param_size, 0);
|
||||
if (error) {
|
||||
break;
|
||||
}
|
||||
error = gs_param_serialize_item(param, addr[i], value, flags, buf, buf_size, buf_pos);
|
||||
if (error) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*param_pos = i;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_param_deserialize_item(gs_param_table_instance_t * tinst,
|
||||
const gs_param_table_row_t * param,
|
||||
uint16_t addr,
|
||||
const void * item,
|
||||
uint32_t flags)
|
||||
{
|
||||
const gs_param_type_t param_type = GS_PARAM_TYPE(param);
|
||||
const unsigned int param_size = GS_PARAM_SIZE(param);
|
||||
|
||||
if (flags & F_FROM_BIG_ENDIAN) {
|
||||
if (gs_param_require_endian_swap(param_type)) {
|
||||
|
||||
// Copy to temporary storage, so we don't mess with input memory
|
||||
void * tmp = alloca(param_size);
|
||||
memcpy(tmp, item, param_size);
|
||||
|
||||
gs_param_betoh(param_type, tmp);
|
||||
|
||||
// Replace input pointer
|
||||
item = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
gs_error_t error = GS_OK;
|
||||
if ((flags & F_DRY_RUN) == 0) {
|
||||
error = gs_param_set(tinst, addr, param_type, item, param_size, GS_PARAM_FLAGS(param));
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_param_deserialize(gs_param_table_instance_t * tinst, const uint8_t * buf, unsigned int buf_size, uint32_t flags)
|
||||
{
|
||||
unsigned int pos = 0;
|
||||
unsigned int count = 0;
|
||||
gs_error_t error = GS_OK;
|
||||
while ((pos < buf_size) && (error == GS_OK)) {
|
||||
|
||||
if (flags & F_PACKED) {
|
||||
/** PACKED */
|
||||
|
||||
/* Find in table */
|
||||
const gs_param_table_row_t * param = &tinst->rows[count];
|
||||
const unsigned int param_array_size = GS_PARAM_ARRAY_SIZE(param);
|
||||
const unsigned int param_size = GS_PARAM_SIZE(param);
|
||||
|
||||
/* For each item in array */
|
||||
for (unsigned int j = 0; (j < param_array_size) && (error == GS_OK); j++) {
|
||||
uint16_t addr = GS_PARAM_ADDR(param) + (param_size * j);
|
||||
error = gs_param_deserialize_item(tinst, param, addr, &buf[pos], flags);
|
||||
pos += param_size;
|
||||
}
|
||||
|
||||
} else {
|
||||
/** NOT PACKED */
|
||||
|
||||
/* Read address from data */
|
||||
uint16_t addr;
|
||||
memcpy(&addr, &buf[pos], sizeof(addr));
|
||||
if (flags & F_FROM_BIG_ENDIAN) {
|
||||
addr = util_betoh16(addr);
|
||||
}
|
||||
pos += sizeof(addr);
|
||||
|
||||
/* Find in table */
|
||||
const gs_param_table_row_t * param = gs_param_row_by_address(addr, tinst->rows, tinst->row_count);
|
||||
if (param == NULL) {
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* Copy value */
|
||||
error = gs_param_deserialize_item(tinst, param, addr, &buf[pos], flags);
|
||||
pos += GS_PARAM_SIZE(param);
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
9
gomspace/libparam_client/src/serialize_local.h
Normal file
9
gomspace/libparam_client/src/serialize_local.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef SRC_SERIALIZE_LOCAL_H
|
||||
#define SRC_SERIALIZE_LOCAL_H
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/param/internal/serialize.h>
|
||||
|
||||
gs_error_t gs_param_serialize_item_direct(gs_param_type_t param_type, unsigned int param_size, uint16_t addr, const void * item, uint32_t flags, uint8_t * buf, unsigned int buf_size, unsigned int * buf_pos);
|
||||
|
||||
#endif
|
589
gomspace/libparam_client/src/string.c
Normal file
589
gomspace/libparam_client/src/string.c
Normal file
@ -0,0 +1,589 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#define GS_PARAM_INTERNAL_USE 1
|
||||
|
||||
#include <gs/param/internal/types.h>
|
||||
#include <gs/param/internal/table.h>
|
||||
#include <gs/util/string.h>
|
||||
#include <gs/util/log.h>
|
||||
#include <gs/util/check.h>
|
||||
#include <gs/util/bytebuffer.h>
|
||||
#include <ctype.h>
|
||||
|
||||
size_t gs_param_calc_table_size(const gs_param_table_row_t * rows, size_t row_count)
|
||||
{
|
||||
if (rows && row_count) {
|
||||
const gs_param_table_row_t * last_row = rows;
|
||||
// table rows may not be in assending address- so we have to run through the entire table.
|
||||
for (size_t i = 0; i < row_count; ++i, ++rows) {
|
||||
if (GS_PARAM_ADDR(rows) > GS_PARAM_ADDR(last_row)) {
|
||||
last_row = rows;
|
||||
}
|
||||
}
|
||||
return (GS_PARAM_ADDR(last_row) + (GS_PARAM_SIZE(last_row) * GS_PARAM_ARRAY_SIZE(last_row)));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const gs_param_table_row_t * gs_param_row_by_name(const char * name, const gs_param_table_row_t * rows, size_t row_count)
|
||||
{
|
||||
if (rows) {
|
||||
for (unsigned int i = 0; i < row_count; ++i, ++rows) {
|
||||
if (GS_PGM_STRNCASECMP(name, rows->name, GS_PARAM_MAX_NAME) == 0) {
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const gs_param_table_row_t * gs_param_row_by_address(uint16_t addr, const gs_param_table_row_t * rows, size_t row_count)
|
||||
{
|
||||
if (rows) {
|
||||
for (unsigned int i = 0; i < row_count; ++i, ++rows) {
|
||||
const unsigned int param_array_size = GS_PARAM_ARRAY_SIZE(rows);
|
||||
const unsigned int param_size = GS_PARAM_SIZE(rows);
|
||||
const unsigned int param_addr = GS_PARAM_ADDR(rows);
|
||||
for (unsigned int j = 0; j < param_array_size; ++j) {
|
||||
if ((param_addr + (j * param_size)) == addr) {
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
memcpy is used, because data/value may not be aligned correctly and cause crash if accessed directly.
|
||||
*/
|
||||
gs_error_t gs_param_to_string_buffer(const gs_param_table_row_t * param, const void * value, bool with_type, uint32_t flags, gs_bytebuffer_t *bb)
|
||||
{
|
||||
flags |= param->flags;
|
||||
const uint8_t param_type = GS_PARAM_TYPE(param);
|
||||
switch (param_type) {
|
||||
case GS_PARAM_BOOL: {
|
||||
if (with_type == 1) {
|
||||
gs_bytebuffer_printf(bb, "BL ");
|
||||
}
|
||||
gs_bytebuffer_printf(bb, "%s", *(uint8_t *) value ? "true" : "false");
|
||||
break;
|
||||
}
|
||||
case GS_PARAM_INT8: {
|
||||
if (with_type == 1) {
|
||||
gs_bytebuffer_printf(bb, "I8 ");
|
||||
}
|
||||
gs_bytebuffer_printf(bb, "%d", *(int8_t *) value);
|
||||
break;
|
||||
}
|
||||
case PARAM_X8:
|
||||
flags |= GS_PARAM_F_SHOW_HEX;
|
||||
// fallthrough
|
||||
case GS_PARAM_UINT8: {
|
||||
if (with_type == 1) {
|
||||
gs_bytebuffer_printf(bb, "U8 ");
|
||||
}
|
||||
if (flags & GS_PARAM_F_SHOW_HEX) {
|
||||
gs_bytebuffer_printf(bb, "0x%02"PRIx8, *(uint8_t *) value);
|
||||
} else {
|
||||
gs_bytebuffer_printf(bb, "%u", *(uint8_t *) value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GS_PARAM_INT16: {
|
||||
int16_t tmp;
|
||||
memcpy(&tmp, value, sizeof(tmp));
|
||||
if (with_type == 1) {
|
||||
gs_bytebuffer_printf(bb, "I16 ");
|
||||
}
|
||||
gs_bytebuffer_printf(bb, "%"PRId16, tmp);
|
||||
break;
|
||||
}
|
||||
case PARAM_X16:
|
||||
flags |= GS_PARAM_F_SHOW_HEX;
|
||||
// fallthrough
|
||||
case GS_PARAM_UINT16: {
|
||||
uint16_t tmp;
|
||||
memcpy(&tmp, value, sizeof(tmp));
|
||||
if (with_type == 1) {
|
||||
gs_bytebuffer_printf(bb, "U16 ");
|
||||
}
|
||||
if (flags & GS_PARAM_F_SHOW_HEX) {
|
||||
gs_bytebuffer_printf(bb, "0x%04"PRIx16, tmp);
|
||||
} else {
|
||||
gs_bytebuffer_printf(bb, "%"PRIu16, tmp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GS_PARAM_INT32: {
|
||||
int32_t tmp;
|
||||
memcpy(&tmp, value, sizeof(tmp));
|
||||
if (with_type == 1) {
|
||||
gs_bytebuffer_printf(bb, "I32 ");
|
||||
}
|
||||
gs_bytebuffer_printf(bb, "%"PRId32, tmp);
|
||||
break;
|
||||
}
|
||||
case PARAM_X32:
|
||||
flags |= GS_PARAM_F_SHOW_HEX;
|
||||
// fallthrough
|
||||
case GS_PARAM_UINT32: {
|
||||
uint32_t tmp;
|
||||
memcpy(&tmp, value, sizeof(tmp));
|
||||
if (with_type == 1) {
|
||||
gs_bytebuffer_printf(bb, "U32 ");
|
||||
}
|
||||
if (flags & GS_PARAM_F_SHOW_HEX) {
|
||||
gs_bytebuffer_printf(bb, "0x%08"PRIx32, tmp);
|
||||
} else {
|
||||
gs_bytebuffer_printf(bb, "%"PRIu32, tmp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifdef PRIu64
|
||||
case GS_PARAM_INT64: {
|
||||
int64_t tmp;
|
||||
memcpy(&tmp, value, sizeof(tmp));
|
||||
if (with_type == 1) {
|
||||
gs_bytebuffer_printf(bb, "I64 ");
|
||||
}
|
||||
gs_bytebuffer_printf(bb, "%"PRId64, tmp);
|
||||
break;
|
||||
}
|
||||
case PARAM_X64:
|
||||
flags |= GS_PARAM_F_SHOW_HEX;
|
||||
// fallthrough
|
||||
case GS_PARAM_UINT64: {
|
||||
uint64_t tmp;
|
||||
memcpy(&tmp, value, sizeof(tmp));
|
||||
if (with_type == 1) {
|
||||
gs_bytebuffer_printf(bb, "U64 ");
|
||||
}
|
||||
if (flags & GS_PARAM_F_SHOW_HEX) {
|
||||
gs_bytebuffer_printf(bb, "0x%016"PRIx64, tmp);
|
||||
} else {
|
||||
gs_bytebuffer_printf(bb, "%"PRIu64, tmp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case GS_PARAM_FLOAT: {
|
||||
float tmp;
|
||||
memcpy(&tmp, value, sizeof(tmp));
|
||||
if (with_type == 1) {
|
||||
gs_bytebuffer_printf(bb, "FLT ");
|
||||
}
|
||||
if (flags & GS_PARAM_F_SHOW_SCIENTIFIC) {
|
||||
gs_bytebuffer_printf(bb, "%e", (double) tmp);
|
||||
} else {
|
||||
gs_bytebuffer_printf(bb, "%f", (double) tmp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GS_PARAM_DOUBLE: {
|
||||
double tmp;
|
||||
memcpy(&tmp, value, sizeof(tmp));
|
||||
if (with_type == 1) {
|
||||
gs_bytebuffer_printf(bb, "DBL ");
|
||||
}
|
||||
if (flags & GS_PARAM_F_SHOW_SCIENTIFIC) {
|
||||
gs_bytebuffer_printf(bb, "%e", tmp);
|
||||
} else {
|
||||
gs_bytebuffer_printf(bb, "%f", tmp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GS_PARAM_STRING: {
|
||||
if (with_type == 1) {
|
||||
gs_bytebuffer_printf(bb, "STR ");
|
||||
}
|
||||
gs_bytebuffer_append(bb, "\"", 1);
|
||||
// handle missing NUL termination.
|
||||
const size_t len = strnlen((const char*)value, GS_PARAM_SIZE(param));
|
||||
gs_bytebuffer_append(bb, value, len);
|
||||
gs_bytebuffer_append(bb, "\"", 1);
|
||||
break;
|
||||
}
|
||||
case GS_PARAM_DATA: {
|
||||
if (with_type == 1) {
|
||||
gs_bytebuffer_printf(bb, "DAT ");
|
||||
}
|
||||
for (int i = 0; i < GS_PARAM_SIZE(param); i++) {
|
||||
gs_bytebuffer_printf(bb, "%02"PRIX8, ((uint8_t *) value)[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
log_error("%s: Unknown param type %u", __FUNCTION__, param_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_param_to_string2(const gs_param_table_row_t * param, const void * value, bool with_type, uint32_t flags, char * buf, unsigned int buf_size, unsigned int buf_pos, unsigned int * buf_written)
|
||||
{
|
||||
GS_CHECK_ARG(buf_pos <= buf_size);
|
||||
gs_bytebuffer_t bb;
|
||||
gs_bytebuffer_init(&bb, &buf[buf_pos], (buf_size - buf_pos));
|
||||
gs_error_t error = gs_param_to_string_buffer(param, value, with_type, flags, &bb);
|
||||
if (error == GS_OK) {
|
||||
gs_bytebuffer_get_as_string(&bb, &error); // this will add NUL termination, but may truncate buffer
|
||||
error = gs_bytebuffer_get_state(&bb);
|
||||
if (buf_written) {
|
||||
*buf_written = bb.used;
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
const char * gs_param_type_to_string(gs_param_type_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case GS_PARAM_BOOL: return "bool";
|
||||
case GS_PARAM_UINT8: return "uint8_t";
|
||||
case GS_PARAM_UINT16: return "uint16_t";
|
||||
case GS_PARAM_UINT32: return "uint32_t";
|
||||
case GS_PARAM_UINT64: return "uint65_t";
|
||||
case GS_PARAM_INT8: return "int8_t";
|
||||
case GS_PARAM_INT16: return "int16_t";
|
||||
case GS_PARAM_INT32: return "int32_t";
|
||||
case GS_PARAM_INT64: return "int64_t";
|
||||
case PARAM_X8: return "uint8_t";
|
||||
case PARAM_X16: return "uint16_t";
|
||||
case PARAM_X32: return "uint32_t";
|
||||
case PARAM_X64: return "uint64_t";
|
||||
case GS_PARAM_FLOAT: return "float";
|
||||
case GS_PARAM_DOUBLE: return "double";
|
||||
case GS_PARAM_STRING: return "char";
|
||||
case GS_PARAM_DATA: return "char";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static inline int to_int(char c)
|
||||
{
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'A' && c <= 'F') return 10 + c - 'A';
|
||||
if (c >= 'a' && c <= 'f') return 10 + c - 'a';
|
||||
return -1;
|
||||
}
|
||||
|
||||
gs_error_t gs_param_from_string(const gs_param_table_row_t * param, const char * string, void * value)
|
||||
{
|
||||
if ((param == NULL) || (string == NULL) || (value == NULL)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (GS_PARAM_TYPE(param) != GS_PARAM_STRING) {
|
||||
// skip only space - not white-space, e.g. isspace()
|
||||
for (; *string == ' '; ++string);
|
||||
}
|
||||
|
||||
gs_error_t error = GS_OK;
|
||||
|
||||
switch(GS_PARAM_TYPE(param)) {
|
||||
|
||||
case GS_PARAM_BOOL:
|
||||
{
|
||||
bool parsein = false;
|
||||
error = gs_string_to_bool(string, &parsein);
|
||||
if (error == GS_OK) {
|
||||
*((uint8_t *) value) = parsein;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_PARAM_UINT8:
|
||||
{
|
||||
uint8_t parsein;
|
||||
error = gs_string_to_uint8(string, &parsein);
|
||||
if (error == GS_OK) {
|
||||
*((uint8_t *) value) = parsein;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_PARAM_UINT16:
|
||||
{
|
||||
uint16_t parsein;
|
||||
error = gs_string_to_uint16(string, &parsein);
|
||||
if (error == GS_OK) {
|
||||
*((uint16_t *) value) = parsein;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_PARAM_UINT32:
|
||||
{
|
||||
uint32_t parsein;
|
||||
error = gs_string_to_uint32(string, &parsein);
|
||||
if (error == GS_OK) {
|
||||
*((uint32_t *) value) = parsein;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_PARAM_UINT64:
|
||||
{
|
||||
uint64_t parsein;
|
||||
error = gs_string_to_uint64(string, &parsein);
|
||||
if (error == GS_OK) {
|
||||
*((uint64_t *) value) = parsein;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_PARAM_INT8:
|
||||
{
|
||||
int8_t parsein;
|
||||
error = gs_string_to_int8(string, &parsein);
|
||||
if (error == GS_OK) {
|
||||
*((int8_t *) value) = parsein;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_PARAM_INT16:
|
||||
{
|
||||
int16_t parsein;
|
||||
error = gs_string_to_int16(string, &parsein);
|
||||
if (error == GS_OK) {
|
||||
*((int16_t *) value) = parsein;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_PARAM_INT32:
|
||||
{
|
||||
int32_t parsein;
|
||||
error = gs_string_to_int32(string, &parsein);
|
||||
if (error == GS_OK) {
|
||||
*((int32_t *) value) = parsein;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_PARAM_INT64:
|
||||
{
|
||||
int64_t parsein;
|
||||
error = gs_string_to_int64(string, &parsein);
|
||||
if (error == GS_OK) {
|
||||
*((int64_t *) value) = parsein;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PARAM_X8:
|
||||
{
|
||||
uint32_t parsein;
|
||||
error = gs_string_hex_to_uint32(string, &parsein);
|
||||
if (error == GS_OK) {
|
||||
if (parsein <= UINT8_MAX) {
|
||||
*((uint8_t *) value) = parsein;
|
||||
} else {
|
||||
error = GS_ERROR_OVERFLOW;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PARAM_X16:
|
||||
{
|
||||
uint32_t parsein;
|
||||
error = gs_string_hex_to_uint32(string, &parsein);
|
||||
if (error == GS_OK) {
|
||||
if (parsein <= UINT16_MAX) {
|
||||
*((uint16_t *) value) = parsein;
|
||||
} else {
|
||||
error = GS_ERROR_OVERFLOW;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PARAM_X32:
|
||||
{
|
||||
uint32_t parsein;
|
||||
error = gs_string_hex_to_uint32(string, &parsein);
|
||||
if (error == GS_OK) {
|
||||
*((uint32_t *) value) = parsein;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PARAM_X64:
|
||||
{
|
||||
uint64_t parsein;
|
||||
error = gs_string_hex_to_uint64(string, &parsein);
|
||||
if (error == GS_OK) {
|
||||
*((uint64_t *) value) = parsein;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_PARAM_FLOAT:
|
||||
{
|
||||
float parsein;
|
||||
error = gs_string_to_float(string, &parsein);
|
||||
if (error == GS_OK) {
|
||||
*((float *) value) = parsein;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_PARAM_DOUBLE:
|
||||
{
|
||||
double parsein;
|
||||
error = gs_string_to_double(string, &parsein);
|
||||
if (error == GS_OK) {
|
||||
*((double *) value) = parsein;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_PARAM_STRING:
|
||||
{
|
||||
const size_t ilen = strnlen(string, GS_PARAM_SIZE(param) + 1); // 0 terminator
|
||||
if (ilen <= GS_PARAM_SIZE(param)) {
|
||||
memset(value, 0, GS_PARAM_SIZE(param));
|
||||
memcpy(value, string, ilen);
|
||||
} else {
|
||||
error = GS_ERROR_OVERFLOW;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_PARAM_DATA:
|
||||
{
|
||||
const size_t MAX_LEN = (GS_PARAM_SIZE(param) * 2);
|
||||
const size_t ilen = strnlen(string, MAX_LEN + 1);
|
||||
if (ilen > MAX_LEN) {
|
||||
error = GS_ERROR_OVERFLOW;
|
||||
} else if ((ilen % 2) == 0) {
|
||||
|
||||
// validate data first - not to end up with invalid/strange data
|
||||
for (unsigned int i = 0; i < ilen; ++i) {
|
||||
if (to_int(string[i]) < 0) {
|
||||
error = GS_ERROR_DATA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (error == GS_OK) {
|
||||
uint8_t * out = (uint8_t *) value;
|
||||
memset(out, 0, GS_PARAM_SIZE(param));
|
||||
for (unsigned int i = 0; i < ilen; i += 2, ++out) {
|
||||
*out = (16 * to_int(string[i])) + to_int(string[i+1]);
|
||||
}
|
||||
error = GS_OK;
|
||||
}
|
||||
} else {
|
||||
error = GS_ERROR_DATA;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_param_list_single_to_stream(gs_param_table_instance_t * tinst, const gs_param_table_row_t * param,
|
||||
bool list_data, uint32_t flags, FILE * out)
|
||||
{
|
||||
if (param == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
gs_error_t error = GS_OK;
|
||||
const uint16_t addr = GS_PARAM_ADDR(param);
|
||||
|
||||
fprintf(out, " 0x%04X %-16.14"GS_PGM_FMT_STR, addr, param->name); // ensure missing NUL termination doesn't cause problems.
|
||||
|
||||
if (list_data) {
|
||||
const gs_param_type_t param_type = GS_PARAM_TYPE(param);
|
||||
const unsigned int param_array_size = GS_PARAM_ARRAY_SIZE(param);
|
||||
const unsigned int param_size = GS_PARAM_SIZE(param);
|
||||
uint8_t value[param_size];
|
||||
char buf[100];
|
||||
for (unsigned int j = 0; (j < param_array_size) && (error == GS_OK); j++) {
|
||||
error = gs_param_get(tinst, addr + (param_size * j), param_type, value, param_size, 0);
|
||||
if (error == GS_OK) {
|
||||
gs_param_to_string2(param, value, (j == 0) ? 1 : 0, flags, buf, sizeof(buf), 0, NULL);
|
||||
fprintf(out, "%s ", buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(out, "\r\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_param_list_to_stream(gs_param_table_instance_t * tinst, bool list_data, uint32_t flags, FILE * out)
|
||||
{
|
||||
GS_CHECK_HANDLE(tinst != NULL);
|
||||
|
||||
gs_error_t error = GS_OK;
|
||||
for (unsigned int i = 0; (i < tinst->row_count) && (error == GS_OK); ++i) {
|
||||
error = gs_param_list_single_to_stream(tinst, &tinst->rows[i], list_data, flags, out);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_param_parse_name_and_array_index(const char * inp, char * name, size_t size_name, uint8_t * return_index, bool * return_is_array)
|
||||
{
|
||||
if (inp == NULL) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
uint8_t a_index;
|
||||
size_t name_len;
|
||||
gs_error_t error;
|
||||
bool is_array;
|
||||
const char * pai = strchr(inp, '['); // look for array index
|
||||
if (pai) {
|
||||
name_len = pai - inp;
|
||||
char tmp[20];
|
||||
GS_STRNCPY(tmp, pai+1);
|
||||
char * endp = strchr(tmp, ']');
|
||||
if (endp) {
|
||||
*endp = 0;
|
||||
}
|
||||
error = gs_string_to_uint8(tmp, &a_index);
|
||||
is_array = true;
|
||||
} else {
|
||||
error = GS_OK;
|
||||
name_len = strlen(inp);
|
||||
is_array = false;
|
||||
a_index = 0;
|
||||
}
|
||||
|
||||
if (error == GS_OK) {
|
||||
if (name_len >= size_name) {
|
||||
error = GS_ERROR_OVERFLOW;
|
||||
} else {
|
||||
strncpy(name, inp, name_len);
|
||||
name[name_len] = 0;
|
||||
|
||||
// remove trailing white-space
|
||||
if (name_len) {
|
||||
for (int i = name_len-1; i >= 0; --i) {
|
||||
if (name[i] && isspace((int)name[i])) {
|
||||
name[i] = 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (return_index) {
|
||||
*return_index = (is_array) ? a_index : 0;
|
||||
}
|
||||
if (return_is_array) {
|
||||
*return_is_array = is_array;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
393
gomspace/libparam_client/src/table.c
Normal file
393
gomspace/libparam_client/src/table.c
Normal file
@ -0,0 +1,393 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#define GS_PARAM_INTERNAL_USE 1
|
||||
|
||||
#include <gs/param/internal/types.h>
|
||||
#include <gs/param/internal/table.h>
|
||||
#include <gs/param/serialize.h>
|
||||
#include <gs/util/check.h>
|
||||
#include <gs/util/byteorder.h>
|
||||
#include <gs/util/endian.h>
|
||||
#include <gs/util/fletcher.h>
|
||||
|
||||
#include <alloca.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void _copy_data(gs_param_type_t type, void * dst, const void * src, size_t size)
|
||||
{
|
||||
#if (GS_PARAM_ATOMIC_ACCESS)
|
||||
switch (type) {
|
||||
case GS_PARAM_UINT8:
|
||||
case GS_PARAM_INT8:
|
||||
case PARAM_X8:
|
||||
case GS_PARAM_STRING:
|
||||
case GS_PARAM_DATA:
|
||||
case GS_PARAM_BOOL:
|
||||
break;
|
||||
|
||||
case GS_PARAM_UINT16:
|
||||
case GS_PARAM_INT16:
|
||||
case PARAM_X16:
|
||||
if (!((intptr_t)src & 1) && !((intptr_t)dst & 1)) {
|
||||
// (u)int16 aligned correctly
|
||||
const uint16_t * s = src;
|
||||
uint16_t * d = dst;
|
||||
const unsigned int count = (size / sizeof(*d));
|
||||
for (unsigned int i = 0; i < count; ++i, ++d, ++s) {
|
||||
*d = *s;
|
||||
}
|
||||
//printf("%s:int16\r\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_PARAM_UINT32:
|
||||
case GS_PARAM_INT32:
|
||||
case PARAM_X32:
|
||||
case GS_PARAM_FLOAT:
|
||||
if (!((intptr_t)src & 3) && !((intptr_t)dst & 3)) {
|
||||
// (u)int32 aligned correctly
|
||||
const uint32_t * s = src;
|
||||
uint32_t * d = dst;
|
||||
const unsigned int count = (size / sizeof(*d));
|
||||
for (unsigned int i = 0; i < count; ++i, ++d, ++s) {
|
||||
*d = *s;
|
||||
}
|
||||
//printf("%s:int32\r\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case GS_PARAM_UINT64:
|
||||
case GS_PARAM_INT64:
|
||||
case PARAM_X64:
|
||||
case GS_PARAM_DOUBLE:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
// fallback - do byte copy
|
||||
memcpy(dst, src, size);
|
||||
}
|
||||
|
||||
gs_error_t gs_param_table_lock(gs_param_table_instance_t * tinst)
|
||||
{
|
||||
if (tinst->lock) {
|
||||
return gs_mutex_lock(tinst->lock);
|
||||
}
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_param_table_unlock(gs_param_table_instance_t * tinst)
|
||||
{
|
||||
if (tinst->lock) {
|
||||
return gs_mutex_unlock(tinst->lock);
|
||||
}
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static gs_error_t gs_param_table_lock_free(gs_param_table_instance_t * tinst)
|
||||
{
|
||||
if (tinst && tinst->lock) {
|
||||
gs_mutex_destroy(tinst->lock);
|
||||
tinst->lock = NULL;
|
||||
}
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_param_table_free(gs_param_table_instance_t * tinst)
|
||||
{
|
||||
if (tinst) {
|
||||
if (tinst->flags & GS_PARAM_TABLE_F_ALLOC_MEMORY) {
|
||||
free(tinst->memory);
|
||||
}
|
||||
if (tinst->flags & GS_PARAM_TABLE_F_ALLOC_ROWS) {
|
||||
free((void*)tinst->rows);
|
||||
}
|
||||
if (tinst->lock) {
|
||||
gs_param_table_lock_free(tinst);
|
||||
}
|
||||
memset(tinst, 0, sizeof(*tinst));
|
||||
}
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static void checksum_update(gs_param_table_instance_t * tinst)
|
||||
{
|
||||
const uint16_t no_swap = gs_fletcher16(tinst->rows, (sizeof(*tinst->rows) * tinst->row_count));
|
||||
|
||||
// fletcher16 with swapped fields > 1 byte
|
||||
gs_fletcher16_t f16;
|
||||
gs_fletcher16_init(&f16);
|
||||
for (unsigned int i = 0; i < tinst->row_count; ++i) {
|
||||
gs_param_table_row_t row = tinst->rows[i];
|
||||
row.addr = gs_bswap_16(row.addr);
|
||||
gs_fletcher16_update(&f16, &row, sizeof(row));
|
||||
}
|
||||
const uint16_t swap = gs_fletcher16_finalize(&f16);
|
||||
|
||||
if (gs_endian_big()) {
|
||||
tinst->checksum_be = no_swap;
|
||||
tinst->checksum_le = swap;
|
||||
} else {
|
||||
tinst->checksum_be = swap;
|
||||
tinst->checksum_le = no_swap;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t gs_param_table_checksum_be(gs_param_table_instance_t * tinst)
|
||||
{
|
||||
if (tinst && tinst->rows && tinst->row_count) {
|
||||
if (tinst->checksum_be == 0) {
|
||||
checksum_update(tinst);
|
||||
}
|
||||
return tinst->checksum_be;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t gs_param_table_checksum_le(gs_param_table_instance_t * tinst)
|
||||
{
|
||||
if (tinst && tinst->rows && tinst->row_count) {
|
||||
if (tinst->checksum_le == 0) {
|
||||
checksum_update(tinst);
|
||||
}
|
||||
return tinst->checksum_le;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t gs_param_table_checksum(gs_param_table_instance_t * tinst)
|
||||
{
|
||||
if (gs_endian_big()) {
|
||||
return gs_param_table_checksum_be(tinst);
|
||||
} else {
|
||||
return gs_param_table_checksum_le(tinst);
|
||||
}
|
||||
}
|
||||
|
||||
gs_error_t gs_param_get(gs_param_table_instance_t * tinst, uint16_t addr, gs_param_type_t type, void * value, size_t value_size, uint32_t flags)
|
||||
{
|
||||
if (tinst == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
if ((addr + value_size) > tinst->memory_size) {
|
||||
return GS_ERROR_RANGE;
|
||||
}
|
||||
|
||||
gs_error_t error = GS_ERROR_NOT_SUPPORTED;
|
||||
if (tinst->function_interface.get) {
|
||||
error = (tinst->function_interface.get)(tinst->function_interface.context, tinst, addr, type, value, value_size, flags);
|
||||
} else if (tinst->memory) {
|
||||
gs_param_table_lock(tinst);
|
||||
_copy_data(type, value, ((uint8_t*)(tinst->memory)) + addr, value_size);
|
||||
gs_param_table_unlock(tinst);
|
||||
error = GS_OK;
|
||||
}
|
||||
|
||||
if ((error == GS_OK) && (flags & GS_PARAM_SF_TO_BIG_ENDIAN) && !gs_endian_big()) {
|
||||
gs_param_htobe(type, value);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_param_set(gs_param_table_instance_t * tinst, uint16_t addr, gs_param_type_t type, const void * value, size_t value_size, uint32_t flags)
|
||||
{
|
||||
if (tinst == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
if ((addr + value_size) > tinst->memory_size) {
|
||||
return GS_ERROR_RANGE;
|
||||
}
|
||||
|
||||
if ((flags & GS_PARAM_SF_FROM_BIG_ENDIAN) && !gs_endian_big()) {
|
||||
void * tmp = alloca(value_size); // this must be aligned
|
||||
memcpy(tmp, value, value_size);
|
||||
value = tmp;
|
||||
gs_param_betoh(type, tmp);
|
||||
}
|
||||
|
||||
gs_error_t error = GS_ERROR_NOT_SUPPORTED;
|
||||
if (tinst->function_interface.set) {
|
||||
error = (tinst->function_interface.set)(tinst->function_interface.context, tinst, addr, type, value, value_size, flags);
|
||||
} else if (tinst->memory) {
|
||||
gs_param_table_lock(tinst);
|
||||
_copy_data(type, ((uint8_t*)(tinst->memory)) + addr, value, value_size);
|
||||
gs_param_table_unlock(tinst);
|
||||
|
||||
if (tinst->auto_persist.set && (flags & GS_PARAM_F_AUTO_PERSIST)) {
|
||||
(tinst->auto_persist.set)(tinst, addr, type, value, value_size, flags);
|
||||
}
|
||||
error = GS_OK;
|
||||
}
|
||||
|
||||
// Callbacks
|
||||
if ((error == GS_OK) && tinst->callback && ((flags & GS_PARAM_F_NO_CALLBACK) == 0)) {
|
||||
(tinst->callback)(addr, tinst);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
uint8_t gs_param_type_size(gs_param_type_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case GS_PARAM_UINT8:
|
||||
case GS_PARAM_INT8:
|
||||
case PARAM_X8:
|
||||
case GS_PARAM_STRING:
|
||||
case GS_PARAM_DATA:
|
||||
return sizeof(int8_t);
|
||||
case GS_PARAM_INT16:
|
||||
case GS_PARAM_UINT16:
|
||||
case PARAM_X16:
|
||||
return sizeof(int16_t);
|
||||
case GS_PARAM_INT32:
|
||||
case GS_PARAM_UINT32:
|
||||
case PARAM_X32:
|
||||
return sizeof(int32_t);
|
||||
case GS_PARAM_INT64:
|
||||
case GS_PARAM_UINT64:
|
||||
case PARAM_X64:
|
||||
return sizeof(int64_t);
|
||||
case GS_PARAM_DOUBLE:
|
||||
return sizeof(double);
|
||||
case GS_PARAM_FLOAT:
|
||||
return sizeof(float);
|
||||
case GS_PARAM_BOOL:
|
||||
return sizeof(bool);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void * gs_param_table_get_memory(gs_param_table_instance_t * tinst, size_t * return_size)
|
||||
{
|
||||
if (tinst && tinst->memory) {
|
||||
if (return_size) {
|
||||
*return_size = tinst->memory_size;
|
||||
}
|
||||
return tinst->memory;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const gs_param_table_row_t * gs_param_table_get_rows(gs_param_table_instance_t * tinst, size_t * return_count)
|
||||
{
|
||||
if (tinst && tinst->rows && tinst->row_count) {
|
||||
if (return_count) {
|
||||
*return_count = tinst->row_count;
|
||||
}
|
||||
return tinst->rows;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t gs_param_table_instance_size(void)
|
||||
{
|
||||
return sizeof(gs_param_table_instance_t);
|
||||
}
|
||||
|
||||
gs_param_table_instance_t * gs_param_table_instance_clear(void * var, size_t var_size)
|
||||
{
|
||||
gs_param_table_instance_t * tinst = NULL;
|
||||
if (var && (var_size >= sizeof(*tinst))) {
|
||||
tinst = (gs_param_table_instance_t *) var;
|
||||
memset(tinst, 0, sizeof(*tinst));
|
||||
}
|
||||
return tinst;
|
||||
}
|
||||
|
||||
gs_param_table_instance_t * gs_param_table_instance_alloc(void)
|
||||
{
|
||||
return calloc(1, sizeof(gs_param_table_instance_t));
|
||||
}
|
||||
|
||||
gs_error_t gs_param_get_string(gs_param_table_instance_t * tinst, uint16_t addr, char * buf, size_t buf_size, uint32_t flags)
|
||||
{
|
||||
if (tinst == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
if (buf == NULL) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
const gs_param_table_row_t * param = gs_param_row_by_address(addr, tinst->rows, tinst->row_count);
|
||||
if (param == NULL) {
|
||||
return GS_ERROR_RANGE;
|
||||
}
|
||||
|
||||
if (buf_size <= param->size) {
|
||||
return GS_ERROR_OVERFLOW;
|
||||
}
|
||||
|
||||
gs_error_t error = gs_param_get(tinst, addr, param->type, buf, param->size, flags);
|
||||
buf[param->size] = 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_param_set_string(gs_param_table_instance_t * tinst, uint16_t addr, const char * value, uint32_t flags)
|
||||
{
|
||||
if (tinst == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
if (value == NULL) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
const gs_param_table_row_t * param = gs_param_row_by_address(addr, tinst->rows, tinst->row_count);
|
||||
if (param == NULL) {
|
||||
return GS_ERROR_RANGE;
|
||||
}
|
||||
|
||||
const size_t len = strlen(value) + 1;
|
||||
if (len > GS_PARAM_SIZE(param)) {
|
||||
return GS_ERROR_OVERFLOW;
|
||||
}
|
||||
|
||||
return gs_param_set(tinst, addr, param->type, value, len, flags); // flags have full control
|
||||
}
|
||||
|
||||
gs_error_t gs_param_get_data(gs_param_table_instance_t * tinst, uint16_t addr, void * buf, size_t buf_size, uint32_t flags)
|
||||
{
|
||||
if (tinst == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
if (buf == NULL) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
const gs_param_table_row_t * param = gs_param_row_by_address(addr, tinst->rows, tinst->row_count);
|
||||
if (param == NULL) {
|
||||
return GS_ERROR_RANGE;
|
||||
}
|
||||
|
||||
if (buf_size < param->size) {
|
||||
return GS_ERROR_OVERFLOW;
|
||||
}
|
||||
|
||||
return gs_param_get(tinst, addr, param->type, buf, param->size, flags);
|
||||
}
|
||||
|
||||
gs_error_t gs_param_set_data(gs_param_table_instance_t * tinst, uint16_t addr, const void * value, size_t value_size, uint32_t flags)
|
||||
{
|
||||
if (tinst == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
if (value == NULL) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
const gs_param_table_row_t * param = gs_param_row_by_address(addr, tinst->rows, tinst->row_count);
|
||||
if (param == NULL) {
|
||||
return GS_ERROR_RANGE;
|
||||
}
|
||||
|
||||
if (value_size > GS_PARAM_SIZE(param)) {
|
||||
return GS_ERROR_OVERFLOW;
|
||||
}
|
||||
|
||||
return gs_param_set(tinst, addr, param->type, value, value_size, flags); // flags have full control
|
||||
}
|
Reference in New Issue
Block a user