save failed integration state

This commit is contained in:
Jakob Meier 2020-11-26 10:24:23 +01:00
parent 473aa805c0
commit 6bd55e7c22
194 changed files with 45450 additions and 2 deletions

View File

@ -5,7 +5,15 @@ CSRC += $(wildcard $(CURRENTPATH)/libcsp/src/rtable/csp_rtable_cidr.c)
CSRC += $(wildcard $(CURRENTPATH)/libcsp/src/crypto/*.c)
CSRC += $(wildcard $(CURRENTPATH)/libcsp/src/arch/posix/*.c)
CSRC += $(wildcard $(CURRENTPATH)/libcsp/src/transport/*.c)
CSRC += $(wildcard $(CURRENTPATH)/p60-dock_client/src/*.c)
CSRC += $(wildcard $(CURRENTPATH)/libparam_client/src/*.c)
CSRC += $(wildcard $(CURRENTPATH)/libparam_client/src/rparam/*.c)
INCLUDES += $(CURRENTPATH)/libcsp/include
INCLUDES += $(CURRENTPATH)/libcsp/include/csp/crypto
INCLUDES += $(CURRENTPATH)/libcsp
INCLUDES += $(CURRENTPATH)/p60-dock_client/include/gs/p60-dock/param
INCLUDES += $(CURRENTPATH)/libparam_client/include
INCLUDES += $(CURRENTPATH)/libparam_client/include/deprecated
INCLUDES += $(CURRENTPATH)/libp60_client/include
INCLUDES += $(CURRENTPATH)/libutil/include

View File

@ -0,0 +1,48 @@
#ifndef _P60_H_
#define _P60_H_
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
#define P60_PORT_RPARAM 7
#define P60_PORT_GNDWDT_RESET 9
#define P60_PORT_CMDCONTROL 10
#define P60_PORT_GSSB_SERVICE 15
#define P60_PORT_GSCRIPT 22
/** FRAM MEMORY MAP */
#define P60_FRAM_BOARD 0x0000
/** FRAM FILENAMES */
#define P60_FNO_BOARD 0
#define P60_FNO_BOARD_DFL 4
#define P60_FRAM_WP_BEGIN (0x1000)
#define P60_FRAM_WP_END (0x1C00 - 1)
/** GND WD FRAM ADDR **/
#define P60_FRAM_GNDWDT 0x1F00
/** PARAM INDEX MAP */
#define P60_BOARD_PARAM 0
#define DEVICE_FM24CL64B 0
typedef enum {
UNKNOWN_RST = 0,
GND_WDT_RST,
I2C_WDT_RST,
CAN_WDT_RST,
EXT_HARD_RST,
EXT_SOFT_RST,
} p60_reset_cause_t;
extern const uint8_t board_fallback_type;
extern const uint8_t csp_fallback_addr;
extern const uint8_t board_rs422_mode;
extern void module_init_early(void);
extern void module_init(void);
extern void wdt_gnd_clear(void);
extern uint16_t command_control(uint8_t *packet, uint16_t length);
extern void module_task(void * pvParameters);
#endif /* _P60_H_ */

View File

@ -0,0 +1,35 @@
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
* NanoCom firmware
*
*/
#ifndef P60_PARAM_H_
#define P60_PARAM_H_
#include <stdint.h>
#include <param/param_types.h>
/**
* Define memory space
*/
#define P60_BOARD_UID 0x00
#define P60_BOARD_TYPE 0x10
#define P60_BOARD_REV 0x11
#define P60_BOARD_CSP_ADDR 0x12
#define P60_BOARD_I2C_ADDR 0x13
#define P60_BOARD_I2C_SPEED_KHZ 0x14
#define P60_BOARD_CAN_SPEED_KHZ 0x16
#define P60_BOARD_KISS_ENABLE 0x18
#define P60_BOARD_RS422_MODE 0x19
#define P60_BOARD_RS422_SPEED_KHZ 0x1C
#define P60_BOARD_RTABLE_STR 0x20 //! This one is 0x60 = 96 bytes
#define P60_BOARD_RTABLE_STR_SIZE 0x60
/** Define the memory size */
#define P60_BOARD_PARAM_SIZE 0x80
extern const param_table_t p60_config[];
extern const int p60_config_count;
#endif /* P60_PARAM_H_ */

View File

@ -0,0 +1,62 @@
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
* NanoPower firmware
*
*/
#ifndef POWER_IF_H_
#define POWER_IF_H_
#include <stdint.h>
#define POWER_IF_SET 1
#define POWER_IF_GET 2
#define POWER_IF_LIST 3
#define POWER_IF_STATUS_OK 0
#define POWER_IF_STATUS_ERROR 1
#define POWER_IF_NAME_LEN 8
typedef struct __attribute__((packed)) {
uint8_t ch_idx;
uint8_t mode;
uint16_t on_cnt;
uint16_t off_cnt;
uint16_t cur_lu_lim;
uint16_t cur_lim;
uint16_t voltage;
int16_t current;
uint16_t latchup;
char name[POWER_IF_NAME_LEN];
} power_if_ch_status_t;
typedef struct __attribute__((packed)) {
uint8_t ch_idx;
uint8_t mode;
char name[8];
} power_if_list_t;
typedef struct __attribute__((packed)) {
uint8_t cmd;
uint8_t status;
power_if_ch_status_t ch_status;
} power_if_cmd_request_t;
typedef struct __attribute__((packed)) {
uint8_t cmd;
uint8_t status;
power_if_ch_status_t ch_status;
} power_if_cmd_response_t;
typedef struct __attribute__((packed)) {
uint8_t cmd;
uint8_t status;
uint8_t count;
power_if_list_t list[16];
} power_if_cmd_list_response_t;
int p60_power_if_cmd(uint8_t node, uint8_t port, uint32_t timeout, uint8_t cmd, void * ch_status_p);
uint8_t p60_power_if_get_ch_idx(char * ch_name, uint8_t * ch_no, uint8_t ch_no_max);
#endif /* POWER_IF_H_ */

View File

@ -0,0 +1,147 @@
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include <csp/csp.h>
#include <util/console.h>
#include <power_if.h>
static uint8_t power_if_port = 10;
static uint32_t power_if_timeout = 5000;
static int cmd_power_if_port(struct command_context * ctx) {
if (ctx->argc < 2) {
printf("Current port is %d\n", power_if_port);
return CMD_ERROR_NONE;
}
power_if_port = atoi(ctx->argv[1]);
return CMD_ERROR_NONE;
}
static int cmd_power_if_timeout(struct command_context *ctx) {
if (ctx->argc < 2) {
printf("Current timeout is %"PRIu32"\n", power_if_timeout);
return CMD_ERROR_NONE;
}
if (sscanf(command_args(ctx), "%"SCNu32, &power_if_timeout) != 1)
return CMD_ERROR_SYNTAX;
if (power_if_timeout > 30000) {
printf("Timeout set to high, limited to 30000 ms\n");
power_if_timeout = 30000;
}
printf("Timeout set to %"PRIu32"\n", power_if_timeout);
return CMD_ERROR_NONE;
}
static int cmd_power_if_set_get(struct command_context * ctx) {
power_if_ch_status_t ch_status;
if (ctx->argc < 3) {
return CMD_ERROR_SYNTAX;
}
uint8_t node = atoi(ctx->argv[1]);
memset(&ch_status, 0, sizeof(ch_status));
strncpy(ch_status.name, ctx->argv[2], 7);
ch_status.name[7] = 0;
char * cmd = ctx->argv[0];
if (!strcmp(cmd, "status") && (ctx->argc == 3)) {
if (!p60_power_if_cmd(node, power_if_port, power_if_timeout, POWER_IF_GET, &ch_status)) {
return CMD_ERROR_FAIL;
}
} else {
if (!strcmp(cmd, "on")) {
ch_status.mode = 1;
} else if (!strcmp(cmd, "off")) {
ch_status.mode = 0;
}
ch_status.on_cnt = (ctx->argc > 3) ? atoi(ctx->argv[3]) : 0;
ch_status.off_cnt = (ctx->argc > 4) ? atoi(ctx->argv[4]) : 0;
if (!p60_power_if_cmd(node, power_if_port, power_if_timeout, POWER_IF_SET, &ch_status)) {
return CMD_ERROR_FAIL;
}
}
printf("Node %u, Output channel '%s' (%u) is %s\r\n", node, ch_status.name, ch_status.ch_idx, (ch_status.mode ? "ON": "OFF"));
printf(" ch_idx: %u\r\n", ch_status.ch_idx);
printf(" mode: %u\r\n", ch_status.mode);
printf(" on_cnt: %u\r\n", ch_status.on_cnt);
printf(" off_cnt: %u\r\n", ch_status.off_cnt);
printf(" cur_lu_lim: %u\r\n", ch_status.cur_lu_lim);
printf(" cur_lim: %u\r\n", ch_status.cur_lim);
printf(" voltage: %u\r\n", ch_status.voltage);
printf(" current: %d\r\n", ch_status.current);
printf(" latchup: %u\r\n", ch_status.latchup);
return CMD_ERROR_NONE;
}
static int cmd_power_if_list(struct command_context * ctx) {
if (ctx->argc < 2) {
return CMD_ERROR_SYNTAX;
}
uint8_t node = atoi(ctx->argv[1]);
power_if_cmd_list_response_t ch_list;
if (!p60_power_if_cmd(node, power_if_port, power_if_timeout, POWER_IF_LIST, &ch_list)) {
return CMD_ERROR_FAIL;
}
printf("ch name status\r\n");
for (uint8_t ch = 0; ch < ch_list.count; ch++) {
printf("%2u %-8s %s\r\n", ch_list.list[ch].ch_idx, ch_list.list[ch].name, ch_list.list[ch].mode ? "ON": "OFF");
}
return CMD_ERROR_NONE;
}
command_t power_if_commands[] = {
{
.name = "port",
.help = "Set power interface port (default is 10)",
.usage = "<port>",
.handler = cmd_power_if_port,
},
{
.name = "timeout",
.help = "Set power interface timeout in milliseconds",
.usage = "<timeout>",
.handler = cmd_power_if_timeout,
},
{
.name = "status",
.help = "Get power channel status",
.usage = "<node> <channel>",
.handler = cmd_power_if_set_get,
},
{
.name = "on",
.help = "Turn power channel on",
.usage = "<node> <channel> [<on_cnt> <off_cnt>]",
.handler = cmd_power_if_set_get,
},
{
.name = "off",
.help = "Turn power channel off",
.usage = "<node> <channel> off [<on_cnt> <off_cnt>]",
.handler = cmd_power_if_set_get,
},
{
.name = "list",
.help = "Get list power channels",
.usage = "<node>",
.handler = cmd_power_if_list,
},
};
command_t __root_command power_if_root_command[] = {
{
.name = "power",
.help = "client: NanoPower P60",
.chain = INIT_CHAIN(power_if_commands),
}
};
void cmd_power_if_setup(void) {
command_register(power_if_root_command);
}

View File

@ -0,0 +1,33 @@
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
* NanoCom firmware
*
*/
#include <stddef.h>
#include <stdlib.h>
#include <csp/csp.h>
#include <csp/csp_endian.h>
#include <p60.h>
#include <p60_board.h>
/**
* Setup info about board configuration parameters
*/
const param_table_t p60_config[] = {
{.name = "uid", .addr = P60_BOARD_UID, .type = PARAM_STRING, .size = 16},
{.name = "type", .addr = P60_BOARD_TYPE, .type = PARAM_UINT8, .size = sizeof(uint8_t)},
{.name = "rev", .addr = P60_BOARD_REV, .type = PARAM_UINT8, .size = sizeof(uint8_t)},
{.name = "csp_addr", .addr = P60_BOARD_CSP_ADDR, .type = PARAM_UINT8, .size = sizeof(uint8_t)},
{.name = "i2c_addr", .addr = P60_BOARD_I2C_ADDR, .type = PARAM_UINT8, .size = sizeof(uint8_t)},
{.name = "i2c_speed", .addr = P60_BOARD_I2C_SPEED_KHZ, .type = PARAM_UINT16, .size = sizeof(uint16_t)},
{.name = "can_speed", .addr = P60_BOARD_CAN_SPEED_KHZ, .type = PARAM_UINT16, .size = sizeof(uint16_t)},
{.name = "kiss_en", .addr = P60_BOARD_KISS_ENABLE, .type = PARAM_UINT8, .size = sizeof(uint8_t)},
{.name = "rs422_mode", .addr = P60_BOARD_RS422_MODE, .type = PARAM_UINT8, .size = sizeof(uint8_t)},
{.name = "rs422_speed", .addr = P60_BOARD_RS422_SPEED_KHZ, .type = PARAM_UINT32, .size = sizeof(uint32_t)},
{.name = "csp_rtable", .addr = P60_BOARD_RTABLE_STR, .type = PARAM_STRING, .size = P60_BOARD_RTABLE_STR_SIZE},
};
const int p60_config_count = sizeof(p60_config) / sizeof(p60_config[0]);

View File

@ -0,0 +1,91 @@
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
* NanoPower firmware
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <ctype.h>
#include <csp/csp.h>
#include <csp/csp_endian.h>
#include <power_if.h>
uint8_t p60_power_if_get_ch_idx(char * ch_name, uint8_t * ch_no, uint8_t ch_no_max) {
uint8_t result = 1;
uint8_t len = strlen(ch_name);
for (int i = 0; i < len; i++) {
if (!isdigit(ch_name[i])) {
result = 0;
break;
}
}
if (result) {
*ch_no = atoi(ch_name);
if (*ch_no >= ch_no_max) {
result = 0;
}
}
return result;
}
int p60_power_if_cmd(uint8_t node, uint8_t port, uint32_t timeout, uint8_t cmd, void * ch_status_p) {
power_if_cmd_request_t req;
if ((cmd == POWER_IF_SET) || (cmd == POWER_IF_GET)) {
power_if_cmd_response_t resp;
power_if_ch_status_t * ch_status = (power_if_ch_status_t *)ch_status_p;
if (cmd == POWER_IF_SET) {
req.cmd = POWER_IF_SET;
req.ch_status.mode = ch_status->mode;
req.ch_status.on_cnt = csp_hton16(ch_status->on_cnt);
req.ch_status.off_cnt = csp_hton16(ch_status->off_cnt);
} else {
req.cmd = POWER_IF_GET;
req.ch_status.mode = 0;
req.ch_status.on_cnt = 0;
req.ch_status.off_cnt = 0;
}
ch_status->name[POWER_IF_NAME_LEN - 1] = 0;
strcpy(req.ch_status.name, ch_status->name);
if (csp_transaction(CSP_PRIO_HIGH, node, port, timeout, &req, sizeof(power_if_cmd_request_t), &resp, sizeof(power_if_cmd_response_t))) {
if ((resp.cmd == POWER_IF_SET) || (resp.cmd == POWER_IF_GET)) {
if (resp.status == POWER_IF_STATUS_OK) {
ch_status->ch_idx = resp.ch_status.ch_idx;
ch_status->mode = resp.ch_status.mode;
ch_status->on_cnt = csp_ntoh16(resp.ch_status.on_cnt);
ch_status->off_cnt = csp_ntoh16(resp.ch_status.off_cnt);
ch_status->cur_lu_lim = csp_ntoh16(resp.ch_status.cur_lu_lim);
ch_status->cur_lim = csp_ntoh16(resp.ch_status.cur_lim);
ch_status->voltage = csp_ntoh16(resp.ch_status.voltage);
ch_status->current = csp_ntoh16(resp.ch_status.current);
ch_status->latchup = csp_ntoh16(resp.ch_status.latchup);
strncpy(ch_status->name, resp.ch_status.name, POWER_IF_NAME_LEN - 1);
/* Ensure zero termination*/
ch_status->name[POWER_IF_NAME_LEN - 1] = 0;
return 1;
}
}
}
} else if (cmd == POWER_IF_LIST) {
power_if_cmd_list_response_t * ch_list = (power_if_cmd_list_response_t *)ch_status_p;
req.cmd = POWER_IF_LIST;
req.ch_status.mode = 0;
req.ch_status.on_cnt = 0;
req.ch_status.off_cnt = 0;
req.ch_status.name[0] = 0;
if (csp_transaction(CSP_PRIO_HIGH, node, port, timeout, &req, sizeof(power_if_cmd_request_t), ch_list, sizeof(power_if_cmd_list_response_t))) {
if ((ch_list->cmd == POWER_IF_LIST) && (ch_list->status == POWER_IF_STATUS_OK)) {
return 1;
}
}
}
return 0;
}

View File

@ -0,0 +1,28 @@
#!/usr/bin/env python
# encoding: utf-8
# Copyright (c) 2013-2018 GomSpace A/S. All rights reserved.
APPNAME = 'p60_client'
def options(ctx):
gr = ctx.add_option_group('NanoPower-P60 library client options')
gr.add_option('--disable-libp60-cmd', action='store_true', help='Disable client cmd code for NanoPower-P60 library')
def configure(ctx):
ctx.env.append_unique('FILES_LIBP60_CLIENT', ['src/*.c'])
if not ctx.options.disable_libp60_cmd:
ctx.env.append_unique('FILES_LIBP60_CLIENT', ['src/cmd/*.c'])
def build(ctx):
public_include = APPNAME + '_h'
ctx(export_includes=['include'], name=public_include)
ctx.objects(source=ctx.path.ant_glob(ctx.env.FILES_LIBP60_CLIENT),
target=APPNAME,
use=['csp', 'gosh', 'param', 'param_client', 'util', public_include])
def gs_dist(ctx):
ctx.add_default_files(source_module=True)

View File

@ -0,0 +1,15 @@
#ifndef PARAM_PARAM_CLIENT_H
#define PARAM_PARAM_CLIENT_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Legacy/deprecated include of header files for libparam client.
*/
#include <param/param_serializer.h>
#include <param/param_string.h>
#include <param/param_lock.h>
#include <param/rparam_client.h>
#endif

View File

@ -0,0 +1,27 @@
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
* GomSpace Parameter System.
*/
#ifndef PARAM_PARAM_LOCK_H_
#define PARAM_PARAM_LOCK_H_
#include <gs/param/table.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline void param_lock(param_index_t * mem)
{
gs_param_table_lock((gs_param_table_instance_t *) mem);
}
static inline void param_unlock(param_index_t * mem)
{
gs_param_table_unlock((gs_param_table_instance_t *) mem);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,55 @@
#ifndef PARAM_PARAM_SERIALIZER_H
#define PARAM_PARAM_SERIALIZER_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
* GomSpace Parameter System
*/
#include <param/param_types.h>
#include <gs/param/serialize.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef gs_param_serialize_flags_t param_serializer_flags;
static inline int param_betoh(param_type_t type, void * item)
{
return gs_param_betoh(type, item);
}
static inline int param_htobe(param_type_t type, void * item)
{
return gs_param_htobe(type, item);
}
static inline int param_serialize_full_table(param_index_t * mem, unsigned int *start, uint8_t * buf, unsigned int maxbuflen, param_serializer_flags flags)
{
unsigned int buf_pos = 0;
gs_error_t error = gs_param_serialize_full_table((gs_param_table_instance_t *) mem, start, flags, buf, maxbuflen, &buf_pos);
return (error == GS_OK) ? (int) buf_pos : -1;
}
static inline gs_error_t param_serialize_item(const param_table_t * param, uint16_t addr, uint8_t * buf, uint16_t * pos, unsigned int maxlen, void * item, param_serializer_flags flags)
{
unsigned int tmp_pos = *pos;
gs_error_t error = gs_param_serialize_item((const gs_param_table_row_t*) param, addr, item, flags, buf, maxlen, &tmp_pos);
*pos = tmp_pos;
return (error == GS_OK) ? 0 : -1;
}
static inline gs_error_t param_deserialize(param_index_t * mem, uint8_t * buf, int len, param_serializer_flags flags)
{
return (gs_param_deserialize((gs_param_table_instance_t *) mem, buf, len, flags) == GS_OK) ? 0 : -1;
}
static inline gs_error_t param_deserialize_item(const param_table_t * param, uint16_t addr, param_index_t * mem, void * item, param_serializer_flags flags)
{
return (gs_param_deserialize_item((gs_param_table_instance_t *) mem, (const gs_param_table_row_t*)param, addr, item, flags) == GS_OK) ? 0 : -1;
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,74 @@
#ifndef PARAM_PARAM_STRING_H
#define PARAM_PARAM_STRING_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
* GomSpace Parameter System
*/
#include <param/param_types.h>
#include <gs/param/table.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef union {
const param_table_t * row3;
const gs_param_table_row_t * row4;
} gs_param_row_align_t;
static inline const param_table_t * param_find_name(const param_table_t rows[], size_t row_count, const char * name)
{
gs_param_row_align_t row_in = {.row3 = rows};
gs_param_row_align_t row_out = {.row4 = gs_param_row_by_name(name, row_in.row4, row_count)};
return row_out.row3;
}
static inline const param_table_t * param_find_addr(const param_table_t rows[], size_t row_count, uint16_t addr)
{
gs_param_row_align_t row_in = {.row3 = rows};
gs_param_row_align_t row_out = {.row4 = gs_param_row_by_address(addr, row_in.row4, row_count)};
return row_out.row3;
}
static inline void param_list_single(param_table_t * param, param_index_t * mem, int do_read)
{
gs_param_list_single((gs_param_table_instance_t *) mem, (const gs_param_table_row_t *) param, (do_read != 0));
}
static inline void param_list(param_index_t * mem, int do_read)
{
gs_param_list((gs_param_table_instance_t *) mem, (do_read != 0));
}
static inline gs_error_t param_from_string(const param_table_t * param , const char * string, void * value)
{
return gs_param_from_string((const gs_param_table_row_t *)param , string, value);
}
static inline int param_to_string(const param_table_t * param, char * buf, int pos, const void * value, int with_type, int max_size)
{
unsigned int written = 0;
gs_param_to_string((const gs_param_table_row_t *)param, value, with_type, buf, max_size, pos, &written);
return (int) written;
}
static inline const char * param_type_to_string(param_type_t type)
{
return gs_param_type_to_string(type);
}
static inline uint16_t param_index_chksum(param_index_t * mem)
{
return gs_param_table_checksum((gs_param_table_instance_t*)mem);
}
static inline uint16_t param_index_chksum2(param_index_t * mem)
{
return gs_param_table_checksum2((gs_param_table_instance_t*)mem);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,92 @@
#ifndef PARAM_PARAM_TYPES_H
#define PARAM_PARAM_TYPES_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Legacy/deprecated parameter types and definitions - use <gs/param/types.h>.
*/
#include <gs/param/table.h>
#include <gs/param/rparam.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MAX_PARAM_NAME_LEN GS_PARAM_MAX_NAME
/*
Legacy parameter type definition - matches gs_param_type_t exactly.
*/
typedef gs_param_type_t param_type_t;
/*
Legacy table row definition - matches gs_param_table_row_t exactly.
*/
typedef struct param_table_s {
uint16_t addr;
param_type_t type;
uint8_t size;
uint8_t count; // -> array_size
uint8_t flags;
char name[MAX_PARAM_NAME_LEN];
} param_table_t;
#define PARAM_COUNT gs_max(GS_PGM_UINT8((param)->count), 1)
#define PARAM_SIZE GS_PARAM_SIZE(param)
#define PARAM_TYPE GS_PARAM_TYPE(param)
#define PARAM_ADDR GS_PARAM_ADDR(param)
#define PARAM_FLAGS GS_PARAM_FLAGS(param)
#define RPARAM_QUERY_MAX_LEN GS_RPARAM_MAX_QUERY
#define PARAM_MAX_FILESIZE 0x400
struct param_index_s;
/*
Legacy callback - matches gs_param_callback_func_t exactly.
*/
typedef void (*param_callback_func)(uint16_t addr, struct param_index_s * index);
typedef gs_param_table_id_t param_mem;
/*
Legacy table instance definition - matches gs_param_table_instance_t exactly.
*/
typedef struct param_index_s {
const char * name;
param_mem mem_id;
const param_table_t * table;
unsigned int count;
void * physaddr;
unsigned int size;
const gs_param_function_interface_t function_interface;
const uint16_t table_chksum;
const uint16_t chksum2;
const uint16_t checksum_le;
const gs_mutex_t lock;
param_callback_func callback;
const char * const var1;
const char * const var2;
const void * const var3;
const void * const var4;
const void * const var5;
const void * const var6;
const void * const var7;
const uint32_t var8;
} param_index_t;
/**
Return base size of a parameter type.
*/
static inline uint8_t param_type_size(param_type_t type)
{
return gs_param_type_size(type);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,208 @@
#ifndef PARAM_RPARAM_CLIENT_H
#define PARAM_RPARAM_CLIENT_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
* GomSpace Parameter System
*/
#include <param/param_types.h>
#include <gs/param/rparam.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Legacy magic checksum.
@deprecated use #GS_RPARAM_MAGIC_CHECKSUM (if remote products supports it).
*/
#define GS_RPARAM_LEGACY_MAGIC_CHECKSUM 0xb00b
static inline int rparam_get_full_table(param_index_t* idx, int node, int port, int index_id, uint32_t timeout_ms)
{
return (gs_rparam_get_full_table((gs_param_table_instance_t *) idx,
node,
index_id,
GS_RPARAM_LEGACY_MAGIC_CHECKSUM,
timeout_ms) == GS_OK) ? 0 : -1;
}
static inline int rparam_download_table_spec_from_remote_and_save_to_file2(const char* fname, uint8_t node, uint8_t port, param_index_t* idx, uint16_t* return_checksum, uint32_t timeout_ms)
{
return (gs_rparam_download_table_spec((gs_param_table_instance_t *) idx,
fname,
node,
idx->mem_id,
timeout_ms,
return_checksum) == GS_OK) ? 0 : -1;
}
static inline int rparam_download_table_spec_from_remote_and_save_to_file(const char* fname, uint8_t node, uint8_t port, param_index_t* idx, uint16_t* checksum)
{
return rparam_download_table_spec_from_remote_and_save_to_file2(fname, node, port, idx, checksum, 10000);
}
static inline int rparam_load_table_spec_from_file(const char* fname, param_index_t* idx, uint16_t* return_checksum)
{
return (gs_rparam_load_table_spec((gs_param_table_instance_t *) idx, fname, return_checksum) == GS_OK) ? 0 : -1;
}
static inline int rparam_get_single(void * out, uint16_t addr, param_type_t type, int size, int mem_id, int node, int port, uint32_t timeout_ms)
{
return (gs_rparam_get(node, mem_id, addr, type, GS_RPARAM_LEGACY_MAGIC_CHECKSUM, timeout_ms, out, size) == GS_OK) ? size : -1;
}
static inline int rparam_set_single(const void * in, uint16_t addr, param_type_t type, int size, int mem_id, int node, int port, uint32_t timeout_ms)
{
return (gs_rparam_set(node, mem_id, addr, type, GS_RPARAM_LEGACY_MAGIC_CHECKSUM, timeout_ms, in, size) == GS_OK) ? size : -1;
}
static inline int rparam_copy(uint8_t node, uint8_t port, uint32_t timeout_ms, uint8_t from, uint8_t to)
{
return (gs_rparam_copy(node, timeout_ms, from, to) == GS_OK) ? 1 : 0;
}
static inline int rparam_save(uint8_t node, uint8_t port, uint32_t timeout_ms, uint8_t from, uint8_t to)
{
return (gs_rparam_save(node, timeout_ms, from, to) == GS_OK) ? 1 : 0;
}
static inline int rparam_load(uint8_t node, uint8_t port, uint32_t timeout_ms, uint8_t from, uint8_t to)
{
return (gs_rparam_load(node, timeout_ms, from, to) == GS_OK) ? 1 : 0;
}
/**
* Remote getters for parameters.
* @param out pointer to the output buffer/parameter
* @param outlen length of the supplied out-buffer, only applies to rparam_get_string()
* @param addr local address of the parameter
* @param node CSP address of the node to query
* @param port CSP port of the parameter server
* @param timeout timeout on remote CSP calls
* @returns <0 on error in which case the contents of out is invalid, >0 on success
*/
static inline int rparam_get_string(char *out, int outlen, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_get_single(out, addr, PARAM_STRING, outlen, index_id, node, port, timeout_ms);
}
/**
* Remote setters for parameters.
* @param in pointer to the buffer/parameter value to set
* @param inlen length of the supplied in-buffer, only applies to rparam_set_string()
* @param addr local address of the parameter
* @param node CSP address of the node to query
* @param port CSP port of the parameter server
* @param timeout timeout on remote CSP calls
* @returns <0 on error in which case the contents of out is invalid, >0 on success
*/
static inline int rparam_set_string(const char *in, int inlen, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_set_single(in, addr, PARAM_STRING, inlen, index_id, node, port, timeout_ms);
}
static inline int rparam_get_uint8(uint8_t * out, uint16_t addr, int tinst_id, int node, int port, uint32_t timeout_ms)
{
return rparam_get_single(out, addr, PARAM_UINT8, sizeof(uint8_t), tinst_id, node, port, timeout_ms);
}
static inline int rparam_set_uint8(const uint8_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_set_single(in, addr, PARAM_UINT8, sizeof(uint8_t), index_id, node, port, timeout_ms);
}
static inline int rparam_get_uint16(uint16_t * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_get_single(out, addr, PARAM_UINT16, sizeof(uint16_t), index_id, node, port, timeout_ms);
}
static inline int rparam_set_uint16(const uint16_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_set_single(in, addr, PARAM_UINT16, sizeof(uint16_t), index_id, node, port, timeout_ms);
}
static inline int rparam_get_uint32(uint32_t * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_get_single(out, addr, PARAM_UINT32, sizeof(uint32_t), index_id, node, port, timeout_ms);
}
static inline int rparam_set_uint32(const uint32_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_set_single(in, addr, PARAM_UINT32, sizeof(uint32_t), index_id, node, port, timeout_ms);
}
static inline int rparam_get_uint64(uint64_t * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_get_single(out, addr, PARAM_UINT64, sizeof(uint64_t), index_id, node, port, timeout_ms);
}
static inline int rparam_set_uint64(const uint64_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_set_single(in, addr, PARAM_UINT64, sizeof(uint64_t), index_id, node, port, timeout_ms);
}
static inline int rparam_get_int8(int8_t * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_get_single(out, addr, PARAM_INT8, sizeof(int8_t), index_id, node, port, timeout_ms);
}
static inline int rparam_set_int8(const int8_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_set_single(in, addr, PARAM_INT8, sizeof(int8_t), index_id, node, port, timeout_ms);
}
static inline int rparam_get_int16(int16_t * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_get_single(out, addr, PARAM_INT16, sizeof(int16_t), index_id, node, port, timeout_ms);
}
static inline int rparam_set_int16(const int16_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_set_single(in, addr, PARAM_INT16, sizeof(int16_t), index_id, node, port, timeout_ms);
}
static inline int rparam_get_int32(int32_t * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_get_single(out, addr, PARAM_INT32, sizeof(int32_t), index_id, node, port, timeout_ms);
}
static inline int rparam_set_int32(const int32_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_set_single(in, addr, PARAM_INT32, sizeof(int32_t), index_id, node, port, timeout_ms);
}
static inline int rparam_get_int64(int64_t * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_get_single(out, addr, PARAM_INT64, sizeof(int64_t), index_id, node, port, timeout_ms);
}
static inline int rparam_set_int64(const int64_t * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_set_single(in, addr, PARAM_INT64, sizeof(int64_t), index_id, node, port, timeout_ms);
}
static inline int rparam_get_float(float * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_get_single(out, addr, PARAM_FLOAT, sizeof(float), index_id, node, port, timeout_ms);
}
static inline int rparam_set_float(const float * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_set_single(in, addr, PARAM_FLOAT, sizeof(float), index_id, node, port, timeout_ms);
}
static inline int rparam_get_double(double * out, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_get_single(out, addr, PARAM_DOUBLE, sizeof(double), index_id, node, port, timeout_ms);
}
static inline int rparam_set_double(const double * in, uint16_t addr, int index_id, int node, int port, uint32_t timeout_ms)
{
return rparam_set_single(in, addr, PARAM_DOUBLE, sizeof(double), index_id, node, port, timeout_ms);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,42 @@
#ifndef GS_PARAM_INTERNAL_PP_I2C_I2C_H
#define GS_PARAM_INTERNAL_PP_I2C_I2C_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
#if (GS_PARAM_INTERNAL_USE)
#include <gs/param/pp/i2c/i2c.h>
#include <gs/param/internal/pp/i2c/slave_dispatch.h>
#ifdef __cplusplus
extern "C" {
#endif
#define GS_PARAM_I2C_LENGTH_TABLE(length, table) ((length << 3) | (table & 0x07))
#define GS_PARAM_I2C_LENGTH_TABLE_TO_LENGTH(lt) ((lt >> 3) & 0x1f)
#define GS_PARAM_I2C_LENGTH_TABLE_TO_TABLE(lt) (lt & 0x07)
/**
Data structure for setting parameter.
*/
typedef struct __attribute__ ((packed)) gs_param_i2c_set_request {
gs_i2c_slave_dispatch_header_t header;
uint8_t length_table;
uint8_t addr;
uint8_t data[]; // data (+ checksum)
} gs_param_i2c_set_request_t;
/**
Data structure for getting parameter.
*/
typedef struct __attribute__ ((packed)) gs_param_i2c_get_request {
gs_i2c_slave_dispatch_header_t header;
uint8_t length_table;
uint8_t addr;
uint8_t checksum; // optional
} gs_param_i2c_get_request_t;
#ifdef __cplusplus
}
#endif
#endif
#endif

View File

@ -0,0 +1,71 @@
#ifndef GS_PARAM_INTERNAL_PP_I2C_SLAVE_DISPATCH_H
#define GS_PARAM_INTERNAL_PP_I2C_SLAVE_DISPATCH_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
#if (GS_PARAM_INTERNAL_USE)
#include <gs/util/drivers/i2c/slave.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Header for I2C slave dispatch protocol - must be first in all protocols.
*/
typedef struct __attribute__ ((packed)) gs_i2c_slave_dispatch_header {
uint8_t domain_command;
} gs_i2c_slave_dispatch_header_t;
/**
Make header from domain and command.
*/
#define GS_I2C_SLAVE_DISPATCH_HEADER(domain, command) ((domain << 5) | (command & 0x1f))
/**
Extract domain from header.
*/
#define GS_I2C_SLAVE_DISPATCH_HEADER_TO_DOMAIN(header) ((header >> 5) & 0x7)
/**
Extract comman from header.
*/
#define GS_I2C_SLAVE_DISPATCH_HEADER_TO_COMMAND(header) (header & 0x1f)
/**
Domain handler.
Basically same features as normal I2C slave rx callback. The generic I2C dispatch interface will parse the header (first byte)
and call the associated handler, based on the domain.
@param[in] channel I2C channel (handle).
@param[in] cmd domain specific command.
@param[in] rx_buffer Pointer to start of rx buffer.
@param[in] rx number of bytes received so far.
@param[in] cswitch If called from within an ISR (embedded platform), this will none NULL.
@return total number of bytes to receive before next call back. Return 0 to ignore rest of data - no additional call backs will be done for current I2C transaction.
*/
typedef void (* gs_i2c_slave_dispatch_handler_t)(uint8_t channel, uint8_t cmd, const uint8_t * rx, size_t rx_length, gs_context_switch_t * cswitch);
/**
Dispatch domains.
Standard domains should be assigned from the lowest value.
Application/board/product should be assigned from highest value.
*/
typedef enum {
GS_I2C_SLAVE_DISPATCH_DOMAIN_RESERVED = 0, //! Avoid use - reserved for GSSB, GSSB broadcasts UID request on domain=0, cmd=13
GS_I2C_SLAVE_DISPATCH_DOMAIN_USER1, //! Avoid use - overlap with GSSB commands
GS_I2C_SLAVE_DISPATCH_DOMAIN_USER2, //! Avoid use - may overlap with GSSB commands
GS_I2C_SLAVE_DISPATCH_DOMAIN_USER3, //! Avoid use - may overlap with GSSB commands
GS_I2C_SLAVE_DISPATCH_DOMAIN_PARAM, //! Reserved for libparam.
GS_I2C_SLAVE_DISPATCH_DOMAIN_USER5,
GS_I2C_SLAVE_DISPATCH_DOMAIN_USER6,
GS_I2C_SLAVE_DISPATCH_DOMAIN_USER7,
} gs_i2c_slave_dispatch_domain_t;
#ifdef __cplusplus
}
#endif
#endif
#endif

View File

@ -0,0 +1,80 @@
#ifndef GS_PARAM_INTERNAL_PP_SPI_SLAVE_DISPATCH_H
#define GS_PARAM_INTERNAL_PP_SPI_SLAVE_DISPATCH_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
#if (GS_PARAM_INTERNAL_USE)
#include <gs/util/drivers/spi/slave.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Header for SPI slave dispatch protocol - must be first in all protocols.
*/
typedef struct __attribute__ ((packed)) gs_spi_slave_dispatch_header {
uint8_t domain_command;
} gs_spi_slave_dispatch_header_t;
/**
Make header from domain and command.
*/
#define GS_SPI_SLAVE_DISPATCH_HEADER(domain, command) ((domain << 5) | (command & 0x1f))
/**
Extract domain from header.
*/
#define GS_SPI_SLAVE_DISPATCH_HEADER_TO_DOMAIN(header) ((header >> 5) & 0x7)
/**
Extract comman from header.
*/
#define GS_SPI_SLAVE_DISPATCH_HEADER_TO_COMMAND(header) (header & 0x1f)
/**
Domain handler.
Basically same features as normal SPI slave rx callback. The generic SPI dispatch interface will parse the header (first byte)
and call the associated handler, based on the domain.
@param[in] channel SPI channel (handle).
@param[in] cmd domain specific command.
@param[in] rx_buffer Pointer to start of rx buffer.
@param[in] rx number of bytes received so far.
@param[in] cswitch If called from within an ISR (embedded platform), this will none NULL.
@return total number of bytes to receive before next call back. Return 0 to ignore rest of data - no additional call backs will be done for current SPI transaction.
*/
typedef uint8_t (* gs_spi_slave_dispatch_handler_t)(uint8_t channel, uint8_t cmd, const uint8_t * rx_buffer, size_t rx, gs_context_switch_t * cswitch);
/**
Dispatch domains.
Standard domains should be assigned form the lowest value.
Application/board/product should be assigned from highest value.
*/
typedef enum {
GS_SPI_SLAVE_DISPATCH_DOMAIN_RESERVED = 0, //! Avoid using 0,
GS_SPI_SLAVE_DISPATCH_DOMAIN_USER1,
GS_SPI_SLAVE_DISPATCH_DOMAIN_USER2,
GS_SPI_SLAVE_DISPATCH_DOMAIN_USER3,
GS_SPI_SLAVE_DISPATCH_DOMAIN_PARAM,
GS_SPI_SLAVE_DISPATCH_DOMAIN_USER5,
GS_SPI_SLAVE_DISPATCH_DOMAIN_USER6,
GS_SPI_SLAVE_DISPATCH_DOMAIN_USER7,
} gs_spi_slave_dispatch_domain_t;
/**
Slave dispatch SPI receiver.
Must be added on the channel as receiver function, using gs_spi_slave_set_rx().
@param[in] cswitch If called from within an ISR (embedded platform), this will be set and allow for triggering context switch.
*/
uint8_t gs_spi_slave_dispatch_rx(uint8_t channel, const uint8_t * rx_buffer, size_t rx, bool new_request, gs_context_switch_t * cswitch);
#ifdef __cplusplus
}
#endif
#endif
#endif

View File

@ -0,0 +1,65 @@
#ifndef GS_PARAM_INTERNAL_PP_SPI_SPI_H
#define GS_PARAM_INTERNAL_PP_SPI_SPI_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
#if (GS_PARAM_INTERNAL_USE)
#include <gs/param/pp/spi/spi.h>
#include <gs/param/internal/pp/spi/slave_dispatch.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Domain commands.
*/
typedef enum {
GS_PARAM_SPI_COMMAND_SET = 1,
GS_PARAM_SPI_COMMAND_GET = 2,
GS_PARAM_SPI_COMMAND_SET_WITH_CHECKSUM = 10,
GS_PARAM_SPI_COMMAND_GET_WITH_CHECKSUM = 11,
} gs_param_spi_command_t;
#define GS_PARAM_SPI_LENGTH_TABLE(length, table) ((length << 3) | (table & 0x07))
#define GS_PARAM_SPI_LENGTH_TABLE_TO_LENGTH(lt) ((lt >> 3) & 0x1f)
#define GS_PARAM_SPI_LENGTH_TABLE_TO_TABLE(lt) (lt & 0x07)
/**
Data structure for setting parameter.
*/
typedef struct __attribute__ ((packed)) gs_param_spi_set {
gs_spi_slave_dispatch_header_t header;
uint8_t length_table;
uint8_t addr;
uint8_t data[]; // data (+ checksum)
} gs_param_spi_set_t;
/**
Data structure for getting parameter.
*/
typedef struct __attribute__ ((packed)) gs_param_spi_get {
gs_spi_slave_dispatch_header_t header;
uint8_t length_table;
uint8_t addr;
uint8_t filler; // filler/delay - allow slave to find and prepare data/response
uint8_t data[]; // data
} gs_param_spi_get_t;
/**
Data structure for getting parameter with checksum
*/
typedef struct __attribute__ ((packed)) gs_param_spi_get_with_checksum {
gs_spi_slave_dispatch_header_t header;
uint8_t length_table;
uint8_t addr;
uint8_t checksum;
uint8_t filler; // filler/delay - allow slave to find and prepare data/response
uint8_t data[]; // data + checksum
} gs_param_spi_get_with_checksum_t;
#ifdef __cplusplus
}
#endif
#endif
#endif

View File

@ -0,0 +1,146 @@
#ifndef GS_PARAM_INTERNAL_RPARAM_H
#define GS_PARAM_INTERNAL_RPARAM_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
#if (GS_PARAM_INTERNAL_USE)
#include <gs/param/rparam.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Max query payload in a single message (bytes).
*/
#define GS_RPARAM_QUERY_MAX_PAYLOAD 180
/**
Macro for calculating total query message size, header + payload.
*/
#define RPARAM_QUERY_LENGTH(query, payload_size) (sizeof(*query) - sizeof(query->payload) + payload_size)
/**
R(emote) parameter request codes.
*/
typedef enum {
/**
Get one or more parameters.
*/
RPARAM_GET = 0x00,
/**
Reply to a request.
*/
RPARAM_REPLY = 0x55,
/**
Set one or more parameters.
*/
RPARAM_SET = 0xFF,
// RPARAM_SET_TO_FILE = 0xEE,
/**
Download table specification.
*/
RPARAM_TABLE = 0x44,
/**
Copy memory slot to memory slot.
@version 4.x: Not supported.
*/
RPARAM_COPY = 0x77,
/**
Load from file (slot) to memory (slot).
@version 4.x: Only load from primary store - file (slot) is ignored.
*/
RPARAM_LOAD = 0x88,
/**
Load from file (slot) to memory (slot).
@version 4.x: load by name(s).
*/
RPARAM_LOAD_FROM_STORE = 0x89,
/**
Save from memory (slot) to file (slot).
@version 4.x: Only save to primary store - file (slot) is ignored.
*/
RPARAM_SAVE = 0x99,
/**
Save from memory (slot) to file (slot).
@version 4.x: save by name(s).
*/
RPARAM_SAVE_TO_STORE = 0x9a,
// RPARAM_CLEAR = 0xAA, - completely removed
} gs_rparam_action_t;
/**
R(emote) parameter reply/completion codes.
*/
typedef enum {
RPARAM_SET_OK = 1,
RPARAM_LOAD_OK = 2,
RPARAM_SAVE_OK = 3,
RPARAM_COPY_OK = 4,
// RPARAM_CLEAR_OK = 5,
RPARAM_ERROR = 0xFF,
} gs_rparam_reply_t;
/**
Payload - save/load to/from stores
@version 4
*/
typedef struct __attribute__ ((packed)) {
char table[25 + 1];
char store[25 + 1];
char slot[25 + 1];
} gs_rparam_query_payload_store_t;
/**
Payload.
*/
typedef union __attribute__ ((packed)) {
uint16_t addr[0]; //! action = RPARAM_GET
uint8_t packed[0]; //! action = RPARAM_REPLY | RPARAM_SET
struct { //! action = RPARAM_COPY | RPARAM_LOAD | RPARM_SAVE
uint8_t from;
uint8_t to;
} copy;
} gs_rparam_query_payload_t;
/**
Protocol between client and server.
@version 4.x: layout (size) has not changed - only naming of certain fields.
*/
typedef struct __attribute__ ((packed)) {
/**
Request (gs_rparam_action_t) or Reply (gs_rparam_reply_t).
*/
uint8_t action;
/**
Table id.
Name changed in 4.0 from \a mem.
*/
uint8_t table_id;
/**
Length/size of \a payload in bytes.
*/
uint16_t length;
/**
Fletcher's checksum.
*/
uint16_t checksum;
/**
Sequence number when split over multiple frames (messages).
*/
uint16_t seq;
/**
Total number of frames.
*/
uint16_t total;
/**
Payload.
*/
gs_rparam_query_payload_t payload;
} gs_rparam_query_t;
#ifdef __cplusplus
}
#endif
#endif
#endif

View File

@ -0,0 +1,29 @@
#ifndef GS_PARAM_INTERNAL_SERIALIZE_H
#define GS_PARAM_INTERNAL_SERIALIZE_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
#if (GS_PARAM_INTERNAL_USE)
#include <gs/param/serialize.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Serialize data
* @param mem Pointer to indexed parameter table
* @param addr Array of addresses to serialize
* @param addr_count number of items in addr array
* @param[in|out] param_offset table parameter offset
* @param flags Set options using combination of flags
* @param buf Pointer to output
* @param buf_size Size of \a buf.
*/
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);
#ifdef __cplusplus
}
#endif
#endif
#endif

View File

@ -0,0 +1,19 @@
#ifndef GS_PARAM_INTERNAL_TABLE_H
#define GS_PARAM_INTERNAL_TABLE_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
#if (GS_PARAM_INTERNAL_USE)
#include <gs/param/table.h>
#ifdef __cplusplus
extern "C" {
#endif
gs_error_t gs_param_parse_name_and_array_index(const char * inp, char * name, size_t size_name, uint8_t * array_index, bool * return_is_array);
#ifdef __cplusplus
}
#endif
#endif
#endif

View File

@ -0,0 +1,129 @@
#ifndef GS_PARAM_INTERNAL_TYPES_H
#define GS_PARAM_INTERNAL_TYPES_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
#if (GS_PARAM_INTERNAL_USE)
#include <gs/param/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Table instance.
*/
struct gs_param_table_instance {
/**
Name of table.
*/
const char * name;
/**
Table id.
*/
gs_param_table_id_t id;
/**
Table elements/rows.
*/
const gs_param_table_row_t * rows;
/**
Table element/row count.
*/
unsigned int row_count;
/**
Table memory - volatile parameter store.
The allocated size must be at least \a memory_size bytes.
*/
void * memory;
/**
Size of table data memory in bytes, normally size of memory allocated \a memory.
Size must always be specified, even when using function interface and no memory is allocated.
*/
unsigned int memory_size;
/**
Function interface, e.g. get/set.
*/
gs_param_function_interface_t function_interface;
/**
Checksum - based on host order (e.g. le or be).
@see gs_param_table_checksum()
*/
uint16_t checksum;
/**
Checksum - based on big-endian (address converted to big-endian).
@see gs_param_table_checksum_be()
*/
uint16_t checksum_be;
/**
Checksum - based on little-endian (address converted to little-endian).
@see gs_param_table_checksum_le()
*/
uint16_t checksum_le;
/**
Lock.
Internal access/use only, use gs_param_lock() and gs_param_unlock() to lock and un-lock table.
*/
gs_mutex_t lock;
/**
Callback for table (data) change.
*/
void (*callback)(uint16_t addr, gs_param_table_instance_t * tinst);
/**
Store location(s).
CSV format, e.g. \"persistent,protected\".
*/
const char * stores;
/**
Auto-persist.
*/
struct {
/**
Store.
*/
const char * store;
/**
User context(s) for the \a set function.
*/
void * context1;
void * context2;
/**
Set/write parameter.
*/
gs_error_t (*set)(gs_param_table_instance_t * tinst, uint16_t addr, gs_param_type_t type, const void * item, size_t size, uint32_t flags);
} auto_persist;
/**
Function for initializing table.
*/
gs_error_t (*initializer_function)(gs_param_table_instance_t * tinst);
/**
Default values for initializing table.
*/
const void * default_values;
/**
Future flags.
*/
uint32_t flags;
};
#ifdef __cplusplus
}
#endif
#endif
#endif

View File

@ -0,0 +1,60 @@
#ifndef GS_PARAM_PP_I2C_I2C_H
#define GS_PARAM_PP_I2C_I2C_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
I2C param protocol client interface.
*/
#include <gs/param/pp/pp.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Commands.
*/
typedef enum {
/**
Set parameter.
*/
GS_PARAM_I2C_COMMAND_SET = 1,
/**
Get parameter.
*/
GS_PARAM_I2C_COMMAND_GET = 2,
/**
Lock table.
*/
GS_PARAM_I2C_COMMAND_SET_LOCK_WITH_CHECKSUM = 3,
/**
Get table lock state.
*/
GS_PARAM_I2C_COMMAND_GET_LOCK_WITH_CHECKSUM = 4,
/**
Set parameter with checksum.
*/
GS_PARAM_I2C_COMMAND_SET_WITH_CHECKSUM = 10,
/**
Get parameter with checksum.
*/
GS_PARAM_I2C_COMMAND_GET_WITH_CHECKSUM = 11,
} gs_param_i2c_command_t;
/**
Initialize the param protocol handle for I2C.
@param[in] pp handle.
@param[in] bus bus to communicate on.
@param[in] addr address of node.
@param[in] big_endian \a true if slave is big endian. Used to convert to host endian.
@return_gs_error_t
*/
gs_error_t gs_pp_i2c_init(gs_pp_t * pp, uint8_t bus, uint8_t addr, bool big_endian);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,214 @@
#ifndef GS_PARAM_PP_PP_H
#define GS_PARAM_PP_PP_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Param Protocol (PP) API - generic interface for getting/setting parameters over SPI, I2C, etc.
*/
#include <gs/util/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Flags used in gs_pp_xxx() functions.
@{
*/
/**
Use checksum in transfer.
@see gs_pp_checksum8()
*/
#define GS_PP_FLAG_CHECKSUM 0x0001
/**@}*/
/**
Handle for a protocol connection.
*/
typedef struct gs_pp gs_pp_t;
/**
Callback for getting a parameter.
*/
typedef gs_error_t (*gs_pp_get_t)(gs_pp_t * pp, uint8_t table_id, uint16_t addr, void * value, size_t value_size, uint32_t flags);
/**
Callback for setting a parameter.
*/
typedef gs_error_t (*gs_pp_set_t)(gs_pp_t * pp, uint8_t table_id, uint16_t addr, const void * value, size_t value_size, uint32_t flags);
/**
Callback for setting table lock.
*/
typedef gs_error_t (*gs_pp_get_table_lock_t)(gs_pp_t * pp, uint8_t table_id, bool * value, uint32_t flags);
/**
Callback for setting table lock.
*/
typedef gs_error_t (*gs_pp_set_table_lock_t)(gs_pp_t * pp, uint8_t table_id, const bool * value, uint32_t flags);
/**
Handle for a protocol connection.
*/
struct gs_pp {
/**
Endian type of slave.
*/
bool big_endian;
/**
Callback function for \a get.
*/
gs_pp_get_t get;
/**
Callback function for \a set.
*/
gs_pp_set_t set;
/**
Callback function for \a get_table_lock.
*/
gs_pp_get_table_lock_t get_table_lock;
/**
Callback function for \a set_table_lock.
*/
gs_pp_set_table_lock_t set_table_lock;
/**
Protocol specifics.
*/
union {
/**
SPI connection.
*/
struct {
/**
SPI slave id.
*/
uint8_t slave;
} spi;
/**
I2C connection.
*/
struct {
/**
I2C bus.
*/
uint8_t bus;
/**
I2C address.
*/
uint8_t addr;
} i2c;
} pp;
};
/**
Calculate very simple 8 bit checksum.
The checksum is calculated by adding all bytes. If the checksum is 0 (zero), the checksum is set to 1 (one).
@param[in] data data to calculate checksum for.
@param[in] length data length.
@return checksum
*/
uint8_t gs_pp_checksum8(const void * data, size_t length);
/**
Get lock value
@param[in] pp Handle for connection
@param[in] table_id Table ID
@param[out] value Lock state (0 = unlocked, 1 = locked)
@param[in] flags
@return_gs_error_t
*/
gs_error_t gs_pp_get_table_lock(gs_pp_t * pp, uint8_t table_id, bool * value, uint32_t flags);
/**
Set lock value
@param[in] pp Handle for connection
@param[in] table_id Table ID
@param[in] value Lock state (0 = unlocked, 1 = locked)
@param[in] flags
@return_gs_error_t
*/
gs_error_t gs_pp_set_table_lock(gs_pp_t * pp, uint8_t table_id, const bool * value, uint32_t flags);
/**
Get int8.
*/
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);
/**
Set int8.
*/
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);
/**
Get uint8.
*/
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);
/**
Set uint8.
*/
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);
/**
Get int16.
*/
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);
/**
Set int16.
*/
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);
/**
Get uint16.
*/
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);
/**
Set uint16.
*/
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);
/**
Get int32.
*/
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);
/**
Set int32.
*/
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);
/**
Get uint32.
*/
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);
/**
Set uint32.
*/
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);
/**
Get float.
*/
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);
/**
Set float.
*/
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);
/**
Register commands.
*/
gs_error_t gs_pp_register_commands(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,29 @@
#ifndef GS_PARAM_PP_SPI_SPI_H
#define GS_PARAM_PP_SPI_SPI_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
SPI Param Protocol (pp) client interface.
*/
#include <gs/param/pp/pp.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Initialize the param protocol handle for SPI.
@param[in] pp handle.
@param[in] slave slave to communicate with.
@param[in] big_endian \a true if slave is big endian. Used to convert to host endian.
@return_gs_error_t
*/
gs_error_t gs_pp_spi_init(gs_pp_t * pp, uint8_t slave, bool big_endian);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,395 @@
#ifndef GS_PARAM_REMOTE_H
#define GS_PARAM_REMOTE_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Remote parameter API - pending refactoring.
*/
#include <gs/param/types.h>
#include <gs/util/string.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Magic checksum.
If specifying a magic checksum, the rparam server will ignore/skip checksum validation.
*/
#define GS_RPARAM_MAGIC_CHECKSUM 0x0bb0
/**
Register rparam commands.
@return_gs_error_t
*/
gs_error_t gs_rparam_register_commands(void);
/**
Download all (data) values from a remote table to a local instance.
@param[in] tinst local table instance.
@param[in] node CSP address.
@param[in] table_id remote table id to download.
@param[in] checksum table checksum.
@param[in] timeout_ms timeout.
@return_gs_error_t
*/
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);
/**
Download a table specification from a remote node, store it in memory and save it to local file system.
@note Will free existing rows - do not use this on table instances with static assigned rows.
Table memory will be (re)allocated to match specification.
@param[in] tinst local table instance.
@param[in] fname name of the file to store the table specification in. If NULL, no file will be stored.
@param[in] node CSP address
@param[in] table_id remote table id to download.
@param[in] timeout_ms timeout.
@param[out] return_checksum fletcher16 checksum of downloaded specification "as is" - before network/host swapping.
@return_gs_error_t
@see gs_param_table_free()
*/
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);
/**
Load a table specification from a local file and store it in memory.
@note Will free existing rows - do not use this on table instances with static assigned rows.
Table memory will be (re)allocated to match specification.
@param[in] tinst local table instance.
@param[in] fname name of the file to load the table specification from.
@param[out] return_checksum fletcher16 checksum stored in file.
@return_gs_error_t
@see gs_param_table_free()
*/
gs_error_t gs_rparam_load_table_spec(gs_param_table_instance_t * tinst, const char* fname, uint16_t * return_checksum);
/**
Copy from one table to another table.
@deprecated Not supported by a param 4 backend and future versions.
@param[in] node CSP address
@param[in] timeout_ms timeout on remote CSP calls
@param[in] from from table-id.
@param[in] to to table-id.
@return_gs_error_t
*/
gs_error_t gs_rparam_copy(uint8_t node, uint32_t timeout_ms, uint8_t from, uint8_t to);
/**
Save table.
@note On a param 4 backend, the table will always be saved to it's primary store.
@param[in] node CSP address
@param[in] timeout_ms timeout on remote CSP calls
@param[in] id table to save.
@param[in] to to file slot - ignored on param 4 backends.
@return_gs_error_t
*/
gs_error_t gs_rparam_save(uint8_t node, uint32_t timeout_ms, gs_param_table_id_t id, uint8_t to);
/**
Save table.
@version 4
@param[in] node CSP address
@param[in] timeout_ms timeout on remote CSP calls
@param[in] table_id remote table id.
@param[in] store store name.
@param[in] slot slot within store.
@return_gs_error_t
*/
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);
/**
Load table from store.
@note On a param 4 backend, the specified table will be loadded from it's primary store.
@param[in] node CSP address
@param[in] timeout_ms timeout on remote CSP calls
@param[in] from from file slot - ignored on param 4 backends.
@param[in] id table to load.
@return_gs_error_t
*/
gs_error_t gs_rparam_load(uint8_t node, uint32_t timeout_ms, uint8_t from, gs_param_table_id_t id);
/**
Load table from store.
@version 4
@param[in] node CSP address
@param[in] timeout_ms timeout on remote CSP calls
@param[in] table_id remote table id.
@param[in] store store name.
@param[in] slot slot within store.
@return_gs_error_t
*/
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);
/**
Get parameter.
@param[in] node CSP address
@param[in] table_id remote table id.
@param[in] addr parameter address (remote table).
@param[in] type parameter type.
@param[in] checksum checksum
@param[in] timeout_ms timeout
@param[out] value returned value (user allocated)
@param[in] value_size size of \a value buffer.
@return_gs_error_t
*/
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);
/**
Set parameter.
@param[in] node CSP address
@param[in] table_id remote table id.
@param[in] addr parameter address (remote table).
@param[in] type parameter type.
@param[in] checksum checksum
@param[in] timeout_ms timeout
@param[in] value value to set
@param[in] value_size size of \a value.
@return_gs_error_t
*/
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);
/**
Get string.
*/
static inline gs_error_t gs_rparam_get_string(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, char * value, size_t value_size)
{
return gs_rparam_get(node, table_id, addr, GS_PARAM_STRING, checksum, timeout_ms, value, value_size);
}
/**
Set string.
*/
static inline gs_error_t gs_rparam_set_string(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, const char * value, size_t value_size)
{
return gs_rparam_set(node, table_id, addr, GS_PARAM_STRING, checksum, timeout_ms, value, (value_size == 0) ? (strlen(value) + 1) : value_size);
}
/**
Get int8.
*/
static inline gs_error_t gs_rparam_get_int8(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, int8_t * value)
{
return gs_rparam_get(node, table_id, addr, GS_PARAM_INT8, checksum, timeout_ms, value, sizeof(*value));
}
/**
Set int8.
*/
static inline gs_error_t gs_rparam_set_int8(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, int8_t value)
{
return gs_rparam_set(node, table_id, addr, GS_PARAM_INT8, checksum, timeout_ms, &value, sizeof(value));
}
/**
Get uint8.
*/
static inline gs_error_t gs_rparam_get_uint8(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, uint8_t * value)
{
return gs_rparam_get(node, table_id, addr, GS_PARAM_UINT8, checksum, timeout_ms, value, sizeof(*value));
}
/**
Set uint8.
*/
static inline gs_error_t gs_rparam_set_uint8(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, uint8_t value)
{
return gs_rparam_set(node, table_id, addr, GS_PARAM_UINT8, checksum, timeout_ms, &value, sizeof(value));
}
/**
Get int16.
*/
static inline gs_error_t gs_rparam_get_int16(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, int16_t * value)
{
return gs_rparam_get(node, table_id, addr, GS_PARAM_INT16, checksum, timeout_ms, value, sizeof(*value));
}
/**
Set int16.
*/
static inline gs_error_t gs_rparam_set_int16(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, int16_t value)
{
return gs_rparam_set(node, table_id, addr, GS_PARAM_INT16, checksum, timeout_ms, &value, sizeof(value));
}
/**
Get uint16.
*/
static inline gs_error_t gs_rparam_get_uint16(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, uint16_t * value)
{
return gs_rparam_get(node, table_id, addr, GS_PARAM_UINT16, checksum, timeout_ms, value, sizeof(*value));
}
/**
Set uint16.
*/
static inline gs_error_t gs_rparam_set_uint16(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, uint16_t value)
{
return gs_rparam_set(node, table_id, addr, GS_PARAM_UINT16, checksum, timeout_ms, &value, sizeof(value));
}
/**
Get int32.
*/
static inline gs_error_t gs_rparam_get_int32(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, int32_t * value)
{
return gs_rparam_get(node, table_id, addr, GS_PARAM_INT32, checksum, timeout_ms, value, sizeof(*value));
}
/**
Set int32.
*/
static inline gs_error_t gs_rparam_set_int32(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, int32_t value)
{
return gs_rparam_set(node, table_id, addr, GS_PARAM_INT32, checksum, timeout_ms, &value, sizeof(value));
}
/**
Get uint32.
*/
static inline gs_error_t gs_rparam_get_uint32(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, uint32_t * value)
{
return gs_rparam_get(node, table_id, addr, GS_PARAM_UINT32, checksum, timeout_ms, value, sizeof(*value));
}
/**
Set uint32.
*/
static inline gs_error_t gs_rparam_set_uint32(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, uint32_t value)
{
return gs_rparam_set(node, table_id, addr, GS_PARAM_UINT32, checksum, timeout_ms, &value, sizeof(value));
}
/**
Get int64.
*/
static inline gs_error_t gs_rparam_get_int64(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, int64_t * value)
{
return gs_rparam_get(node, table_id, addr, GS_PARAM_INT64, checksum, timeout_ms, value, sizeof(*value));
}
/**
Set int64.
*/
static inline gs_error_t gs_rparam_set_int64(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, int64_t value)
{
return gs_rparam_set(node, table_id, addr, GS_PARAM_INT64, checksum, timeout_ms, &value, sizeof(value));
}
/**
Get uint64.
*/
static inline gs_error_t gs_rparam_get_uint64(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, uint64_t * value)
{
return gs_rparam_get(node, table_id, addr, GS_PARAM_UINT64, checksum, timeout_ms, value, sizeof(*value));
}
/**
Set uint64.
*/
static inline gs_error_t gs_rparam_set_uint64(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, uint64_t value)
{
return gs_rparam_set(node, table_id, addr, GS_PARAM_UINT64, checksum, timeout_ms, &value, sizeof(value));
}
/**
Get float.
*/
static inline gs_error_t gs_rparam_get_float(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, float * value)
{
return gs_rparam_get(node, table_id, addr, GS_PARAM_FLOAT, checksum, timeout_ms, value, sizeof(*value));
}
/**
Set float.
*/
static inline gs_error_t gs_rparam_set_float(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, float value)
{
return gs_rparam_set(node, table_id, addr, GS_PARAM_FLOAT, checksum, timeout_ms, &value, sizeof(value));
}
/**
Get double.
*/
static inline gs_error_t gs_rparam_get_double(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, double * value)
{
return gs_rparam_get(node, table_id, addr, GS_PARAM_DOUBLE, checksum, timeout_ms, value, sizeof(*value));
}
/**
Set double.
*/
static inline gs_error_t gs_rparam_set_double(uint8_t node, gs_param_table_id_t table_id, uint16_t addr,
uint16_t checksum, uint32_t timeout_ms, double value)
{
return gs_rparam_set(node, table_id, addr, GS_PARAM_DOUBLE, checksum, timeout_ms, &value, sizeof(value));
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,101 @@
#ifndef GS_PARAM_CLIENT_SERIALIZE_H
#define GS_PARAM_CLIENT_SERIALIZE_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Serialize API - pending refactoring.
*/
#include <gs/param/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Serialize/deserialize flags
Flags must be in range: bit 8 - 15, to avoid clash with other parts of the parameter system.
*/
typedef enum {
GS_PARAM_SF_DRY_RUN = (1 << 8), //!< F_DRY_RUN do not write to memory
GS_PARAM_SF_TO_BIG_ENDIAN = (1 << 9), //!< F_TO_BIG_ENDIAN Convert from host to big endian
GS_PARAM_SF_FROM_BIG_ENDIAN = (1 << 10), //!< F_FROM_BIG_ENDIAN Confert from big endian to host order
GS_PARAM_SF_PACKED = (1 << 11), //!< F_PACKED Do not pack addresses
F_DRY_RUN = GS_PARAM_SF_DRY_RUN,
F_TO_BIG_ENDIAN = GS_PARAM_SF_TO_BIG_ENDIAN,
F_FROM_BIG_ENDIAN = GS_PARAM_SF_FROM_BIG_ENDIAN,
F_PACKED = GS_PARAM_SF_PACKED,
} gs_param_serialize_flags_t;
/**
* In-place conversion of a single parameter from big endian to host byte order
* @param type param type
* @param item pointer to parameter memory
* @return 1 if memory has been swapped, 0 if not
*/
bool gs_param_betoh(gs_param_type_t type, void * item);
/**
* In-place conversion of a single parameter from host byte order to big endian
* @param type param type
* @param item porinter to parameter memory
* @return 1 if memory has been swapped, 0 if not
*/
bool gs_param_htobe(gs_param_type_t type, void * item);
/**
Serialize data
@param[in] tinst table.
@param[in,out] param_pos parameter iterator.
@param[in] flags flags.
@param[out] buf user supplied buffer of \a buf_size.
@param[in] buf_size of size of \a buf
@param[in,out] buf_pos index into \a buf
@return_gs_error_t
*/
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);
/**
Serialize single item
@param[in] param parameter to serialize
@param[in] addr Address of item
@param[in] item item to serialize.
@param[in] flags flags.
@param[out] buf user supplied buffer of \a buf_size.
@param[in] buf_size of size of \a buf
@param[in,out] buf_pos index into \a buf
@return_gs_error_t
*/
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);
/**
Deserialize packed parameters into memory
@param[in] tinst table.
@param[in] buf serialized data.
@param[in] buf_size size \a buf containing serialized data
@param[in] flags flags.
@return_gs_error_t
*/
gs_error_t gs_param_deserialize(gs_param_table_instance_t * tinst, const uint8_t * buf, unsigned int buf_size, uint32_t flags);
/**
Deserialize a sginle item from a string into memory
@param[in] tinst table.
@param param Pointer to specific parameter to deserialize
@param addr Address of parameter
@param item Pointer to memory area where item should be written
@param flags Set options using combination of flags
@return_gs_error_t
*/
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);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,428 @@
#ifndef GS_PARAM_TABLE_H
#define GS_PARAM_TABLE_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Client table API.
*/
#include <gs/param/types.h>
#include <gs/util/stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Allocate table memory when creating a table.
Flags must be in range: bit 16 - 23, to avoid clash with other parts of the parameter system.
*/
#define GS_PARAM_TABLE_F_ALLOC_MEMORY 0x0100
/**
Allocate table rows.
Flags must be in range: bit 16 - 23, to avoid clash with other parts of the parameter system.
*/
#define GS_PARAM_TABLE_F_ALLOC_ROWS 0x0200
/**
Disable table locking.
Flags must be in range: bit 16 - 23, to avoid clash with other parts of the parameter system.
*/
#define GS_PARAM_TABLE_F_NO_LOCK 0x0400
/**
Calculate memory size based on table rows.
@param[in] rows rows
@param[in] row_count row count.
@return size of table or 0 in case of invalid arguments.
*/
size_t gs_param_calc_table_size(const gs_param_table_row_t * rows, size_t row_count);
/**
Return size of table instance.
*/
size_t gs_param_table_instance_size(void);
/**
Clear (and check size) of memory for table instance.
@param[in] var user allocated space of at least gs_param_table_instance_size() bytes.
@param[in] var_size of \a var.
@return table instance
@see gs_param_table_instance_size()
@see #GS_PARAM_TINST_VAR
*/
gs_param_table_instance_t * gs_param_table_instance_clear(void * var, size_t var_size);
/**
Allocates aligned space on the stack for a table instance structure.
@param[in] var name of table instsance variable.
*/
#define GS_PARAM_TINST_VAR(var) uint8_t var##__data [gs_param_table_instance_size()] __attribute__ ((aligned(4))); gs_param_table_instance_t * var = gs_param_table_instance_clear(var##__data, sizeof(var##__data))
/**
Allocate memory for table instance.
Use gs_param_table_free() to free any internal resources.
Use standard free() to free allocated memory.
@return table instance on success, otherwise NULL.
*/
gs_param_table_instance_t * gs_param_table_instance_alloc(void);
/**
Find row by name.
@param[in] name parameter name.
@param[in] rows rows
@param[in] row_count row count.
@return row or NULL if not found.
*/
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);
/**
Find row by address.
@param[in] addr parameter address.
@param[in] rows rows
@param[in] row_count row count.
@return row or NULL if not found.
*/
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);
/**
Return table memory.
@note handle with care - any read/write should be atomic to prevent inconsistent data.
@param[in] tinst table instance
@param[out] return_size if not NULL, the memory size is returned.
@return pointer to the table's data memory.
*/
void * gs_param_table_get_memory(gs_param_table_instance_t * tinst, size_t * return_size);
/**
Return table rows.
@param[in] tinst table instance
@param[out] return_count if not NULL, the row count is returned.
@return pointer to the table rows.
*/
const gs_param_table_row_t * gs_param_table_get_rows(gs_param_table_instance_t * tinst, size_t * return_count);
/**
Lock table (recursive).
@param[in] tinst table instance
@return_gs_error_t
*/
gs_error_t gs_param_table_lock(gs_param_table_instance_t * tinst);
/**
Unlock table.
Unlock must be called once for every time gs_param_table_lock() has been called.
@param[in] tinst table instance
@return_gs_error_t
*/
gs_error_t gs_param_table_unlock(gs_param_table_instance_t * tinst);
/**
Free internal resources and clears instance.
@param[in] tinst table instance
@return_gs_error_t
*/
gs_error_t gs_param_table_free(gs_param_table_instance_t * tinst);
/**
Print a single parameter on stream.
@param[in] tinst table instanc.
@param[in] row row to print.
@param[in] list_data \a true includes parameter value.
@param[in] flags flags to control output format: #GS_PARAM_F_SHOW_SCIENTIFIC, #GS_PARAM_F_SHOW_HEX.
@param[in] out output stream.
@return_gs_error_t
*/
gs_error_t gs_param_list_single_to_stream(gs_param_table_instance_t * tinst, const gs_param_table_row_t * row, bool list_data, uint32_t flags, FILE * out);
/**
Print a single parameter on stdout.
@param[in] tinst table instanc.
@param[in] row row to print.
@param[in] list_data \a true includes parameter value.
@return_gs_error_t
*/
static inline gs_error_t gs_param_list_single(gs_param_table_instance_t * tinst, const gs_param_table_row_t * row, bool list_data)
{
return gs_param_list_single_to_stream(tinst, row, list_data, 0, stdout);
}
/**
Print entire table on stream.
@param[in] tinst table instanc.
@param[in] list_data \a true includes parameter value.
@param[in] flags flags to control output format: #GS_PARAM_F_SHOW_SCIENTIFIC, #GS_PARAM_F_SHOW_HEX.
@param[in] out output stream.
@return_gs_error_t
*/
gs_error_t gs_param_list_to_stream(gs_param_table_instance_t * tinst, bool list_data, uint32_t flags, FILE * out);
/**
Print entire table on stdout.
@param[in] tinst table instanc.
@param[in] list_data \a true includes parameter value.
@return_gs_error_t
*/
static inline gs_error_t gs_param_list(gs_param_table_instance_t * tinst, bool list_data)
{
return gs_param_list_to_stream(tinst, list_data, 0, stdout);
}
/**
Convert string to parameter.
@param[in] row row defining the parameter to convert.
@param[in] string string to convert.
@param[out] return_value user supplied buffer for returning the value - must be at least the size specified in \a row
@return_gs_error_t
*/
gs_error_t gs_param_from_string(const gs_param_table_row_t * row, const char * string, void * return_value);
/**
Convert parameter to string.
@param[in] row row defining the parameter to convert.
@param[in] value parameter value to convert.
@param[in] with_type \a true includes data type.
@param[in] flags flags to control output format: #GS_PARAM_F_SHOW_SCIENTIFIC, #GS_PARAM_F_SHOW_HEX.
@param[out] buf user supplied buffer of \a buf_size bytes.
@param[in] buf_size size of \a buf in bytes.
@param[in] buf_pos buffer position to insert string.
@param[out] return_buf_written number of bytes written to \a buf.
@return_gs_error_t
*/
gs_error_t gs_param_to_string2(const gs_param_table_row_t * row, const void * value, bool with_type, uint32_t flags, char * buf, unsigned int buf_size, unsigned int buf_pos, unsigned int * return_buf_written);
/**
Convert parameter to string.
@param[in] row row defining the parameter to convert.
@param[in] value parameter value to convert.
@param[in] with_type \a true includes data type.
@param[out] buf user supplied buffer of \a buf_size bytes.
@param[in] buf_size size of \a buf in bytes.
@param[in] buf_pos buffer position to insert string.
@param[out] return_buf_written number of bytes written to \a buf.
@return_gs_error_t
*/
static inline gs_error_t gs_param_to_string(const gs_param_table_row_t * row, const void * value, bool with_type, char * buf, unsigned int buf_size, unsigned int buf_pos, unsigned int * return_buf_written)
{
return gs_param_to_string2(row, value, with_type, 0, buf, buf_size, buf_pos, return_buf_written);
}
/**
Convert parameter type to string.
@param[in] type parameter type.
@return pointer to a static string.
*/
const char * gs_param_type_to_string(gs_param_type_t type);
/**
Return size of parameter type.
@param[in] type parameter type.
@return size of parameter type in bytes.
*/
uint8_t gs_param_type_size(gs_param_type_t type);
/**
Get table checksum - little-endian.
@note Use/exchange gs_param_table_checksum_be(), as this is calculated the same on all platforms.
@param[in] tinst table instance.
@returns 16-bit fletcher checksum
*/
uint16_t gs_param_table_checksum_le(gs_param_table_instance_t * tinst);
/**
Get table checksum - big-endian/network-order (prefered).
@param[in] tinst table instance.
@returns 16-bit fletcher checksum
*/
uint16_t gs_param_table_checksum_be(gs_param_table_instance_t * tinst);
/**
Get table checksum - host-order (not cross-platform).
@deprecated use gs_param_table_checksum_be()
@param[in] tinst table instance.
@returns 16-bit fletcher checksum
*/
uint16_t gs_param_table_checksum(gs_param_table_instance_t * tinst);
/**
Get table checksum - big-endian.
@deprecated use gs_param_table_checksum_be()
@param[in] tinst table instance.
@returns 16-bit fletcher checksum
*/
static inline uint16_t gs_param_table_checksum2(gs_param_table_instance_t * tinst)
{
return gs_param_table_checksum_be(tinst);
}
/**
Get/read parameter from table.
@param[in] tinst table instanc.
@param[in] addr parameter address (offset in table).
@param[in] type parameter type.
@param[out] return_value value of parameter - user supplied memory of at least \a size size.
@param[in] size number of bytes to get/read - must match \a type, e.g. 4 bytes for an uint32_t.
@param[in] flags flags.
@return_gs_error_t
*/
gs_error_t gs_param_get(gs_param_table_instance_t * tinst, uint16_t addr, gs_param_type_t type, void * return_value, size_t size, uint32_t flags);
/**
Set/write parameter in table.
@param[in] tinst table instanc.
@param[in] addr parameter address (offset in table).
@param[in] type parameter type.
@param[in] value value of parameter.
@param[in] size number of bytes to set/write - must match \a type, e.g. 4 bytes for an uint32_t.
@param[in] flags flags.
@return_gs_error_t
*/
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 size, uint32_t flags);
/**
Get string parameter.
@param[in] tinst table instanc.
@param[in] addr parameter address (offset in table).
@param[out] buf value of parameter - user supplied memory of at least parameter size + 1 to hold NUL termination.
@param[in] buf_size size of \a buf - ensure room for NUL termination.
@param[in] flags flags.
@return GS_ERROR_OVERFLOW if string + NUL termination exceeds \a buf_size.
@return_gs_error_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);
/**
Set string parameter.
@param[in] tinst table.
@param[in] addr parameter address (offset in table).
@param[in] value string to save - parameter must be able to hold string + NUL termination.
@param[in] flags flags.
@return GS_ERROR_OVERFLOW if string + NUL termination exceeds parameter size.
@return_gs_error_t
*/
gs_error_t gs_param_set_string(gs_param_table_instance_t * tinst, uint16_t addr, const char * value, uint32_t flags);
/**
Get data parameter.
@param[in] tinst table instanc.
@param[in] addr parameter address (offset in table).
@param[out] buf value of parameter - user supplied memory of at least parameter size.
@param[in] buf_size size of \a buf.
@param[in] flags flags.
@return GS_ERROR_OVERFLOW if parameter size is greater than \a buf_size.
@return_gs_error_t
*/
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);
/**
Set data parameter.
@param[in] tinst table instanc.
@param[in] addr parameter address (offset in table).
@param[in] value value of parameter.
@param[in] value_size size of \a value.
@param[in] flags flags.
@return GS_ERROR_OVERFLOW if parameter size is greater than \a buf_size.
@return_gs_error_t
*/
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);
/**
Macro for expanding get/set functions.
@param[in] name function suffix name.
@param[in] native_type native type
@param[in] param_type parameter type
*/
#define GS_PARAM_PASTE(name, native_type, param_type) \
static inline gs_error_t gs_param_get_##name(gs_param_table_instance_t * tinst, uint16_t addr, native_type * buf, uint32_t flags) { \
return gs_param_get(tinst, addr, param_type, buf, sizeof(*buf), flags); \
} \
static inline native_type gs_param_get_##name##_nc(gs_param_table_instance_t * tinst, uint16_t addr, uint32_t flags) { \
native_type value = 0; \
gs_param_get(tinst, addr, param_type, &value, sizeof(value), flags); \
return value; \
} \
static inline gs_error_t gs_param_set_##name(gs_param_table_instance_t * tinst, uint16_t addr, native_type value, uint32_t flags) { \
return gs_param_set(tinst, addr, param_type, &value, sizeof(value), flags); \
}
/**
Get/set boolean.
*/
GS_PARAM_PASTE(bool, bool, GS_PARAM_BOOL)
/**
Get/set uint8.
*/
GS_PARAM_PASTE(uint8, uint8_t, GS_PARAM_UINT8)
/**
Get/set uint16.
*/
GS_PARAM_PASTE(uint16, uint16_t, GS_PARAM_UINT16)
/**
Get/set uint32.
*/
GS_PARAM_PASTE(uint32, uint32_t, GS_PARAM_UINT32)
/**
Get/set uint64.
*/
GS_PARAM_PASTE(uint64, uint64_t, GS_PARAM_UINT64)
/**
Get/set int8.
*/
GS_PARAM_PASTE(int8, int8_t, GS_PARAM_INT8)
/**
Get/set int16.
*/
GS_PARAM_PASTE(int16, int16_t, GS_PARAM_INT16)
/**
Get/set int32.
*/
GS_PARAM_PASTE(int32, int32_t, GS_PARAM_INT32)
/**
Get/set int64.
*/
GS_PARAM_PASTE(int64, int64_t, GS_PARAM_INT64)
/**
Get/set double.
*/
GS_PARAM_PASTE(double, double, GS_PARAM_DOUBLE)
/**
Get/set float.
*/
GS_PARAM_PASTE(float, float, GS_PARAM_FLOAT)
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,272 @@
#ifndef GS_PARAM_TYPES_H
#define GS_PARAM_TYPES_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Parameter types.
*/
#include <gs/util/mutex.h>
#include <gs/util/pgm.h>
#include <gs/util/minmax.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Macros for accessing table row members.
These macros can be used to access members in a cross-platform way, compensating for the AVR8's memory model.
@{
*/
#define GS_PARAM_ARRAY_SIZE(p) gs_max(GS_PGM_UINT8((p)->array_size), 1)
#define GS_PARAM_SIZE(p) GS_PGM_UINT8((p)->size)
#define GS_PARAM_TYPE(p) GS_PGM_UINT8((p)->type)
#define GS_PARAM_ADDR(p) GS_PGM_UINT16((p)->addr)
#define GS_PARAM_FLAGS(p) GS_PGM_UINT8((p)->flags)
/** @} */
/**
Max parameter name - including 0 termination.
@note In some rare/old table definitions, the name may not be NULL terminated.
*/
#define GS_PARAM_MAX_NAME 14
/**
Parameter flags.
Flags must be in range: bit 0 - 7, to avoid clash with other parts of the parameter system.
*/
typedef enum {
/**
Parameter will be stored in configured auto-persist store when set.
@note Flag must be specified when setting the parameter.
*/
GS_PARAM_F_AUTO_PERSIST = (1 << 0),
/**
@deprecated Not supported in version 4.0
*/
PARAM_F_READONLY = (1 << 1),
/**
Skip callback, when parameter is set.
@note Flag must be specified when setting the parameter.
*/
GS_PARAM_F_NO_CALLBACK = (1 << 2),
/**
Show/display parameter in hex.
*/
GS_PARAM_F_SHOW_HEX = (1 << 3),
/**
Show double/float using scientific notation.
*/
GS_PARAM_F_SHOW_SCIENTIFIC = (1 << 4),
PARAM_F_NOCALLBACK = GS_PARAM_F_NO_CALLBACK,
PARAM_F_PERSIST = GS_PARAM_F_AUTO_PERSIST,
} gs_param_flags_t;
/**
* Parameter types.
*/
typedef enum __attribute__((__packed__)) {
/**
Unsigned 8 bit (uint8_t).
*/
GS_PARAM_UINT8 = 0,
/**
Unsigned 16 bit (uint16_t).
*/
GS_PARAM_UINT16 = 1,
/**
Unsigned 32 bit (uint32_t).
*/
GS_PARAM_UINT32 = 2,
/**
Unsigned 64 bit (uint64_t).
*/
GS_PARAM_UINT64 = 3,
/**
Signed 8 bit (int8_t).
*/
GS_PARAM_INT8 = 4,
/**
Signed 16 bit (int16_t).
*/
GS_PARAM_INT16 = 5,
/**
Signed 32 bit (int32_t).
*/
GS_PARAM_INT32 = 6,
/**
Signed 64 bit (int64_t).
*/
GS_PARAM_INT64 = 7,
/**
@deprecated - use #GS_PARAM_UINT8 and #GS_PARAM_F_SHOW_HEX.
*/
PARAM_X8 = 8,
/**
@deprecated - use #GS_PARAM_UINT16 and #GS_PARAM_F_SHOW_HEX.
*/
PARAM_X16 = 9,
/**
@deprecated - use #GS_PARAM_UINT32 and #GS_PARAM_F_SHOW_HEX.
*/
PARAM_X32 = 10,
/**
@deprecated - use #GS_PARAM_UINT64 and #GS_PARAM_F_SHOW_HEX.
*/
PARAM_X64 = 11,
/**
Double.
*/
GS_PARAM_DOUBLE = 12,
/**
Float.
*/
GS_PARAM_FLOAT = 13,
/**
C or null-terminated string.
@note The specified \a size must include space for the NUL character.
*/
GS_PARAM_STRING = 14,
/**
Data (binary blob).
Binary blob: [0, 0x40, 0x4f] -> '00404f'
*/
GS_PARAM_DATA = 15,
/**
Boolean.
Expected same size as uint8_t.
*/
GS_PARAM_BOOL = 16,
PARAM_UINT8 = GS_PARAM_UINT8,
PARAM_UINT16 = GS_PARAM_UINT16,
PARAM_UINT32 = GS_PARAM_UINT32,
PARAM_UINT64 = GS_PARAM_UINT64,
PARAM_INT8 = GS_PARAM_INT8,
PARAM_INT16 = GS_PARAM_INT16,
PARAM_INT32 = GS_PARAM_INT32,
PARAM_INT64 = GS_PARAM_INT64,
PARAM_DOUBLE = GS_PARAM_DOUBLE,
PARAM_FLOAT = GS_PARAM_FLOAT,
PARAM_STRING = GS_PARAM_STRING,
PARAM_DATA = GS_PARAM_DATA,
PARAM_BOOL = GS_PARAM_BOOL,
} gs_param_type_t;
/**
Table row.
A table row defines one parameter, and a table is defined by one or more rows.
@note Make sure to update gs_param_table_checksum2(), if adding fields > 1 byte.
@note AVR8: Table definitions must be located in \a program memory, i.e. must be const.
*/
typedef struct __attribute__((__packed__)) {
/**
Address (or offset) in table.
*/
uint16_t addr;
/**
Type.
*/
gs_param_type_t type;
/**
Size of element.
uint32_t = 4, string[5] = 5 (4 characters + 1 for NUL), etc.
*/
uint8_t size;
/**
Array size.
Size greater than 1, will make the parameter an array - if the value is 0 or 1, the parameter is not an array.
*/
uint8_t array_size;
/**
Flags.
@see gs_param_flags_t
*/
uint8_t flags;
/**
Name (C string).
@note In some rare/old table definitions, the name may not be NUL terminated.
*/
char name[GS_PARAM_MAX_NAME];
} gs_param_table_row_t;
/**
Table instance.
*/
typedef struct gs_param_table_instance gs_param_table_instance_t;
/**
Table id.
Tables can be associated with a number/id, which normally is unique on a specific node.
*/
typedef uint8_t gs_param_table_id_t;
/**
Undefined table id.
*/
#define GS_PARAM_UNDEFINED_TABLE_ID 255
/**
Function for setting a parameter.
@param[in] context user context/reference.
@param[in] tinst table instance.
@param[in] addr parameter address.
@param[in] type parameter type.
@param[in] item parameter value.
@param[in] size parameter size (e.g. how many bytes to copy from \a item).
@param[in] flags flags related to the operation - these may vary depending on the context.
@return_gs_error_t
*/
typedef gs_error_t (*gs_param_table_function_set_t)(void * context, gs_param_table_instance_t * tinst, uint16_t addr, gs_param_type_t type, const void * item, size_t size, uint32_t flags);
/**
Function for getting a parameter.
@param[in] context user context/reference.
@param[in] tinst table instance.
@param[in] addr parameter address.
@param[in] type parameter type.
@param[out] item parameter buffer (provided by the caller).
@param[in] size parameter size (e.g. how many bytes to copy to \a item).
@param[in] flags flags related to the operation - these may vary depending on the context.
@return_gs_error_t
*/
typedef gs_error_t (*gs_param_table_function_get_t)(void * context, gs_param_table_instance_t * tinst, uint16_t addr, gs_param_type_t type, void * item, size_t size, uint32_t flags);
/**
Function interface for setting and getting parameters.
Functions will be invoked, when set/get is called on the table instance.
*/
typedef struct {
/**
User context, provided in the callback functions.
*/
void * context;
/**
Called when setting a parameter.
*/
gs_param_table_function_set_t set;
/**
Called when getting a parameter.
*/
gs_param_table_function_get_t get;
} gs_param_function_interface_t;
/**
Callback function for changed parameter.
See gs_param_table_create() for details.
*/
typedef void (*gs_param_callback_func_t)(uint16_t addr, gs_param_table_instance_t * tinst);
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View 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);
}

View 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;
}

View 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);
}

View 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;
}

View 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);
}

View 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;

View 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;
}

View 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);

View 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, &param_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);
}

View 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;
}

View 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

View 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;
}

View 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
}

View File

@ -0,0 +1,69 @@
#!/usr/bin/env python
# encoding: utf-8
# Copyright (c) 2013-2018 GomSpace A/S. All rights reserved.
import os
import gs_gcc
import gs_doc
APPNAME = 'param_client'
def options(ctx):
ctx.load('gs_gcc gs_doc')
gs_gcc.gs_recurse(ctx)
gr = ctx.add_option_group('libparam client options')
gr.add_option('--param_client-disable-cmd', action='store_true', help='Disable GOSH commands')
gr.add_option('--param-enable-atomic-access', action='store_true', help='Enable atomic read/write of 16/32/float')
def configure(ctx):
ctx.load('gs_gcc gs_doc')
ctx.env.append_unique('USE_PARAM_CLIENT', ['gscsp', 'util'])
ctx.env.append_unique('FILES_PARAM_CLIENT', ['src/*.c', 'src/rparam/*.c',
'src/pp/*.c', 'src/pp/i2c/*.c', 'src/pp/spi/*.c'])
if not ctx.options.param_client_disable_cmd:
ctx.env.append_unique('FILES_PARAM_CLIENT', ['src/rparam/cmd/*.c', 'src/pp/cmd/*.c'])
if ctx.options.param_enable_atomic_access:
ctx.env.append_unique('DEFINES_PARAM_CLIENT', ['GS_PARAM_ATOMIC_ACCESS=1'])
ctx.gs_register_handler(function='param_gen_4_0', filepath='./tools/waf_param.py')
ctx.gs_register_handler(function='param_gen_4_2', filepath='./tools/waf_param.py')
ctx.gs_register_handler(function='param_gen_4_3', filepath='./tools/waf_param.py')
ctx.gs_add_doxygen(exclude=['*/include/deprecated/param/*', '*/include/gs/param/internal/*'])
gs_gcc.gs_recurse(ctx)
def build(ctx):
gs_gcc.gs_recurse(ctx)
public_include = ctx.gs_include(name=APPNAME,
includes=['include', 'include/deprecated', 'include/deprecated/param'])
if ctx.env.GS_ARCH not in ['avr8']:
ctx.gs_objects(source=ctx.path.ant_glob(ctx.env.FILES_PARAM_CLIENT),
target=APPNAME,
defines=ctx.env.DEFINES_PARAM_CLIENT,
use=ctx.env.USE_PARAM_CLIENT + [public_include])
ctx.gs_shlib(source=ctx.path.ant_glob(ctx.env.FILES_PARAM_CLIENT),
target=APPNAME,
defines=ctx.env.DEFINES_PARAM_CLIENT,
gs_use_shlib=ctx.env.USE_PARAM_CLIENT + [public_include])
ctx.gs_python_bindings(source=ctx.path.ant_glob('src/bindings/python/pyparam.c'),
target=APPNAME,
gs_use_shlib=ctx.env.USE_PARAM_CLIENT + [APPNAME, public_include],
package='libparam')
def gs_dist(ctx):
gs_gcc.gs_recurse(ctx)
ctx.add_default_files(source_module=True)

View File

@ -0,0 +1,12 @@
/* WARNING! All changes made to this file will be lost! */
#ifndef W_INCLUDE_CONF_UTIL_H_WAF
#define W_INCLUDE_CONF_UTIL_H_WAF
#define UTIL_LITTLE_ENDIAN 1
/* #undef UTIL_BIG_ENDIAN */
#define GS_CONSOLE_HISTORY_LEN 10
#define GS_CONSOLE_INPUT_LEN 100
/* #undef GS_LOG_ENABLE_ISR_LOGS */
#endif /* W_INCLUDE_CONF_UTIL_H_WAF */

View File

@ -0,0 +1,49 @@
#ifndef GS_GOSH_COMMAND_COMMAND_H
#define GS_GOSH_COMMAND_COMMAND_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
Legacy header file - use gs/util/gosh/command.h
*/
#include <gs/util/gosh/command.h>
#ifdef __cplusplus
extern "C" {
#endif
#define CMD_ERROR_NONE GS_OK
#define CMD_ERROR_FAIL GS_ERROR_UNKNOWN
#define CMD_ERROR_SYNTAX GS_ERROR_ARG
#define CMD_ERROR_NOMEM GS_ERROR_ALLOC
#define CMD_ERROR_INVALID GS_ERROR_DATA
#define CMD_ERROR_NOTFOUND GS_ERROR_NOT_FOUND
#define CMD_HIDDEN GS_COMMAND_FLAG_HIDDEN
#define __root_command GS_COMMAND_ROOT
#define __sub_command GS_COMMAND_SUB
#define INIT_CHAIN(__list) GS_COMMAND_INIT_CHAIN(__list)
#define command_register(__cmd) GS_COMMAND_REGISTER(__cmd)
typedef struct command command_t;
static inline const char * command_args(gs_command_context_t *ctx)
{
return gs_command_args(ctx);
}
static inline int command_run(char *line)
{
gs_error_t result = GS_OK;
gs_error_t error = gs_command_run(line, &result);
if (error == GS_OK) {
return result;
}
return error;
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,22 @@
#ifndef GS_GOSH_GOSH_GETOPT_H
#define GS_GOSH_GOSH_GETOPT_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
Legacy header file - use gs/util/gosh/getopt.h
*/
#include <gs/util/gosh/command.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline int gosh_getopt(gs_command_context_t *ctx, const char *opts)
{
return gs_command_getopt(ctx, opts);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,43 @@
#ifndef GS_GOSH_UTIL_CONSOLE_H
#define GS_GOSH_UTIL_CONSOLE_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
Legacy header file - use gs/util/gosh/console.h
*/
#include <gs/util/gosh/console.h>
#include <gs/gosh/command/command.h>
#ifdef __cplusplus
extern "C" {
#endif
static inline int console_init(void)
{
return gs_console_init();
}
static inline int console_exit(void)
{
return gs_console_exit();
}
static inline void console_set_hostname(const char *host)
{
gs_console_set_prompt(host);
}
static inline void console_clear(void)
{
gs_console_clear();
}
static inline void console_update(void)
{
gs_console_update();
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,26 @@
#ifndef DEPRECATED_UTIL_COLOR_PRINTF_H
#define DEPRECATED_UTIL_COLOR_PRINTF_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
#include <gs/util/stdio.h>
typedef enum color_printf_e {
/* Colors */
COLOR_COLORS = GS_COLOR_COLORS,
COLOR_NONE = GS_COLOR_NONE,
COLOR_BLACK = GS_COLOR_BLACK,
COLOR_RED = GS_COLOR_RED,
COLOR_GREEN = GS_COLOR_GREEN,
COLOR_YELLOW = GS_COLOR_YELLOW,
COLOR_BLUE = GS_COLOR_BLUE,
COLOR_MAGENTA = GS_COLOR_MAGENTA,
COLOR_CYAN = GS_COLOR_CYAN,
COLOR_WHITE = GS_COLOR_WHITE,
/* Attributes */
COLOR_ATTRS = GS_COLOR_ATTRS,
COLOR_BOLD = GS_COLOR_BOLD,
} color_printf_t;
#define color_printf gs_color_printf
#endif

View File

@ -0,0 +1,231 @@
/*
Copyright (c) 2008-2014, Troy D. Hanson http://troydhanson.github.com/uthash/
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* a dynamic array implementation using macros
*/
#ifndef UTARRAY_H
#define UTARRAY_H
#define UTARRAY_VERSION 1.9.9
#ifdef __GNUC__
#define _UNUSED_ __attribute__ ((__unused__))
#else
#define _UNUSED_
#endif
#include <stddef.h> /* size_t */
#include <string.h> /* memset, etc */
#include <stdlib.h> /* exit */
#define oom() exit(-1)
typedef void (ctor_f)(void *dst, const void *src);
typedef void (dtor_f)(void *elt);
typedef void (init_f)(void *elt);
typedef struct {
size_t sz;
init_f *init;
ctor_f *copy;
dtor_f *dtor;
} UT_icd;
typedef struct {
unsigned i,n;/* i: index of next available slot, n: num slots */
UT_icd icd; /* initializer, copy and destructor functions */
char *d; /* n slots of size icd->sz*/
} UT_array;
#define utarray_init(a,_icd) do { \
memset(a,0,sizeof(UT_array)); \
(a)->icd=*_icd; \
} while(0)
#define utarray_done(a) do { \
if ((a)->n) { \
if ((a)->icd.dtor) { \
size_t _ut_i; \
for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \
(a)->icd.dtor(utarray_eltptr(a,_ut_i)); \
} \
} \
free((a)->d); \
} \
(a)->n=0; \
} while(0)
#define utarray_new(a,_icd) do { \
a=(UT_array*)malloc(sizeof(UT_array)); \
utarray_init(a,_icd); \
} while(0)
#define utarray_free(a) do { \
utarray_done(a); \
free(a); \
} while(0)
#define utarray_reserve(a,by) do { \
if (((a)->i+by) > ((a)->n)) { \
while(((a)->i+by) > ((a)->n)) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \
if ( ((a)->d=(char*)realloc((a)->d, (a)->n*(a)->icd.sz)) == NULL) oom(); \
} \
} while(0)
#define utarray_push_back(a,p) do { \
utarray_reserve(a,1); \
if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \
else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \
} while(0)
#define utarray_pop_back(a) do { \
if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \
else { (a)->i--; } \
} while(0)
#define utarray_extend_back(a) do { \
utarray_reserve(a,1); \
if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \
else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \
(a)->i++; \
} while(0)
#define utarray_len(a) ((a)->i)
#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL)
#define _utarray_eltptr(a,j) ((char*)((a)->d + ((a)->icd.sz*(j) )))
#define utarray_insert(a,p,j) do { \
if (j > (a)->i) utarray_resize(a,j); \
utarray_reserve(a,1); \
if ((j) < (a)->i) { \
memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \
((a)->i - (j))*((a)->icd.sz)); \
} \
if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \
else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \
(a)->i++; \
} while(0)
#define utarray_inserta(a,w,j) do { \
if (utarray_len(w) == 0) break; \
if (j > (a)->i) utarray_resize(a,j); \
utarray_reserve(a,utarray_len(w)); \
if ((j) < (a)->i) { \
memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \
_utarray_eltptr(a,j), \
((a)->i - (j))*((a)->icd.sz)); \
} \
if ((a)->icd.copy) { \
size_t _ut_i; \
for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \
(a)->icd.copy(_utarray_eltptr(a,j+_ut_i), _utarray_eltptr(w,_ut_i)); \
} \
} else { \
memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \
utarray_len(w)*((a)->icd.sz)); \
} \
(a)->i += utarray_len(w); \
} while(0)
#define utarray_resize(dst,num) do { \
size_t _ut_i; \
if (dst->i > (size_t)(num)) { \
if ((dst)->icd.dtor) { \
for(_ut_i=num; _ut_i < dst->i; _ut_i++) { \
(dst)->icd.dtor(utarray_eltptr(dst,_ut_i)); \
} \
} \
} else if (dst->i < (size_t)(num)) { \
utarray_reserve(dst,num-dst->i); \
if ((dst)->icd.init) { \
for(_ut_i=dst->i; _ut_i < num; _ut_i++) { \
(dst)->icd.init(utarray_eltptr(dst,_ut_i)); \
} \
} else { \
memset(_utarray_eltptr(dst,dst->i),0,(dst)->icd.sz*(num-dst->i)); \
} \
} \
dst->i = num; \
} while(0)
#define utarray_concat(dst,src) do { \
utarray_inserta((dst),(src),utarray_len(dst)); \
} while(0)
#define utarray_erase(a,pos,len) do { \
if ((a)->icd.dtor) { \
size_t _ut_i; \
for(_ut_i=0; _ut_i < len; _ut_i++) { \
(a)->icd.dtor(utarray_eltptr((a),pos+_ut_i)); \
} \
} \
if ((a)->i > (pos+len)) { \
memmove( _utarray_eltptr((a),pos), _utarray_eltptr((a),pos+len), \
(((a)->i)-(pos+len))*((a)->icd.sz)); \
} \
(a)->i -= (len); \
} while(0)
#define utarray_renew(a,u) do { \
if (a) utarray_clear(a); \
else utarray_new((a),(u)); \
} while(0)
#define utarray_clear(a) do { \
if ((a)->i > 0) { \
if ((a)->icd.dtor) { \
size_t _ut_i; \
for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \
(a)->icd.dtor(utarray_eltptr(a,_ut_i)); \
} \
} \
(a)->i = 0; \
} \
} while(0)
#define utarray_sort(a,cmp) do { \
qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \
} while(0)
#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp)
#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL)
#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : ((((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL))
#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) > 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL))
#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL)
#define utarray_eltidx(a,e) (((char*)(e) >= (char*)((a)->d)) ? (((char*)(e) - (char*)((a)->d))/(size_t)(a)->icd.sz) : -1)
/* last we pre-define a few icd for common utarrays of ints and strings */
static void utarray_str_cpy(void *dst, const void *src) {
char **_src = (char**)src, **_dst = (char**)dst;
*_dst = (*_src == NULL) ? NULL : strdup(*_src);
}
static void utarray_str_dtor(void *elt) {
char **eltc = (char**)elt;
if (*eltc) free(*eltc);
}
static const UT_icd ut_str_icd _UNUSED_ = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor};
static const UT_icd ut_int_icd _UNUSED_ = {sizeof(int),NULL,NULL,NULL};
static const UT_icd ut_ptr_icd _UNUSED_ = {sizeof(void*),NULL,NULL,NULL};
#endif /* UTARRAY_H */

View File

@ -0,0 +1,960 @@
/*
Copyright (c) 2003-2014, Troy D. Hanson http://troydhanson.github.com/uthash/
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef UTHASH_H
#define UTHASH_H
#include <string.h> /* memcmp,strlen */
#include <stddef.h> /* ptrdiff_t */
#include <stdlib.h> /* exit() */
/* These macros use decltype or the earlier __typeof GNU extension.
As decltype is only available in newer compilers (VS2010 or gcc 4.3+
when compiling c++ source) this code uses whatever method is needed
or, for VS2008 where neither is available, uses casting workarounds. */
#if defined(_MSC_VER) /* MS compiler */
#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
#define DECLTYPE(x) (decltype(x))
#else /* VS2008 or older (or VS2010 in C mode) */
#define NO_DECLTYPE
#define DECLTYPE(x)
#endif
#elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__)
#define NO_DECLTYPE
#define DECLTYPE(x)
#else /* GNU, Sun and other compilers */
#define DECLTYPE(x) (__typeof(x))
#endif
#ifdef NO_DECLTYPE
#define DECLTYPE_ASSIGN(dst,src) \
do { \
char **_da_dst = (char**)(&(dst)); \
*_da_dst = (char*)(src); \
} while(0)
#else
#define DECLTYPE_ASSIGN(dst,src) \
do { \
(dst) = DECLTYPE(dst)(src); \
} while(0)
#endif
/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */
#if defined (_WIN32)
#if defined(_MSC_VER) && _MSC_VER >= 1600
#include <stdint.h>
#elif defined(__WATCOMC__)
#include <stdint.h>
#else
typedef unsigned int uint32_t;
typedef unsigned char uint8_t;
#endif
#else
#include <stdint.h>
#endif
#define UTHASH_VERSION 1.9.9
#ifndef uthash_fatal
#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */
#endif
#ifndef uthash_malloc
#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
#endif
#ifndef uthash_free
#define uthash_free(ptr,sz) free(ptr) /* free fcn */
#endif
#ifndef uthash_noexpand_fyi
#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
#endif
#ifndef uthash_expand_fyi
#define uthash_expand_fyi(tbl) /* can be defined to log expands */
#endif
/* initial number of buckets */
#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */
#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */
#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */
/* calculate the element whose hash handle address is hhe */
#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
#define HASH_FIND(hh,head,keyptr,keylen,out) \
do { \
out=NULL; \
if (head) { \
unsigned _hf_bkt,_hf_hashv; \
HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \
if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \
HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \
keyptr,keylen,out); \
} \
} \
} while (0)
#ifdef HASH_BLOOM
#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM)
#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0)
#define HASH_BLOOM_MAKE(tbl) \
do { \
(tbl)->bloom_nbits = HASH_BLOOM; \
(tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \
memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \
(tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
} while (0)
#define HASH_BLOOM_FREE(tbl) \
do { \
uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
} while (0)
#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8)))
#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8)))
#define HASH_BLOOM_ADD(tbl,hashv) \
HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
#define HASH_BLOOM_TEST(tbl,hashv) \
HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
#else
#define HASH_BLOOM_MAKE(tbl)
#define HASH_BLOOM_FREE(tbl)
#define HASH_BLOOM_ADD(tbl,hashv)
#define HASH_BLOOM_TEST(tbl,hashv) (1)
#define HASH_BLOOM_BYTELEN 0
#endif
#define HASH_MAKE_TABLE(hh,head) \
do { \
(head)->hh.tbl = (UT_hash_table*)uthash_malloc( \
sizeof(UT_hash_table)); \
if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \
memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \
(head)->hh.tbl->tail = &((head)->hh); \
(head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
(head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
(head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
(head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \
memset((head)->hh.tbl->buckets, 0, \
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
HASH_BLOOM_MAKE((head)->hh.tbl); \
(head)->hh.tbl->signature = HASH_SIGNATURE; \
} while(0)
#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add)
#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \
do { \
replaced=NULL; \
HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \
if (replaced!=NULL) { \
HASH_DELETE(hh,head,replaced); \
}; \
HASH_ADD(hh,head,fieldname,keylen_in,add); \
} while(0)
#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
do { \
unsigned _ha_bkt; \
(add)->hh.next = NULL; \
(add)->hh.key = (char*)(keyptr); \
(add)->hh.keylen = (unsigned)(keylen_in); \
if (!(head)) { \
head = (add); \
(head)->hh.prev = NULL; \
HASH_MAKE_TABLE(hh,head); \
} else { \
(head)->hh.tbl->tail->next = (add); \
(add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
(head)->hh.tbl->tail = &((add)->hh); \
} \
(head)->hh.tbl->num_items++; \
(add)->hh.tbl = (head)->hh.tbl; \
HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \
(add)->hh.hashv, _ha_bkt); \
HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \
HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \
HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \
HASH_FSCK(hh,head); \
} while(0)
#define HASH_TO_BKT( hashv, num_bkts, bkt ) \
do { \
bkt = ((hashv) & ((num_bkts) - 1)); \
} while(0)
/* delete "delptr" from the hash table.
* "the usual" patch-up process for the app-order doubly-linked-list.
* The use of _hd_hh_del below deserves special explanation.
* These used to be expressed using (delptr) but that led to a bug
* if someone used the same symbol for the head and deletee, like
* HASH_DELETE(hh,users,users);
* We want that to work, but by changing the head (users) below
* we were forfeiting our ability to further refer to the deletee (users)
* in the patch-up process. Solution: use scratch space to
* copy the deletee pointer, then the latter references are via that
* scratch pointer rather than through the repointed (users) symbol.
*/
#define HASH_DELETE(hh,head,delptr) \
do { \
struct UT_hash_handle *_hd_hh_del; \
if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \
uthash_free((head)->hh.tbl->buckets, \
(head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
HASH_BLOOM_FREE((head)->hh.tbl); \
uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
head = NULL; \
} else { \
unsigned _hd_bkt; \
_hd_hh_del = &((delptr)->hh); \
if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \
(head)->hh.tbl->tail = \
(UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
(head)->hh.tbl->hho); \
} \
if ((delptr)->hh.prev) { \
((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
(head)->hh.tbl->hho))->next = (delptr)->hh.next; \
} else { \
DECLTYPE_ASSIGN(head,(delptr)->hh.next); \
} \
if (_hd_hh_del->next) { \
((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \
(head)->hh.tbl->hho))->prev = \
_hd_hh_del->prev; \
} \
HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
(head)->hh.tbl->num_items--; \
} \
HASH_FSCK(hh,head); \
} while (0)
/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
#define HASH_FIND_STR(head,findstr,out) \
HASH_FIND(hh,head,findstr,(unsigned)strlen(findstr),out)
#define HASH_ADD_STR(head,strfield,add) \
HASH_ADD(hh,head,strfield[0],strlen(add->strfield),add)
#define HASH_REPLACE_STR(head,strfield,add,replaced) \
HASH_REPLACE(hh,head,strfield[0],(unsigned)strlen(add->strfield),add,replaced)
#define HASH_FIND_INT(head,findint,out) \
HASH_FIND(hh,head,findint,sizeof(int),out)
#define HASH_ADD_INT(head,intfield,add) \
HASH_ADD(hh,head,intfield,sizeof(int),add)
#define HASH_REPLACE_INT(head,intfield,add,replaced) \
HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
#define HASH_FIND_PTR(head,findptr,out) \
HASH_FIND(hh,head,findptr,sizeof(void *),out)
#define HASH_ADD_PTR(head,ptrfield,add) \
HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \
HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
#define HASH_DEL(head,delptr) \
HASH_DELETE(hh,head,delptr)
/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
* This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
*/
#ifdef HASH_DEBUG
#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
#define HASH_FSCK(hh,head) \
do { \
struct UT_hash_handle *_thh; \
if (head) { \
unsigned _bkt_i; \
unsigned _count; \
char *_prev; \
_count = 0; \
for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \
unsigned _bkt_count = 0; \
_thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
_prev = NULL; \
while (_thh) { \
if (_prev != (char*)(_thh->hh_prev)) { \
HASH_OOPS("invalid hh_prev %p, actual %p\n", \
_thh->hh_prev, _prev ); \
} \
_bkt_count++; \
_prev = (char*)(_thh); \
_thh = _thh->hh_next; \
} \
_count += _bkt_count; \
if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
HASH_OOPS("invalid bucket count %u, actual %u\n", \
(head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
} \
} \
if (_count != (head)->hh.tbl->num_items) { \
HASH_OOPS("invalid hh item count %u, actual %u\n", \
(head)->hh.tbl->num_items, _count ); \
} \
/* traverse hh in app order; check next/prev integrity, count */ \
_count = 0; \
_prev = NULL; \
_thh = &(head)->hh; \
while (_thh) { \
_count++; \
if (_prev !=(char*)(_thh->prev)) { \
HASH_OOPS("invalid prev %p, actual %p\n", \
_thh->prev, _prev ); \
} \
_prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
_thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \
(head)->hh.tbl->hho) : NULL ); \
} \
if (_count != (head)->hh.tbl->num_items) { \
HASH_OOPS("invalid app item count %u, actual %u\n", \
(head)->hh.tbl->num_items, _count ); \
} \
} \
} while (0)
#else
#define HASH_FSCK(hh,head)
#endif
/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
* the descriptor to which this macro is defined for tuning the hash function.
* The app can #include <unistd.h> to get the prototype for write(2). */
#ifdef HASH_EMIT_KEYS
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
do { \
unsigned _klen = fieldlen; \
write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
write(HASH_EMIT_KEYS, keyptr, fieldlen); \
} while (0)
#else
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
#endif
/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
#ifdef HASH_FUNCTION
#define HASH_FCN HASH_FUNCTION
#else
#define HASH_FCN HASH_JEN
#endif
/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */
#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _hb_keylen=keylen; \
char *_hb_key=(char*)(key); \
(hashv) = 0; \
while (_hb_keylen--) { (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; } \
bkt = (hashv) & (num_bkts-1); \
} while (0)
/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
* http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _sx_i; \
char *_hs_key=(char*)(key); \
hashv = 0; \
for(_sx_i=0; _sx_i < keylen; _sx_i++) \
hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
bkt = hashv & (num_bkts-1); \
} while (0)
/* FNV-1a variation */
#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _fn_i; \
char *_hf_key=(char*)(key); \
hashv = 2166136261UL; \
for(_fn_i=0; _fn_i < keylen; _fn_i++) { \
hashv = hashv ^ _hf_key[_fn_i]; \
hashv = hashv * 16777619; \
} \
bkt = hashv & (num_bkts-1); \
} while(0)
#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _ho_i; \
char *_ho_key=(char*)(key); \
hashv = 0; \
for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
hashv += _ho_key[_ho_i]; \
hashv += (hashv << 10); \
hashv ^= (hashv >> 6); \
} \
hashv += (hashv << 3); \
hashv ^= (hashv >> 11); \
hashv += (hashv << 15); \
bkt = hashv & (num_bkts-1); \
} while(0)
#define HASH_JEN_MIX(a,b,c) \
do { \
a -= b; a -= c; a ^= ( c >> 13 ); \
b -= c; b -= a; b ^= ( a << 8 ); \
c -= a; c -= b; c ^= ( b >> 13 ); \
a -= b; a -= c; a ^= ( c >> 12 ); \
b -= c; b -= a; b ^= ( a << 16 ); \
c -= a; c -= b; c ^= ( b >> 5 ); \
a -= b; a -= c; a ^= ( c >> 3 ); \
b -= c; b -= a; b ^= ( a << 10 ); \
c -= a; c -= b; c ^= ( b >> 15 ); \
} while (0)
#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _hj_i,_hj_j,_hj_k; \
unsigned char *_hj_key=(unsigned char*)(key); \
hashv = 0xfeedbeef; \
_hj_i = _hj_j = 0x9e3779b9; \
_hj_k = (unsigned)(keylen); \
while (_hj_k >= 12) { \
_hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
+ ( (unsigned)_hj_key[2] << 16 ) \
+ ( (unsigned)_hj_key[3] << 24 ) ); \
_hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
+ ( (unsigned)_hj_key[6] << 16 ) \
+ ( (unsigned)_hj_key[7] << 24 ) ); \
hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
+ ( (unsigned)_hj_key[10] << 16 ) \
+ ( (unsigned)_hj_key[11] << 24 ) ); \
\
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
\
_hj_key += 12; \
_hj_k -= 12; \
} \
hashv += keylen; \
switch ( _hj_k ) { \
case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \
case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \
case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \
case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \
case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \
case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \
case 5: _hj_j += _hj_key[4]; \
case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \
case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \
case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \
case 1: _hj_i += _hj_key[0]; \
} \
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
bkt = hashv & (num_bkts-1); \
} while(0)
/* The Paul Hsieh hash function */
#undef get16bits
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
#define get16bits(d) (*((const uint16_t *) (d)))
#endif
#if !defined (get16bits)
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
+(uint32_t)(((const uint8_t *)(d))[0]) )
#endif
#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned char *_sfh_key=(unsigned char*)(key); \
uint32_t _sfh_tmp, _sfh_len = keylen; \
\
int _sfh_rem = _sfh_len & 3; \
_sfh_len >>= 2; \
hashv = 0xcafebabe; \
\
/* Main loop */ \
for (;_sfh_len > 0; _sfh_len--) { \
hashv += get16bits (_sfh_key); \
_sfh_tmp = (uint32_t)(get16bits (_sfh_key+2)) << 11 ^ hashv; \
hashv = (hashv << 16) ^ _sfh_tmp; \
_sfh_key += 2*sizeof (uint16_t); \
hashv += hashv >> 11; \
} \
\
/* Handle end cases */ \
switch (_sfh_rem) { \
case 3: hashv += get16bits (_sfh_key); \
hashv ^= hashv << 16; \
hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)] << 18); \
hashv += hashv >> 11; \
break; \
case 2: hashv += get16bits (_sfh_key); \
hashv ^= hashv << 11; \
hashv += hashv >> 17; \
break; \
case 1: hashv += *_sfh_key; \
hashv ^= hashv << 10; \
hashv += hashv >> 1; \
} \
\
/* Force "avalanching" of final 127 bits */ \
hashv ^= hashv << 3; \
hashv += hashv >> 5; \
hashv ^= hashv << 4; \
hashv += hashv >> 17; \
hashv ^= hashv << 25; \
hashv += hashv >> 6; \
bkt = hashv & (num_bkts-1); \
} while(0)
#ifdef HASH_USING_NO_STRICT_ALIASING
/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads.
* For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
* MurmurHash uses the faster approach only on CPU's where we know it's safe.
*
* Note the preprocessor built-in defines can be emitted using:
*
* gcc -m64 -dM -E - < /dev/null (on gcc)
* cc -## a.c (where a.c is a simple test file) (Sun Studio)
*/
#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86))
#define MUR_GETBLOCK(p,i) p[i]
#else /* non intel */
#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0)
#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1)
#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2)
#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3)
#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL))
#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__))
#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24))
#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16))
#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8))
#else /* assume little endian non-intel */
#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24))
#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16))
#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8))
#endif
#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \
(MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \
(MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \
MUR_ONE_THREE(p))))
#endif
#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
#define MUR_FMIX(_h) \
do { \
_h ^= _h >> 16; \
_h *= 0x85ebca6b; \
_h ^= _h >> 13; \
_h *= 0xc2b2ae35l; \
_h ^= _h >> 16; \
} while(0)
#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \
do { \
const uint8_t *_mur_data = (const uint8_t*)(key); \
const int _mur_nblocks = (keylen) / 4; \
uint32_t _mur_h1 = 0xf88D5353; \
uint32_t _mur_c1 = 0xcc9e2d51; \
uint32_t _mur_c2 = 0x1b873593; \
uint32_t _mur_k1 = 0; \
const uint8_t *_mur_tail; \
const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \
int _mur_i; \
for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \
_mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \
_mur_k1 *= _mur_c1; \
_mur_k1 = MUR_ROTL32(_mur_k1,15); \
_mur_k1 *= _mur_c2; \
\
_mur_h1 ^= _mur_k1; \
_mur_h1 = MUR_ROTL32(_mur_h1,13); \
_mur_h1 = _mur_h1*5+0xe6546b64; \
} \
_mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \
_mur_k1=0; \
switch((keylen) & 3) { \
case 3: _mur_k1 ^= _mur_tail[2] << 16; \
case 2: _mur_k1 ^= _mur_tail[1] << 8; \
case 1: _mur_k1 ^= _mur_tail[0]; \
_mur_k1 *= _mur_c1; \
_mur_k1 = MUR_ROTL32(_mur_k1,15); \
_mur_k1 *= _mur_c2; \
_mur_h1 ^= _mur_k1; \
} \
_mur_h1 ^= (keylen); \
MUR_FMIX(_mur_h1); \
hashv = _mur_h1; \
bkt = hashv & (num_bkts-1); \
} while(0)
#endif /* HASH_USING_NO_STRICT_ALIASING */
/* key comparison function; return 0 if keys equal */
#define HASH_KEYCMP(a,b,len) memcmp(a,b,len)
/* iterate over items in a known bucket to find desired item */
#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \
do { \
if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \
else out=NULL; \
while (out) { \
if ((out)->hh.keylen == keylen_in) { \
if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \
} \
if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \
else out = NULL; \
} \
} while(0)
/* add an item to a bucket */
#define HASH_ADD_TO_BKT(head,addhh) \
do { \
head.count++; \
(addhh)->hh_next = head.hh_head; \
(addhh)->hh_prev = NULL; \
if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \
(head).hh_head=addhh; \
if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \
&& (addhh)->tbl->noexpand != 1) { \
HASH_EXPAND_BUCKETS((addhh)->tbl); \
} \
} while(0)
/* remove an item from a given bucket */
#define HASH_DEL_IN_BKT(hh,head,hh_del) \
(head).count--; \
if ((head).hh_head == hh_del) { \
(head).hh_head = hh_del->hh_next; \
} \
if (hh_del->hh_prev) { \
hh_del->hh_prev->hh_next = hh_del->hh_next; \
} \
if (hh_del->hh_next) { \
hh_del->hh_next->hh_prev = hh_del->hh_prev; \
}
/* Bucket expansion has the effect of doubling the number of buckets
* and redistributing the items into the new buckets. Ideally the
* items will distribute more or less evenly into the new buckets
* (the extent to which this is true is a measure of the quality of
* the hash function as it applies to the key domain).
*
* With the items distributed into more buckets, the chain length
* (item count) in each bucket is reduced. Thus by expanding buckets
* the hash keeps a bound on the chain length. This bounded chain
* length is the essence of how a hash provides constant time lookup.
*
* The calculation of tbl->ideal_chain_maxlen below deserves some
* explanation. First, keep in mind that we're calculating the ideal
* maximum chain length based on the *new* (doubled) bucket count.
* In fractions this is just n/b (n=number of items,b=new num buckets).
* Since the ideal chain length is an integer, we want to calculate
* ceil(n/b). We don't depend on floating point arithmetic in this
* hash, so to calculate ceil(n/b) with integers we could write
*
* ceil(n/b) = (n/b) + ((n%b)?1:0)
*
* and in fact a previous version of this hash did just that.
* But now we have improved things a bit by recognizing that b is
* always a power of two. We keep its base 2 log handy (call it lb),
* so now we can write this with a bit shift and logical AND:
*
* ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
*
*/
#define HASH_EXPAND_BUCKETS(tbl) \
do { \
unsigned _he_bkt; \
unsigned _he_bkt_i; \
struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
_he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \
memset(_he_new_buckets, 0, \
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
tbl->ideal_chain_maxlen = \
(tbl->num_items >> (tbl->log2_num_buckets+1)) + \
((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \
tbl->nonideal_items = 0; \
for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \
{ \
_he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \
while (_he_thh) { \
_he_hh_nxt = _he_thh->hh_next; \
HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \
_he_newbkt = &(_he_new_buckets[ _he_bkt ]); \
if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \
tbl->nonideal_items++; \
_he_newbkt->expand_mult = _he_newbkt->count / \
tbl->ideal_chain_maxlen; \
} \
_he_thh->hh_prev = NULL; \
_he_thh->hh_next = _he_newbkt->hh_head; \
if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \
_he_thh; \
_he_newbkt->hh_head = _he_thh; \
_he_thh = _he_hh_nxt; \
} \
} \
uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
tbl->num_buckets *= 2; \
tbl->log2_num_buckets++; \
tbl->buckets = _he_new_buckets; \
tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \
(tbl->ineff_expands+1) : 0; \
if (tbl->ineff_expands > 1) { \
tbl->noexpand=1; \
uthash_noexpand_fyi(tbl); \
} \
uthash_expand_fyi(tbl); \
} while(0)
/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
/* Note that HASH_SORT assumes the hash handle name to be hh.
* HASH_SRT was added to allow the hash handle name to be passed in. */
#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
#define HASH_SRT(hh,head,cmpfcn) \
do { \
unsigned _hs_i; \
unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
if (head) { \
_hs_insize = 1; \
_hs_looping = 1; \
_hs_list = &((head)->hh); \
while (_hs_looping) { \
_hs_p = _hs_list; \
_hs_list = NULL; \
_hs_tail = NULL; \
_hs_nmerges = 0; \
while (_hs_p) { \
_hs_nmerges++; \
_hs_q = _hs_p; \
_hs_psize = 0; \
for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \
_hs_psize++; \
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
((void*)((char*)(_hs_q->next) + \
(head)->hh.tbl->hho)) : NULL); \
if (! (_hs_q) ) break; \
} \
_hs_qsize = _hs_insize; \
while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \
if (_hs_psize == 0) { \
_hs_e = _hs_q; \
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
((void*)((char*)(_hs_q->next) + \
(head)->hh.tbl->hho)) : NULL); \
_hs_qsize--; \
} else if ( (_hs_qsize == 0) || !(_hs_q) ) { \
_hs_e = _hs_p; \
if (_hs_p){ \
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
((void*)((char*)(_hs_p->next) + \
(head)->hh.tbl->hho)) : NULL); \
} \
_hs_psize--; \
} else if (( \
cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
) <= 0) { \
_hs_e = _hs_p; \
if (_hs_p){ \
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
((void*)((char*)(_hs_p->next) + \
(head)->hh.tbl->hho)) : NULL); \
} \
_hs_psize--; \
} else { \
_hs_e = _hs_q; \
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
((void*)((char*)(_hs_q->next) + \
(head)->hh.tbl->hho)) : NULL); \
_hs_qsize--; \
} \
if ( _hs_tail ) { \
_hs_tail->next = ((_hs_e) ? \
ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \
} else { \
_hs_list = _hs_e; \
} \
if (_hs_e) { \
_hs_e->prev = ((_hs_tail) ? \
ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \
} \
_hs_tail = _hs_e; \
} \
_hs_p = _hs_q; \
} \
if (_hs_tail){ \
_hs_tail->next = NULL; \
} \
if ( _hs_nmerges <= 1 ) { \
_hs_looping=0; \
(head)->hh.tbl->tail = _hs_tail; \
DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \
} \
_hs_insize *= 2; \
} \
HASH_FSCK(hh,head); \
} \
} while (0)
/* This function selects items from one hash into another hash.
* The end result is that the selected items have dual presence
* in both hashes. There is no copy of the items made; rather
* they are added into the new hash through a secondary hash
* hash handle that must be present in the structure. */
#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
do { \
unsigned _src_bkt, _dst_bkt; \
void *_last_elt=NULL, *_elt; \
UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
if (src) { \
for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
_src_hh; \
_src_hh = _src_hh->hh_next) { \
_elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
if (cond(_elt)) { \
_dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \
_dst_hh->key = _src_hh->key; \
_dst_hh->keylen = _src_hh->keylen; \
_dst_hh->hashv = _src_hh->hashv; \
_dst_hh->prev = _last_elt; \
_dst_hh->next = NULL; \
if (_last_elt_hh) { _last_elt_hh->next = _elt; } \
if (!dst) { \
DECLTYPE_ASSIGN(dst,_elt); \
HASH_MAKE_TABLE(hh_dst,dst); \
} else { \
_dst_hh->tbl = (dst)->hh_dst.tbl; \
} \
HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \
(dst)->hh_dst.tbl->num_items++; \
_last_elt = _elt; \
_last_elt_hh = _dst_hh; \
} \
} \
} \
} \
HASH_FSCK(hh_dst,dst); \
} while (0)
#define HASH_CLEAR(hh,head) \
do { \
if (head) { \
uthash_free((head)->hh.tbl->buckets, \
(head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \
HASH_BLOOM_FREE((head)->hh.tbl); \
uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
(head)=NULL; \
} \
} while(0)
#define HASH_OVERHEAD(hh,head) \
((head) ? ( \
(size_t)((((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \
((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \
(sizeof(UT_hash_table)) + \
(HASH_BLOOM_BYTELEN)))) : 0)
#ifdef NO_DECLTYPE
#define HASH_ITER(hh,head,el,tmp) \
for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \
el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL))
#else
#define HASH_ITER(hh,head,el,tmp) \
for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \
el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL))
#endif
/* obtain a count of items in the hash */
#define HASH_COUNT(head) HASH_CNT(hh,head)
#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0)
typedef struct UT_hash_bucket {
struct UT_hash_handle *hh_head;
unsigned count;
/* expand_mult is normally set to 0. In this situation, the max chain length
* threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
* the bucket's chain exceeds this length, bucket expansion is triggered).
* However, setting expand_mult to a non-zero value delays bucket expansion
* (that would be triggered by additions to this particular bucket)
* until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
* (The multiplier is simply expand_mult+1). The whole idea of this
* multiplier is to reduce bucket expansions, since they are expensive, in
* situations where we know that a particular bucket tends to be overused.
* It is better to let its chain length grow to a longer yet-still-bounded
* value, than to do an O(n) bucket expansion too often.
*/
unsigned expand_mult;
} UT_hash_bucket;
/* random signature used only to find hash tables in external analysis */
#define HASH_SIGNATURE 0xa0111fe1
#define HASH_BLOOM_SIGNATURE 0xb12220f2
typedef struct UT_hash_table {
UT_hash_bucket *buckets;
unsigned num_buckets, log2_num_buckets;
unsigned num_items;
struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
/* in an ideal situation (all buckets used equally), no bucket would have
* more than ceil(#items/#buckets) items. that's the ideal chain length. */
unsigned ideal_chain_maxlen;
/* nonideal_items is the number of items in the hash whose chain position
* exceeds the ideal chain maxlen. these items pay the penalty for an uneven
* hash distribution; reaching them in a chain traversal takes >ideal steps */
unsigned nonideal_items;
/* ineffective expands occur when a bucket doubling was performed, but
* afterward, more than half the items in the hash had nonideal chain
* positions. If this happens on two consecutive expansions we inhibit any
* further expansion, as it's not helping; this happens when the hash
* function isn't a good fit for the key domain. When expansion is inhibited
* the hash will still work, albeit no longer in constant time. */
unsigned ineff_expands, noexpand;
uint32_t signature; /* used only to find hash tables in external analysis */
#ifdef HASH_BLOOM
uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
uint8_t *bloom_bv;
char bloom_nbits;
#endif
} UT_hash_table;
typedef struct UT_hash_handle {
struct UT_hash_table *tbl;
void *prev; /* prev element in app order */
void *next; /* next element in app order */
struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
struct UT_hash_handle *hh_next; /* next hh in bucket order */
void *key; /* ptr to enclosing struct's key */
unsigned keylen; /* enclosing struct's key len */
unsigned hashv; /* result of hash-fcn(key) */
} UT_hash_handle;
#endif /* UTHASH_H */

View File

@ -0,0 +1,757 @@
/*
Copyright (c) 2007-2014, Troy D. Hanson http://troydhanson.github.com/uthash/
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef UTLIST_H
#define UTLIST_H
#define UTLIST_VERSION 1.9.9
#include <assert.h>
/*
* This file contains macros to manipulate singly and doubly-linked lists.
*
* 1. LL_ macros: singly-linked lists.
* 2. DL_ macros: doubly-linked lists.
* 3. CDL_ macros: circular doubly-linked lists.
*
* To use singly-linked lists, your structure must have a "next" pointer.
* To use doubly-linked lists, your structure must "prev" and "next" pointers.
* Either way, the pointer to the head of the list must be initialized to NULL.
*
* ----------------.EXAMPLE -------------------------
* struct item {
* int id;
* struct item *prev, *next;
* }
*
* struct item *list = NULL:
*
* int main() {
* struct item *item;
* ... allocate and populate item ...
* DL_APPEND(list, item);
* }
* --------------------------------------------------
*
* For doubly-linked lists, the append and delete macros are O(1)
* For singly-linked lists, append and delete are O(n) but prepend is O(1)
* The sort macro is O(n log(n)) for all types of single/double/circular lists.
*/
/* These macros use decltype or the earlier __typeof GNU extension.
As decltype is only available in newer compilers (VS2010 or gcc 4.3+
when compiling c++ code), this code uses whatever method is needed
or, for VS2008 where neither is available, uses casting workarounds. */
#ifdef _MSC_VER /* MS compiler */
#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
#define LDECLTYPE(x) decltype(x)
#else /* VS2008 or older (or VS2010 in C mode) */
#define NO_DECLTYPE
#define LDECLTYPE(x) char*
#endif
#elif defined(__ICCARM__)
#define NO_DECLTYPE
#define LDECLTYPE(x) char*
#else /* GNU, Sun and other compilers */
#define LDECLTYPE(x) __typeof(x)
#endif
/* for VS2008 we use some workarounds to get around the lack of decltype,
* namely, we always reassign our tmp variable to the list head if we need
* to dereference its prev/next pointers, and save/restore the real head.*/
#ifdef NO_DECLTYPE
#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); }
#define _NEXT(elt,list,next) ((char*)((list)->next))
#define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); }
/* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */
#define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); }
#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; }
#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); }
#else
#define _SV(elt,list)
#define _NEXT(elt,list,next) ((elt)->next)
#define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to)
/* #define _PREV(elt,list,prev) ((elt)->prev) */
#define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to)
#define _RS(list)
#define _CASTASGN(a,b) (a)=(b)
#endif
/******************************************************************************
* The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort *
* Unwieldy variable names used here to avoid shadowing passed-in variables. *
*****************************************************************************/
#define LL_SORT(list, cmp) \
LL_SORT2(list, cmp, next)
#define LL_SORT2(list, cmp, next) \
do { \
LDECLTYPE(list) _ls_p; \
LDECLTYPE(list) _ls_q; \
LDECLTYPE(list) _ls_e; \
LDECLTYPE(list) _ls_tail; \
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
if (list) { \
_ls_insize = 1; \
_ls_looping = 1; \
while (_ls_looping) { \
_CASTASGN(_ls_p,list); \
list = NULL; \
_ls_tail = NULL; \
_ls_nmerges = 0; \
while (_ls_p) { \
_ls_nmerges++; \
_ls_q = _ls_p; \
_ls_psize = 0; \
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
_ls_psize++; \
_SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \
if (!_ls_q) break; \
} \
_ls_qsize = _ls_insize; \
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
if (_ls_psize == 0) { \
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
} else if (_ls_qsize == 0 || !_ls_q) { \
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
} else if (cmp(_ls_p,_ls_q) <= 0) { \
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
} else { \
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
} \
if (_ls_tail) { \
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \
} else { \
_CASTASGN(list,_ls_e); \
} \
_ls_tail = _ls_e; \
} \
_ls_p = _ls_q; \
} \
if (_ls_tail) { \
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \
} \
if (_ls_nmerges <= 1) { \
_ls_looping=0; \
} \
_ls_insize *= 2; \
} \
} \
} while (0)
#define DL_SORT(list, cmp) \
DL_SORT2(list, cmp, prev, next)
#define DL_SORT2(list, cmp, prev, next) \
do { \
LDECLTYPE(list) _ls_p; \
LDECLTYPE(list) _ls_q; \
LDECLTYPE(list) _ls_e; \
LDECLTYPE(list) _ls_tail; \
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
if (list) { \
_ls_insize = 1; \
_ls_looping = 1; \
while (_ls_looping) { \
_CASTASGN(_ls_p,list); \
list = NULL; \
_ls_tail = NULL; \
_ls_nmerges = 0; \
while (_ls_p) { \
_ls_nmerges++; \
_ls_q = _ls_p; \
_ls_psize = 0; \
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
_ls_psize++; \
_SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \
if (!_ls_q) break; \
} \
_ls_qsize = _ls_insize; \
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
if (_ls_psize == 0) { \
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
} else if (_ls_qsize == 0 || !_ls_q) { \
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
} else if (cmp(_ls_p,_ls_q) <= 0) { \
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
} else { \
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
} \
if (_ls_tail) { \
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \
} else { \
_CASTASGN(list,_ls_e); \
} \
_SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \
_ls_tail = _ls_e; \
} \
_ls_p = _ls_q; \
} \
_CASTASGN(list->prev, _ls_tail); \
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \
if (_ls_nmerges <= 1) { \
_ls_looping=0; \
} \
_ls_insize *= 2; \
} \
} \
} while (0)
#define CDL_SORT(list, cmp) \
CDL_SORT2(list, cmp, prev, next)
#define CDL_SORT2(list, cmp, prev, next) \
do { \
LDECLTYPE(list) _ls_p; \
LDECLTYPE(list) _ls_q; \
LDECLTYPE(list) _ls_e; \
LDECLTYPE(list) _ls_tail; \
LDECLTYPE(list) _ls_oldhead; \
LDECLTYPE(list) _tmp; \
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
if (list) { \
_ls_insize = 1; \
_ls_looping = 1; \
while (_ls_looping) { \
_CASTASGN(_ls_p,list); \
_CASTASGN(_ls_oldhead,list); \
list = NULL; \
_ls_tail = NULL; \
_ls_nmerges = 0; \
while (_ls_p) { \
_ls_nmerges++; \
_ls_q = _ls_p; \
_ls_psize = 0; \
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
_ls_psize++; \
_SV(_ls_q,list); \
if (_NEXT(_ls_q,list,next) == _ls_oldhead) { \
_ls_q = NULL; \
} else { \
_ls_q = _NEXT(_ls_q,list,next); \
} \
_RS(list); \
if (!_ls_q) break; \
} \
_ls_qsize = _ls_insize; \
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
if (_ls_psize == 0) { \
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \
} else if (_ls_qsize == 0 || !_ls_q) { \
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \
} else if (cmp(_ls_p,_ls_q) <= 0) { \
_ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
_NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \
} else { \
_ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
_NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \
} \
if (_ls_tail) { \
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \
} else { \
_CASTASGN(list,_ls_e); \
} \
_SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \
_ls_tail = _ls_e; \
} \
_ls_p = _ls_q; \
} \
_CASTASGN(list->prev,_ls_tail); \
_CASTASGN(_tmp,list); \
_SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list); \
if (_ls_nmerges <= 1) { \
_ls_looping=0; \
} \
_ls_insize *= 2; \
} \
} \
} while (0)
/******************************************************************************
* singly linked list macros (non-circular) *
*****************************************************************************/
#define LL_PREPEND(head,add) \
LL_PREPEND2(head,add,next)
#define LL_PREPEND2(head,add,next) \
do { \
(add)->next = head; \
head = add; \
} while (0)
#define LL_CONCAT(head1,head2) \
LL_CONCAT2(head1,head2,next)
#define LL_CONCAT2(head1,head2,next) \
do { \
LDECLTYPE(head1) _tmp; \
if (head1) { \
_tmp = head1; \
while (_tmp->next) { _tmp = _tmp->next; } \
_tmp->next=(head2); \
} else { \
(head1)=(head2); \
} \
} while (0)
#define LL_APPEND(head,add) \
LL_APPEND2(head,add,next)
#define LL_APPEND2(head,add,next) \
do { \
LDECLTYPE(head) _tmp; \
(add)->next=NULL; \
if (head) { \
_tmp = head; \
while (_tmp->next) { _tmp = _tmp->next; } \
_tmp->next=(add); \
} else { \
(head)=(add); \
} \
} while (0)
#define LL_DELETE(head,del) \
LL_DELETE2(head,del,next)
#define LL_DELETE2(head,del,next) \
do { \
LDECLTYPE(head) _tmp; \
if ((head) == (del)) { \
(head)=(head)->next; \
} else { \
_tmp = head; \
while (_tmp->next && (_tmp->next != (del))) { \
_tmp = _tmp->next; \
} \
if (_tmp->next) { \
_tmp->next = ((del)->next); \
} \
} \
} while (0)
/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */
#define LL_APPEND_VS2008(head,add) \
LL_APPEND2_VS2008(head,add,next)
#define LL_APPEND2_VS2008(head,add,next) \
do { \
if (head) { \
(add)->next = head; /* use add->next as a temp variable */ \
while ((add)->next->next) { (add)->next = (add)->next->next; } \
(add)->next->next=(add); \
} else { \
(head)=(add); \
} \
(add)->next=NULL; \
} while (0)
#define LL_DELETE_VS2008(head,del) \
LL_DELETE2_VS2008(head,del,next)
#define LL_DELETE2_VS2008(head,del,next) \
do { \
if ((head) == (del)) { \
(head)=(head)->next; \
} else { \
char *_tmp = (char*)(head); \
while ((head)->next && ((head)->next != (del))) { \
head = (head)->next; \
} \
if ((head)->next) { \
(head)->next = ((del)->next); \
} \
{ \
char **_head_alias = (char**)&(head); \
*_head_alias = _tmp; \
} \
} \
} while (0)
#ifdef NO_DECLTYPE
#undef LL_APPEND
#define LL_APPEND LL_APPEND_VS2008
#undef LL_DELETE
#define LL_DELETE LL_DELETE_VS2008
#undef LL_DELETE2
#define LL_DELETE2 LL_DELETE2_VS2008
#undef LL_APPEND2
#define LL_APPEND2 LL_APPEND2_VS2008
#undef LL_CONCAT /* no LL_CONCAT_VS2008 */
#undef DL_CONCAT /* no DL_CONCAT_VS2008 */
#endif
/* end VS2008 replacements */
#define LL_COUNT(head,el,counter) \
LL_COUNT2(head,el,counter,next) \
#define LL_COUNT2(head,el,counter,next) \
{ \
counter = 0; \
LL_FOREACH2(head,el,next){ ++counter; } \
}
#define LL_FOREACH(head,el) \
LL_FOREACH2(head,el,next)
#define LL_FOREACH2(head,el,next) \
for(el=head;el;el=(el)->next)
#define LL_FOREACH_SAFE(head,el,tmp) \
LL_FOREACH_SAFE2(head,el,tmp,next)
#define LL_FOREACH_SAFE2(head,el,tmp,next) \
for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
#define LL_SEARCH_SCALAR(head,out,field,val) \
LL_SEARCH_SCALAR2(head,out,field,val,next)
#define LL_SEARCH_SCALAR2(head,out,field,val,next) \
do { \
LL_FOREACH2(head,out,next) { \
if ((out)->field == (val)) break; \
} \
} while(0)
#define LL_SEARCH(head,out,elt,cmp) \
LL_SEARCH2(head,out,elt,cmp,next)
#define LL_SEARCH2(head,out,elt,cmp,next) \
do { \
LL_FOREACH2(head,out,next) { \
if ((cmp(out,elt))==0) break; \
} \
} while(0)
#define LL_REPLACE_ELEM(head, el, add) \
do { \
LDECLTYPE(head) _tmp; \
assert(head != NULL); \
assert(el != NULL); \
assert(add != NULL); \
(add)->next = (el)->next; \
if ((head) == (el)) { \
(head) = (add); \
} else { \
_tmp = head; \
while (_tmp->next && (_tmp->next != (el))) { \
_tmp = _tmp->next; \
} \
if (_tmp->next) { \
_tmp->next = (add); \
} \
} \
} while (0)
#define LL_PREPEND_ELEM(head, el, add) \
do { \
LDECLTYPE(head) _tmp; \
assert(head != NULL); \
assert(el != NULL); \
assert(add != NULL); \
(add)->next = (el); \
if ((head) == (el)) { \
(head) = (add); \
} else { \
_tmp = head; \
while (_tmp->next && (_tmp->next != (el))) { \
_tmp = _tmp->next; \
} \
if (_tmp->next) { \
_tmp->next = (add); \
} \
} \
} while (0) \
/******************************************************************************
* doubly linked list macros (non-circular) *
*****************************************************************************/
#define DL_PREPEND(head,add) \
DL_PREPEND2(head,add,prev,next)
#define DL_PREPEND2(head,add,prev,next) \
do { \
(add)->next = head; \
if (head) { \
(add)->prev = (head)->prev; \
(head)->prev = (add); \
} else { \
(add)->prev = (add); \
} \
(head) = (add); \
} while (0)
#define DL_APPEND(head,add) \
DL_APPEND2(head,add,prev,next)
#define DL_APPEND2(head,add,prev,next) \
do { \
if (head) { \
(add)->prev = (head)->prev; \
(head)->prev->next = (add); \
(head)->prev = (add); \
(add)->next = NULL; \
} else { \
(head)=(add); \
(head)->prev = (head); \
(head)->next = NULL; \
} \
} while (0)
#define DL_CONCAT(head1,head2) \
DL_CONCAT2(head1,head2,prev,next)
#define DL_CONCAT2(head1,head2,prev,next) \
do { \
LDECLTYPE(head1) _tmp; \
if (head2) { \
if (head1) { \
_tmp = (head2)->prev; \
(head2)->prev = (head1)->prev; \
(head1)->prev->next = (head2); \
(head1)->prev = _tmp; \
} else { \
(head1)=(head2); \
} \
} \
} while (0)
#define DL_DELETE(head,del) \
DL_DELETE2(head,del,prev,next)
#define DL_DELETE2(head,del,prev,next) \
do { \
assert((del)->prev != NULL); \
if ((del)->prev == (del)) { \
(head)=NULL; \
} else if ((del)==(head)) { \
(del)->next->prev = (del)->prev; \
(head) = (del)->next; \
} else { \
(del)->prev->next = (del)->next; \
if ((del)->next) { \
(del)->next->prev = (del)->prev; \
} else { \
(head)->prev = (del)->prev; \
} \
} \
} while (0)
#define DL_COUNT(head,el,counter) \
DL_COUNT2(head,el,counter,next) \
#define DL_COUNT2(head,el,counter,next) \
{ \
counter = 0; \
DL_FOREACH2(head,el,next){ ++counter; } \
}
#define DL_FOREACH(head,el) \
DL_FOREACH2(head,el,next)
#define DL_FOREACH2(head,el,next) \
for(el=head;el;el=(el)->next)
/* this version is safe for deleting the elements during iteration */
#define DL_FOREACH_SAFE(head,el,tmp) \
DL_FOREACH_SAFE2(head,el,tmp,next)
#define DL_FOREACH_SAFE2(head,el,tmp,next) \
for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
/* these are identical to their singly-linked list counterparts */
#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR
#define DL_SEARCH LL_SEARCH
#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2
#define DL_SEARCH2 LL_SEARCH2
#define DL_REPLACE_ELEM(head, el, add) \
do { \
assert(head != NULL); \
assert(el != NULL); \
assert(add != NULL); \
if ((head) == (el)) { \
(head) = (add); \
(add)->next = (el)->next; \
if ((el)->next == NULL) { \
(add)->prev = (add); \
} else { \
(add)->prev = (el)->prev; \
(add)->next->prev = (add); \
} \
} else { \
(add)->next = (el)->next; \
(add)->prev = (el)->prev; \
(add)->prev->next = (add); \
if ((el)->next == NULL) { \
(head)->prev = (add); \
} else { \
(add)->next->prev = (add); \
} \
} \
} while (0)
#define DL_PREPEND_ELEM(head, el, add) \
do { \
assert(head != NULL); \
assert(el != NULL); \
assert(add != NULL); \
(add)->next = (el); \
(add)->prev = (el)->prev; \
(el)->prev = (add); \
if ((head) == (el)) { \
(head) = (add); \
} else { \
(add)->prev->next = (add); \
} \
} while (0) \
/******************************************************************************
* circular doubly linked list macros *
*****************************************************************************/
#define CDL_PREPEND(head,add) \
CDL_PREPEND2(head,add,prev,next)
#define CDL_PREPEND2(head,add,prev,next) \
do { \
if (head) { \
(add)->prev = (head)->prev; \
(add)->next = (head); \
(head)->prev = (add); \
(add)->prev->next = (add); \
} else { \
(add)->prev = (add); \
(add)->next = (add); \
} \
(head)=(add); \
} while (0)
#define CDL_DELETE(head,del) \
CDL_DELETE2(head,del,prev,next)
#define CDL_DELETE2(head,del,prev,next) \
do { \
if ( ((head)==(del)) && ((head)->next == (head))) { \
(head) = 0L; \
} else { \
(del)->next->prev = (del)->prev; \
(del)->prev->next = (del)->next; \
if ((del) == (head)) (head)=(del)->next; \
} \
} while (0)
#define CDL_COUNT(head,el,counter) \
CDL_COUNT2(head,el,counter,next) \
#define CDL_COUNT2(head, el, counter,next) \
{ \
counter = 0; \
CDL_FOREACH2(head,el,next){ ++counter; } \
}
#define CDL_FOREACH(head,el) \
CDL_FOREACH2(head,el,next)
#define CDL_FOREACH2(head,el,next) \
for(el=head;el;el=((el)->next==head ? 0L : (el)->next))
#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \
CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next)
#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \
for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \
(el) && ((tmp2)=(el)->next, 1); \
((el) = (((el)==(tmp1)) ? 0L : (tmp2))))
#define CDL_SEARCH_SCALAR(head,out,field,val) \
CDL_SEARCH_SCALAR2(head,out,field,val,next)
#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \
do { \
CDL_FOREACH2(head,out,next) { \
if ((out)->field == (val)) break; \
} \
} while(0)
#define CDL_SEARCH(head,out,elt,cmp) \
CDL_SEARCH2(head,out,elt,cmp,next)
#define CDL_SEARCH2(head,out,elt,cmp,next) \
do { \
CDL_FOREACH2(head,out,next) { \
if ((cmp(out,elt))==0) break; \
} \
} while(0)
#define CDL_REPLACE_ELEM(head, el, add) \
do { \
assert(head != NULL); \
assert(el != NULL); \
assert(add != NULL); \
if ((el)->next == (el)) { \
(add)->next = (add); \
(add)->prev = (add); \
(head) = (add); \
} else { \
(add)->next = (el)->next; \
(add)->prev = (el)->prev; \
(add)->next->prev = (add); \
(add)->prev->next = (add); \
if ((head) == (el)) { \
(head) = (add); \
} \
} \
} while (0)
#define CDL_PREPEND_ELEM(head, el, add) \
do { \
assert(head != NULL); \
assert(el != NULL); \
assert(add != NULL); \
(add)->next = (el); \
(add)->prev = (el)->prev; \
(el)->prev = (add); \
(add)->prev->next = (add); \
if ((head) == (el)) { \
(head) = (add); \
} \
} while (0) \
#endif /* UTLIST_H */

View File

@ -0,0 +1,393 @@
/*
Copyright (c) 2008-2014, Troy D. Hanson http://troydhanson.github.com/uthash/
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* a dynamic string implementation using macros
*/
#ifndef UTSTRING_H
#define UTSTRING_H
#define UTSTRING_VERSION 1.9.9
#ifdef __GNUC__
#define _UNUSED_ __attribute__ ((__unused__))
#else
#define _UNUSED_
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#define oom() exit(-1)
typedef struct {
char *d;
size_t n; /* allocd size */
size_t i; /* index of first unused byte */
} UT_string;
#define utstring_reserve(s,amt) \
do { \
if (((s)->n - (s)->i) < (size_t)(amt)) { \
(s)->d = (char*)realloc((s)->d, (s)->n + amt); \
if ((s)->d == NULL) oom(); \
(s)->n += amt; \
} \
} while(0)
#define utstring_init(s) \
do { \
(s)->n = 0; (s)->i = 0; (s)->d = NULL; \
utstring_reserve(s,100); \
(s)->d[0] = '\0'; \
} while(0)
#define utstring_done(s) \
do { \
if ((s)->d != NULL) free((s)->d); \
(s)->n = 0; \
} while(0)
#define utstring_free(s) \
do { \
utstring_done(s); \
free(s); \
} while(0)
#define utstring_new(s) \
do { \
s = (UT_string*)calloc(sizeof(UT_string),1); \
if (!s) oom(); \
utstring_init(s); \
} while(0)
#define utstring_renew(s) \
do { \
if (s) { \
utstring_clear(s); \
} else { \
utstring_new(s); \
} \
} while(0)
#define utstring_clear(s) \
do { \
(s)->i = 0; \
(s)->d[0] = '\0'; \
} while(0)
#define utstring_bincpy(s,b,l) \
do { \
utstring_reserve((s),(l)+1); \
if (l) memcpy(&(s)->d[(s)->i], b, l); \
(s)->i += l; \
(s)->d[(s)->i]='\0'; \
} while(0)
#define utstring_concat(dst,src) \
do { \
utstring_reserve((dst),((src)->i)+1); \
if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \
(dst)->i += (src)->i; \
(dst)->d[(dst)->i]='\0'; \
} while(0)
#define utstring_len(s) ((unsigned)((s)->i))
#define utstring_body(s) ((s)->d)
_UNUSED_ static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) {
int n;
va_list cp;
while (1) {
#ifdef _WIN32
cp = ap;
#else
va_copy(cp, ap);
#endif
n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp);
va_end(cp);
if ((n > -1) && ((size_t) n < (s->n-s->i))) {
s->i += n;
return;
}
/* Else try again with more space. */
if (n > -1) utstring_reserve(s,n+1); /* exact */
else utstring_reserve(s,(s->n)*2); /* 2x */
}
}
#ifdef __GNUC__
/* support printf format checking (2=the format string, 3=start of varargs) */
static void utstring_printf(UT_string *s, const char *fmt, ...)
__attribute__ (( format( printf, 2, 3) ));
#endif
_UNUSED_ static void utstring_printf(UT_string *s, const char *fmt, ...) {
va_list ap;
va_start(ap,fmt);
utstring_printf_va(s,fmt,ap);
va_end(ap);
}
/*******************************************************************************
* begin substring search functions *
******************************************************************************/
/* Build KMP table from left to right. */
_UNUSED_ static void _utstring_BuildTable(
const char *P_Needle,
size_t P_NeedleLen,
long *P_KMP_Table)
{
long i, j;
i = 0;
j = i - 1;
P_KMP_Table[i] = j;
while (i < (long) P_NeedleLen)
{
while ( (j > -1) && (P_Needle[i] != P_Needle[j]) )
{
j = P_KMP_Table[j];
}
i++;
j++;
if (i < (long) P_NeedleLen)
{
if (P_Needle[i] == P_Needle[j])
{
P_KMP_Table[i] = P_KMP_Table[j];
}
else
{
P_KMP_Table[i] = j;
}
}
else
{
P_KMP_Table[i] = j;
}
}
return;
}
/* Build KMP table from right to left. */
_UNUSED_ static void _utstring_BuildTableR(
const char *P_Needle,
size_t P_NeedleLen,
long *P_KMP_Table)
{
long i, j;
i = P_NeedleLen - 1;
j = i + 1;
P_KMP_Table[i + 1] = j;
while (i >= 0)
{
while ( (j < (long) P_NeedleLen) && (P_Needle[i] != P_Needle[j]) )
{
j = P_KMP_Table[j + 1];
}
i--;
j--;
if (i >= 0)
{
if (P_Needle[i] == P_Needle[j])
{
P_KMP_Table[i + 1] = P_KMP_Table[j + 1];
}
else
{
P_KMP_Table[i + 1] = j;
}
}
else
{
P_KMP_Table[i + 1] = j;
}
}
return;
}
/* Search data from left to right. ( Multiple search mode. ) */
_UNUSED_ static long _utstring_find(
const char *P_Haystack,
size_t P_HaystackLen,
const char *P_Needle,
size_t P_NeedleLen,
long *P_KMP_Table)
{
long i, j;
long V_FindPosition = -1;
/* Search from left to right. */
i = j = 0;
while ( (j < (int)P_HaystackLen) && (((P_HaystackLen - j) + i) >= P_NeedleLen) )
{
while ( (i > -1) && (P_Needle[i] != P_Haystack[j]) )
{
i = P_KMP_Table[i];
}
i++;
j++;
if (i >= (int)P_NeedleLen)
{
/* Found. */
V_FindPosition = j - i;
break;
}
}
return V_FindPosition;
}
/* Search data from right to left. ( Multiple search mode. ) */
_UNUSED_ static long _utstring_findR(
const char *P_Haystack,
size_t P_HaystackLen,
const char *P_Needle,
size_t P_NeedleLen,
long *P_KMP_Table)
{
long i, j;
long V_FindPosition = -1;
/* Search from right to left. */
j = (P_HaystackLen - 1);
i = (P_NeedleLen - 1);
while ( (j >= 0) && (j >= i) )
{
while ( (i < (int)P_NeedleLen) && (P_Needle[i] != P_Haystack[j]) )
{
i = P_KMP_Table[i + 1];
}
i--;
j--;
if (i < 0)
{
/* Found. */
V_FindPosition = j + 1;
break;
}
}
return V_FindPosition;
}
/* Search data from left to right. ( One time search mode. ) */
_UNUSED_ static long utstring_find(
UT_string *s,
long P_StartPosition, /* Start from 0. -1 means last position. */
const char *P_Needle,
size_t P_NeedleLen)
{
long V_StartPosition;
long V_HaystackLen;
long *V_KMP_Table;
long V_FindPosition = -1;
if (P_StartPosition < 0)
{
V_StartPosition = s->i + P_StartPosition;
}
else
{
V_StartPosition = P_StartPosition;
}
V_HaystackLen = s->i - V_StartPosition;
if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) )
{
V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1));
if (V_KMP_Table != NULL)
{
_utstring_BuildTable(P_Needle, P_NeedleLen, V_KMP_Table);
V_FindPosition = _utstring_find(s->d + V_StartPosition,
V_HaystackLen,
P_Needle,
P_NeedleLen,
V_KMP_Table);
if (V_FindPosition >= 0)
{
V_FindPosition += V_StartPosition;
}
free(V_KMP_Table);
}
}
return V_FindPosition;
}
/* Search data from right to left. ( One time search mode. ) */
_UNUSED_ static long utstring_findR(
UT_string *s,
long P_StartPosition, /* Start from 0. -1 means last position. */
const char *P_Needle,
size_t P_NeedleLen)
{
long V_StartPosition;
long V_HaystackLen;
long *V_KMP_Table;
long V_FindPosition = -1;
if (P_StartPosition < 0)
{
V_StartPosition = s->i + P_StartPosition;
}
else
{
V_StartPosition = P_StartPosition;
}
V_HaystackLen = V_StartPosition + 1;
if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) )
{
V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1));
if (V_KMP_Table != NULL)
{
_utstring_BuildTableR(P_Needle, P_NeedleLen, V_KMP_Table);
V_FindPosition = _utstring_findR(s->d,
V_HaystackLen,
P_Needle,
P_NeedleLen,
V_KMP_Table);
free(V_KMP_Table);
}
}
return V_FindPosition;
}
/*******************************************************************************
* end substring search functions *
******************************************************************************/
#endif /* UTSTRING_H */

View File

@ -0,0 +1,90 @@
#ifndef GS_UTIL_BASE16_H
#define GS_UTIL_BASE16_H
/*
* Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/**
@file
Encoding and decoding base16 arrays to and from strings.
*/
#include <gs/util/error.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Calculate length of base16-encoded data
@param raw_len Raw data length
@return Encoded string length (excluding NUL)
*/
static inline size_t base16_encoded_len(size_t raw_len)
{
return (2 * raw_len);
}
/**
Calculate maximum length of base16-decoded string
@param encoded Encoded string
@return Maximum length of raw data
*/
static inline size_t base16_decoded_max_len(const char *encoded)
{
return ((strlen(encoded) + 1) / 2);
}
/**
Base16-encode data
The buffer must be the correct length for the encoded string. Use
something like
char buf[ base16_encoded_len ( len ) + 1 ];
(the +1 is for the terminating NUL) to provide a buffer of the
correct size.
@param raw Raw data
@param len Length of raw data
@param encoded Buffer for encoded string
*/
void base16_encode(const uint8_t *raw, size_t len, char *encoded);
/**
Base16-decode data
The buffer must be large enough to contain the decoded data. Use
something like
char buf[ base16_decoded_max_len ( encoded ) ];
to provide a buffer of the correct size.
@param encoded Encoded string
@param raw Raw data
@return Length of raw data, or negative error (gs_error_t)
*/
int base16_decode(const char *encoded, uint8_t *raw);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,173 @@
#ifndef GS_UTIL_BYTEBUFFER_h
#define GS_UTIL_BYTEBUFFER_h
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Byte buffer provides formatting/serialzing of text/binary data. The buffer keeps track of used space, and prevents overrun.
The current buffer state can be checked using gs_bytebuffer_state().
@dontinclude bytebuffer/bytebuffer_test.c
@skip TEST_gs_bytebuffer_use_case
@until }
*/
#include <gs/util/error.h>
#include <stdarg.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Buffer handle.
Never access handle members directly.
*/
typedef struct {
/**
Internal: Pointer to user supplied buffer.
@see gs_bytebuffer_init()
*/
uint8_t * buffer;
/**
Internal: Size of user supplied buffer.
@see gs_bytebuffer_init()
*/
size_t size;
/**
Internal: Number of bytes used.
*/
size_t used;
/**
Internal: FUTURE: Committed used
*/
size_t committed_used;
/**
Internal: flags to keep track of buffer state.
*/
uint8_t flags;
} gs_bytebuffer_t;
/**
Initialize buffer.
@param[in] bb handle.
@param[in] buffer user supplied buffer of \a buffer_size size (bytes). If NULL, the buffer will keep track of required bytes.
@param[in] buffer_size size of \a buffer.
@return_gs_error_t
*/
gs_error_t gs_bytebuffer_init(gs_bytebuffer_t * bb, void * buffer, size_t buffer_size);
/**
Insert data using vprintf.
@param[in] bb handle.
@param[in] format printf syntax for formatting data
@param[in] ap variable argument list.
*/
void gs_bytebuffer_vprintf(gs_bytebuffer_t * bb, const char * format, va_list ap);
/**
Insert data using printf.
@param[in] bb handle.
@param[in] format printf syntax for formatting data
*/
void gs_bytebuffer_printf(gs_bytebuffer_t * bb, const char * format, ...) __attribute__ ((format (__printf__, 2, 3)));
/**
Append data to buffer.
@param[in] bb handle.
@param[in] data data to append to buffer.
@param[in] length length of data (bytes).
*/
void gs_bytebuffer_append(gs_bytebuffer_t * bb, const void * data, size_t length);
/**
Append string to buffer.
@param[in] bb handle.
@param[in] string string to append to buffer.
*/
void gs_bytebuffer_append_string(gs_bytebuffer_t * bb, const char * string);
/**
Append string to buffer.
@param[in] bb handle.
@param[in] string string to append to buffer.
@param[in] max_length max characters to append from \a string.
*/
void gs_bytebuffer_append_string_max(gs_bytebuffer_t * bb, const char * string, size_t max_length);
/**
Return buffer as string - enforcing NUL termination.
This will always add a NUL termination (zero), which may lead to overflow/truncation of the string.
The NUL termination is NOT added to \a used count.
@param[in] bb handle.
@param[out] error optional, state of buffer - see gs_bytebuffer_error().
@return C-string (NUL terminated)
*/
char * gs_bytebuffer_get_as_string(gs_bytebuffer_t * bb, gs_error_t * error);
/**
Return buffer state.
@param[in] bb handle.
@return GS_ERROR_OVERFLOW if data has been truncated.
@return GS_ERROR_DATA in case of error during formatting.
@return_gs_error_t
*/
gs_error_t gs_bytebuffer_get_state(gs_bytebuffer_t * bb);
/**
Return buffer (user supplied).
@param[in] bb handle.
*/
static inline void * gs_bytebuffer_get_buffer(gs_bytebuffer_t * bb)
{
return bb->buffer;
}
/**
Return buffer size (user supplied).
@param[in] bb handle.
@return buffer size
*/
static inline size_t gs_bytebuffer_get_size(gs_bytebuffer_t * bb)
{
return bb->size;
}
/**
Return number of free bytes.
@param[in] bb handle.
@return number of free bytes.
*/
static inline size_t gs_bytebuffer_get_free(gs_bytebuffer_t * bb)
{
return (bb->size) ? (bb->size - bb->used) : 0;
}
/**
Return number of used bytes.
@param[in] bb handle.
@return used bytes.
*/
static inline size_t gs_bytebuffer_get_used(gs_bytebuffer_t * bb)
{
return bb->used;
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,341 @@
#ifndef GS_UTIL_BYTEORDER_H
#define GS_UTIL_BYTEORDER_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Convert numbers between host and network order.
*/
#include <gs/util/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Convert value from host order to network order
@param[in] value value to convert.
@return converted value.
*/
uint16_t util_htons(uint16_t value);
/**
Convert value from network order to host order
@param[in] value value to convert.
@return converted value.
*/
uint16_t util_ntohs(uint16_t value);
/**
Convert value from host order to network order
@param[in] value value to convert.
@return converted value.
*/
uint32_t util_htonl(uint32_t value);
/**
Convert value from network order to host order
@param[in] value value to convert.
@return converted value.
*/
uint32_t util_ntohl(uint32_t value);
/**
Convert value from host order to network order
@param[in] value value to convert.
@return converted value.
*/
uint16_t util_hton16(uint16_t value);
/**
Convert value from host order to network order
@param[in] from value to convert.
@param[out] to value converted.
@param[in] count element count
*/
void util_hton16_array(const uint16_t * from, uint16_t * to, size_t count);
/**
Convert value from network order to host order
@param[in] value value to convert.
@return converted value.
*/
uint16_t util_ntoh16(uint16_t value);
/**
Convert value from network order to host order
@param[in] from value to convert.
@param[out] to value converted.
@param[in] count element count
*/
void util_ntoh16_array(const uint16_t * from, uint16_t * to, size_t count);
/**
Convert value from host order to network order
@param[in] value value to convert.
@return converted value.
*/
uint32_t util_hton32(uint32_t value);
/**
Convert value from host order to network order
@param[in] from value to convert.
@param[out] to value converted.
@param[in] count element count
*/
void util_hton32_array(const uint32_t * from, uint32_t * to, size_t count);
/**
Convert value from network order to host order
@param[in] value value to convert.
@return converted value.
*/
uint32_t util_ntoh32(uint32_t value);
/**
Convert value from network order to host order
@param[in] from value to convert.
@param[out] to value converted.
@param[in] count element count
*/
void util_ntoh32_array(const uint32_t * from, uint32_t * to, size_t count);
/**
Convert value from host order to network order
@param[in] value value to convert.
@return converted value.
*/
uint64_t util_hton64(uint64_t value);
/**
Convert value from host order to network order
@param[in] from value to convert.
@param[out] to value converted.
@param[in] count element count
*/
void util_hton64_array(const uint64_t * from, uint64_t * to, size_t count);
/**
Convert value from network order to host order
@param[in] value value to convert.
@return converted value.
*/
uint64_t util_ntoh64(uint64_t value);
/**
Convert value from network order to host order
@param[in] from value to convert.
@param[out] to value converted.
@param[in] count element count
*/
void util_ntoh64_array(const uint64_t * from, uint64_t * to, size_t count);
/**
Convert value from host order to network order
@param[in] value value to convert.
@return converted value.
*/
float util_htonflt(float value);
/**
Convert value from host order to network order
@param[in] from value to convert.
@param[out] to value converted.
@param[in] count element count
*/
void util_htonflt_array(const float * from, float * to, size_t count);
/**
Convert value from network order to host order
@param[in] value value to convert.
@return converted value.
*/
float util_ntohflt(float value);
/**
Convert value from network order to host order
@param[in] from value to convert.
@param[out] to value converted.
@param[in] count element count
*/
void util_ntohflt_array(const float * from, float * to, size_t count);
/**
Convert value from host order to network order
@param[in] value value to convert.
@return converted value.
*/
double util_htondbl(double value);
/**
Convert value from host order to network order
@param[in] from value to convert.
@param[out] to value converted.
@param[in] count element count
*/
void util_htondbl_array(const double * from, double * to, size_t count);
/**
Convert value from network order to host order
@param[in] value value to convert.
@return converted value.
*/
double util_ntohdbl(double value);
/**
Convert value from network order to host order
@param[in] from value to convert.
@param[out] to value converted.
@param[in] count element count
*/
void util_ntohdbl_array(const double * from, double * to, size_t count);
/**
Convert value from host order to big endian.
@param[in] value value to convert.
@return converted value.
*/
uint16_t util_htobe16(uint16_t value);
/**
Convert value from host order to little endian.
@param[in] value value to convert.
@return converted value.
*/
uint16_t util_htole16(uint16_t value);
/**
Convert value from big endian to host order.
@param[in] value value to convert.
@return converted value.
*/
uint16_t util_betoh16(uint16_t value);
/**
Convert value from little endian to host order.
@param[in] value value to convert.
@return converted value.
*/
uint16_t util_letoh16(uint16_t value);
/**
Convert value from host order to big endian.
@param[in] value value to convert.
@return converted value.
*/
uint32_t util_htobe32(uint32_t value);
/**
Convert value from host order to little endian.
@param[in] value value to convert.
@return converted value.
*/
uint32_t util_htole32(uint32_t value);
/**
Convert value from big endian to host order.
@param[in] value value to convert.
@return converted value.
*/
uint32_t util_betoh32(uint32_t value);
/**
Convert value from little endian to host order.
@param[in] value value to convert.
@return converted value.
*/
uint32_t util_letoh32(uint32_t value);
/**
Convert value from host order to big endian.
@param[in] value value to convert.
@return converted value.
*/
uint64_t util_htobe64(uint64_t value);
/**
Convert value from host order to little endian.
@param[in] value value to convert.
@return converted value.
*/
uint64_t util_htole64(uint64_t value);
/**
Convert value from big endian to host order.
@param[in] value value to convert.
@return converted value.
*/
uint64_t util_betoh64(uint64_t value);
/**
Convert value from little endian to host order.
@param[in] value value to convert.
@return converted value.
*/
uint64_t util_letoh64(uint64_t value);
/**
Byte swap.
@param[in] value value to byteswap.
@return swapped value
*/
uint16_t gs_bswap_16(uint16_t value);
/**
Byte swap array.
@param[in] from from address.
@param[out] to to address.
@param[in] count element count.
*/
void gs_bswap_16_array(const uint16_t * from, uint16_t * to, size_t count);
/**
Byte swap.
@param[in] value value to byteswap.
@return swapped value
*/
uint32_t gs_bswap_32(uint32_t value);
/**
Byte swap array.
@param[in] from from address.
@param[out] to to address.
@param[in] count element count.
*/
void gs_bswap_32_array(const uint32_t * from, uint32_t * to, size_t count);
/**
Byte swap.
@param[in] value value to byteswap.
@return swapped value
*/
uint64_t gs_bswap_64(uint64_t value);
/**
Byte swap array.
@param[in] from from address.
@param[out] to to address.
@param[in] count element count.
*/
void gs_bswap_64_array(const uint64_t * from, uint64_t * to, size_t count);
/**
Byte swap.
@param[in] value value to byteswap.
@return swapped value
*/
float gs_bswap_float(float value);
/**
Byte swap array.
@param[in] from from address.
@param[out] to to address.
@param[in] count element count.
*/
void gs_bswap_float_array(const float * from, float * to, size_t count);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,54 @@
#ifndef GS_UTIL_CHECK_H
#define GS_UTIL_CHECK_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Argument checking.
Logs can be enabled through a define.
*/
#include <gs/util/error.h>
#include <gs/util/log.h>
#include <gs/util/string.h>
#ifdef __cplusplus
extern "C" {
#endif
#if (GS_CHECK_LOG)
#define GS_CHECK_HANDLE(check) if (!(check)) { log_error("Invalid handle - assert: " GS_DEF2STRING(check)); return GS_ERROR_HANDLE;}
#define GS_CHECK_ARG(check) if (!(check)) { log_error("Invalid argument - assert: " GS_DEF2STRING(check)); return GS_ERROR_ARG;}
#define GS_CHECK_SUPPORTED(check) if (!(check)) { log_error("Not supported - assert: " GS_DEF2STRING(check)); return GS_ERROR_NOT_SUPPORTED;}
#define GS_CHECK_RANGE(check) if (!(check)) { log_error("Invalid range - assert: " GS_DEF2STRING(check)); return GS_ERROR_RANGE;}
#else
/**
Perform evalution of 'check' and return GS_ERROR_HANDLE if not 'true'.
*/
#define GS_CHECK_HANDLE(check) if (!(check)) { return GS_ERROR_HANDLE;}
/**
Perform evalution of 'check' and return GS_ERROR_ARG if not 'true'.
*/
#define GS_CHECK_ARG(check) if (!(check)) { return GS_ERROR_ARG;}
/**
Perform evalution of 'check' and return GS_ERROR_NOT_SUPPORTED if not 'true'.
*/
#define GS_CHECK_SUPPORTED(check) if (!(check)) { return GS_ERROR_NOT_SUPPORTED;}
/**
Perform evalution of 'check' and return GS_ERROR_RANGE if not 'true'.
*/
#define GS_CHECK_RANGE(check) if (!(check)) { return GS_ERROR_RANGE;}
#endif
/**
Assert on 'value'.
@deprecated use GS_STATIC_ASSERT()
*/
#define GS_CHECK_STATIC_ASSERT(condition, name) GS_STATIC_ASSERT(condition, name)
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,88 @@
#ifndef GS_UTIL_CLOCK_H
#define GS_UTIL_CLOCK_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Get/set time (including RTC), convert to/from string.
*/
#include <gs/util/error.h>
#include <gs/util/timestamp.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Returns real time/clock (UTC - time since Epoch/1970).
If the platform supports a Real Time Clock, the RTC is normally read on first call. An offset is calculated for the relative clock, which
then is used to calculate the actual time.
@note clock_get_time() is proto-typed in libcsp as weak, but with different argument which MUST match gs_timestamp_t.
@param[out] time user allocated buffer, contaning the current UTC time.
*/
void gs_clock_get_time(gs_timestamp_t * time);
/**
Set real time/clock (UTC).
If the platform supports a Real Time Clock, the RTC is also updated.
@param[in] time UTC time.
@return_gs_error_t
*/
gs_error_t gs_clock_set_time(const gs_timestamp_t * time);
/**
Returns elapsed time since some unspecified starting point.
@param[out] time user allocated buffer, receives elapsed time.
@see gs_time_rel_ms()
*/
void gs_clock_get_monotonic(gs_timestamp_t * time);
/**
Returns number of elapsed nano-seconds since some unspecified starting point.
@return nano-seconds.
*/
uint64_t gs_clock_get_nsec(void);
/**
Buffer length for containing full ISO8601 timestamp - including zero (0) termination.
*/
#define GS_CLOCK_ISO8601_BUFFER_LENGTH 21
/**
Convert UTC to a ISO8601 string.
ISO8601 timestamp: 2017-03-30T06:20:45Z
@param[in] utc_time UTC time.
@param[out] buffer user allocated buffer.
@param[in] buffer_size size of \a buf.
@return_gs_error_t
*/
gs_error_t gs_clock_to_iso8601_string(const gs_timestamp_t * utc_time, char * buffer, size_t buffer_size);
/**
Convert UTC to a ISO8601 string.
ISO8601 timestamp: 2017-03-30T06:20:45Z
@param[in] utc_sec UTC seconds.
@param[out] buffer user allocated buffer.
@param[in] buffer_size size of \a buf.
@return_gs_error_t
*/
gs_error_t gs_clock_to_iso8601_string2(uint32_t utc_sec, char * buffer, size_t buffer_size);
/**
Convert string (UTC time) to timstamp.
Parse string as:
1. \<seconds\>.\<nano-seconds\> - number of seconds elapsed since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
2. YYYY-MM-DDTHH:MM:SSZ - ISO8601
@param[in] str time
@param[out] ts time
@return_gs_error_t
*/
gs_error_t gs_clock_from_string(const char * str, gs_timestamp_t * ts);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,55 @@
#ifndef GS_UTIL_CRC32_H
#define GS_UTIL_CRC32_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
CRC32 checksumes.
https://en.wikipedia.org/wiki/Cyclic_redundancy_check.
*/
#include <gs/util/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Return init/seed value for CRC-32.
@return initial/seed value for CRC-32, using 0xffffffff.
@see gs_crc32_update(), gs_crc32_finalize()
*/
uint32_t gs_crc32_init(void);
/**
Update CRC-32.
@param[in] crc current CRC-32
@param[in] block start of memory block.
@param[in] length length of \a block.
@return updated CRC-32.
@see gs_crc32_init(), gs_crc32_finalize()
*/
uint32_t gs_crc32_update(uint32_t crc, const void * block, size_t length);
/**
Return finalized CRC-32.
@param[in] crc Checksum is finalized by xor'ing 0xffffffff.
@return finalized CRC-32.
@see gs_crc32_init(), gs_crc32_update()
*/
uint32_t gs_crc32_finalize(uint32_t crc);
/**
Return finalized CRC-32 on amemory block.
@param[in] block block to calculate CRC-32 on.
@param[in] length length/size of \a block.
@return finalized CRC-32.
*/
uint32_t gs_crc32(const void *block, size_t length);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,55 @@
#ifndef GS_UTIL_CRC8_H
#define GS_UTIL_CRC8_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
CRC8 checksumes.
https://en.wikipedia.org/wiki/Cyclic_redundancy_check.
*/
#include <gs/util/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Return init/seed value for CRC-8.
@return initial/seed value for CRC-8, using 0xff.
@see gs_crc8_update(), gs_crc8_finalize()
*/
uint8_t gs_crc8_init(void);
/**
Update CRC-8.
@param[in] crc current CRC-8
@param[in] block start of memory block.
@param[in] length length of \a block.
@return updated CRC-8.
@see gs_crc8_init(), gs_crc8_finalize()
*/
uint8_t gs_crc8_update(uint8_t crc, const void * block, size_t length);
/**
Return finalized CRC-8.
@param[in] crc Checksum is finalized by xor'ing 0xffffffff.
@return finalized CRC-8.
@see gs_crc8_init(), gs_crc8_update()
*/
uint8_t gs_crc8_finalize(uint8_t crc);
/**
Return finalized CRC-8 on amemory block.
@param[in] block block to calculate CRC-8 on.
@param[in] length length/size of \a block.
@return finalized CRC-8.
*/
uint8_t gs_crc8(const void *block, size_t length);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,42 @@
#ifndef GS_UTIL_DELAY_H
#define GS_UTIL_DELAY_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Delay execution.
@note Most implementations uses busy waiting.
*/
#include <gs/util/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Delay for number of microseconds.
@note Linux doesn't busy wait.
@param us Number of microseconds to wait
*/
void gs_delay_us(uint32_t us);
/**
Return current counter used for us delays
@return timestamp in us
*/
uint16_t gs_delay_ts_get(void);
/**
Wait until delay has passed since timestamp
@param[in] ts Timestamp in us
@param[in] delay The requested delay since ts
*/
void gs_delay_from_ts(uint16_t ts, uint16_t delay);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,122 @@
#ifndef GS_UTIL_DRIVERS_CAN_CAN_H
#define GS_UTIL_DRIVERS_CAN_CAN_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
CAN interface.
*/
#include <gs/util/log.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Default log group for CAN driver.
*/
GS_LOG_GROUP_EXTERN(gs_can_log);
/**
Bit-rate (default).
*/
#define GS_CAN_DEFAULT_BPS 1000000
/**
Callback for handling received data (from CAN driver).
@param[in] device hardware device
@param[in] canMsgId standard or extended message id.
@param[in] extendedMsgId \a true if extended id, \a false if standard id.
@param[in] data pointer to data.
@param[in] data_size size of data.
@param[in] nowMs current relative time in mS.
@param[in] user_data user data.
@param[in] cswitch If called from within an ISR (embedded platform), this will none NULL.
*/
typedef void (*gs_can_rxdata_callback_t)(int hdl,
uint32_t canMsgId,
bool extendedMsgId,
const void * data,
size_t data_size,
uint32_t nowMs,
void * user_data,
gs_context_switch_t * cswitch);
/**
Send CAN message with standard id (11 bits).
@param[in] device hardware device
@param[in] canMsgId standard CAN message id.
@param[in] data pointer to data.
@param[in] data_size size of data.
@param[in] timeout_ms timeout in mS.
@return GS_ERROR_FULL if Tx queue is full
@return_gs_error_t
*/
gs_error_t gs_can_send_standard(uint8_t device, uint32_t canMsgId, const void * data, size_t data_size, int timeout_ms);
/**
Send CAN message with exended id (29 bits).
@param[in] device hardware device
@param[in] canExtMsgId exteneded message id.
@param[in] data pointer to data.
@param[in] data_size size of data.
@param[in] timeout_ms timeout in mS.
@return GS_ERROR_FULL if Tx queue is full
@return_gs_error_t
*/
gs_error_t gs_can_send_extended(uint8_t device, uint32_t canExtMsgId, const void * data, size_t data_size, int timeout_ms);
/**
Set filter and callback for standard message id.
@param[in] device hardware device
@param[in] canMsgId standard message id.
@param[in] mask filter mask.
@param[in] rx_callback callback function.
@param[in] rx_user_data user data provided in callback.
@return GS_ERROR_FULL if all message id slots are used.
@return_gs_error_t
*/
gs_error_t gs_can_set_standard_filter_mask(uint8_t device, uint32_t canMsgId, uint32_t mask, gs_can_rxdata_callback_t rx_callback, void * rx_user_data);
/**
Set filter and callback for extended message id.
@param[in] device hardware device
@param[in] canExtMsgId extended message id.
@param[in] mask filter mask.
@param[in] rx_callback callback function.
@param[in] rx_user_data user data provided in callback.
@return GS_ERROR_FULL if all message id slots are used.
@return_gs_error_t
*/
gs_error_t gs_can_set_extended_filter_mask(uint8_t device, uint32_t canExtMsgId, uint32_t mask, gs_can_rxdata_callback_t rx_callback, void * rx_user_data);
/**
Stop CAN layer.
If a CAN transceiver is present and controlled, it will be disabled.
@param[in] device hardware device
@return_gs_error_t
*/
gs_error_t gs_can_stop(uint8_t device);
/**
Start CAN layer.
Clear all buffers and start CAN.
If a CAN transceiver is present and controlled, it will be enabled.
@param[in] device hardware device
@return_gs_error_t
*/
gs_error_t gs_can_start(uint8_t device);
/**
Get current CAN layer error state.
@param[in] device hardware device
@param[out] restart_required \a true if CAN layer should be re-started. Pass NULL, if not wanted.
@return_gs_error_t
*/
gs_error_t gs_can_error_state(uint8_t device, bool * restart_required);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,91 @@
#ifndef GS_UTIL_DRIVERS_GPIO_GPIO_H
#define GS_UTIL_DRIVERS_GPIO_GPIO_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
GPIO interface provides a generic interface toward hardware GPIO's.
*/
#include <gs/util/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
GPIO definition.
*/
typedef struct {
//! Chip/group/port number which the GPIO belongs to.
uint16_t port;
//! The pin number of the GPIO.
uint16_t pin;
} gs_gpio_t;
/**
GPIO interrupt function.
*/
typedef void (*gs_gpio_isr_t)(gs_context_switch_t * cswitch);
/**
Configuration for interrupt related to a GPIO.
*/
typedef struct {
//! True if it shall trigger on rising edge.
bool rising_edge;
//! True if it shall trigger on falling edge.
bool falling_edge;
//! True if it shall have high priority (if nested isr supported).
bool high_priority;
//! ISR to be called on trigger.
gs_gpio_isr_t isr;
} gs_interrupt_conf_t;
/**
GPIO get value
@param[in] gpio The gpio to read
@param[in] value Returned GPIO value (true/false = High/Low)
@return_gs_error_t
*/
gs_error_t gs_gpio_get(gs_gpio_t gpio, bool *value);
/**
GPIO get value without error check
@param[in] gpio The gpio to read
@return GPIO value (true/false = High/Low)
*/
bool gs_gpio_get_nc(gs_gpio_t gpio);
/**
GPIO set value
@param[in] gpio The gpio to set
@param[in] value GPIO value (true/false = High/Low)
@return_gs_error_t
*/
gs_error_t gs_gpio_set(gs_gpio_t gpio, bool value);
/**
GPIO set value without error check
@param[in] gpio The gpio to set
@param[in] value GPIO value (true/false = High/Low)
*/
void gs_gpio_set_nc(gs_gpio_t gpio, bool value);
/**
Initialize GPIO as an external interrupt pin.
@param[in] gpio The gpio to configure
@param[in] conf Configuration of interrupt pin
@return_gs_error_t
*/
gs_error_t gs_gpio_init_as_interrupt(gs_gpio_t gpio, const gs_interrupt_conf_t * conf);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,88 @@
#ifndef GS_UTIL_DRIVERS_I2C_COMMON_H
#define GS_UTIL_DRIVERS_I2C_COMMON_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Common (master and slave) I2C definitions.
*/
#include <gs/util/log.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Default log group for I2C driver.
*/
GS_LOG_GROUP_EXTERN(gs_i2c_log);
/**
I2C mode.
*/
typedef enum {
//! Master mode
GS_I2C_MASTER = 0,
//! Multimaster mode
GS_I2C_MULTI_MASTER = 1,
//! Slave mode
GS_I2C_SLAVE = 2,
} gs_i2c_mode_t;
/**
Cross-platform I2C configuration.
*/
typedef struct {
//! Data order, True: MSB first, False: LSB first (default = True)
bool data_order_msb;
//! Device mode (master, multimaster, or slave)
gs_i2c_mode_t mode;
//! Address of node in multimaster and slave mode (not used in master mode)
uint16_t addr;
//! Bits per second (default is #GS_I2C_DEFAULT_BPS)
uint32_t bps;
//! Address size in bits, 7, 8 or 10 bits (default/prefered is #GS_I2C_DEFAULT_ADDRESS_SIZE)
uint8_t addrbits;
} gs_i2c_config_t;
/**
Cross-platform I2C configuration.
@deprecated use gs_i2c_config_t.
*/
typedef gs_i2c_config_t gs_i2c_bus_config_t;
/**
Default bit-rate.
*/
#define GS_I2C_DEFAULT_BPS 100000
/**
Default address size.
*/
#define GS_I2C_DEFAULT_ADDRESS_SIZE 7
/**
Default data order (MSB).
*/
#define GS_I2C_DEFAULT_DATA_ORDER_MSB 1
/**
Speed (command line sub-option).
*/
#define GS_I2C_COMMAND_LINE_SPEED "speed"
/**
Device (command line sub-option).
*/
#define GS_I2C_COMMAND_LINE_DEVICE "device"
/**
Address (command line sub-option).
*/
#define GS_I2C_COMMAND_LINE_ADDRESS "address"
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,32 @@
#ifndef GS_UTIL_DRIVERS_I2C_MASTER_H
#define GS_UTIL_DRIVERS_I2C_MASTER_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
I2C master interface.
*/
#include <gs/util/drivers/i2c/common.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Perform transaction to I2C slave.
@param[in] device hardware device (bus)
@param[in] addr slave address
@param[in] tx transmit buffer
@param[in] txlen number of bytes to transmit
@param[out] rx receive buffer - can be NULL.
@param[in] rxlen number of bytes to receive.
@param[in] timeout_ms timeout in milliseconds, primarily for locking the I2C channel.
@return_gs_error_t
*/
gs_error_t gs_i2c_master_transaction(uint8_t device, uint8_t addr, const void * tx, size_t txlen, void * rx, size_t rxlen, int timeout_ms);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,79 @@
#ifndef GS_UTIL_DRIVERS_I2C_SLAVE_H
#define GS_UTIL_DRIVERS_I2C_SLAVE_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
I2C slave interface.
*/
#include <gs/util/drivers/i2c/common.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Start/enable I2C bus reception.
Reception should not automatically be enabled by their init() functions, as this will complicate adding additional layers/hooks.
@param[in] device I2C bus (handle)
@return_gs_error_t
*/
gs_error_t gs_i2c_slave_start(uint8_t device);
/**
Rx callback.
Function called when data has been received on the bus (I2C write operation complete).
@param[in] device I2C bus (handle).
@param[in] rx receive buffer.
@param[in] rx_length number of bytes received.
@param_cswitch
*/
typedef void (* gs_i2c_slave_receive_t)(uint8_t device, const uint8_t * rx, size_t rx_length, gs_context_switch_t * cswitch);
/**
Set rx callback.
@param[in] device I2C bus (handle).
@param[in] rx Rx callback.
@return_gs_error_t
*/
gs_error_t gs_i2c_slave_set_rx(uint8_t device, gs_i2c_slave_receive_t rx);
/**
Get rx buffer callback.
Function called from driver, for getting a pointer to the rx buffer.
@param[in] device I2C bus (handle).
*/
typedef void * (* gs_i2c_slave_get_rx_buf_t)(uint8_t device);
/**
Set rx buffer get callback.
@param[in] device I2C bus (handle).
@param[in] get_rx_buf get rx buffer callback.
@param[in] buf_length length of buffer retrieved with this callback.
@return_gs_error_t
*/
gs_error_t gs_i2c_slave_set_get_rx_buf(uint8_t device, gs_i2c_slave_get_rx_buf_t get_rx_buf, size_t buf_length);
/**
Set response data.
@param[in] device I2C bus (handle).
@param[in] tx pointer to data. NOTE: data is not copied due to performance, so data must stay valid until the response has been sent.
@param[in] tx_length length of data.
@return_gs_error_t
*/
gs_error_t gs_i2c_slave_set_response(uint8_t device, const uint8_t * tx, size_t tx_length);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,66 @@
#ifndef GS_UTIL_DRIVERS_SPI_COMMON_H
#define GS_UTIL_DRIVERS_SPI_COMMON_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Common (master and slave) SPI definitions.
*/
#include <gs/util/log.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Default log group for SPI driver.
*/
GS_LOG_GROUP_EXTERN(gs_spi_log);
/**
SPI mode - clock polarity and phase.
*/
typedef enum {
/**
Polarity = 0, Phase = 0 (default).
*/
GS_SPI_MODE_CPOL0_CPHA0 = 0,
/**
Polarity = 0, Phase = 1.
*/
GS_SPI_MODE_CPOL0_CPHA1 = 1,
/**
Polarity = 1, Phase = 0.
*/
GS_SPI_MODE_CPOL1_CPHA0 = 2,
/**
Polarity = 1, Phase = 1.
*/
GS_SPI_MODE_CPOL1_CPHA1 = 3
} gs_spi_mode_t;
/**
Default bit-rate.
*/
#define GS_SPI_DEFAULT_BPS 400000
/**
Speed (command line sub-option).
*/
#define GS_SPI_COMMAND_LINE_SPEED "speed"
/**
Slave (command line sub-option).
*/
#define GS_SPI_COMMAND_LINE_SLAVE "slave"
/**
Device (command line sub-option).
*/
#define GS_SPI_COMMAND_LINE_DEVICE "device"
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,95 @@
#ifndef GS_UTIL_DRIVERS_SPI_MASTER_H
#define GS_UTIL_DRIVERS_SPI_MASTER_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
SPI master interface.
*/
#include <gs/util/drivers/spi/common.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Cross-platform master SPI configuration.
*/
typedef struct {
/**
Data order, \a True: MSB first, \a False: LSB first
Default: \a True.
*/
bool data_order_msb;
/**
Bits per second.
Default: #GS_SPI_DEFAULT_BPS.
*/
uint32_t bps;
/**
Mode, specifying polarity and phase.
Default: #GS_SPI_MODE_CPOL0_CPHA0.
*/
gs_spi_mode_t mode;
/**
Character size in bits, 8-16 bits.
Default: 8 bits (prefered).
*/
uint8_t bits;
} gs_spi_master_slave_config_t;
/**
Single master transaction.
*/
typedef struct {
/**
Pointer to tx data, or NULL if no tx.
*/
const void *tx;
/**
Pointer to rx buffer, or NULL if no rx.
*/
void *rx;
/**
Size/length of rx/tx (bytes).
*/
size_t size;
} gs_spi_master_trans_t;
/**
Close/free slave.
Freeing resources associated with the slave.
@param[in] slave SPI slave
@return_gs_error_t
*/
gs_error_t gs_spi_master_close_slave(uint8_t slave);
/**
Perform transaction to/from a pre-configured SPI slave.
Basically for i < size: send tx[i] and receive rx[i].
@note: 8 bit SPI character size required!
@param[in] slave SPI slave
@param[in] tx tx buffer
@param[out] rx rx buffer - can be NULL.
@param[in] size number of to send and also receive.
@param[in] timeout_ms timeout in milliseconds, primarily for locking the SPI device.
@return_gs_error_t
*/
gs_error_t gs_spi_master_transaction(uint8_t slave, const void * tx, void * rx, size_t size, int timeout_ms);
/**
Perform N transaction to/from a pre-configured SPI slave within one chip selection
@note: 8 bit SPI character size required!
@param[in] slave SPI slave
@param[in] trans Pointer to transactions
@param[in] count Number of transactions (rx and/or tx) to complete
@param[in] timeout_ms timeout in milliseconds, primarily for locking the SPI device.
@return_gs_error_t
*/
gs_error_t gs_spi_master_transactions(uint8_t slave, gs_spi_master_trans_t *trans, size_t count, int timeout_ms);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,84 @@
#ifndef GS_UTIL_DRIVERS_SPI_SLAVE_H
#define GS_UTIL_DRIVERS_SPI_SLAVE_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
SPI slave interface.
*/
#include <gs/util/drivers/spi/common.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Cross-platform slave SPI configuration.
*/
typedef struct {
/**
Data order, \a True: MSB first, \a False: LSB first
Default: \a True.
*/
bool data_order_msb;
/**
Mode, specifying polarity and phase.
Default: #GS_SPI_MODE_CPOL0_CPHA0.
*/
gs_spi_mode_t mode;
/**
Character size in bits, 8-16 bits.
Default: 8 bits (prefered).
*/
uint8_t bits;
} gs_spi_slave_config_t;
/**
Start/enable SPI device reception.
Reception should not automatically be enabled by their init() functions, as this will complicate adding additional layers/hooks.
@param[in] device SPI device (handle)
@return_gs_error_t
*/
gs_error_t gs_spi_slave_start(uint8_t device);
/**
Rx callback.
Function called as data is recevied on the device.
@param[in] device SPI device (handle).
@param[in] rx_buffer Pointer to start of rx buffer.
@param[in] rx number of bytes received so far.
@param[in] new_request \a true on the first callback of new data, \a false on receiving additional data during same \a chip-select. Can be used to bring receiver back in sync with new request.
@param_cswitch
@return total number of bytes to receive before next call back. Return 0 to ignore rest of data - no additional call backs will be done for current SPI transaction.
*/
typedef uint8_t (* gs_spi_slave_receive_t)(uint8_t device, const uint8_t * rx_buffer, size_t rx, bool new_request, gs_context_switch_t * cswitch);
/**
Set rx callback.
@param[in] device SPI device (handle).
@param[in] rx Rx callback.
@return_gs_error_t
*/
gs_error_t gs_spi_slave_set_rx(uint8_t device, gs_spi_slave_receive_t rx);
/**
Set response data.
@param[in] device SPI device (handle).
@param[in] offset offset (in bytes) for the response, counted from start of request, i.e. offset of 2 means data will be sent as the 3rd byte.
@param[in] tx pointer to data. NOTE: data is not copied due to performance, so data must stay valid until the response has been sent.
@param[in] size size of data.
@return_gs_error_t
*/
gs_error_t gs_spi_slave_set_response(uint8_t device, size_t offset, const uint8_t * tx, size_t size);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,92 @@
#ifndef GS_UTIL_DRIVERS_SYS_MEMORY_H
#define GS_UTIL_DRIVERS_SYS_MEMORY_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Cross platform memory status API.
*/
#include <gs/util/stdio.h>
#include <gs/util/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
RAM status
Containing different size parameters describing RAM usage.
All sizes are in bytes.
If a parameter is not available/supported on a specific platform, the parameter is set to -1.
*/
typedef struct {
//! total size of RAM
long total;
//! max available RAM for allocation after initialization of of global/static variables
long max_available;
//! available RAM at runtime for dynamic allocation
long available;
//! Lowest registered available RAM since boot
long min_available;
} gs_mem_ram_stat_t;
/**
RAM types
Defines the different RAM types (external/internal) supported on
the various platforms.
*/
typedef enum {
GS_MEM_RAM_TYPE_INTERNAL = 0,//!< Internal RAM type
GS_MEM_RAM_TYPE_EXTERNAL //!< External RAM type
} gs_mem_ram_type_t;
/**
Get status of internal RAM
@param[out] ram_stat RAM status, each member of struct is -1 if not supported on specific platform
@return_gs_error_t
*/
gs_error_t gs_mem_get_int_ram_stat(gs_mem_ram_stat_t * ram_stat);
/**
Get status of external RAM
@param[out] ram_stat RAM status, each member of struct is -1 if not supported on specific platform
@return_gs_error_t
*/
gs_error_t gs_mem_get_ext_ram_stat(gs_mem_ram_stat_t * ram_stat);
/**
Get status of selected RAM
@param[in] type RAM type to query status for
@param[out] ram_stat RAM status, each member of struct is -1 if not supported on specific platform
@return_gs_error_t
*/
gs_error_t gs_mem_get_ram_stat(gs_mem_ram_type_t type, gs_mem_ram_stat_t * ram_stat);
/**
Get default RAM type
returns the default RAM type used for allocations (Heap).
@return gs_mem_ram_type_t
*/
gs_mem_ram_type_t gs_mem_get_ram_default();
/**
Print RAM status.
@param[in] ram_stat RAM status
@param[in] out output stream
@return_gs_error_t
*/
gs_error_t gs_mem_print_ram_stat(gs_mem_ram_stat_t * ram_stat, FILE * out);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,61 @@
#ifndef GS_UTIL_DRIVERS_HW_WATCHDOG_H
#define GS_UTIL_DRIVERS_HW_WATCHDOG_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Hardward watchdog (HWWD) device interface.
Hardware Watchdog interface which provides a generic interface towards
any HWWD. Most HWWD implementation should be able to fit behind
this interface, with just a small "adaption" layer needed.
*/
#include <gs/util/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Hardware watchdog driver interface.
*/
typedef struct gs_watchdog_dev_ops gs_watchdog_dev_ops_t;
/**
Hardware watchdog (HWWD) device structure
Structure that describes the HWWD device and holds
the parameters needed for storing e.g. timeout values etc.
*/
typedef struct gs_watchdog_device {
int id; /**< An ID for the HWWD device - This is currently not used. */
const gs_watchdog_dev_ops_t *ops; /**< Pointer to ops struct defining the operations a HWWD device supports. */
unsigned int timeout; /**< The timeout value that the HWWD device should be configured with. */
unsigned int pretimeout; /**< The pretimeout (if supported) by the HWWD device */
unsigned int min_timeout; /**< Minimum timeout value supported by the HWWD device */
unsigned int max_timeout; /**< Maximum timeout value supported by the HWWD device */
void *driver_data; /**< Pointer to driver specific data can be used by the HWWD driver impl. */
} gs_watchdog_device_t;
/**
Hardware watchdog driver interface.
*/
struct gs_watchdog_dev_ops
{
/* mandatory operations */
gs_error_t (*start)(gs_watchdog_device_t *); /**< Starts the HWWD device */
gs_error_t (*stop)(gs_watchdog_device_t *); /**< Stops the HWWD device */
gs_error_t (*ping)(gs_watchdog_device_t *); /**< Polls the HWWD device and restart count-down */
/* optional operations */
gs_error_t (*set_timeout)(gs_watchdog_device_t *, unsigned int); /**< (Optional) Set timeout of the HWWD device */
gs_error_t (*set_pretimeout)(gs_watchdog_device_t *, unsigned int); /**< (Optional) Set Pre-timeout of the HWWD device */
gs_error_t (*restart)(gs_watchdog_device_t *); /**< (Optional) Restart the HWWD device */
unsigned int (*get_timeleft)(gs_watchdog_device_t *); /**< (Optional) Get time left until HWWD device times out. */
int (*status)(gs_watchdog_device_t *); /**< (Optional) Reads status of the HWWD device */
};
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,53 @@
#ifndef GS_UTIL_ENDIAN_H
#define GS_UTIL_ENDIAN_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Detecting endian type.
*/
// generated by waf configure, defines either UTIL_BIG_ENDIAN or UTIL_LITTLE_ENDIAN
#include "../../conf_util.h"
#ifdef __cplusplus
extern "C" {
#endif
#if !UTIL_BIG_ENDIAN && !UTIL_LITTLE_ENDIAN
#error No endian defined
#endif
#if UTIL_BIG_ENDIAN && UTIL_LITTLE_ENDIAN
#error Both big and little endian defined
#endif
#include <gs/util/byteorder.h>
/**
Returns \a true if platform is big endian.
*/
static inline bool gs_endian_big(void)
{
#if (UTIL_BIG_ENDIAN)
return true;
#else
return false;
#endif
}
/**
Returns \a true if platform is little endian.
*/
static inline bool gs_endian_little(void)
{
#if (UTIL_LITTLE_ENDIAN)
return true;
#else
return false;
#endif
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,199 @@
#ifndef GS_UTIL_ERROR_H
#define GS_UTIL_ERROR_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Common error code definitions.
*/
#include <gs/util/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Common/generic error codes.
Based on POSIX \a errno values, but negative instead of positive.
*/
typedef enum gs_error_t {
/**
Success - ok (POSIX).
*/
GS_OK = 0,
/**
Operation not permitted (POSIX.1: EPERM).
*/
GS_ERROR_PERM = -1,
/**
Interrupted system call (or Interrupted function call) (POSIX: EINTR).
*/
GS_ERROR_INTR = -4,
/**
Input/output error (POSIX.1: EIO)
*/
GS_ERROR_IO = -5,
/**
Resource temporarily unavailable (may be the same value as EWOULDBLOCK) (POSIX.1: EAGAIN).
*/
GS_ERROR_AGAIN = -11,
/**
Cannot allocate memory (or Not enough space) (POSIX.1: ENOMEM).
*/
GS_ERROR_ALLOC = -12,
/**
Permission denied (POSIX.1: EACCES).
*/
GS_ERROR_ACCESS = -13,
/**
Device or resource busy (POSIX.1: EBUSY).
*/
GS_ERROR_BUSY = -16,
/**
File exists (POSIX.1-2001: EEXIST).
*/
GS_ERROR_EXIST = -17,
/**
Invalid argument (POSIX.1: EINVAL).
*/
GS_ERROR_ARG = -22,
/**
Function not implemented (POSIX.1: ENOSYS)
*/
GS_ERROR_NOT_IMPLEMENTED = -38,
/**
Value too large to be stored in data type (POSIX.1: EOVERFLOW).
Example: trying to put 50 characters into a 10 character array.
@see GS_ERROR_RANGE.
*/
GS_ERROR_OVERFLOW = -75,
/**
Operation not supported (POSIX.1: ENOTSUP)
*/
GS_ERROR_NOT_SUPPORTED = -95,
/**
Address already in use (POSIX.1: EADDRINUSE).
*/
GS_ERROR_IN_USE = -98,
/**
Connection reset (POSIX.1-2001: ECONNRESET).
*/
GS_ERROR_CONNECTION_RESET = -104,
/**
No buffer space available (POSIX.1 (XSI STREAMS option): ENOBUFS).
*/
GS_ERROR_NO_BUFFERS = -105,
/**
Timeout (POSIX.1-2001: ETIMEDOUT).
*/
GS_ERROR_TIMEOUT = -110,
/**
Connection already in progress (POSIX.1-2001: EALREADY).
*/
GS_ERROR_ALREADY_IN_PROGRESS = -114,
/**
Handle error (GOMspace).
*/
GS_ERROR_HANDLE = -2000, // from errno.h: #define __ELASTERROR 2000 /* Users can add values starting here */
/**
Not found (GOMspace).
*/
GS_ERROR_NOT_FOUND = -2001,
/**
Full (GOMspace).
*/
GS_ERROR_FULL = -2002,
/**
Range error (GOMspace).
Example: specifying 120 hours, where only 0-23 is valid.
@see GS_ERROR_OVERFLOW
*/
GS_ERROR_RANGE = -2003,
/**
Data error (GOMspace).
*/
GS_ERROR_DATA = -2004,
/**
Unknown error (GOMspace).
@note avoid use - use specific error to improve debugging/troubleshooting.
*/
GS_ERROR_UNKNOWN = -2005,
/**
No data available (GOMspace).
*/
GS_ERROR_NO_DATA = -2006,
/**
Stale data - not updated (GOMspace).
*/
GS_ERROR_STALE = -2007,
/**
Type error (GOMspace).
*/
GS_ERROR_TYPE = -2008,
/**
Ambiguous error (GOMspace).
*/
GS_ERROR_AMBIGUOUS = -2009,
/**
State error (GOMspace).
*/
GS_ERROR_STATE = -2010,
} gs_error_t;
/**
* Convert an error code to a string.
* Uses standard POSIX strerror() under the hood.
* @param[in] error error to convert. If negative (e.g. \a gs_error_t), it is first converted to a positive value.
* @return string usefull for logging purposes (should not be used for programatically processing).
*/
const char * gs_error_string(int error);
/**
Convert standard POSIX \a errno to gs_error_t.
@param[in] error POSIX error code (errno).
@return convert error code, by simply converting to a negative number.
*/
gs_error_t gs_error(int error);
#if (GS_UTIL_DEPRECATED_ERROR_CODES)
/**
Legacy error definitions.
@deprecated Use standard gs_error_t codes - these defines are only kept, so very old code (not yet update to use #gs_error_t) can compile.
@{
*/
#define E_NO_ERR -1
#define E_NO_DEVICE -2
#define E_MALLOC_FAIL -3
#define E_THREAD_FAIL -4
#define E_NO_QUEUE -5
#define E_INVALID_BUF_SIZE -6
#define E_INVALID_PARAM -7
#define E_NO_SS -8
#define E_GARBLED_BUFFER -9
#define E_FLASH_ERROR -10
#define E_BOOT_SER -13
#define E_BOOT_DEBUG -14
#define E_BOOT_FLASH -15
#define E_TIMEOUT -16
#define E_NO_BUFFER -17
#define E_OUT_OF_MEM -18
#define E_FAIL -19
/** @} */
/**
Converts legacy error definitions to string.
@deprecated Use standard gs_error_t codes - this function is only kept, so very old code (not yet update to use #gs_error_t) can compile.
@param[in] code error code
@return string describing the error.
*/
const char * error_string(int code) __attribute__((deprecated));
#endif // GS_UTIL_DEPRECATED_ERROR_CODES
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,89 @@
#ifndef GS_UTIL_FLETCHER_H
#define GS_UTIL_FLETCHER_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Fletcher16 checksum,
*/
#include <gs/util/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Fletcher16 checksum (read using copy function).
Data is read from \a data, using the specified \a memcpyfcn function.
@param[in] data data.
@param[in] size number of \a data bytes.
@param[in] memcpyfcn memory copy function. If NULL is specified, standard memcpy will be used.
@returns fletcher16 checksum
*/
uint16_t gs_fletcher16_memcpy(const void * data, size_t size, void * (*memcpyfcn)(void *, const void *, size_t));
/**
Fletcher16 checksum (read from program memory).
AVR8: reads from program memory.
Other architectures: identical to gs_fletcher16().
@param[in] data_in data.
@param[in] size number of \a data bytes.
@returns fletcher16 checksum
*/
uint16_t gs_fletcher16_P(const void * data_in, size_t size);
/**
Fletcher16 checksum.
@param[in] data data.
@param[in] size number of \a data bytes.
@returns fletcher16 checksum
*/
uint16_t gs_fletcher16(const void * data, size_t size);
/**
Fletcher16 working set.
@see gs_fletcher16_init(), gs_fletcher16_update(), gs_fletcher16_finalize()
*/
typedef struct {
/**
Sum1 - internal.
*/
uint16_t sum1;
/**
Sum2 - internal.
*/
uint16_t sum2;
} gs_fletcher16_t;
/**
Initialize fletcher16 working set.
@param[in] f16 working set.
*/
void gs_fletcher16_init(gs_fletcher16_t * f16);
/**
Update fletcher16 checksum.
@param[in] f16 working set.
@param[in] data data.
@param[in] size number of \a data bytes.
*/
void gs_fletcher16_update(gs_fletcher16_t * f16, const void * data, size_t size);
/**
Finalize fletcher16 checksum and return it.
@param[in] f16 working set.
@returns fletcher16 checksum
*/
uint16_t gs_fletcher16_finalize(gs_fletcher16_t * f16);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,79 @@
#ifndef GS_UTIL_FUNCTION_SCHEDULER
#define GS_UTIL_FUNCTION_SCHEDULER
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Function scheduler.
Simple framework for invoking functions at intervals.
Instead of creating a lot of tasks (which uses memory), this framework can be used to schedule execution of functions at specified intervals.
Once setup, calling gs_function_scheduler_execute_ms() will execute all functions timed out and return the time, until the next function has
to be executed or max timeout specified (or max wait time supported on the platform).
The API supports multiple schedulers, but is not thread-safe.
@note Do NOT use for time critical control, as the actual time interval is influenced by the host thread and other scheduled functions.
*/
#include <gs/util/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Scheduler handle.
*/
typedef struct gs_function_scheduler gs_function_scheduler_t;
/**
Function callback.
@return timeout in mS until next callback.
*/
typedef uint32_t (*gs_function_scheduler_function_t)(void * user_data);
/**
Initialize scheduler.
Memory is allocated once for \a max_entries.
@param[in] max_timeout_ms max timeout in mS.
@param[in] max_entries max number of entries for this scheduler.
@param[out] scheduler reference to created scheduler.
@return_gs_error_t
*/
gs_error_t gs_function_scheduler_create(uint32_t max_timeout_ms, unsigned int max_entries, gs_function_scheduler_t ** scheduler);
/**
Free scheduler (release resources).
@param[in] scheduler scheduler.
@return_gs_error_t
*/
gs_error_t gs_function_scheduler_destroy(gs_function_scheduler_t * scheduler);
/**
Execute scheduled function(s) and returns number of mS until next execute must be called again.
@note Return type is \a int to prevent overflow on platforms where int is less than 32 bits.
@param[in] scheduler scheduler.
@return next timeout in mS.
*/
int gs_function_scheduler_execute_ms(gs_function_scheduler_t * scheduler);
/**
Register function to be executed at mS intervals.
@param[in] scheduler scheduler.
@param[in] first_timeout_ms mS until first execution.
@param[in] func function to execute.
@param[in] user_data function user data.
@return_gs_error_t
*/
gs_error_t gs_function_scheduler_register_ms(gs_function_scheduler_t * scheduler, uint32_t first_timeout_ms, gs_function_scheduler_function_t func, void * user_data);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,503 @@
#ifndef GS_UTIL_GOSH_COMMAND_H
#define GS_UTIL_GOSH_COMMAND_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Command framework.
Provides a simple way of organizing commands in a hierarchy. A command is a text string mapping to a function - supporting arguments.
*/
#include <gs/util/stdio.h>
#include <gs/util/pgm.h>
#include <gs/util/check.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Max langth of a command (including NUL termination).
*/
#define GS_COMMAND_MAX_LEN_COMMAND 20
/**
Flag for hiding command in help and tab-complete.
*/
#define GS_COMMAND_FLAG_HIDDEN 0x02
/**
'root' command attribute.
On embedded (none Linux) systems, it is prefered to store as much as possible in \a program memory, in order to save RAM.
This is accomplished by tagging all \a root commands with this attribute, which instructs the linker to put all commands in a named
section. This section is then through the linker-script, placed in \a program memory.
The command framework can read commands directly from this section, and therefore doesn't need an RAM to maintain the list.
The gs_command_register() must still be called for all \a root commands, which ensures that the linker doesn't throw away the
command objects, due to missing code reference.
On a Linux system, the commands are not group in a section. Instead gs_command_register() dynamicly builds a list with the commands.
@see gs_command_register()
*/
#if (__linux__ == 0)
#define GS_COMMAND_ROOT __attribute__ ((section(".commands")))
#else
#define GS_COMMAND_ROOT
#endif
/**
Sub command attribute,
Only necesasry on AVR8, due to its memory model.
*/
#define GS_COMMAND_SUB GS_PGM_OBJECT
/**
Macro for initializing command chains.
*/
#define GS_COMMAND_INIT_CHAIN(__list) {.list = __list, .count = GS_ARRAY_SIZE(__list)}
/**
Macro for registering commands.
@see gs_command_register()
*/
#define GS_COMMAND_REGISTER(__cmd) gs_command_register(__cmd, GS_ARRAY_SIZE(__cmd))
/**
Command reference.
@note Use gs_command_t instead of 'struct command'.
*/
typedef struct command gs_command_t;
/**
Commands context reference
@note Use gs_command_context_t instead of struct command_context
*/
typedef struct command_context gs_command_context_t;
/**
Command output interface
*/
typedef struct command_io_functions {
/**
Function interface for setting result
@param output_ctx pointer to output context for the given impl.
@param group Group name specifies the group that a given key/value pair belongs to.
@param key key name
@param value string value of the result
@return_gs_error_t
*/
gs_error_t (*set_result)(gs_command_context_t *ctx, const char *group, const char *key, const char *value);
/**
Function interface for flushing results. Used by the command handler to ensure output/results
are flushed to stdout/File or any other receiver of the output.
@param output_ctx pointer to output context for the given impl.
@return_gs_error_t
*/
gs_error_t (*flush)(gs_command_context_t *ctx);
/**
Function interface for waiting for key/input
@param output_ctx pointer to output context for the given impl.
@param ch pointer to character returned by function
@param timeout_ms maximum time to wait of the character.
@return_gs_error_t
*/
gs_error_t (*wait_for_key)(gs_command_context_t *ctx, int *ch, int timeout_ms);
} gs_command_io_functions_t;
/**
Command context for executing a command.
*/
struct command_context {
/**
Input (raw) command line, including arguments.
*/
const char * command_line;
/**
Command being executed.
*/
const gs_command_t * command;
/**
Number of arguments (standard argc style).
*/
int argc;
/**
Argument array (standard argv style).
*/
char **argv;
/**
FILE handle for capturing stdout from command.
*/
FILE* out;
/**
getopt variable.
*/
int optind;
/**
getopt variable.
*/
int optopt;
/**
getopt variable.
*/
char *optarg;
/**
getopt variable.
*/
int optsp;
/**
Function interface for I/O operations
*/
const gs_command_io_functions_t *io_functions;
/**
Pointer for storing the context used by the I/O functions
*/
void * io_ctx;
};
/**
Command logging call-back
logs information on the command called.
@param[in] cmd_line command line string
@param[in] ret return code from command execution framework
@param[in] cmd_ret return code from the executed command
@param[in] start time_stamp when command execution started.
@param[in] end time_stamp when command execution completed.
@param[in] ctx context pointer for the logger.
@return_gs_error_t
*/
typedef gs_error_t (*gs_command_log_t)(const char *cmd_line, gs_error_t ret, gs_error_t cmd_ret, gs_timestamp_t start, gs_timestamp_t end, void * ctx);
/**
Command handler.
*/
typedef int (*gs_command_handler_t)(gs_command_context_t * ctx);
/**
Completer call-back (tab complete).
@param[in] ctx command context.
@param[in] arg_to_complete argument to complete
@return #GS_OK Found at least 1 match.
The \a completer is expected to have completed more of the command line.
If the framework detects multiple matches, the framework will proceed as if #GS_ERROR_AMBIGUOUS was returned.
The framework doesn't expect anything to be printed to \a out, but will update/refresh the console line.
@return #GS_ERROR_AMBIGUOUS Ambiguous - multiple matches or force the framework to show help.
The \a completer may have extended/completed more of the command line.
The framework expects the \a completer to have printed to \a out, and will show help/usage for the command on a new line.
@return #GS_ERROR_NOT_FOUND (or others) No matches found or no more arguments to complete.
The framewrok doesn't expect anything to be printed to \a out, and will not update the console.
*/
typedef gs_error_t (*gs_command_completer_t)(gs_command_context_t * ctx, int arg_to_complete);
/**
Add token - helper to 'tab complete' argument(s).
@param[in] ctx command context.
@param[in] token possible completion - the API will find the common part.
@param[in] exact \a true if \a token is an exact match - all other added tokens will be ignored.
@return number of tokens added.
*/
unsigned int gs_command_completer_add_token(gs_command_context_t * ctx, const char * token, bool exact);
/**
Chain element for chaning sub-commands.
*/
typedef struct {
/**
Command list.
*/
const gs_command_t * list;
/**
Number of commands in the \a list.
*/
unsigned int count;
} gs_command_chain_t;
/**
Signals no command arguments in command definition, see mandatory arguments.
*/
#define GS_COMMAND_NO_ARGS 255
/**
Command definition.
*/
struct command {
#if (__AVR__)
char name[GS_COMMAND_MAX_LEN_COMMAND];
char help[50];
char usage[50];
#else
/**
Name.
*/
const char * const name;
/**
Help text.
*/
const char * const help;
/**
Usage text.
*/
const char * const usage;
#endif
/**
Command handler - the "actual command function".
*/
gs_command_handler_t handler;
#if (__AVR__ == 0)
/**
Completer function - helps completing an argument.
*/
gs_command_completer_t completer;
#endif
/**
Sub-command (if any).
*/
gs_command_chain_t chain;
/**
Mode/flags.
See #GS_COMMAND_FLAG_HIDDEN.
*/
unsigned int mode;
/**
Number of mandatory (minimum) arguments.
@note Due to backwards compatibility, 0 (zero) cannot be used to signal no arguments - use #GS_COMMAND_NO_ARGS instead, if command doesn't take any arguments (mandatory or optional).
*/
uint8_t mandatory_args;
/**
Number of optional arguments.
*/
uint8_t optional_args;
/**
Filler for future use.
*/
uint8_t filler[2];
};
/**
Returns the arguments as a string, where arguments are separated by spaces.
@param ctx command context.
@return Pointer to concatenated arguments
*/
const char * gs_command_args(gs_command_context_t *ctx);
/**
Execute command.
@deprecated Replaced by gs_command_execute & gs_command_execute_stdio
Looks up a command and executes it. If the command is executed (this function returns GS_OK), the command's return
result is stored in \a command_result.
@param[in] command Command to execute, including arguments separated by spaces.
@param[out] command_result Result returned by \a command (if executed). Use \a NULL to ignore result.
@return #GS_ERROR_NOT_FOUND if command wasn't found.
@return #GS_ERROR_ARG if number of argumenets exceeds \a mandatory + \a optional count.
@return_gs_error_t
*/
gs_error_t gs_command_run(const char * command, gs_error_t * command_result);
/**
Execute command.
Looks up a command and executes it. If the command is executed (this function returns GS_OK), the command's return
result is stored in \a command_result.
@param[in] command Command to execute, including arguments separated by spaces.
@param[out] command_result Result returned by \a command (if executed). Use \a NULL to ignore result.
@param[in] out output (FILE) stream
@param[in] iof Pointer to function interface of IO operations
@param[in] iof_ctx Pointer to context used by the IO function interface
@return #GS_ERROR_NOT_FOUND if command wasn't found.
@return #GS_ERROR_ARG if number of argumenets exceeds \a mandatory + \a optional count.
@return_gs_error_t
*/
gs_error_t gs_command_execute(const char * command, gs_error_t * command_result, FILE *out, const gs_command_io_functions_t * iof, void * iof_ctx);
/**
Execute command.
Looks up a command and executes it. If the command is executed (this function returns GS_OK), the command's return
result is stored in \a command_result. The results are printed on stdout and input captured on stdin.
@param[in] command Command to execute, including arguments separated by spaces.
@param[out] command_result Result from command. Use \a NULL to ignore result.
@return #GS_OK if command was executed - result returned in \a command_result. Otherwise an error indicating why the command wasn't executed.
*/
gs_error_t gs_command_execute_stdio(const char * command, gs_error_t * command_result);
/**
Set output
Sets output from command, using the io function struct in ctx.
@param[in] ctx the command context
@param[in] group a string specifying the group of the result. Leave blank if not used.
@param[in] key a string specifying the key/name of the result variable.
@param[in] value a string representation of the result value.
@return_gs_error_t
*/
gs_error_t gs_command_set_output(gs_command_context_t *ctx, const char* group, const char* key, const char* value);
/**
Set output
Sets output from command using printf formatting, using the io function struct in ctx.
@param[in] ctx the command context
@param[in] group a string specifying the group of the result. Leave blank if not used.
@param[in] key a string specifying the key/name of the result variable.
@param[in] format printf syntax for formatting data
@return_gs_error_t
*/
gs_error_t gs_command_set_output_printf(gs_command_context_t *ctx, const char* group, const char* key, const char * format, ...);
/**
Flush output/Results
Instruct the command output stream & results to be flushed from it's buffers.
@param[in] ctx the command context
@return_gs_error_t
*/
gs_error_t gs_command_flush_output(gs_command_context_t *ctx);
/**
Wait for any key input
Instruct the command input stream to wait for any key.
@param[in] ctx the command context
@param[in] timeout_ms timeout, < 0: block forever, 0: poll, > 0: wait number of milliseconds.
@return true if command should proceed (either because of key press present or if no input stream available)
*/
bool gs_command_wait_any_key(gs_command_context_t *ctx, int timeout_ms);
/**
Wait for key input
Instruct the io stream to wait for a key, and return the pressed key in ch.
@param[in] ctx the command context
@param[out] ch the character that was read on the input stream
@param[in] timeout_ms timeout, < 0: block forever, 0: poll, > 0: wait number of milliseconds.
@return #GS_OK if key was read
@return #GS_ERROR_HANDLE if no input stream is present
@return #GS_ERROR_TIMEOUT on timeout.
*/
gs_error_t gs_command_wait_key(gs_command_context_t *ctx, int* ch, int timeout_ms);
/**
Register commands.
gs_command_init() must be called prior to registering any commands.
See #GS_COMMAND_ROOT for details.
@param[in] cmds Pointer to command array
@param[in] cmd_count Number of commands in command array
@return_gs_error_t
*/
gs_error_t gs_command_register(const gs_command_t * cmds, size_t cmd_count);
/**
Initialize command system and register default commands.
Registers following commands: gs_log_register_commands() and gs_command_register_default_commands().
@param[in] min_stack_size Minimum stack size required for executing commands. The stack size will be used by other sub-systems such as gs_console, g-script. Stack size may be ignored on some platforms, e.g. Linux.
@return_gs_error_t
@see gs_command_init_no_commands()
*/
gs_error_t gs_command_init(size_t min_stack_size);
/**
Initialize command system (without any default commands).
@param[in] min_stack_size Minimum stack size required for executing commands. The stack size will be used by other sub-systems such as gs_console, g-script. Stack size may be ignored on some platforms, e.g. Linux.
@return_gs_error_t
@see gs_command_init()
*/
gs_error_t gs_command_init_no_commands(size_t min_stack_size);
/**
Register a call-back used for logging of command execution.
@param[in] log_cb the logging call back.
@param[in] log_ctx pointer to context data. Set to NULL if not used.
@return_gs_error_t
*/
gs_error_t gs_command_register_logger(gs_command_log_t log_cb, void *log_ctx);
/**
Default implementation of the command logger, that can be used if no other
custom command logger is provided by the system.
@param[in] cmd_line command line string
@param[in] ret return code provided by the command execution function.
@param[in] cmd_ret return code provided by the executed command.
@param[in] t_start time stamp when command execution started.
@param[in] t_end time stamp when command execution completed.
@param[in] log_ctx context for the command logger.
@return_gs_error_t
*/
gs_error_t gs_command_logger_default(const char* cmd_line, gs_error_t ret, gs_error_t cmd_ret, gs_timestamp_t t_start, gs_timestamp_t t_end, void * log_ctx);
/**
Return minimum stack size.
@return minimm stack size required for executing commands. The minimum stack size is set by call to gs_command_init().
*/
size_t gs_command_get_stack_size(void);
/**
Register set of default commands.
@return_gs_error_t
*/
gs_error_t gs_command_register_default_commands(void);
/**
Split line into argc/argv.
@param[in] line line to split - the line will be chop up into argv.
@param[out] argc argc count.
@param[out] argv argv array.
@param[in] max_argc max argv elements.
@return \a true if successfull, else \a false.
*/
bool gs_command_build_argv(char *line, int *argc, char **argv, int max_argc);
/**
Parse options.
Adapted from AT&T public domain source from:
http://www.informatica.co.cr/unix-source-code/research/1985/1103.html
@param[in] ctx command context.
@param[in] opts options
@return option character
*/
int gs_command_getopt(gs_command_context_t *ctx, const char *opts);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,123 @@
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
#ifndef GS_UTIL_GOSH_CONSOLE_H
#define GS_UTIL_GOSH_CONSOLE_H
/**
@file
Console (stdin/stdout) interface for running commands.
This assumes a VT102 terminal emulator, and tries to fix some of minicom's quirks with e.g. home/end keys.
*/
#include <gs/util/thread.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Initialize the API and console.
@deprecated version 3.4, use gs_console_start()
@return_gs_error_t
*/
gs_error_t gs_console_init(void);
/**
Restores terminal settings (only relevant on Linux).
@deprecated version 3.4, this is handled by an installed exit-handler in gs_console_start().
@return_gs_error_t
*/
gs_error_t gs_console_exit(void);
/**
Set console prompt.
@param[in] prompt user prompt - the API only stores the pointer, so do not modify/delete content. NULL or empty string is ignored (no change).
*/
void gs_console_set_prompt(const char * prompt);
/**
Clear the console screen
*/
void gs_console_clear(void);
/**
Update console.
*/
void gs_console_update(void);
/**
Create console thread.
The console thread reads from stdin and writes to stdout.
The thread is created with low priority, #GS_THREAD_PRIORITY_LOW.
@deprecated version 3.4, use gs_console_start()
@param[out] handle handle to created thread - thread will be created joinable if supported by platform. Use NULL, if not wanted.
@return_gs_error_t
*/
gs_error_t gs_console_create_thread(gs_thread_t * handle);
/**
Create console thread with priority.
The console thread reads from stdin and writes to stdout.
@deprecated version 3.4, use gs_console_start()
@param[in] priority thread priority, normally #GS_THREAD_PRIORITY_LOW.
@param[out] handle handle to created thread - thread will be created joinable if supported by platform. Use NULL, if not wanted.
@return_gs_error_t
*/
gs_error_t gs_console_create_thread_with_priority(gs_thread_priority_t priority, gs_thread_t * handle);
/**
@anchor GS_CONSOLE_F
@defgroup GS_CONSOLE_F Console flags.
Use with gs_console_start() to configure behaviour.
@{
*/
/**
Linux only: no signal handlers installed (e.g. to catch SIG_TERM).
@see gs_console_start()
*/
#define GS_CONSOLE_F_NO_SIGNAL_HANDLER 0x0001
/** @} */
/**
Start console thread (priority: #GS_THREAD_PRIORITY_LOW).
The console thread reads from stdin and writes to stdout. The thread is created with low priority, #GS_THREAD_PRIORITY_LOW.
Linux: Changes terminal settings and installs an atexit() handler to restore the settings, Signal handlers will be installed to catch SIG_TERM -> exit() and ignore SIG_INT (controlled by option on command line) - unless #GS_CONSOLE_F_NO_SIGNAL_HANDLER is specified.
@param[in] prompt set console prompt by calling gs_console_set_prompt().
@param[in] flags configure behaviour, see @ref GS_CONSOLE_F definitions.
@return #GS_ERROR_EXIST if console thread already created.
@return_gs_error_t
*/
gs_error_t gs_console_start(const char * prompt, uint32_t flags);
/**
Stop (and join with) console thread.
@note This is only supported on Linux.
The thread is interrupted using pthread_cancel(), which does not guarantee \a clean shutdown if the thread is busy executing a command.
@return #GS_ERROR_NOT_SUPPORTED if not supported on current platform.
@return #GS_ERROR_HANDLE if no console has been started with gs_console_start().
@return_gs_error_t
*/
gs_error_t gs_console_stop(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,53 @@
#ifndef GS_UTIL_HEXDUMP_H
#define GS_UTIL_HEXDUMP_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Dump memory as hex numbers and ascii characters.
*/
#include <stdio.h>
#include <gs/util/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Dump memory to an output stream.
@param[in] src memory address.
@param[in] len number of bytes to dump.
@param[in] disp_addr display address, used instead of \a src.
@param[in] out output stream.
*/
void gs_hexdump_to_stream(const void * src, size_t len, const void * disp_addr, FILE* out);
/**
Dump memory on stdout.
@param[in] src memory address.
@param[in] len number of bytes to dump.
*/
static inline void gs_hexdump(const void *src, size_t len)
{
gs_hexdump_to_stream(src, len, src, stdout);
}
/**
Dump memory on stdout.
@param[in] src memory address.
@param[in] len number of bytes to dump.
@param[in] disp_addr display address, used instead of \a src.
*/
static inline void gs_hexdump_addr(const void * src, size_t len, const void * disp_addr)
{
gs_hexdump_to_stream(src, len, disp_addr, stdout);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,40 @@
#ifndef GS_UTIL_LINUX_ARGP_H
#define GS_UTIL_LINUX_ARGP_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Extensions to GNU argp parser (convenience functions).
*/
#include <argp.h>
#include <gs/util/linux/exitcode.h>
#include <gs/util/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Wrapper for argp_parse.
This function will call exit/terminate the process, if parsing fails.
\a argv may be re-organized.
@param[in] argp argp struct.
@param[in] argc argument count, i.e. standard argc.
@param[in] argv argument array, i.e. standard argv.
@param[in] flags future use.
@param[out] arg_index first unparsed option (-> argv modified).
@param[in] revision program revision, e.g. 3.0.1-12-g0cf1b59+.
*/
void gs_argp_parse(const struct argp * argp,
int argc, char ** argv,
unsigned int flags, int * arg_index,
const char * revision);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,42 @@
#ifndef GS_UTIL_LINUX_COMMAND_LINE_H
#define GS_UTIL_LINUX_COMMAND_LINE_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Command line support.
*/
#include <gs/util/linux/argp.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Command line options for ignoring CTRL-C
*/
extern const struct argp_child gs_console_command_line_ignore_ctrlc_argp;
/**
Command line options for adding -h (help).
*/
const struct argp_child gs_help_command_line_argp;
/**
Return if ctrl-c ignored on command line.
@return \a true i ctrl-c ignored.
*/
bool gs_command_line_ignore_ctrlc(void);
/**
Return program name based on argv[0].
@param[in] argv expected to be argv[0] amd point to the program name (possibly with full path).
@return program name.
*/
const char * gs_command_line_program_name(const char * argv);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,29 @@
#ifndef GS_UTIL_LINUX_DRIVERS_CAN_CAN_H
#define GS_UTIL_LINUX_DRIVERS_CAN_CAN_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Linux CAN interface.
@note Only 1 filter/mask can be set, using gs_can_set_standard_filter_mask() or gs_can_set_extended_filter_mask()
*/
#include <gs/util/drivers/can/can.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Open and initialize a CAN handle.
@param[in] ifname name of CAN interface.
@param[out] handle opened CAN handle.
@return_gs_error_t
*/
gs_error_t gs_can_open(const char * ifname, int * handle);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,146 @@
#ifndef GS_UTIL_LINUX_DRIVERS_GPIO_H
#define GS_UTIL_LINUX_DRIVERS_GPIO_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
@brief GPIO interface
GPIO interface provides a generic interface where specific GPIO drivers can be plugged in.
*/
#include <gs/util/error.h>
#include <gs/util/drivers/gpio/gpio.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
GomSpace linux driver GPIO get value
@param[in] gpio The gpio to read
@param[in] value Returned GPIO value (true/false = High/Low)
@param[in] driver_data data to specific driver
@return_gs_error_t
*/
typedef gs_error_t (*gs_gpio_get_t)(gs_gpio_t gpio, bool *value, void * driver_data);
/**
GomSpace linux driver GPIO get value without error check
@param[in] gpio The gpio to read
@param[in] driver_data data to specific driver
@return GPIO value (true/false = High/Low)
*/
typedef bool (*gs_gpio_get_nc_t)(gs_gpio_t gpio, void * driver_data);
/**
GomSpace linux driver GPIO set value
@param[in] gpio The gpio to set
@param[in] value GPIO value (true/false = High/Low)
@param[in] driver_data data to specific driver
@return_gs_error_t
*/
typedef gs_error_t (*gs_gpio_set_t)(gs_gpio_t gpio, bool value, void * driver_data);
/**
GomSpace linux driver GPIO set value without error check
@param[in] gpio The gpio to set
@param[in] value GPIO value (true/false = High/Low)
@param[in] driver_data data to specific driver
*/
typedef void (*gs_gpio_set_nc_t)(gs_gpio_t gpio, bool value, void * driver_data);
/**
GomSpace linux driver initialize GPIO as an external interrupt pin
@param[in] gpio The gpio to configure
@param[in] conf Configuration of interrupt pin
@param[in] driver_data data to specific driver
@return_gs_error_t
*/
typedef gs_error_t (*gs_gpio_init_as_interrupt_t)(gs_gpio_t gpio, const gs_interrupt_conf_t * conf, void * driver_data);
/**
Every port.
*/
#define GS_GPIO_ALL_PORTS UINT16_MAX
/**
Every pin.
*/
#define GS_GPIO_ALL_PINS UINT16_MAX
/**
GPIO driver.
*/
typedef struct {
/**
Function for handling GPIO get.
*/
gs_gpio_get_t get_handler;
/**
Function for handling GPIO get no check.
*/
gs_gpio_get_nc_t get_nc_handler;
/**
Function for handling GPIO set.
*/
gs_gpio_set_t set_handler;
/**
Function for handling GPIO set no check.
*/
gs_gpio_set_nc_t set_nc_handler;
/**
Function for handling GPIO initialize as interrupt.
*/
gs_gpio_init_as_interrupt_t init_as_interrupt_handler;
} gs_gpio_driver_t;
/**
GPIO driver entry
*/
typedef struct {
/**
GPIO port, to which the driver is used (if GS_GPIO_ALL_PORTS, then all ports uses this driver).
*/
uint16_t port;
/**
GPIO pin, to which the driver is used (if GS_GPIO_ALL_PINS, then all pins uses this driver).
*/
uint16_t pin;
/**
GPIO driver.
*/
const gs_gpio_driver_t * driver;
/**
Driver specific data.
*/
void * driver_data;
} gs_gpio_driver_entry_t;
/**
Register a driver.
A specific driver can be assigned to a port and pin or it can be assigned to all pins and/or all ports.
The latest registered driver, which fit the GPIO, is the one used.
@param[in] driver_entry driver and configuration to be registered
@return_gs_error_t
*/
gs_error_t gs_gpio_register_driver(const gs_gpio_driver_entry_t * driver_entry);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,91 @@
#ifndef LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_GPIO_GPIO_SYSFS_H_
#define LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_GPIO_GPIO_SYSFS_H_
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
@brief Linux GPIO driver based on sysfs.
This driver needs to be registered in the generic GPIO linux driver @see 'gs/util/linux/drivers/gpio/gpio.h'
*/
#include <gs/util/linux/drivers/gpio/gpio.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
GPIO sysfs driver data.
@note Driver takes no driver data, so a NULL pointer is valid
*/
typedef void * gs_gpio_sysfs_driver_data_t;
/**
GPIO sysfs driver interface.
*/
extern const gs_gpio_driver_t gs_gpio_sysfs_driver;
/**
GPIO sysfs initialize
@param[in] gpio The gpio to initialize
@param[in] output Direction of pin (True/False = Output/Input)
@param[in] init_value Pin state if configured as output (True/False = High/Low)
@param[in] active_low if set pin is configured as active low (so a gs_gpio_sysfs_set with 1 will actually set value low)
@return_gs_error_t
*/
gs_error_t gs_gpio_sysfs_initialize(gs_gpio_t gpio, bool output, bool init_value, bool active_low);
/**
GPIO sysfs get value
@param[in] gpio The gpio to read
@param[in] value Returned GPIO value (true/false = High/Low)
@param[in] driver_data data to driver (not used)
@return_gs_error_t
*/
gs_error_t gs_gpio_sysfs_get(gs_gpio_t gpio, bool *value, void * driver_data);
/**
GPIO sysfs get value without error check
@param[in] gpio The gpio to read
@param[in] driver_data data to driver (not used)
@return GPIO value (true/false = High/Low)
*/
bool gs_gpio_sysfs_get_nc(gs_gpio_t gpio, void * driver_data);
/**
GPIO sysfs set value
@param[in] gpio The gpio to set
@param[in] value GPIO value (true/false = High/Low)
@param[in] driver_data data to driver (not used)
@return_gs_error_t
*/
gs_error_t gs_gpio_sysfs_set(gs_gpio_t gpio, bool value, void * driver_data);
/**
GPIO sysfs set value without error check
@param[in] gpio The gpio to set
@param[in] value GPIO value (true/false = High/Low)
@param[in] driver_data data to driver (not used)
*/
void gs_gpio_sysfs_set_nc(gs_gpio_t gpio, bool value, void * driver_data);
/**
Initialize GPIO sysfs as an external interrupt pin
@param[in] gpio The gpio to configure
@param[in] conf Configuration of interrupt pin
@param[in] driver_data data to driver (not used)
@return_gs_error_t
*/
gs_error_t gs_gpio_sysfs_init_as_interrupt(gs_gpio_t gpio, const gs_interrupt_conf_t * conf, void * driver_data);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,125 @@
#ifndef LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_GPIO_GPIO_VIRTUAL_H_
#define LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_GPIO_GPIO_VIRTUAL_H_
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
@brief Linux GPIO driver to be used in unit tests.
This driver needs to be registered in the generic GPIO linux driver @see 'gs/util/linux/drivers/gpio/gpio.h'
*/
#include <gs/util/linux/drivers/gpio/gpio.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
GPIO virtual driver data.
@note Driver takes no driver data, so a NULL pointer is valid
*/
typedef void * gs_gpio_virtual_driver_data_t;
/**
GPIO virtual driver interface.
*/
extern const gs_gpio_driver_t gs_gpio_virtual_driver;
/**
GPIO virtual driver entry, where all ports and pins are routed to virtual driver
*/
extern const gs_gpio_driver_entry_t gs_gpio_virtual_driver_entry_all;
/**
GPIO virtual initialize
@param[in] gpio The gpio to initialize
@param[in] output Direction of pin (True/False = Output/Input)
@param[in] value Pin state if configured as output (True/False = High/Low)
@return_gs_error_t
*/
gs_error_t gs_gpio_virtual_initialize(gs_gpio_t gpio, bool output, bool value);
/**
GPIO virtual get value
@param[in] gpio The gpio to read
@param[in] value Returned GPIO value (true/false = High/Low)
@param[in] driver_data data to driver (not used)
@return_gs_error_t
*/
gs_error_t gs_gpio_virtual_get(gs_gpio_t gpio, bool *value, void * driver_data);
/**
GPIO virtual get value without error check
@param[in] gpio The gpio to read
@param[in] driver_data data to driver (not used)
@return GPIO value (true/false = High/Low)
*/
bool gs_gpio_virtual_get_nc(gs_gpio_t gpio, void * driver_data);
/**
GPIO virtual set value
@param[in] gpio The gpio to set
@param[in] value GPIO value (true/false = High/Low)
@param[in] driver_data data to driver (not used)
@return_gs_error_t
*/
gs_error_t gs_gpio_virtual_set(gs_gpio_t gpio, bool value, void * driver_data);
/**
GPIO virtual set value without error check
@param[in] gpio The gpio to set
@param[in] value GPIO value (true/false = High/Low)
@param[in] driver_data data to driver (not used)
*/
void gs_gpio_virtual_set_nc(gs_gpio_t gpio, bool value, void * driver_data);
/**
Initialize GPIO virtual as an external interrupt pin
@param[in] gpio The gpio to configure
@param[in] conf Configuration of interrupt pin
@param[in] driver_data data to driver (not used)
@return_gs_error_t
*/
gs_error_t gs_gpio_virtual_init_as_interrupt(gs_gpio_t gpio, const gs_interrupt_conf_t * conf, void * driver_data);
/**
Force set a pin
This sets a pin regardless if it is configured as input, output or interrupt
If the pin is configured as interrupt, the registered ISR's will be called within this function,
if the transition matches (rising/falling)
@note This function is specific to this driver and is should not be registered.
@param[in] gpio The gpio to set
@param[in] value GPIO value (true/false = High/Low)
@return_gs_error_t
*/
gs_error_t gs_gpio_virtual_force_set(gs_gpio_t gpio, bool value);
/**
Get transitions
This gives the number of transitions ((high -> low) + (low -> high)),
since last time this function was called at this pin. This function resets the counter of the pin.
An even number means, that the pin has the same state as it was initialized to.
@note This function is specific to this driver and should not be registered
@param[in] gpio The gpio, of which transitions are given
@param[out] transitions Number of transitions
@return
*/
gs_error_t gs_gpio_virtual_get_transistions(gs_gpio_t gpio, uint32_t * transitions);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,198 @@
#ifndef LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_I2C_I2C_H_
#define LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_I2C_I2C_H_
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
@brief Linux I2C plugin driver
*/
#include <gs/util/drivers/i2c/master.h>
#include <gs/util/drivers/i2c/slave.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
GomSpace linux driver I2C master transaction.
@see 'gs/util/drivers/i2c/master.h'
@param[in] device I2C device
@param[in] addr I2C address
@param[in] tx tx buffer
@param[in] txlen bytes to be sent
@param[out] rx rx buffer
@param[in] rxlen bytes to be received
@param[in] timeout_ms timeout in milliseconds
@param[in] driver_data data to specific driver
@return_gs_error_t
*/
typedef gs_error_t (* gs_i2c_master_transaction_t)(uint8_t device, uint8_t addr, const void * tx, size_t txlen,
void * rx, size_t rxlen, int timeout_ms, void * driver_data);
/**
GomSpace linux driver I2C slave start.
@see 'gs/util/drivers/i2c/slave.h'
@param[in] device I2C device
@param[in] driver_data data to specific driver
@return_gs_error_t
*/
typedef gs_error_t (* gs_i2c_slave_start_t)(uint8_t device, void * driver_data);
/**
GomSpace linux driver I2C set rx callback
@see 'gs/util/drivers/i2c/slave.h'
@param[in] device I2C device
@param[in] rx rx callback
@param[in] driver_data data to specific driver
@return_gs_error_t
*/
typedef gs_error_t (* gs_i2c_slave_set_rx_t)(uint8_t device, gs_i2c_slave_receive_t rx, void * driver_data);
/**
GomSpace linux driver I2C slave set 'get_rx_buffer' callback.
@see 'gs/util/drivers/i2c/slave.h'
@param[in] device I2C device
@param[in] get_rx_buf get_rx_buf callback
@param[in] buf_length length of buffer received by calling callback
@param[in] driver_data data to specific driver
@return_gs_error_t
*/
typedef gs_error_t (* gs_i2c_slave_set_get_rx_buf_t)(uint8_t device, gs_i2c_slave_get_rx_buf_t get_rx_buf, size_t buf_length, void * driver_data);
/**
GomSpace linux driver I2C slave set slave response.
@see 'gs/util/drivers/i2c/slave.h'
@param[in] device I2C device
@param[in] tx tx buffer
@param[in] tx_length bytes to be send
@param[in] driver_data data to specific driver
@return_gs_error_t
*/
typedef gs_error_t (* gs_i2c_slave_set_response_t)(uint8_t device, const uint8_t * tx, size_t tx_length, void * driver_data);
/**
Every I2C device ([0 : 254]).
*/
#define GS_I2C_ALL_DEVICES 255
/**
Every I2C address (0 : 127]).
*/
#define GS_I2C_ALL_ADDR 255
/**
I2C master driver.
*/
typedef struct {
/**
Function for handling master transactions.
*/
gs_i2c_master_transaction_t master_transaction_handler;
} gs_i2c_master_driver_t;
/**
I2C master driver entry
*/
typedef struct {
/**
I2C device, to which the driver is used (if GS_I2C_ALL_DEVICES, then all devices uses this driver).
*/
uint8_t device;
/**
I2C addr, to which the driver is used (if GS_I2C_ALL_ADDR, then all addr on given device uses this driver).
*/
uint8_t addr;
/**
I2C master driver.
*/
const gs_i2c_master_driver_t * driver;
/**
Driver specific data.
*/
void * driver_data;
} gs_i2c_master_driver_entry_t;
/**
I2C slave driver
*/
typedef struct {
/**
Function for handling slave start.
*/
gs_i2c_slave_start_t start_handler;
/**
Function for handling the 'setting of rx callback'.
*/
gs_i2c_slave_set_rx_t set_rx_handler;
/**
Function for handling setting of an 'rx buff get' function.
*/
gs_i2c_slave_set_get_rx_buf_t set_get_rx_buf_handler;
/**
Function for handling 'set response'.
*/
gs_i2c_slave_set_response_t set_response_handler;
} gs_i2c_slave_driver_t;
/**
I2C slave driver entry.
*/
typedef struct {
/**
I2C device, to which the driver is used (if GS_I2C_ALL_DEVICES, then all devices uses this driver).
*/
uint8_t device;
/**
I2C slave driver.
*/
const gs_i2c_slave_driver_t * driver;
/**
Driver specific data.
*/
void * driver_data;
} gs_i2c_slave_driver_entry_t;
/**
Register a master driver.
A specific driver can be assigned to a specific address and device
or it can be registered to every address on a device or every address on every device.
The latest registered driver, which fit the device an address, is the one used.
@param[in] driver_entry driver and configuration to be registered
@return_gs_error_t
*/
gs_error_t gs_i2c_master_register_driver(const gs_i2c_master_driver_entry_t * driver_entry);
/**
Register a slave driver
A specific driver can be assigned to a specific device or a driver can be assigned to every device.
The latest registered driver, which fit the device, is the one used.
@param[in] driver_entry driver and configuration to be registered
@return_gs_error_t
*/
gs_error_t gs_i2c_slave_register_driver(const gs_i2c_slave_driver_entry_t * driver_entry);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,175 @@
#ifndef LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_SPI_SPI_H_
#define LIB_LIBUTIL_INCLUDE_GS_UTIL_LINUX_DRIVERS_SPI_SPI_H_
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Linux SPI plugin driver
*/
#include <gs/util/drivers/spi/master.h>
#include <gs/util/drivers/spi/slave.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Linux driver SPI master transactions.
@see 'gs/util/drivers/spi/master.h'
@param[in] slave SPI slave
@param[in] trans Pointer to transactions
@param[in] count Number of transactions (rx and/or tx) to complete
@param[in] timeout_ms timeout in milliseconds, primarily for locking the SPI device.
@param[in] driver_data data to specific driver
@return_gs_error_t
*/
typedef gs_error_t (*gs_spi_master_transactions_t)(uint8_t slave, gs_spi_master_trans_t *trans, size_t count,
int timeout_ms, void * driver_data);
/**
Linux driver SPI slave start.
@see 'gs/util/drivers/spi/slave.h'
@param[in] device SPI device (handle)
@param[in] driver_data data to specific driver
@return_gs_error_t
*/
typedef gs_error_t (* gs_spi_slave_start_t)(uint8_t device, void * driver_data);
/**
Linux driver SPI set rx callback
@see 'gs/util/drivers/spi/slave.h'
@param[in] device SPI device (handle).
@param[in] rx Rx callback.
@param[in] driver_data data to specific driver
@return_gs_error_t
*/
typedef gs_error_t (* gs_spi_slave_set_rx_t)(uint8_t device, gs_spi_slave_receive_t rx, void * driver_data);
/**
Linux driver SPI slave set slave response.
@see 'gs/util/drivers/spi/slave.h'
@param[in] device SPI device (handle).
@param[in] offset offset (in bytes) for the response, counted from start of request, i.e. offset of 2 means data will be sent as the 3rd byte.
@param[in] tx pointer to data. NOTE: data is not copied due to performance, so data must stay valid until the response has been sent.
@param[in] size size of data.
@param[in] driver_data data to specific driver
@return_gs_error_t
*/
typedef gs_error_t (* gs_spi_slave_set_response_t)(uint8_t device, size_t offset, const uint8_t * tx, size_t size, void * driver_data);
/**
Every SPI slave ([0 : 254]).
*/
#define GS_SPI_ALL_SLAVES 255
/**
Every SPI device (0 : 254]).
*/
#define GS_SPI_ALL_DEVICES 255
/**
SPI master driver.
*/
typedef struct {
/**
Function for handling master transactions.
*/
gs_spi_master_transactions_t master_transactions_handler;
} gs_spi_master_driver_t;
/**
SPI master driver entry
*/
typedef struct {
/**
SPI slave, to which the driver is used (if #GS_SPI_ALL_SLAVES, then all slaves uses this driver).
*/
uint8_t slave;
/**
SPI master driver.
*/
const gs_spi_master_driver_t * driver;
/**
Driver specific data.
*/
void * driver_data;
} gs_spi_master_driver_entry_t;
/**
SPI slave driver
*/
typedef struct {
/**
Function for handling slave start.
*/
gs_spi_slave_start_t start_handler;
/**
Function for handling the 'setting of rx callback'.
*/
gs_spi_slave_set_rx_t set_rx_handler;
/**
Function for handling 'set response'.
*/
gs_spi_slave_set_response_t set_response_handler;
} gs_spi_slave_driver_t;
/**
SPI slave driver entry.
*/
typedef struct {
/**
SPI device, to which the driver is used (if #GS_SPI_ALL_DEVICES, then all devices uses this driver).
*/
uint8_t device;
/**
SPI slave driver.
*/
const gs_spi_slave_driver_t * driver;
/**
Driver specific data.
*/
void * driver_data;
} gs_spi_slave_driver_entry_t;
/**
Register a master driver.
A specific driver can be assigned to a slave or it can be assigned to every slave.
The latest registered driver, which fit the slave, is the one used.
@param[in] driver_entry driver and configuration to be registered
@return_gs_error_t
*/
gs_error_t gs_spi_master_register_driver(const gs_spi_master_driver_entry_t * driver_entry);
/**
Register a slave driver
A specific driver can be assigned to a specific device or a driver can be assigned to every device.
The latest registered driver, which fit the device, is the one used.
@param[in] driver_entry driver and configuration to be registered
@return_gs_error_t
*/
gs_error_t gs_spi_slave_register_driver(const gs_spi_slave_driver_entry_t * driver_entry);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,40 @@
#ifndef GS_UTIL_LINUX_EXITCODE_H
#define GS_UTIL_LINUX_EXITCODE_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
"standard" Linux exit codes.
*/
#include <stdlib.h>
#include <sysexits.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Program completed ok (from stdlib.h)
*/
#define GS_EXITCODE_OK EXIT_SUCCESS
/**
Program terminated due to an error (from stdlib.h).
*/
#define GS_EXITCODE_ERROR EXIT_FAILURE
/**
Program terminated due to invalid usage, eg argument (from sysexits.h).
*/
#define GS_EXITCODE_USAGE EX_USAGE
/**
Program terminated due to a signal (from [TLDP](http://www.tldp.org/LDP/abs/html/exitcodes.html)).
*/
#define GS_EXITCODE_SIGNAL(sig) (128 + sig)
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,49 @@
#ifndef GS_UTIL_LINUX_FUNCTION_H
#define GS_UTIL_LINUX_FUNCTION_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Function interface - invokes a function by name.
*/
#include <gs/util/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Function prototype.
@param[in] arg argument provided to gs_function_invoke().
@return_gs_error_t
*/
typedef gs_error_t (*gs_function_t)(void * arg);
/**
Register \a function by name.
@param[in] short_name short name for function, used by gs_function_invoke() to find function to invoke.
@param[in] long_name long name (unique) for function, used by gs_function_invoke() to find function to invoke.
@param[in] function function to be invoked by gs_function_invoke()
@return #GS_ERROR_FULL if registry is full.
@return_gs_error_t
*/
gs_error_t gs_function_register(const char * short_name, const char * long_name, gs_function_t function);
/**
Invoke \a function by name.
The return value is from the registered function, except for #GS_ERROR_NOT_IMPLEMENTED.
@param[in] name registered function name.
@param[in] arg argument for function.
@return #GS_ERROR_NOT_IMPLEMENTED if the \a name isn't found.
@return_gs_error_t
*/
gs_error_t gs_function_invoke(const char * name, void * arg);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,28 @@
#ifndef GS_UTIL_LINUX_RTC_H
#define GS_UTIL_LINUX_RTC_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Real Time Clock interface (linux).
*/
#include <gs/util/rtc.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Register Real Time Clock interface.
@note Setting the RTC will normally require special permission.
@param[in] get if true, get will be registered.
@param[in] set if true, set will be registered.
@return_gs_error_t
*/
gs_error_t gs_rtc_register_linux(bool get, bool set);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,40 @@
#ifndef GS_UTIL_LINUX_SIGNAL_H
#define GS_UTIL_LINUX_SIGNAL_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Signal helpers - catch and ignore.
*/
#include <signal.h>
#include <gs/util/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Standard Linux signal handler.
*/
typedef void (*gs_signal_handler)(int signal, siginfo_t *si, void *context);
/**
Register/catch signal and invoke handler.
@param[in] signal signal to catch.
@param[in] handler signal handler. If \a handler is NULL, a default handler will be invoked, which calls exit(#GS_EXITCODE_SIGNAL + signal).
@return_gs_error_t
*/
gs_error_t gs_signal_catch(int signal, gs_signal_handler handler);
/**
Ignore signal
@param[in] signal signal to ignore.
@return_gs_error_t
*/
gs_error_t gs_signal_ignore(int signal);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,30 @@
#ifndef GS_UTIL_SYSFS_HELPER_H
#define GS_UTIL_SYSFS_HELPER_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Sysfs interface.
*/
#include <gs/util/types.h>
#include <gs/util/error.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Sysfs write (GPIO).
*/
gs_error_t gs_sysfs_write_file(const char *path, const char *value);
/**
Sysfs read (GPIO).
*/
gs_error_t gs_sysfs_read_file(const char *path, char *value, size_t len);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,15 @@
#ifndef GS_UTIL_LOG_H
#define GS_UTIL_LOG_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Log interface.
The log interface supports logging to different group.
Logging is done through groups (domains), which can runtime be re-configured with level.
*/
#include <gs/util/log/log.h>
#endif

View File

@ -0,0 +1,189 @@
#ifndef GS_UTIL_LOG_APPENDER_APPENDER_H
#define GS_UTIL_LOG_APPENDER_APPENDER_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Log Appender interface.
The log appender interface supports logging to different "stores".
Logging is done through groups, which can be registered to different log appenders.
Each log appender has it's own filter (level mask).
Examples of log appenders could be: console, file, vmem, ...
*/
#include <gs/util/log/log.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Log appender (forward declaration)
All log groups log to one or more appenders. The Log appender is responsible
for putting the actual log data to a store/console or some other log medium.
*/
typedef struct gs_log_appender gs_log_appender_t;
/**
Log appender record iterator callback function
@param[in] ctx context data for iterator.
@param[in] level log level of record being iterated
@param[in] ts timestamp of record being iterated
@param[in] group group string (zero terminated) of record being iterated
@param[in] msg message string (zero terminated) of record being iterated
@return true/false: Return false to discontinue iteration.
*/
typedef bool (*gs_log_record_iterator_t)(void *ctx, gs_log_level_t level, const gs_timestamp_t *ts, const char *group, const char *msg);
/**
Log appender driver interface
*/
typedef struct {
/** appender init function */
gs_error_t (*init)(gs_log_appender_t *appender);
/** appender function */
void (*append)(gs_log_appender_t *appender, gs_log_level_t level, const gs_log_group_t *group, const gs_timestamp_t * ts, const char * format, va_list va);
/** appender function for isr context */
void (*append_isr)(gs_log_appender_t *appender, gs_log_level_t level, const gs_log_group_t *group, const gs_timestamp_t * ts, const char * format, va_list va);
/** appender function for getting appender details string */
void (*info)(gs_log_appender_t *appender, char * info_str, uint8_t str_size);
/** appender function for iterating stored appenders log history */
void (*hist)(gs_log_appender_t *appender, void * ctx, gs_log_record_iterator_t iter);
/** appender function for clearing it's log history */
void (*clear)(gs_log_appender_t *appender);
/** appender function for flushing cached log entries to it's store.
This is only relevant for appenders implementing a log cache. */
void (*flush)(gs_log_appender_t *appender);
} gs_log_appender_driver_t;
/**
Log appender
All log groups log to one or more appenders. The Log appender is responsible
for putting the actual log data to a store/console or some other log medium.
*/
struct gs_log_appender {
/** Name of the appender */
const char * name;
/** appender driver interface */
const gs_log_appender_driver_t * drv;
/** appender driver configuration data */
const void * drv_config;
/** appender driver data - dynamic/internal data */
void * drv_data;
/** appender level mask */
uint8_t mask;
};
/**
Register an appender for the given log group.
All logging, where the mask matches the groups \a level_mask, will be forwarded to this appender.
@param[in] group_name Name of the group.
@param[in] appender_name Name of appender to register for this group.
@return gs_error_t
*/
gs_error_t gs_log_group_register_appender(const char * group_name, const char * appender_name);
/**
Log appender iterator callback function
@param[in] ctx context data for iterator.
@param[in] appender log appender being iterated
@return true/false: Return false to discontinue iteration.
*/
typedef bool (*gs_log_appender_iterator_t)(void *ctx, gs_log_appender_t * appender);
/**
Iterate all or specific log appender(s).
@param[in] name name of log appender, or NULL/\"all\" for all groups.
@param[in] ctx user context data.
@param[in] iter iterator, return \a true to continue, \a false to break iteration.
@return_gs_error_t
*/
gs_error_t gs_log_appender_iterate(const char * name, void * ctx, gs_log_appender_iterator_t iter);
/**
Iterate registered appenders for a specific group.
@param[in] group log group to iterate appenders on.
@param[in] ctx user context data.
@param[in] iter appender iterator, return \a true to continue, \a false to break iteration.
@return gs_error_t
*/
gs_error_t gs_log_group_appender_iterate(gs_log_group_t * group, void * ctx, gs_log_appender_iterator_t iter);
/**
Register log appender.
The log appender will be registered and initialized (if the appender has en init function, see #gs_log_appender_driver_t)
The appender will not be attached to any log groups. For registering an appender to a group, use gs_log_group_register_appender()
@param[in] appender appender - must stay in memory during the life-time of the application
@return_gs_error_t
*/
gs_error_t gs_log_appender_register(gs_log_appender_t *appender);
/**
Add log appender(s).
The log appender will be registered and initialized (if the appender has en init function, see #gs_log_appender_driver_t)
The appender will not be attached to any log groups. For registering an appender to a group, use gs_log_group_register_appender()
@deprecated impossible to determine which appender fails, use gs_log_appender_register()
@param[in] appenders array of appender(s) - must stay in memory during the life-time of the application
@param[in] count array count - number of appenders.
@return_gs_error_t
*/
gs_error_t gs_log_appender_add(gs_log_appender_t *appenders, uint16_t count);
/**
Set log appender level mask.
@param[in] appender_name log appender name
@param[in] mask level mask to set.
@return_gs_error_t
*/
gs_error_t gs_log_appender_set_level_mask(const char * appender_name, uint8_t mask);
/**
Get log appender level mask.
@param[in] appender_name log appender name
@param[out] mask returned current level mask.
@return_gs_error_t
*/
gs_error_t gs_log_appender_get_level_mask(const char * appender_name, uint8_t *mask);
/**
Iterate log history for all or specific log appender.
@param[in] name name of log appender, or NULL/\"all\" for all appenders.
@param[in] ctx user context data for iterator.
@param[in] iter iterator, return \a true to continue, \a false to break iteration.
@return gs_error_t
*/
gs_error_t gs_log_appender_history_iterate(const char * name, void * ctx, gs_log_record_iterator_t iter);
/**
Flush all log appenders data to storage.
This will call the flush API (if implemented) for all log appenders
available on the system. This should be called on regular basis from
a system thread to ensure all cached data is correctly flushed to their
stores.
@return gs_error_t
*/
gs_error_t gs_log_appender_flush_all();
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,57 @@
#ifndef GS_UTIL_LOG_APPENDER_CONSOLE_H
#define GS_UTIL_LOG_APPENDER_CONSOLE_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Console log appender - logs to stdout.
*/
#include <gs/util/log/appender/appender.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Log appender for console
This log appender is the standard appender which is always available
on any system. The appender should be registered to the root group,
in order to get console/stdio logs.
*/
extern gs_log_appender_t gs_log_appender_console;
/**
Log appender for console callback type
This callback function can be used for registering a user defined logger function if
the default can not be used for the given system.
@param[in] appender pointer to the console appender.
@param[in] level log level for log message
@param[in] group log group for log message
@param[in] ts timestamp for log message
@param[in] format format of message in printf style
@param[in] va variable argument list in printf style
@return void
*/
typedef void (*gs_log_appender_console_cb_t)(gs_log_appender_t *appender, gs_log_level_t level, const gs_log_group_t *group, const gs_timestamp_t * ts, const char * format, va_list va);
/**
Set Log appender for console callback
When set, the given callback is called instead of the default console log function.
To revert back to the default console log function, call this function with NULL as parameter.
@param[in] cb callback to use for console logging.
@return gs_error_t
*/
gs_error_t gs_log_appender_console_set_cb(gs_log_appender_console_cb_t cb);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,41 @@
#ifndef GS_UTIL_LOG_APPENDER_SIMPLE_FILE_H
#define GS_UTIL_LOG_APPENDER_SIMPLE_FILE_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Simple log-file appender.
*/
#include <gs/util/log/appender/appender.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Simple File Log Appender driver configuration
*/
typedef struct gs_log_appender_simple_file_config {
/**
Name of file to create/write logs to
*/
const char *filename;
/**
Truncate the file, when opening the log file.
*/
bool truncate;
/**
Uee local time stamps when logging to log file, otherwise UTC.
*/
bool use_local_time;
} gs_log_appender_simple_file_config_t;
/**
Log appender for file.
*/
extern const gs_log_appender_driver_t gs_log_appender_simple_file_driver;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,853 @@
#ifndef GS_UTIL_LOG_LOG_H
#define GS_UTIL_LOG_LOG_H
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
/**
@file
Log interface.
Logging is done through groups (domains), where the level mask can be changed runtime.
*/
#include <gs/util/error.h>
#include <gs/util/timestamp.h>
#include <gs/util/pgm.h>
#include <inttypes.h>
#include <stdarg.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Name of the root log group
*/
#define GS_LOG_GROUP_ROOT "root"
/**
Log levels.
The levels can easily be mapped to standard syslog severity levels (https://en.wikipedia.org/wiki/Syslog).
*/
typedef enum {
/**
Trace (more detailed than \a debug).
syslog: maps to \a debug (or \a trace if supported).
*/
GS_LOG_TRACE = 0,
/**
Debug.
syslog: maps to \a debug.
*/
GS_LOG_DEBUG = 1,
/**
Informational.
syslog: maps to \a informational.
*/
GS_LOG_INFO = 2,
/**
Normal but significant conditions.
syslog: maps to \a notice.
*/
GS_LOG_NOTICE = 3,
/**
Warning.
syslog: maps to \a warning.
*/
GS_LOG_WARNING = 4,
/**
Error.
syslog: maps to \a error.
*/
GS_LOG_ERROR = 5,
/**
Trace (more detailed than \a debug).
@deprecated use #GS_LOG_TRACE
*/
LOG_TRACE = GS_LOG_TRACE,
/**
Debug.
@deprecated use #GS_LOG_DEBUG
*/
LOG_DEBUG = GS_LOG_DEBUG,
/**
Informational.
@deprecated use #GS_LOG_INFO
*/
LOG_INFO = GS_LOG_INFO,
/**
Normal but significant conditions.
@deprecated use #GS_LOG_NOTICE
*/
LOG_NOTICE = GS_LOG_NOTICE,
/**
Warning.
@deprecated use #GS_LOG_WARNING
*/
LOG_WARNING = GS_LOG_WARNING,
/**
Error.
@deprecated use #GS_LOG_ERROR
*/
LOG_ERROR = GS_LOG_ERROR,
} gs_log_level_t;
/**
Log categories.
The category is a way of grouping information about which sub-systems have logged. It is primarily used in the \a
telemetry table, to indicate what sub-systems have logged an \a error or \a warning - indicating a possible problem.
Up to 32 categories are supported (stored in a uint32).
Categories should be unique within a single node. However, nothing happens if categories clashes - it will only be more difficult to determine what part of the system logged.
Standard categories are defined from #GS_LOG_CAT_1 and up. Products or mission specific software should start from #GS_LOG_CAT_32 and down.
*/
typedef enum {
//! Standard, used for #GS_LOG_CAT_DEFAULT
GS_LOG_CAT_1 = 1 << 0,
//! Standard, used for #GS_LOG_CAT_DRIVER
GS_LOG_CAT_2 = 1 << 1,
//! Standard, used for #GS_LOG_CAT_CSP
GS_LOG_CAT_3 = 1 << 2,
//! Standard, used for #GS_LOG_CAT_PARAM
GS_LOG_CAT_4 = 1 << 3,
//! Standard, used for #GS_LOG_CAT_FILE_SYSTEM
GS_LOG_CAT_5 = 1 << 4,
//! Standard, used for #GS_LOG_CAT_COMMAND
GS_LOG_CAT_6 = 1 << 5,
//! Standard, used for #GS_LOG_CAT_HK
GS_LOG_CAT_7 = 1 << 6,
//! Standard, used for #GS_LOG_CAT_FP
GS_LOG_CAT_8 = 1 << 7,
//! Standard, used for #GS_LOG_CAT_ADCS
GS_LOG_CAT_9 = 1 << 8,
GS_LOG_CAT_10 = 1 << 9,
GS_LOG_CAT_11 = 1 << 10,
GS_LOG_CAT_12 = 1 << 11,
GS_LOG_CAT_13 = 1 << 12,
GS_LOG_CAT_14 = 1 << 13,
GS_LOG_CAT_15 = 1 << 14,
GS_LOG_CAT_16 = 1 << 15,
#if (__AVR__ == 0)
GS_LOG_CAT_17 = 1 << 16,
GS_LOG_CAT_18 = 1 << 17,
GS_LOG_CAT_19 = 1 << 18,
GS_LOG_CAT_20 = 1 << 19,
GS_LOG_CAT_21 = 1 << 20,
GS_LOG_CAT_22 = 1 << 21,
GS_LOG_CAT_23 = 1 << 22,
GS_LOG_CAT_24 = 1 << 23,
GS_LOG_CAT_25 = 1 << 24,
GS_LOG_CAT_26 = 1 << 25,
GS_LOG_CAT_27 = 1 << 26,
GS_LOG_CAT_28 = 1 << 27,
GS_LOG_CAT_29 = 1 << 28,
GS_LOG_CAT_30 = 1 << 29,
GS_LOG_CAT_31 = 1 << 30,
//! Product or mission specific - start here and down
GS_LOG_CAT_32 = 1 << 31,
#endif
} gs_log_category_t;
/**
@defgroup reserved_log_categories Reserved/assigned log categories.
These categories are assigned/reserved for certain sub-systems.
@{
*/
/**
Default, used if nothing else fits.
*/
#define GS_LOG_CAT_DEFAULT GS_LOG_CAT_1
/**
Driver layer.
*/
#define GS_LOG_CAT_DRIVER GS_LOG_CAT_2
/**
CSP.
*/
#define GS_LOG_CAT_CSP GS_LOG_CAT_3
/**
Parameter system.
*/
#define GS_LOG_CAT_PARAM GS_LOG_CAT_4
/**
File system.
*/
#define GS_LOG_CAT_FILE_SYSTEM GS_LOG_CAT_5
/**
Command framework and execution.
*/
#define GS_LOG_CAT_COMMAND GS_LOG_CAT_6
/**
Housekeeping System.
*/
#define GS_LOG_CAT_HK GS_LOG_CAT_7
/**
Flight Planner.
*/
#define GS_LOG_CAT_FP GS_LOG_CAT_8
/**
ADCS
*/
#define GS_LOG_CAT_ADCS GS_LOG_CAT_9
/** @} */
struct gs_log_list; /* forward declared private log list struct */
/**
Log list type (private)
Private gs_log_list type.
*/
typedef struct gs_log_list gs_log_list_t;
/**
Log group.
All logs are logged to a \a group. The group contains the current log level mask,
which controls whether the log is carried through or not.
*/
typedef struct {
/**
Name of log group.
*/
const char * name;
/**
Category, see #gs_log_category_t.
*/
uint32_t category;
/**
Current level mask, see #gs_log_level_t.
*/
uint8_t mask;
/**
Is group additive, if \a true (default) logging will be done on both root appenders and this groups appenders - if \a false, logging will only be done to this groups appenders.
*/
bool additivity;
/**
Private list of appenders.
*/
gs_log_list_t * appenders;
#if (__AVR__)
uint16_t dummy_align;
#endif
} gs_log_group_t;
/**
Log masks (levels converted to mask).
@{
*/
/**
Trace level enabled.
*/
#define GS_LOG_TRACE_MASK (1 << GS_LOG_TRACE)
/**
Debug level enabled.
*/
#define GS_LOG_DEBUG_MASK (1 << GS_LOG_DEBUG)
/**
Info level enabled.
*/
#define GS_LOG_INFO_MASK (1 << GS_LOG_INFO)
/**
Notice level enabled.
*/
#define GS_LOG_NOTICE_MASK (1 << GS_LOG_NOTICE)
/**
Warning level enabled.
*/
#define GS_LOG_WARNING_MASK (1 << GS_LOG_WARNING)
/**
Error level enabled.
*/
#define GS_LOG_ERROR_MASK (1 << GS_LOG_ERROR)
/**
All levels enabled.
*/
#define GS_LOG_ALL_MASK (GS_LOG_TRACE_MASK | GS_LOG_DEBUG_MASK | GS_LOG_INFO_MASK | GS_LOG_NOTICE_MASK | GS_LOG_WARNING_MASK | GS_LOG_ERROR_MASK)
/**
Default levels enabled - #GS_LOG_ERROR, #GS_LOG_WARNING and #GS_LOG_NOTICE.
*/
#define GS_LOG_DEFAULT_MASK (GS_LOG_ERROR_MASK | GS_LOG_WARNING_MASK | GS_LOG_NOTICE_MASK)
/**
Trace level enabled.
@deprecated use #GS_LOG_TRACE_MASK
*/
#define LOG_TRACE_MASK GS_LOG_TRACE_MASK
/**
Debug level enabled.
@deprecated use #GS_LOG_DEBUG_MASK
*/
#define LOG_DEBUG_MASK GS_LOG_DEBUG_MASK
/**
Info level enabled.
@deprecated use #GS_LOG_INFO_MASK
*/
#define LOG_INFO_MASK GS_LOG_INFO_MASK
/**
Notice level enabled.
@deprecated use #GS_LOG_NOTICE_MASK
*/
#define LOG_NOTICE_MASK GS_LOG_NOTICE_MASK
/**
Warning level enabled.
@deprecated use #GS_LOG_WARNING_MASK
*/
#define LOG_WARNING_MASK GS_LOG_WARNING_MASK
/**
Error level enabled.
@deprecated use #GS_LOG_ERROR_MASK
*/
#define LOG_ERROR_MASK GS_LOG_ERROR_MASK
/**
All levels enabled.
@deprecated use #GS_LOG_ALL_MASK
*/
#define LOG_ALL_MASK GS_LOG_ALL_MASK
/**
Default levels enabled - #GS_LOG_ERROR, #GS_LOG_WARNING and #GS_LOG_NOTICE.
@deprecated use #GS_LOG_DEFAULT_MASK
*/
#define LOG_DEFAULT_MASK GS_LOG_DEFAULT_MASK
/**@}*/
/**
Define/Create a log group.
@note name clash: This defines a variable, which potentially is \a global, meaning possibility of name clashes. Therefore log group should always
be prefixed with something that makes it fairly unique, i.e. component name. Example: gs_a3200dock_log - log group used by liba3200dock library.
@param[in] group name of variables created. See note above about name clash.
@param[in] name_in display name
@param[in] cat_in log group category
@param[in] level_mask log level mask.
*/
#define GS_LOG_GROUP(group, name_in, cat_in, level_mask) \
gs_log_group_t group##_s = {.name = name_in, .category = cat_in, \
.mask = level_mask, .additivity = true, \
.appenders = NULL}; \
gs_log_group_t * group = &group##_s
/**
Define log group with initial mask for \a print and \a store.
@note name clash: This defines a variable, which potentially is \a global, meaning possibility of name clashes. Therefore log group should always
be prefixed with something that makes it fairly unique, i.e. component name. Example: gs_a3200dock_log - log group used by liba3200dock library.
@deprecated This MACRO is no longer supported, use #GS_LOG_GROUP(...) instead.
@param[in] group name of variables created. See note above about name clash.
@param[in] name_in display name
@param[in] print_mask enable mask for \a print.
@param[in] store_mask enable mask for \a store.
*/
#define LOG_GROUP_MASKED(group, name_in, print_mask, store_mask) GS_LOG_GROUP(group, name_in, GS_LOG_CAT_DEFAULT, (print_mask | store_mask))
/**
Declare log group as external (defined else where).
@param[in] group the log group variable defined elsewhere.
*/
#define GS_LOG_GROUP_EXTERN(group) extern gs_log_group_t * group
/**
Define log group - levels are #GS_LOG_DEFAULT_MASK
@deprecated This MACRO is no longer supported, use #GS_LOG_GROUP(..) instead.
*/
#define LOG_GROUP(group, name_in) GS_LOG_GROUP(group, name_in, GS_LOG_CAT_DEFAULT, LOG_DEFAULT_MASK)
/**
Define verbose log group - all levels are enabled (#GS_LOG_ALL_MASK)
@deprecated This MACRO is no longer supported, use #GS_LOG_GROUP(..) instead.
*/
#define LOG_GROUP_VERBOSE(group, name_in) GS_LOG_GROUP(group, name_in, GS_LOG_CAT_DEFAULT, LOG_ALL_MASK)
/**
Define silent log group - all levels are disabled.
@deprecated This MACRO is no longer supported, use #GS_LOG_GROUP(..) instead.
*/
#define LOG_GROUP_SILENT(group, name_in) GS_LOG_GROUP(group, name_in, GS_LOG_CAT_DEFAULT, 0)
/**
Declare log group as external (defined else where).
@deprecated use #GS_LOG_GROUP_EXTERN(...) instead.
*/
#define LOG_GROUP_EXTERN(group) GS_LOG_GROUP_EXTERN(group)
/**
Default log group.
This can be overridden by a define
*/
extern gs_log_group_t * LOG_DEFAULT;
/**
Initializes the log system.
@param[in] with_console_appender Enable/Disable console log appender
@return_gs_error_t
*/
gs_error_t gs_log_init(bool with_console_appender);
/**
Set log group level mask.
@param[in] group_name log group name
@param[in] mask level mask to set.
@return_gs_error_t
*/
gs_error_t gs_log_group_set_level_mask(const char * group_name, uint8_t mask);
/**
Get log group level mask.
@param[in] group_name log group name
@param[out] mask returned current level mask.
@return_gs_error_t
*/
gs_error_t gs_log_group_get_level_mask(const char * group_name, uint8_t *mask);
/**
Log group iterator callback function
@param[in] ctx context data for iterator.
@param[in] group log group being iterated.
@return true/false: Return false to discontinue iteration.
*/
typedef bool (*gs_log_group_iterator_t)(void *ctx, gs_log_group_t * group);
/**
Iterate all or specific log group(s).
@param[in] group_name name of log group, or NULL/\"all\" for all groups.
@param[in] ctx user context data.
@param[in] iter iterator, return \a true to continue, \a false to break iteration.
@return_gs_error_t
*/
gs_error_t gs_log_group_iterate(const char * group_name, void * ctx, gs_log_group_iterator_t iter);
/**
Register a log group in the log system.
The log group will be added to a system wide list of log groups, enabling list and set of level.
@note The group must remain valid during the life-time of the application.
@param[in] group The log group to be added to the system.
@return_gs_error_t
*/
gs_error_t gs_log_group_register(gs_log_group_t *group);
/**
Register a log group in the log system.
@note The group must stay in memory during the life-time of the application
@see gs_log_group_register()
@param[in] group The log group to be added to the system.
@return_gs_error_t
*/
static inline gs_error_t gs_log_group_add(gs_log_group_t *group)
{
return gs_log_group_register(group);
}
/**
Checks if a level is enabled on a log group
@param[in] group The log group to check.
@param[in] level The log level to check if it's set on the group.
@return bool (true if enabled / false if not enabled)
*/
bool gs_log_group_is_level_enabled(gs_log_group_t *group, gs_log_level_t level);
/**
Convert string to log level.
@param[in] str log level.
@param[out] return_level converted log level.
@return_gs_error_t
*/
gs_error_t gs_log_string_to_level(const char * str, gs_log_level_t * return_level);
/**
Convert level to single character.
@param[in] level log level
@return single character representing the \a level.
*/
char gs_log_level_to_char(gs_log_level_t level);
/**
Register Log commands.
@return_gs_error_t
*/
gs_error_t gs_log_register_commands(void);
/**
Generic log.
@note This function should not be called directly, use log macros.
@param level log level
@param group log group. If NULL, the \a default log group will be used.
@param format Format string (printf style).
*/
void gs_log(gs_log_level_t level, gs_log_group_t * group, const char * format, ...) __attribute__ ((format (__printf__, 3, 4)));
/**
Generic log from ISR.
@note This function should not be called directly, use log macros.
@param level log level
@param group log group. If NULL, the \a default log group will be used.
@param format Format string (printf style).
*/
void gs_log_isr(gs_log_level_t level, gs_log_group_t * group, const char * format, ...) __attribute__ ((format (__printf__, 3, 4)));
/**
Generic log (va_list).
@note This function should not be called directly, use log macros.
@param level log level
@param group log group. If NULL, the \a default log group will be used.
@param format Format string (printf style).
@param args arguments for \a format.
*/
void gs_log_va(gs_log_level_t level, gs_log_group_t * group, const char * format, va_list args);
/**
Enable/disable color in \a print logs.
Default is \a enabled/true.
@param[in] color \a true to enable color, \a false disable color.
*/
void gs_log_set_print_color(bool color);
/**
Level to color (begin).
@param[in] level log level.
@return color string.
*/
const char * gs_log_level_to_color_begin(gs_log_level_t level);
/**
Level to color (end).
@return color string.
*/
const char * gs_log_level_to_color_end(void);
/**
Take a level as input an create a level mask enabling all
levels with priority >= level.
If level is e.g. LOG_INFO, the mask will enable Error, Warn & Info.
* @param level the log level.
* @return level mask
*/
uint8_t gs_log_level_to_mask(gs_log_level_t level);
/**
Convert string to log mask.
Format: [+-]level[,[+-]level]
+ add level, - remove level.
@param[in] str log mask
@param[in] current_mask current mask, used when input format contains + or -.
@param[out] return_mask converted log mask.
@return_gs_error_t
*/
gs_error_t gs_log_string_to_mask(const char *str, uint8_t current_mask, uint8_t * return_mask);
#if !(__DOXYGEN__)
/**
Internal macro for checking if log is enabled, before making log.
*/
#define __gs_log(level, group, format, ...) \
if (group->mask & (1 << level)) { \
gs_log(level, group, GS_PGM_STR(format), ##__VA_ARGS__); \
}
/**
Internal macro for checking if log is enabled for isr, before making log.
*/
#define __gs_log_isr(level, group, format, ...) \
if (group->mask & (1 << level)) { \
gs_log_isr(level, group, GS_PGM_STR(format), ##__VA_ARGS__); \
}
/**
Internal macro used for performing a log only once.
@note This creates a \a static \a variable.
*/
#define __gs_log_once(level, group, format, ...) \
({ \
static bool print_once; \
if (!print_once) { \
print_once = true; \
__gs_log(level, group, format, ##__VA_ARGS__); \
} \
})
#endif // __DOXYGEN__
/**
Default compile-time enabling/disabling of all levels
Unless levels are individually defined, this will be the default value.
*/
#if !defined(GS_LOG_DISABLE_ALL)
#define GS_LOG_DISABLE_ALL 0
#endif
/**
Disable \a error level compile-time by defining a value > 0
*/
#if !defined(GS_LOG_DISABLE_ERROR)
#define GS_LOG_DISABLE_ERROR GS_LOG_DISABLE_ALL
#endif
/**
Disable \a warning level compile-time by defining a value > 0
*/
#if !defined(GS_LOG_DISABLE_WARNING)
#define GS_LOG_DISABLE_WARNING GS_LOG_DISABLE_ALL
#endif
/**
Disable \a notice level compile-time by defining a value > 0
*/
#if !defined(GS_LOG_DISABLE_NOTICE)
#define GS_LOG_DISABLE_NOTICE GS_LOG_DISABLE_ALL
#endif
/**
Disable \a info level compile-time by defining a value > 0
*/
#if !defined(GS_LOG_DISABLE_INFO)
#define GS_LOG_DISABLE_INFO GS_LOG_DISABLE_ALL
#endif
/**
Disable \a debug level compile-time by defining a value > 0
*/
#if !defined(GS_LOG_DISABLE_DEBUG)
#define GS_LOG_DISABLE_DEBUG GS_LOG_DISABLE_ALL
#endif
/**
Disable \a trace level compile-time by defining a value > 0
*/
#if !defined(GS_LOG_DISABLE_TRACE)
#define GS_LOG_DISABLE_TRACE GS_LOG_DISABLE_ALL
#endif
/**
Log \a error to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_error(format, ...) { if (!GS_LOG_DISABLE_ERROR) __gs_log(LOG_ERROR, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a error from ISR to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_error_isr(format, ...) { if (!GS_LOG_DISABLE_ERROR) __gs_log_isr(LOG_ERROR, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a error to group.
@param[in] group log group (gs_log_group_t *).
@param[in] format Format string (printf style).
*/
#define log_error_group(group, format, ...) { if (!GS_LOG_DISABLE_ERROR) __gs_log(LOG_ERROR, (group), format, ##__VA_ARGS__); }
/**
Log \a error only once to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_error_once(format, ...) { if (!GS_LOG_DISABLE_ERROR) __gs_log_once(LOG_ERROR, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a error only once to group.
@param[in] group log group (gs_log_group_t *).
@param[in] format Format string (printf style).
*/
#define log_error_once_group(group, format, ...) { if (!GS_LOG_DISABLE_ERROR) __gs_log_once(LOG_ERROR, (group), format, ##__VA_ARGS__); }
/**
Log \a warning to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_warning(format, ...) { if (!GS_LOG_DISABLE_WARNING) __gs_log(LOG_WARNING, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a warning from ISR to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_warning_isr(format, ...) { if (!GS_LOG_DISABLE_WARNING) __gs_log_isr(LOG_WARNING, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a warning to group.
@param[in] group log group (gs_log_group_t *).
@param[in] format Format string (printf style).
*/
#define log_warning_group(group, format, ...) { if (!GS_LOG_DISABLE_WARNING) __gs_log(LOG_WARNING, (group), format, ##__VA_ARGS__); }
/**
Log \a warning only once to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_warning_once(format, ...) { if (!GS_LOG_DISABLE_WARNING) __gs_log_once(LOG_WARNING, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a warning only once to group.
@param[in] group log group (gs_log_group_t *).
@param[in] format Format string (printf style).
*/
#define log_warning_once_group(group, format, ...) { if (!GS_LOG_DISABLE_WARNING) __gs_log_once(LOG_WARNING, (group), format, ##__VA_ARGS__); }
/**
Log \a notice to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_notice(format, ...) { if (!GS_LOG_DISABLE_NOTICE) __gs_log(LOG_NOTICE, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a notice from ISR to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_notice_isr(format, ...) { if (!GS_LOG_DISABLE_NOTICE) __gs_log_isr(LOG_NOTICE, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a notice to group.
@param[in] group log group (gs_log_group_t *).
@param[in] format Format string (printf style).
*/
#define log_notice_group(group, format, ...) { if (!GS_LOG_DISABLE_NOTICE) __gs_log(LOG_NOTICE, (group), format, ##__VA_ARGS__); }
/**
Log \a notice only once to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_notice_once(format, ...) { if (!GS_LOG_DISABLE_NOTICE) __gs_log_once(LOG_NOTICE, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a notice only once to group.
@param[in] group log group (gs_log_group_t *).
@param[in] format Format string (printf style).
*/
#define log_notice_once_group(group, format, ...) { if (!GS_LOG_DISABLE_NOTICE) __gs_log_once(LOG_NOTICE, (group), format, ##__VA_ARGS__); }
/**
Log \a info to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_info(format, ...) { if (!GS_LOG_DISABLE_INFO) __gs_log(LOG_INFO, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a info from ISR to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_info_isr(format, ...) { if (!GS_LOG_DISABLE_INFO) __gs_log_isr(LOG_INFO, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a info to group.
@param[in] group log group (gs_log_group_t *).
@param[in] format Format string (printf style).
*/
#define log_info_group(group, format, ...) { if (!GS_LOG_DISABLE_INFO) __gs_log(LOG_INFO, (group), format, ##__VA_ARGS__); }
/**
Log \a info only once to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_info_once(format, ...) { if (!GS_LOG_DISABLE_INFO) __gs_log_once(LOG_INFO, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a info only once to group.
@param[in] group log group (gs_log_group_t *).
@param[in] format Format string (printf style).
*/
#define log_info_once_group(group, format, ...) { if (!GS_LOG_DISABLE_INFO) __gs_log_once(LOG_INFO, (group), format, ##__VA_ARGS__); }
/**
Log \a debug to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_debug(format, ...) { if (!GS_LOG_DISABLE_DEBUG) __gs_log(LOG_DEBUG, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a debug from ISR to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_debug_isr(format, ...) { if (!GS_LOG_DISABLE_DEBUG) __gs_log_isr(LOG_DEBUG, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a debug to group.
@param[in] group log group (gs_log_group_t *).
@param[in] format Format string (printf style).
*/
#define log_debug_group(group, format, ...) { if (!GS_LOG_DISABLE_DEBUG) __gs_log(LOG_DEBUG, (group), format, ##__VA_ARGS__); }
/**
Log \a debug only once to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_debug_once(format, ...) { if (!GS_LOG_DISABLE_DEBUG) __gs_log_once(LOG_DEBUG, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a debug only once to group.
@param[in] group log group (gs_log_group_t *).
@param[in] format Format string (printf style).
*/
#define log_debug_once_group(group, format, ...) { if (!GS_LOG_DISABLE_DEBUG) __gs_log_once(LOG_DEBUG, (group), format, ##__VA_ARGS__); }
/**
Log \a trace to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_trace(format, ...) { if (!GS_LOG_DISABLE_TRACE) __gs_log(LOG_TRACE, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a trace from ISR to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_trace_isr(format, ...) { if (!GS_LOG_DISABLE_TRACE) __gs_log_isr(LOG_TRACE, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a trace to group.
@param[in] group log group (gs_log_group_t *).
@param[in] format Format string (printf style).
*/
#define log_trace_group(group, format, ...) { if (!GS_LOG_DISABLE_TRACE) __gs_log(LOG_TRACE, (group), format, ##__VA_ARGS__); }
/**
Log \a trace only once to default group (LOG_DEFAULT).
@param[in] format Format string (printf style).
*/
#define log_trace_once(format, ...) { if (!GS_LOG_DISABLE_TRACE) __gs_log_once(LOG_TRACE, LOG_DEFAULT, format, ##__VA_ARGS__); }
/**
Log \a trace only once to group.
@param[in] group log group (gs_log_group_t *).
@param[in] format Format string (printf style).
*/
#define log_trace_once_group(group, format, ...) { if (!GS_LOG_DISABLE_TRACE) __gs_log_once(LOG_TRACE, (group), format, ##__VA_ARGS__); }
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,67 @@
#ifndef GS_UTIL_MINMAX_H
#define GS_UTIL_MINMAX_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Min/max utilities.
*/
#ifdef __cplusplus
extern "C" {
#endif
/**
Return minimum value.
@param[in] x value
@param[in] y value
@return the lowest value of the input parameters.
*/
#define gs_min(x,y) ({ \
__typeof__ (x) _x = (x); \
__typeof__ (y) _y = (y); \
_x < _y ? _x : _y; })
/**
Return maximum value.
@param[in] x value
@param[in] y value
@return the maximum value of the input parameters.
*/
#define gs_max(x,y) ({ \
__typeof__ (x) _x = (x); \
__typeof__ (y) _y = (y); \
_x > _y ? _x : _y; })
/**
Return minimum value.
@param[in] x value
@param[in] y value
@param[in] z value
@return the lowest value of the input parameters.
*/
#define gs_min3(x,y,z) gs_min(gs_min((x),(y)), (z))
/**
Return maximum value.
@param[in] x value
@param[in] y value
@param[in] z value
@return the maximum value of the input parameters.
*/
#define gs_max3(x,y,z) gs_max(gs_max((x),(y)), (z))
/**
Clamp value within min/max.
@param[in] x value
@param[in] _max max value
@param[in] _min min value
@return value between min and max.
*/
#define gs_clamp(x, _min, _max) ({ \
gs_min(gs_max((x), (_min)), (_max)); })
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,63 @@
#ifndef GS_UTIL_MUTEX_H
#define GS_UTIL_MUTEX_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Mutex (recursive).
The mutex API wraps POSIX \a pthread_mutex and FreeRTOS \a mutex.
@note Mutex can not be used from within an ISR routine - use gs_sem instead.
*/
#include <gs/util/error.h>
#if __linux__
#include <pthread.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if __linux__
/**
Mutex handle.
*/
typedef pthread_mutex_t * gs_mutex_t;
#else
typedef struct gs_freertos_mutex_t * gs_mutex_t;
#endif
/**
Create mutex.
@param[out] mutex handle.
@return error code.
*/
gs_error_t gs_mutex_create(gs_mutex_t * mutex);
/**
Destroy mutex - free resources.
@param[in] mutex handle.
@return error code.
*/
gs_error_t gs_mutex_destroy(gs_mutex_t mutex);
/**
Lock mutex.
@param[in] mutex handle.
@return error code.
*/
gs_error_t gs_mutex_lock(gs_mutex_t mutex);
/**
Unlock mutex.
@param[in] mutex handle.
@return error code.
*/
gs_error_t gs_mutex_unlock(gs_mutex_t mutex);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,162 @@
#ifndef GS_UTIL_PROGMEM_H
#define GS_UTIL_PROGMEM_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Macros for handling special memory access.
On most targets/processors, constant data/strings are located in the program space and can be read in the same way as data in the data space.
However, on a few targets (e.g. avr/avr8), data/strings must be marked in a special way in order to go into the program space, see #GS_PGM_STR()
Using following macros, will make it easier to make cross-platform code and avoid \#if/\#endif.
These macros should only be used where the code also needs to run on avr/avr8.
@note Including this header on avr/avr8 will REDEFINE printf!.
http://www.atmel.com/webdoc/avrlibcreferencemanual/group__avr__pgmspace.html.
http://www.nongnu.org/avr-libc/user-manual/pgmspace.html.
*/
#include <gs/util/stdio.h>
#if defined(__AVR__)
#include <avr/pgmspace.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__AVR__) || (__DOXYGEN__)
/**
Special program/data memory handling.
*/
#define GS_PGM 1
/**
Place object in program space (must be const).
Example: static const uint8_t data8[] GS_PGM_OBJECT = {1, 255};
*/
#define GS_PGM_OBJECT PROGMEM
/**
Place const string in program space.
By default the string goes into data, uses thereby uses up space.
Once the string is placed in program space, xx_P functions must be used to access them - see #GS_PGM_PRINTF.
@note printf is re-defined by including this header
*/
#define GS_PGM_STR(str) PSTR(str)
/**
Read uint8 from program space (near).
*/
#define GS_PGM_UINT8(value) pgm_read_byte(&(value))
/**
Read uint8 from program space using a pointer (near).
*/
#define GS_PGM_UINT8_BY_PTR(value) pgm_read_byte(value)
/**
Read word from program space (near).
*/
#define GS_PGM_UINT16(value) pgm_read_word(&(value))
/**
Read word from program space using a pointer (near).
*/
#define GS_PGM_UINT16_BY_PTR(value) pgm_read_word(value)
/**
Read dword from program space (near).
*/
#define GS_PGM_UINT32(value) pgm_read_dword(&(value))
/**
Read word from program space using a pointer (near).
*/
#define GS_PGM_UINT32_BY_PTR(value) pgm_read_dword(value)
/**
Memcpy from program space (near).
@param[in] dst destination.
@param[in] src source - program space.
@param[in] n number of bytes to copy
*/
#define GS_PGM_MEMCPY(dst, src, n) memcpy_P(dst, src, n)
/**
String compare (program space)
@param[in] s1 string 1
@param[in] s2 string 2 - program space.
@param[in] n max number of bytes to compare
*/
#define GS_PGM_STRNCMP(s1,s2,n) strncmp_P(s1, s2, n)
/**
String compare (program space)
@param[in] s1 string 1
@param[in] s2 string 2 - program space.
@param[in] n max number of bytes to compare
*/
#define GS_PGM_STRNCASECMP(s1,s2,n) strncasecmp_P(s1, s2, n)
/**
String formatting character for referencing a string placed in programs space.
*/
#define GS_PGM_FMT_STR "S"
/**
printf (format string in program space).
Example: print \a param->name (from prgram space) and \a value from data space, using a format string in program space.
GS_PGM_PRINTF(GS_PGM_STR("%"GS_PGM_FMT_STR", %d"), param->name, value)
*/
#define GS_PGM_PRINTF(format, ...) printf_P(format, ##__VA_ARGS__)
/**
vprintf (format string in program space).
*/
#define GS_PGM_VPRINTF(format, va) vfprintf_P(stdout, format, va)
/**
snprintf (format string in program space).
*/
#define GS_PGM_SNPRINTF(buf, bufsize, format, ...) snprintf_P(buf, bufsize, format, ##__VA_ARGS__)
/**
vsnprintf (format string in program space).
*/
#define GS_PGM_VSNPRINTF(buf, bufsize, format, va) vsnprintf_P(buf, bufsize, format, va)
/**
redefines printf (puts format string in program space)
*/
#undef printf
#define printf(format, ...) GS_PGM_PRINTF(GS_PGM_STR(format), ## __VA_ARGS__)
#else
#undef GS_PGM
#define GS_PGM_OBJECT
#define GS_PGM_STR(str) (str)
#define GS_PGM_UINT8(value) (value)
#define GS_PGM_UINT8_BY_PTR(value) (*(value))
#define GS_PGM_UINT16(value) (value)
#define GS_PGM_UINT16_BY_PTR(value) (*(value))
#define GS_PGM_UINT32(value) (value)
#define GS_PGM_UINT32_BY_PTR(value) (*(value))
#define GS_PGM_MEMCPY(dst, src, size) memcpy(dst, src, size)
#define GS_PGM_STRNCMP(s1,pgmstr,size) strncmp(s1, pgmstr, size)
#define GS_PGM_STRNCASECMP(s1,pgmstr,size) strncasecmp(s1, pgmstr, size)
#define GS_PGM_FMT_STR "s"
#define GS_PGM_PRINTF(format, ...) printf(format, ## __VA_ARGS__)
#define GS_PGM_VPRINTF(format, va) vprintf(format, va)
#define GS_PGM_SNPRINTF(buf, bufsize, format, ...) snprintf(buf, bufsize, format, ##__VA_ARGS__)
#define GS_PGM_VSNPRINTF(buf, bufsize, format, va) vsnprintf(buf, bufsize, format, va)
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,102 @@
#ifndef GS_UTIL_QUEUE_H
#define GS_UTIL_QUEUE_H
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
/**
@file
Queue.
The queue API wraps FreeRTOS \a queue.
*/
#include <gs/util/error.h>
#ifdef __cplusplus
extern "C" {
#endif
#if __linux__
/**
Queue handle.
*/
typedef struct gs_pthread_queue * gs_queue_t;
#else
typedef struct gs_freertos_queue_t * gs_queue_t;
#endif
/**
Create queue.
@param[in] items max number of items on the queue.
@param[in] item_size size of item (bytes).
@param[out] queue created queue.
@return_gs_error_t
*/
gs_error_t gs_queue_create(size_t items, size_t item_size, gs_queue_t * queue);
/**
Destroy queue - free resources.
@param[in] queue handle.
@return_gs_error_t
*/
gs_error_t gs_queue_destroy(gs_queue_t queue);
/**
Enqueue object on queue.
@param[in] queue handle.
@param[in] value pointer to object, size specified at gs_queue_create().
@param_int_timeout_ms
@return_gs_error_timeout
@return_gs_error_t
*/
gs_error_t gs_queue_enqueue(gs_queue_t queue, const void *value, int timeout_ms);
/**
Enqueue object on queue from within an ISR.
@param[in] queue handle.
@param[in] value pointer to object, size specified at gs_queue_create().
@param[in] cswitch context switch.
@return GS_ERROR_FULL if queue is full.
@return_gs_error_t
*/
gs_error_t gs_queue_enqueue_isr(gs_queue_t queue, const void * value, gs_context_switch_t * cswitch);
/**
Dequeue object from queue.
@param[in] queue handle.
@param[out] buf element - size specified in gs_queue_create().
@param_int_timeout_ms
@return_gs_error_timeout
@return_gs_error_t
*/
gs_error_t gs_queue_dequeue(gs_queue_t queue, int timeout_ms, void *buf);
/**
Dequeue object from queue from within an ISR.
@param[in] queue handle.
@param[in] cswitch context switch.
@param[out] buf element - size specified in gs_queue_create().
@return GS_ERROR_NOT_FOUND if no elements in queue.
@return_gs_error_t
*/
gs_error_t gs_queue_dequeue_isr(gs_queue_t queue, gs_context_switch_t * cswitch, void * buf);
/**
Return queue size.
@param[in] queue handle.
@return queue size
*/
unsigned int gs_queue_size(gs_queue_t queue);
/**
Return queue size from within an ISR.
@param[in] queue handle.
@return queue size
*/
unsigned int gs_queue_size_isr(gs_queue_t queue);
#ifdef __cplusplus
}
#endif
#endif

Some files were not shown because too many files have changed in this diff Show More