save failed integration state
This commit is contained in:
12
gomspace/libutil/include/conf_util.h
Normal file
12
gomspace/libutil/include/conf_util.h
Normal 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 */
|
@ -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
|
22
gomspace/libutil/include/deprecated/gs/gosh/gosh/getopt.h
Normal file
22
gomspace/libutil/include/deprecated/gs/gosh/gosh/getopt.h
Normal 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
|
43
gomspace/libutil/include/deprecated/gs/gosh/util/console.h
Normal file
43
gomspace/libutil/include/deprecated/gs/gosh/util/console.h
Normal 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
|
26
gomspace/libutil/include/deprecated/util/color_printf.h
Normal file
26
gomspace/libutil/include/deprecated/util/color_printf.h
Normal 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
|
231
gomspace/libutil/include/gs/uthash/utarray.h
Normal file
231
gomspace/libutil/include/gs/uthash/utarray.h
Normal 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 */
|
960
gomspace/libutil/include/gs/uthash/uthash.h
Normal file
960
gomspace/libutil/include/gs/uthash/uthash.h
Normal 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 */
|
757
gomspace/libutil/include/gs/uthash/utlist.h
Normal file
757
gomspace/libutil/include/gs/uthash/utlist.h
Normal 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 */
|
||||
|
393
gomspace/libutil/include/gs/uthash/utstring.h
Normal file
393
gomspace/libutil/include/gs/uthash/utstring.h
Normal 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 */
|
90
gomspace/libutil/include/gs/util/base16.h
Normal file
90
gomspace/libutil/include/gs/util/base16.h
Normal 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
|
173
gomspace/libutil/include/gs/util/bytebuffer.h
Normal file
173
gomspace/libutil/include/gs/util/bytebuffer.h
Normal 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
|
341
gomspace/libutil/include/gs/util/byteorder.h
Normal file
341
gomspace/libutil/include/gs/util/byteorder.h
Normal 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
|
54
gomspace/libutil/include/gs/util/check.h
Normal file
54
gomspace/libutil/include/gs/util/check.h
Normal 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
|
88
gomspace/libutil/include/gs/util/clock.h
Normal file
88
gomspace/libutil/include/gs/util/clock.h
Normal 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
|
55
gomspace/libutil/include/gs/util/crc32.h
Normal file
55
gomspace/libutil/include/gs/util/crc32.h
Normal 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
|
55
gomspace/libutil/include/gs/util/crc8.h
Normal file
55
gomspace/libutil/include/gs/util/crc8.h
Normal 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
|
42
gomspace/libutil/include/gs/util/delay.h
Normal file
42
gomspace/libutil/include/gs/util/delay.h
Normal 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
|
122
gomspace/libutil/include/gs/util/drivers/can/can.h
Normal file
122
gomspace/libutil/include/gs/util/drivers/can/can.h
Normal 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
|
91
gomspace/libutil/include/gs/util/drivers/gpio/gpio.h
Normal file
91
gomspace/libutil/include/gs/util/drivers/gpio/gpio.h
Normal 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
|
88
gomspace/libutil/include/gs/util/drivers/i2c/common.h
Normal file
88
gomspace/libutil/include/gs/util/drivers/i2c/common.h
Normal 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
|
32
gomspace/libutil/include/gs/util/drivers/i2c/master.h
Normal file
32
gomspace/libutil/include/gs/util/drivers/i2c/master.h
Normal 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
|
79
gomspace/libutil/include/gs/util/drivers/i2c/slave.h
Normal file
79
gomspace/libutil/include/gs/util/drivers/i2c/slave.h
Normal 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
|
66
gomspace/libutil/include/gs/util/drivers/spi/common.h
Normal file
66
gomspace/libutil/include/gs/util/drivers/spi/common.h
Normal 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
|
95
gomspace/libutil/include/gs/util/drivers/spi/master.h
Normal file
95
gomspace/libutil/include/gs/util/drivers/spi/master.h
Normal 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
|
84
gomspace/libutil/include/gs/util/drivers/spi/slave.h
Normal file
84
gomspace/libutil/include/gs/util/drivers/spi/slave.h
Normal 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
|
92
gomspace/libutil/include/gs/util/drivers/sys/memory.h
Normal file
92
gomspace/libutil/include/gs/util/drivers/sys/memory.h
Normal 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
|
61
gomspace/libutil/include/gs/util/drivers/watchdog/device.h
Normal file
61
gomspace/libutil/include/gs/util/drivers/watchdog/device.h
Normal 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
|
53
gomspace/libutil/include/gs/util/endian.h
Normal file
53
gomspace/libutil/include/gs/util/endian.h
Normal 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
|
199
gomspace/libutil/include/gs/util/error.h
Normal file
199
gomspace/libutil/include/gs/util/error.h
Normal 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
|
89
gomspace/libutil/include/gs/util/fletcher.h
Normal file
89
gomspace/libutil/include/gs/util/fletcher.h
Normal 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
|
79
gomspace/libutil/include/gs/util/function_scheduler.h
Normal file
79
gomspace/libutil/include/gs/util/function_scheduler.h
Normal 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
|
503
gomspace/libutil/include/gs/util/gosh/command.h
Normal file
503
gomspace/libutil/include/gs/util/gosh/command.h
Normal 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
|
123
gomspace/libutil/include/gs/util/gosh/console.h
Normal file
123
gomspace/libutil/include/gs/util/gosh/console.h
Normal 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
|
53
gomspace/libutil/include/gs/util/hexdump.h
Normal file
53
gomspace/libutil/include/gs/util/hexdump.h
Normal 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
|
||||
|
||||
|
40
gomspace/libutil/include/gs/util/linux/argp.h
Normal file
40
gomspace/libutil/include/gs/util/linux/argp.h
Normal 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
|
42
gomspace/libutil/include/gs/util/linux/command_line.h
Normal file
42
gomspace/libutil/include/gs/util/linux/command_line.h
Normal 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
|
29
gomspace/libutil/include/gs/util/linux/drivers/can/can.h
Normal file
29
gomspace/libutil/include/gs/util/linux/drivers/can/can.h
Normal 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
|
146
gomspace/libutil/include/gs/util/linux/drivers/gpio/gpio.h
Normal file
146
gomspace/libutil/include/gs/util/linux/drivers/gpio/gpio.h
Normal 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
|
@ -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
|
@ -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
|
198
gomspace/libutil/include/gs/util/linux/drivers/i2c/i2c.h
Normal file
198
gomspace/libutil/include/gs/util/linux/drivers/i2c/i2c.h
Normal 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
|
175
gomspace/libutil/include/gs/util/linux/drivers/spi/spi.h
Normal file
175
gomspace/libutil/include/gs/util/linux/drivers/spi/spi.h
Normal 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
|
40
gomspace/libutil/include/gs/util/linux/exitcode.h
Normal file
40
gomspace/libutil/include/gs/util/linux/exitcode.h
Normal 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
|
49
gomspace/libutil/include/gs/util/linux/function.h
Normal file
49
gomspace/libutil/include/gs/util/linux/function.h
Normal 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
|
28
gomspace/libutil/include/gs/util/linux/rtc.h
Normal file
28
gomspace/libutil/include/gs/util/linux/rtc.h
Normal 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
|
40
gomspace/libutil/include/gs/util/linux/signal.h
Normal file
40
gomspace/libutil/include/gs/util/linux/signal.h
Normal 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
|
30
gomspace/libutil/include/gs/util/linux/sysfs_helper.h
Normal file
30
gomspace/libutil/include/gs/util/linux/sysfs_helper.h
Normal 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
|
15
gomspace/libutil/include/gs/util/log.h
Normal file
15
gomspace/libutil/include/gs/util/log.h
Normal 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
|
189
gomspace/libutil/include/gs/util/log/appender/appender.h
Normal file
189
gomspace/libutil/include/gs/util/log/appender/appender.h
Normal 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
|
57
gomspace/libutil/include/gs/util/log/appender/console.h
Normal file
57
gomspace/libutil/include/gs/util/log/appender/console.h
Normal 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
|
41
gomspace/libutil/include/gs/util/log/appender/simple_file.h
Normal file
41
gomspace/libutil/include/gs/util/log/appender/simple_file.h
Normal 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
|
853
gomspace/libutil/include/gs/util/log/log.h
Normal file
853
gomspace/libutil/include/gs/util/log/log.h
Normal 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
|
67
gomspace/libutil/include/gs/util/minmax.h
Normal file
67
gomspace/libutil/include/gs/util/minmax.h
Normal 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
|
63
gomspace/libutil/include/gs/util/mutex.h
Normal file
63
gomspace/libutil/include/gs/util/mutex.h
Normal 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
|
162
gomspace/libutil/include/gs/util/pgm.h
Normal file
162
gomspace/libutil/include/gs/util/pgm.h
Normal 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
|
102
gomspace/libutil/include/gs/util/queue.h
Normal file
102
gomspace/libutil/include/gs/util/queue.h
Normal 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
|
62
gomspace/libutil/include/gs/util/rtc.h
Normal file
62
gomspace/libutil/include/gs/util/rtc.h
Normal file
@ -0,0 +1,62 @@
|
||||
#ifndef GS_UTIL_RTC_H
|
||||
#define GS_UTIL_RTC_H
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
Real Time Clock interface.
|
||||
|
||||
The RTC driver is used by gs_clock_get_time() and gs_clock_set_time().
|
||||
*/
|
||||
|
||||
#include <gs/util/error.h>
|
||||
#include <gs/util/timestamp.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Platform supporting RTC must register the driver, before the rest of the system can access it.
|
||||
@see gs_rtc_register()
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
Call-back for getting RTC time.
|
||||
@param[out] time user allocated struct for returning time.
|
||||
*/
|
||||
gs_error_t (*get_time)(void * driver_data, gs_timestamp_t * time);
|
||||
/**
|
||||
Call-back for setting RTC time.
|
||||
@param[in] time user allocated struct for returning time.
|
||||
*/
|
||||
gs_error_t (*set_time)(void * driver_data, const gs_timestamp_t * time);
|
||||
} gs_rtc_driver_t;
|
||||
|
||||
/**
|
||||
Register RTC driver.
|
||||
@param[in] driver driver - data/struct must remain valid as long as registered.
|
||||
@param[in] driver_data driver specific data, forwarded to driver when set/get is called.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_rtc_register(const gs_rtc_driver_t * driver, void * driver_data);
|
||||
|
||||
/**
|
||||
Return GS_OK if RTC is supported.
|
||||
*/
|
||||
gs_error_t gs_rtc_supported(void);
|
||||
|
||||
/**
|
||||
Set RTC.
|
||||
*/
|
||||
gs_error_t gs_rtc_get_time(gs_timestamp_t * time);
|
||||
|
||||
/**
|
||||
Get RTC.
|
||||
*/
|
||||
gs_error_t gs_rtc_set_time(const gs_timestamp_t * time);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
75
gomspace/libutil/include/gs/util/sem.h
Normal file
75
gomspace/libutil/include/gs/util/sem.h
Normal file
@ -0,0 +1,75 @@
|
||||
#ifndef GS_UTIL_SEM_H
|
||||
#define GS_UTIL_SEM_H
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
Semaphore.
|
||||
|
||||
The semaphore API wraps POSIX \a semaphore and FreeRTOS \a counted semaphore.
|
||||
|
||||
Main difference is that FreeRTOS uses different API calls, when called from within
|
||||
an ISR routine.
|
||||
*/
|
||||
|
||||
#include <gs/util/error.h>
|
||||
#if __linux__
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if __linux__
|
||||
/**
|
||||
Semaphore handle.
|
||||
*/
|
||||
typedef sem_t * gs_sem_t;
|
||||
#else
|
||||
typedef struct gs_freertos_sem_t * gs_sem_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
Create semaphore.
|
||||
@param[in] initialValue initial value.
|
||||
@param[out] sem created semaphore.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_sem_create(unsigned int initialValue, gs_sem_t * sem);
|
||||
|
||||
/**
|
||||
Destroy semaphore - free resources.
|
||||
@param[in] sem handle.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_sem_destroy(gs_sem_t sem);
|
||||
|
||||
/**
|
||||
Wait for semaphore to be signaled.
|
||||
@param[in] sem handle.
|
||||
@param_int_timeout_ms
|
||||
@return_gs_error_timeout
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_sem_wait(gs_sem_t sem, int timeout_ms);
|
||||
|
||||
/**
|
||||
Post/signal semaphore.
|
||||
@param[in] sem handle.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_sem_post(gs_sem_t sem);
|
||||
|
||||
/**
|
||||
Post/signal semaphore from within a ISR.
|
||||
@param[in] sem handle.
|
||||
@param[in] cswitch context switch.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_sem_post_isr(gs_sem_t sem, gs_context_switch_t * cswitch);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
117
gomspace/libutil/include/gs/util/stdio.h
Normal file
117
gomspace/libutil/include/gs/util/stdio.h
Normal file
@ -0,0 +1,117 @@
|
||||
#ifndef GS_UTIL_STDIO_H
|
||||
#define GS_UTIL_STDIO_H
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
GomSpace extensions to standard \a stdio.h.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <gs/util/error.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Put character on stdout.
|
||||
*/
|
||||
gs_error_t gs_stdio_putchar(int ch);
|
||||
|
||||
/**
|
||||
Read character from stdin with timeout.
|
||||
@param[in] timeout_ms timeout, < 0: block forever, 0: poll, > 0: wait number of milli seconds.
|
||||
@param[out] ch character read. If NULL, one character from stdin is still consumed - but nothing returned.
|
||||
@return GS_ERROR_TIMEOUT on timeout
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_stdio_getchar_timed(int timeout_ms, int *ch);
|
||||
|
||||
/**
|
||||
Read character from stdin.
|
||||
Blocks until a character is available.
|
||||
@param[out] ch character read. If NULL, one character from stdin is still consumed - but nothing returned.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
static inline gs_error_t gs_stdio_getchar(int * ch)
|
||||
{
|
||||
return gs_stdio_getchar_timed(-1, ch);
|
||||
}
|
||||
|
||||
/**
|
||||
Read characters from stdin.
|
||||
Blocks until all characters are read.
|
||||
@param[in,out] buf user supplied buffer for receiving characters.
|
||||
@param[in] n number of characters to read.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_stdio_get(char * buf, size_t n);
|
||||
|
||||
/**
|
||||
Write characters to stdout.
|
||||
Blocks until characters are written.
|
||||
@param[in] buf characters to write.
|
||||
@param[in] n number of characters to write.
|
||||
@param[in] text if \a true, new lines (\\n) are converted to \\r\\n.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_stdio_put(const char * buf, size_t n, bool text);
|
||||
|
||||
/**
|
||||
Pattern for printing a byte as binary.
|
||||
@see GS_STDIO_BYTETOBINARY()
|
||||
*/
|
||||
#define GS_STDIO_BYTETOBINARYPATTERN "%d%d%d%d%d%d%d%d"
|
||||
|
||||
/**
|
||||
Macro for splitting a byte info 'bits'.
|
||||
*/
|
||||
#define GS_STDIO_BYTETOBINARY(byte) \
|
||||
(byte & 0x80 ? 1 : 0), \
|
||||
(byte & 0x40 ? 1 : 0), \
|
||||
(byte & 0x20 ? 1 : 0), \
|
||||
(byte & 0x10 ? 1 : 0), \
|
||||
(byte & 0x08 ? 1 : 0), \
|
||||
(byte & 0x04 ? 1 : 0), \
|
||||
(byte & 0x02 ? 1 : 0), \
|
||||
(byte & 0x01 ? 1 : 0)
|
||||
|
||||
/**
|
||||
Color definitions for gs_color_printf()
|
||||
@see gs_color_printf()
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
Colors.
|
||||
*/
|
||||
GS_COLOR_COLORS = 0x00ff,
|
||||
GS_COLOR_NONE = 0,
|
||||
GS_COLOR_BLACK = 1,
|
||||
GS_COLOR_RED = 2,
|
||||
GS_COLOR_GREEN = 3,
|
||||
GS_COLOR_YELLOW = 4,
|
||||
GS_COLOR_BLUE = 5,
|
||||
GS_COLOR_MAGENTA = 6,
|
||||
GS_COLOR_CYAN = 7,
|
||||
GS_COLOR_WHITE = 8,
|
||||
/**
|
||||
Attributes
|
||||
*/
|
||||
GS_COLOR_ATTRS = 0xff00,
|
||||
GS_COLOR_BOLD = 0x100,
|
||||
} gs_color_printf_t;
|
||||
|
||||
/**
|
||||
Printf with colors on stdout.
|
||||
|
||||
Using the standard terminal escape sequences for setting the color.
|
||||
@param[in] color color settings.
|
||||
@param[in] format standard printf format string.
|
||||
*/
|
||||
void gs_color_printf(gs_color_printf_t color, const char * format, ...) __attribute__ ((format (__printf__, 2, 3)));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
391
gomspace/libutil/include/gs/util/string.h
Normal file
391
gomspace/libutil/include/gs/util/string.h
Normal file
@ -0,0 +1,391 @@
|
||||
#ifndef GS_UTIL_STRING_H
|
||||
#define GS_UTIL_STRING_H
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
String utilitizes.
|
||||
|
||||
All string parsing functions will return #GS_OK if the string was parsed entirely.
|
||||
If the string contains characters that are not part of the selected base, the functions will return #GS_ERROR_DATA.
|
||||
If the value parsed is bigger than the output type, the functions will return #GS_ERROR_OVERFLOW.
|
||||
Spaces are ignored by all functions.
|
||||
*/
|
||||
|
||||
#include <gs/util/error.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Macro helper for concatening tokens.
|
||||
*/
|
||||
#define GS_STRINGZ(x) #x
|
||||
|
||||
/**
|
||||
Stringify a preprocessing token.
|
||||
*/
|
||||
#define GS_DEF2STRING(x) GS_STRINGZ(x)
|
||||
|
||||
/**
|
||||
* Strncpy (using size of destination) and forced zero termination.
|
||||
*/
|
||||
#define GS_STRNCPY(dst,src) strncpy(dst,src,GS_ARRAY_SIZE(dst));dst[GS_ARRAY_SIZE(dst)-1] = 0
|
||||
|
||||
/**
|
||||
Convert string to int32 (decimal or hexadecimal).
|
||||
Accepts: decimal or hexadecimal, 1234 (decimal), 0x1234 (hexadecimal)
|
||||
@param[in] string string to convert.
|
||||
@param[out] value converted value
|
||||
@return GS_ERROR_OVERFLOW if the resulting value is larger than the output type
|
||||
@return GS_ERROR_DATA if the input string could not be parsed completely
|
||||
@return GS_ERROR_ARG if the input string is a NULL pointer
|
||||
*/
|
||||
gs_error_t gs_string_to_int32(const char * string, int32_t * value);
|
||||
|
||||
/**
|
||||
Convert string to uint32 (decimal or hexadecimal).
|
||||
Accepts: decimal or hexadecimal, 1234 (decimal), 0x1234 (hexadecimal)
|
||||
@param[in] string string to convert.
|
||||
@param[out] value converted value
|
||||
@return GS_ERROR_OVERFLOW if the resulting value is larger than the output type
|
||||
@return GS_ERROR_DATA if the input string could not be parsed completely
|
||||
@return GS_ERROR_ARG if the input string is a NULL pointer
|
||||
*/
|
||||
gs_error_t gs_string_to_uint32(const char * string, uint32_t * value);
|
||||
|
||||
/**
|
||||
Convert string to int64 (decimal or hexadecimal).
|
||||
Accepts: decimal or hexadecimal, 1234 (decimal), 0x1234 (hexadecimal)
|
||||
@param[in] string string to convert.
|
||||
@param[out] value converted value
|
||||
@return GS_ERROR_OVERFLOW if the resulting value is larger than the output type
|
||||
@return GS_ERROR_DATA if the input string could not be parsed completely
|
||||
@return GS_ERROR_ARG if the input string is a NULL pointer
|
||||
*/
|
||||
gs_error_t gs_string_to_int64(const char * string, int64_t * value);
|
||||
|
||||
/**
|
||||
Convert string to uint64 (decimal or hexadecimal).
|
||||
Accepts: decimal or hexadecimal, 1234 (decimal), 0x1234 (hexadecimal)
|
||||
@param[in] string string to convert.
|
||||
@param[out] value converted value
|
||||
@return GS_ERROR_OVERFLOW if the resulting value is larger than the output type
|
||||
@return GS_ERROR_DATA if the input string could not be parsed completely
|
||||
@return GS_ERROR_ARG if the input string is a NULL pointer
|
||||
*/
|
||||
gs_error_t gs_string_to_uint64(const char * string, uint64_t * value);
|
||||
|
||||
/**
|
||||
Convert string to int8 (decimal or hexadecimal).
|
||||
Accepts: decimal or hexadecimal, 12 (decimal), 0x12 (hexadecimal)
|
||||
@param[in] string string to convert.
|
||||
@param[out] value converted value
|
||||
@return GS_ERROR_OVERFLOW if the resulting value is larger than the output type
|
||||
@return GS_ERROR_DATA if the input string could not be parsed completely
|
||||
@return GS_ERROR_ARG if the input string is a NULL pointer
|
||||
*/
|
||||
gs_error_t gs_string_to_int8(const char * string, int8_t * value);
|
||||
|
||||
/**
|
||||
Convert string to uint8 (decimal or hexadecimal).
|
||||
Accepts: decimal or hexadecimal, 12 (decimal), 0x12 (hexadecimal)
|
||||
@param[in] string string to convert.
|
||||
@param[out] value converted value
|
||||
@return GS_ERROR_OVERFLOW if the resulting value is larger than the output type
|
||||
@return GS_ERROR_DATA if the input string could not be parsed completely
|
||||
@return GS_ERROR_ARG if the input string is a NULL pointer
|
||||
*/
|
||||
gs_error_t gs_string_to_uint8(const char * string, uint8_t * value);
|
||||
|
||||
/**
|
||||
Convert string to int16 (decimal or hexadecimal).
|
||||
Accepts: decimal or hexadecimal, 1234 (decimal), 0x1234 (hexadecimal)
|
||||
@param[in] string string to convert.
|
||||
@param[out] value converted value
|
||||
@return GS_ERROR_OVERFLOW if the resulting value is larger than the output type
|
||||
@return GS_ERROR_DATA if the input string could not be parsed completely
|
||||
@return GS_ERROR_ARG if the input string is a NULL pointer
|
||||
*/
|
||||
gs_error_t gs_string_to_int16(const char * string, int16_t * value);
|
||||
|
||||
/**
|
||||
Convert string to uint16 (decimal or hexadecimal).
|
||||
Accepts: decimal or hexadecimal, 1234 (decimal), 0x1234 (hexadecimal)
|
||||
@param[in] string string to convert.
|
||||
@param[out] value converted value
|
||||
@return GS_ERROR_OVERFLOW if the resulting value is larger than the output type
|
||||
@return GS_ERROR_DATA if the input string could not be parsed completely
|
||||
@return GS_ERROR_ARG if the input string is a NULL pointer
|
||||
*/
|
||||
gs_error_t gs_string_to_uint16(const char * string, uint16_t * value);
|
||||
|
||||
/**
|
||||
Convert string to uint32 (hexadecimal).
|
||||
Accepts: hexadecimal (no leading 0x), e.g. a123, A123.
|
||||
@param[in] string string to convert.
|
||||
@param[out] value converted value
|
||||
@return GS_ERROR_OVERFLOW if the resulting value is larger than the output type
|
||||
@return GS_ERROR_DATA if the input string could not be parsed completely
|
||||
@return GS_ERROR_ARG if the input string is a NULL pointer
|
||||
*/
|
||||
gs_error_t gs_string_hex_to_uint32(const char * string, uint32_t * value);
|
||||
|
||||
/**
|
||||
Convert string to uint64 (hexadecimal).
|
||||
Accepts: hexadecimal (no leading 0x), e.g. a123, A123.
|
||||
@param[in] string string to convert.
|
||||
@param[out] value converted value
|
||||
@return GS_ERROR_OVERFLOW if the resulting value is larger than the output type
|
||||
@return GS_ERROR_DATA if the input string could not be parsed completely
|
||||
@return GS_ERROR_ARG if the input string is a NULL pointer
|
||||
*/
|
||||
gs_error_t gs_string_hex_to_uint64(const char * string, uint64_t * value);
|
||||
|
||||
/**
|
||||
Convert string to boolean.
|
||||
Accepts: true, false, on, off, 1, 0 (ignores case)
|
||||
@param[in] string string to convert.
|
||||
@param[out] pvalue converted value
|
||||
@return GS_ERROR_DATA if the input string could not be parsed completely
|
||||
@return GS_ERROR_ARG if the input string is a NULL pointer
|
||||
*/
|
||||
gs_error_t gs_string_to_bool(const char * string, bool * pvalue);
|
||||
|
||||
/**
|
||||
Convert string to float.
|
||||
@param[in] string string to convert.
|
||||
@param[out] pvalue converted value
|
||||
@return GS_ERROR_DATA if the input string could not be parsed completely
|
||||
@return GS_ERROR_ARG if the input string is a NULL pointer
|
||||
*/
|
||||
gs_error_t gs_string_to_float(const char * string, float * pvalue);
|
||||
|
||||
/**
|
||||
Convert string to double.
|
||||
@param[in] string string to convert.
|
||||
@param[out] pvalue converted value
|
||||
@return GS_ERROR_DATA if the input string could not be parsed completely
|
||||
@return GS_ERROR_ARG if the input string is a NULL pointer
|
||||
*/
|
||||
gs_error_t gs_string_to_double(const char * string, double * pvalue);
|
||||
|
||||
/**
|
||||
Return string for boolean value (true or false).
|
||||
@param[in] value value
|
||||
@return \a 'true' if input is true, else \a 'false'.
|
||||
*/
|
||||
const char * gs_string_from_bool(bool value);
|
||||
|
||||
/**
|
||||
Convert string to pointer (decimal or hexadecimal).
|
||||
Accepts: decimal or hexadecimal, 1234 (decimal), 0x1234 (hexadecimal)
|
||||
@param[in] string string to convert.
|
||||
@param[out] value converted value
|
||||
@return GS_ERROR_OVERFLOW if the resulting value is larger than the output type
|
||||
@return GS_ERROR_DATA if the input string could not be parsed completely
|
||||
@return GS_ERROR_ARG if the input string is a NULL pointer
|
||||
*/
|
||||
gs_error_t gs_string_to_pointer(const char * string, void ** value);
|
||||
|
||||
/**
|
||||
Format size as Bytes, Kilo or Mega.
|
||||
|
||||
Output examples: \a 512.0B, \a 1.0K and \a 1.0M.
|
||||
|
||||
@param[in] size size in bytes
|
||||
@param[out] buffer formatted size
|
||||
@param[in] buffer_size size of \a buf
|
||||
@return GS_ERROR_OVERFLOW if the resulting value is larger than the output type
|
||||
@return GS_ERROR_DATA if the input string could not be parsed completely
|
||||
@return GS_ERROR_ARG if the input string is a NULL pointer
|
||||
*/
|
||||
char * gs_string_bytesize(long size, char *buffer, size_t buffer_size);
|
||||
|
||||
/**
|
||||
GS implementation of gcc's strtol
|
||||
Instead of setting errno this function takes a pointer to err which is set
|
||||
the same way as with gcc's strtol
|
||||
|
||||
@param[in] nptr input string
|
||||
@param[out] endptr the pointer to the end of the string parsed
|
||||
@param[in] base number system (10 or 16)
|
||||
@param[out] err return value if overflow
|
||||
@return converted value
|
||||
*/
|
||||
int32_t gs_string_strto32int(const char *nptr, char **endptr, uint8_t base, gs_error_t * err);
|
||||
|
||||
/**
|
||||
GS implementation of gcc's strtoul
|
||||
Instead of setting errno this function takes a pointer to err which is set
|
||||
the same way as with gcc's strtoul
|
||||
|
||||
@param[in] nptr input string
|
||||
@param[out] endptr the pointer to the end of the string parsed
|
||||
@param[in] base number system (10 or 16)
|
||||
@param[out] err return value if overflow
|
||||
@return converted value
|
||||
*/
|
||||
uint64_t gs_string_strto64uint(const char *nptr, char **endptr, uint8_t base, gs_error_t * err);
|
||||
|
||||
/**
|
||||
GS implementation of gcc's strtoul
|
||||
Instead of setting errno this function takes a pointer to err which is set
|
||||
the same way as with gcc's strtoul
|
||||
|
||||
@param[in] nptr input string
|
||||
@param[out] endptr the pointer to the end of the string parsed
|
||||
@param[in] base number system (10 or 16)
|
||||
@param[out] err return value if overflow
|
||||
@return converted value
|
||||
*/
|
||||
int64_t gs_string_strto64int(const char *nptr, char **endptr, uint8_t base, gs_error_t * err);
|
||||
|
||||
/**
|
||||
GS implementation of gcc's strtoul
|
||||
Instead of setting errno this function takes a pointer to err which is set
|
||||
the same way as with gcc's strtoul
|
||||
|
||||
@param[in] nptr input string
|
||||
@param[out] endptr the pointer to the end of the string parsed
|
||||
@param[in] base number system (10 or 16)
|
||||
@param[out] err return value if overflow
|
||||
@return converted value
|
||||
*/
|
||||
uint32_t gs_string_strto32uint(const char *nptr, char **endptr, uint8_t base, gs_error_t * err);
|
||||
|
||||
/**
|
||||
Returns pointer to first none-space character.
|
||||
|
||||
@param[in] string string
|
||||
@return NULL if \a string is NULL, otherwise first none-space character.
|
||||
*/
|
||||
const char * gs_string_skip_leading_spaces(const char * string);
|
||||
|
||||
/**
|
||||
Check if a string is NULL or empty.
|
||||
|
||||
@param[in] string string
|
||||
@return true if string is empty or NULL.
|
||||
*/
|
||||
bool gs_string_empty(const char * string);
|
||||
|
||||
/**
|
||||
Case-insentive wilcard match (similiar to fnmatch).
|
||||
|
||||
Supports following wildcard(s):
|
||||
- * (asterix) zero or more characters.
|
||||
|
||||
This may be extended in future versions and will not be considered a break of the API.
|
||||
|
||||
@param[in] pattern pattern to match against \a string.
|
||||
@param[in] string string to match against \a pattern
|
||||
@return \a true if match, else \ false
|
||||
*/
|
||||
bool gs_string_match(const char * pattern, const char * string);
|
||||
|
||||
/**
|
||||
Returns \a true if string contains wildcards.
|
||||
|
||||
@param[in] string string to check for wildcards.
|
||||
@return \a true if string contains wildcards recognized by gs_string_match().
|
||||
*/
|
||||
bool gs_string_has_wildcards(const char * string);
|
||||
|
||||
/**
|
||||
Trim string in buffer by removing leading/trailing white space.
|
||||
|
||||
Uses isspace(c).
|
||||
|
||||
@param[in] buffer buffer to trim.
|
||||
@param[in] buffer_size size of \a buffer.
|
||||
*/
|
||||
void gs_string_trim(char * buffer, size_t buffer_size);
|
||||
|
||||
/**
|
||||
Returns \a true if string ends with endswith.
|
||||
|
||||
@param[in] string string to check
|
||||
@param[in] endswith string that string should end with
|
||||
@return \a true if string endswith endswith
|
||||
*/
|
||||
bool gs_string_endswith(const char * string, const char * endswith);
|
||||
|
||||
/**
|
||||
Extract suboption from a string.
|
||||
|
||||
@param[in] options options string, e.g. \"/dev/ttyUSB1,speed=9600,parity=no,databits=8\".
|
||||
@param[in] suboption sub-option to extract. If NULL or empty, the first option will be extracted (if present).
|
||||
@param[out] buf user buffer for returning value of sub-option.
|
||||
@param[in] buf_size size of \a buf user buffer.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_string_get_suboption(const char * options, const char * suboption, char * buf, size_t buf_size);
|
||||
|
||||
/**
|
||||
Extract suboption (as string) from a string.
|
||||
|
||||
@param[in] options options string, e.g. \"/dev/ttyUSB1,speed=9600,parity=no,databits=8\".
|
||||
@param[in] suboption sub-option to extract. If NULL or empty, the first option will be extracted (if present).
|
||||
@param[in] def default value, returned if sub-option isn't found.
|
||||
@param[out] buf user buffer for returning value of sub-option.
|
||||
@param[in] buf_size size of \a buf user buffer.
|
||||
@return If the sub-option isn't found, the \a def default value will be copied to \a buf and #GS_OK will be returned.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_string_get_suboption_string(const char * options, const char * suboption, const char * def, char * buf, size_t buf_size);
|
||||
|
||||
/**
|
||||
Extract suboption (as uint8) from a string.
|
||||
|
||||
@param[in] options options string, e.g. \"/dev/ttyUSB1,speed=9600,parity=no,databits=8\".
|
||||
@param[in] suboption sub-option to extract. If NULL or empty, the first option will be extracted (if present).
|
||||
@param[in] def default value, returned if sub-option isn't found.
|
||||
@param[out] value user supplied buffer for returning the value.
|
||||
@return If the sub-option isn't found, the \a def default value will be copied to \a value and #GS_OK will be returned.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_string_get_suboption_uint8(const char * options, const char * suboption, uint8_t def, uint8_t * value);
|
||||
|
||||
/**
|
||||
Extract suboption (as uint16) from a string.
|
||||
|
||||
@param[in] options options string, e.g. \"/dev/ttyUSB1,speed=9600,parity=no,databits=8\".
|
||||
@param[in] suboption sub-option to extract. If NULL or empty, the first option will be extracted (if present).
|
||||
@param[in] def default value, returned if sub-option isn't found.
|
||||
@param[out] value user supplied buffer for returning the value.
|
||||
@return If the sub-option isn't found, the \a def default value will be copied to \a value and #GS_OK will be returned.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_string_get_suboption_uint16(const char * options, const char * suboption, uint16_t def, uint16_t * value);
|
||||
|
||||
/**
|
||||
Extract suboption (as uint32) from a string.
|
||||
|
||||
@param[in] options options string, e.g. \"/dev/ttyUSB1,speed=9600,parity=no,databits=8\".
|
||||
@param[in] suboption sub-option to extract. If NULL or empty, the first option will be extracted (if present).
|
||||
@param[in] def default value, returned if sub-option isn't found.
|
||||
@param[out] value user supplied buffer for returning the value.
|
||||
@return If the sub-option isn't found, the \a def default value will be copied to \a value and #GS_OK will be returned.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_string_get_suboption_uint32(const char * options, const char * suboption, uint32_t def, uint32_t * value);
|
||||
|
||||
/**
|
||||
Extract suboption (as bool) from a string.
|
||||
|
||||
@param[in] options options string, e.g. \"/dev/ttyUSB1,speed=9600,parity=no,databits=8\".
|
||||
@param[in] suboption sub-option to extract. If NULL or empty, the first option will be extracted (if present).
|
||||
@param[in] def default value, returned if sub-option isn't found.
|
||||
@param[out] value user supplied buffer for returning the value.
|
||||
@return If the sub-option isn't found, the \a def default value will be copied to \a value and #GS_OK will be returned.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_string_get_suboption_bool(const char * options, const char * suboption, bool def, bool * value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
136
gomspace/libutil/include/gs/util/test/cmocka.h
Normal file
136
gomspace/libutil/include/gs/util/test/cmocka.h
Normal file
@ -0,0 +1,136 @@
|
||||
#ifndef GS_UTIL_TEST_CMOCKA_H
|
||||
#define GS_UTIL_TEST_CMOCKA_H
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
Cmocka extensions.
|
||||
|
||||
Official site for cmocka https://cmocka.org.
|
||||
*/
|
||||
|
||||
#include <gs/util/string.h>
|
||||
|
||||
// cmocka
|
||||
#include <stdio.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if !(__DOXYGEN__)
|
||||
// internal helpers - use macros
|
||||
void _gs_assert_int_equal(const intptr_t a, const intptr_t b, bool equal, const char * const file, const int line);
|
||||
void _gs_assert_uint_equal(const uintptr_t a, const uintptr_t b, bool equal, bool hex, const char * const file, const int line);
|
||||
void _gs_assert_error_equal(const int a, const int b, bool equal, const char * const file, const int line);
|
||||
void _gs_assert_float_equal(const float a, const float b, const float diff, bool equal, const char * const file, const int line);
|
||||
void _gs_assert_double_equal(const double a, const double b, const double diff, bool equal, const char * const file, const int line);
|
||||
#endif
|
||||
|
||||
/**
|
||||
Assert int (print value as signed).
|
||||
*/
|
||||
#define GS_ASSERT_INT_EQUAL(a,b) _gs_assert_int_equal((intptr_t) (a), (intptr_t) (b), true, __FILE__, __LINE__)
|
||||
/**
|
||||
Assert unsigned int (print value as unsigned).
|
||||
*/
|
||||
#define GS_ASSERT_UINT_EQUAL(a,b) _gs_assert_uint_equal((uintptr_t) (a), (uintptr_t) (b), true, false, __FILE__, __LINE__)
|
||||
/**
|
||||
Assert int (print value as hex).
|
||||
*/
|
||||
#define GS_ASSERT_XINT_EQUAL(a,b) _gs_assert_uint_equal((uintptr_t) (a), (uintptr_t) (b), true, true, __FILE__, __LINE__)
|
||||
/**
|
||||
Assert #gs_error_t (print value and error text).
|
||||
*/
|
||||
#define GS_ASSERT_ERROR_EQUAL(a,b) _gs_assert_error_equal(a, b, true, __FILE__, __LINE__)
|
||||
/**
|
||||
Assert #GS_OK (print value and error text).
|
||||
*/
|
||||
#define GS_ASSERT_ERROR_OK(a) _gs_assert_error_equal(a, GS_OK, true, __FILE__, __LINE__)
|
||||
/**
|
||||
Assert float (print value as signed).
|
||||
*/
|
||||
#define GS_ASSERT_FLOAT_EQUAL(a,b,diff) _gs_assert_float_equal(a, b, diff, true, __FILE__, __LINE__)
|
||||
/**
|
||||
Assert double (print value as signed).
|
||||
*/
|
||||
#define GS_ASSERT_DOUBLE_EQUAL(a,b,diff) _gs_assert_double_equal(a, b, diff, true, __FILE__, __LINE__)
|
||||
|
||||
/**
|
||||
Assert int (print value as signed).
|
||||
*/
|
||||
#define GS_ASSERT_INT_NOT_EQUAL(a,b) _gs_assert_int_equal((intptr_t) (a), (intptr_t) (b), false, __FILE__, __LINE__)
|
||||
/**
|
||||
Assert unsigned int (print value as unsigned).
|
||||
*/
|
||||
#define GS_ASSERT_UINT_NOT_EQUAL(a,b) _gs_assert_uint_equal((uintptr_t) (a), (uintptr_t) (b), false, false, __FILE__, __LINE__)
|
||||
/**
|
||||
Assert int (print value as hex).
|
||||
*/
|
||||
#define GS_ASSERT_XINT_NOT_EQUAL(a,b) _gs_assert_uint_equal((uintptr_t) (a), (uintptr_t) (b), false, true, __FILE__, __LINE__)
|
||||
/**
|
||||
Assert #GS_OK (print value and error text).
|
||||
*/
|
||||
#define GS_ASSERT_ERROR_NOT_EQUAL(a,b) _gs_assert_error_equal(a, b, false, __FILE__, __LINE__)
|
||||
|
||||
/**
|
||||
Code reference.
|
||||
*/
|
||||
#define GS_REF() __FILE__,__LINE__
|
||||
/**
|
||||
Assert int with code reference (print value as signed).
|
||||
*/
|
||||
#define GS_ASSERT_INT_EQUAL_REF(a,b,file,line) _gs_assert_int_equal((intptr_t) (a), (intptr_t) (b), true, file, file, line)
|
||||
/**
|
||||
Assert unsigned int with code reference (print value as unsigned).
|
||||
*/
|
||||
#define GS_ASSERT_UINT_EQUAL_REF(a,b,file,line) _gs_assert_uint_equal((uintptr_t) (a), (uintptr_t) (b), true, false, file, line)
|
||||
/**
|
||||
Assert int with code reference (print value as hex).
|
||||
*/
|
||||
#define GS_ASSERT_XINT_EQUAL_REF(a,b,file,line) _gs_assert_uint_equal((uintptr_t) (a), (uintptr_t) (b), true, true, file, line)
|
||||
/**
|
||||
Assert #gs_error_t with code reference (print value and error text).
|
||||
*/
|
||||
#define GS_ASSERT_ERROR_EQUAL_REF(a,b,file,line) _gs_assert_error_equal(a, b, true, file, line)
|
||||
/**
|
||||
Assert #GS_OK with code reference (print value and error text).
|
||||
*/
|
||||
#define GS_ASSERT_ERROR_OK_REF(a,file,line) _gs_assert_error_equal(a, GS_OK, true, file, line)
|
||||
|
||||
/**
|
||||
Run \a cmocka test group.
|
||||
|
||||
@param[in] name name of test. If name is \a tests and GS_TEST_NAME is set, GS_TEST_NAME will be used instead.
|
||||
@param[in] tests array of tests.
|
||||
@param[in] num_tests number of tests.
|
||||
@param[in] setup setup function, can be NULL.
|
||||
@param[in] teardown teardown function, can be NULL.
|
||||
@return 0 on success.
|
||||
*/
|
||||
static inline int gs_cmocka_run_group_tests(const char *name,
|
||||
const struct CMUnitTest * const tests,
|
||||
const size_t num_tests,
|
||||
CMFixtureFunction setup,
|
||||
CMFixtureFunction teardown)
|
||||
{
|
||||
#ifdef GS_TEST_NAME // set by buildtools::gs_test_cmocka.py
|
||||
if (strcasecmp(name, "tests") == 0) {
|
||||
name = GS_DEF2STRING(GS_TEST_NAME);
|
||||
}
|
||||
#endif
|
||||
return _cmocka_run_group_tests(name, tests, num_tests, setup, teardown);
|
||||
}
|
||||
|
||||
#ifdef GS_TEST_NAME
|
||||
// hi-jack cmocka's macro
|
||||
#undef cmocka_run_group_tests
|
||||
#define cmocka_run_group_tests(tests, setup, teardown) gs_cmocka_run_group_tests(GS_DEF2STRING(tests), tests, GS_ARRAY_SIZE(tests), setup, teardown)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
80
gomspace/libutil/include/gs/util/test/command.h
Normal file
80
gomspace/libutil/include/gs/util/test/command.h
Normal file
@ -0,0 +1,80 @@
|
||||
#ifndef GS_UTIL_TEST_COMMAND_H
|
||||
#define GS_UTIL_TEST_COMMAND_H
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
Command Test framework.
|
||||
|
||||
Provides a simple way of unit-testing/validating commands.
|
||||
*/
|
||||
|
||||
#include <gs/util/gosh/command.h>
|
||||
#include <gs/util/test/cmocka.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Validate command execution.
|
||||
|
||||
Runs a commands and validates the output/results agains the inputs.
|
||||
Asserts if the results does not match.
|
||||
|
||||
@param[in] cmd command (including arguments) to execute.
|
||||
@param[in] ret expected return code from the command execution framework.
|
||||
@param[in] cmd_ret expected return code from the commands handler. This is only validated if (ret = GS_OK).
|
||||
@param[in] std_in string with expected command input.
|
||||
@param[in] std_out string with expected command output. Wildcards (*\/?) are supported.
|
||||
@param[in] file string with file name.
|
||||
@param[in] line string with line no.
|
||||
@return void
|
||||
*/
|
||||
void _gs_assert_command_validate(const char *cmd, gs_error_t ret, gs_error_t cmd_ret, const char *std_in, const char *std_out, const char * const file, const int line);
|
||||
|
||||
/**
|
||||
Validate command results returned from last command execution.
|
||||
Asserts if the results does not match.
|
||||
|
||||
@param[in] no the result no to verify.
|
||||
@param[in] group string with expected group id. Wildcards (*\/?) are supported.
|
||||
@param[in] key string with expected key. Wildcards (*\/?) are supported.
|
||||
@param[in] value string with expected value. Wildcards (*\/?) are supported.
|
||||
@param[in] file string with file name.
|
||||
@param[in] line string with line no.
|
||||
@return void
|
||||
*/
|
||||
void _gs_assert_command_validate_last_result(unsigned int no, const char *group, const char *key, const char *value, const char * const file, const int line);
|
||||
|
||||
/**
|
||||
Validate command execution.
|
||||
|
||||
Runs a commands and validates the output/results agains the inputs.
|
||||
Asserts if the results does not match.
|
||||
|
||||
@param[in] cmd command (including arguments) to execute.
|
||||
@param[in] ret expected return code from the command execution framework.
|
||||
@param[in] cmd_ret expected return code from the commands handler. This is only validated if (ret = GS_OK).
|
||||
@param[in] std_in string with expected command input.
|
||||
@param[in] std_out string with expected command output. Wildcards (*\/?) are supported.
|
||||
@return void
|
||||
*/
|
||||
#define GS_ASSERT_COMMAND(cmd,ret,cmd_ret,std_in,std_out) _gs_assert_command_validate(cmd,ret,cmd_ret,std_in,std_out, __FILE__, __LINE__);
|
||||
|
||||
/**
|
||||
Validate command results returned from last command execution.
|
||||
Asserts if the results does not match.
|
||||
|
||||
@param[in] no the result no to verify.
|
||||
@param[in] group string with expected group id. Wildcards (*\/?) are supported.
|
||||
@param[in] key string with expected key. Wildcards (*\/?) are supported.
|
||||
@param[in] value string with expected value. Wildcards (*\/?) are supported.
|
||||
@return void
|
||||
*/
|
||||
#define GS_ASSERT_COMMAND_RESULT(no,group,key,value) _gs_assert_command_validate_last_result(no,group,key,value, __FILE__, __LINE__);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
88
gomspace/libutil/include/gs/util/test/log.h
Normal file
88
gomspace/libutil/include/gs/util/test/log.h
Normal file
@ -0,0 +1,88 @@
|
||||
#ifndef GS_UTIL_TEST_LOG_H
|
||||
#define GS_UTIL_TEST_LOG_H
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
Log Test framework.
|
||||
|
||||
Provides a simple way of veriyfing logs generated during unit-testing.
|
||||
*/
|
||||
|
||||
#include <gs/util/log/log.h>
|
||||
#include <gs/util/test/cmocka.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Assert log count - internal helper function.
|
||||
*/
|
||||
void gs_assert_log_count(int level, unsigned int count, const char * file, int line);
|
||||
|
||||
/**
|
||||
Assert log messag and count - internal helper function.
|
||||
*/
|
||||
void gs_assert_log(unsigned int stack_index, unsigned int count, gs_log_level_t level, const char * pattern, const char * file, int line);
|
||||
|
||||
/**
|
||||
Initialize framework, by installing a callback for \a print.
|
||||
@param[in] verbose of \a true, logs will be printed on stdout.
|
||||
*/
|
||||
void gs_test_log_init(bool verbose);
|
||||
|
||||
/**
|
||||
Clear log stats.
|
||||
*/
|
||||
void gs_test_log_clear(void);
|
||||
|
||||
/**
|
||||
Assert number of error logs.
|
||||
*/
|
||||
#define GS_ASSERT_LOG_ERROR(cnt) gs_assert_log_count(LOG_ERROR, cnt, __FILE__, __LINE__);
|
||||
|
||||
/**
|
||||
Assert number of warning logs.
|
||||
*/
|
||||
#define GS_ASSERT_LOG_WARNING(cnt) gs_assert_log_count(LOG_WARNING, cnt, __FILE__, __LINE__);
|
||||
|
||||
/**
|
||||
Assert number of notice logs.
|
||||
*/
|
||||
#define GS_ASSERT_LOG_NOTICE(cnt) gs_assert_log_count(LOG_NOTICE, cnt, __FILE__, __LINE__);
|
||||
|
||||
/**
|
||||
Assert number of info logs.
|
||||
*/
|
||||
#define GS_ASSERT_LOG_INFO(cnt) gs_assert_log_count(LOG_INFO, cnt, __FILE__, __LINE__);
|
||||
|
||||
/**
|
||||
Assert number of debug logs.
|
||||
*/
|
||||
#define GS_ASSERT_LOG_DEBUG(cnt) gs_assert_log_count(LOG_DEBUG, cnt, __FILE__, __LINE__);
|
||||
|
||||
/**
|
||||
Assert number of trace logs.
|
||||
*/
|
||||
#define GS_ASSERT_LOG_TRACE(cnt) gs_assert_log_count(LOG_TRACE, cnt, __FILE__, __LINE__);
|
||||
|
||||
/**
|
||||
Assert number of all logs.
|
||||
*/
|
||||
#define GS_ASSERT_LOG_ALL(cnt) gs_assert_log_count(-1, cnt, __FILE__, __LINE__);
|
||||
|
||||
/**
|
||||
Assert/find number of entries matching level and pattern.
|
||||
*/
|
||||
#define GS_ASSERT_LOG(count,level,pattern) gs_assert_log(-1, count, level, pattern, __FILE__, __LINE__)
|
||||
|
||||
/**
|
||||
Assert log at stack index against matching level and pattern.
|
||||
*/
|
||||
#define GS_ASSERT_LOG_AT(stack_index,level,pattern) gs_assert_log(stack_index, 1, level, pattern, __FILE__, __LINE__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
173
gomspace/libutil/include/gs/util/thread.h
Normal file
173
gomspace/libutil/include/gs/util/thread.h
Normal file
@ -0,0 +1,173 @@
|
||||
#ifndef GS_UTIL_THREAD_H
|
||||
#define GS_UTIL_THREAD_H
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
Thread/task API based on POSIX standard.
|
||||
|
||||
The thread API wraps POSIX \a pthread and FreeRTOS \a task.
|
||||
*/
|
||||
|
||||
#include <gs/util/error.h>
|
||||
#if __linux__
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if __linux__
|
||||
/**
|
||||
Thread handle.
|
||||
*/
|
||||
typedef pthread_t gs_thread_t;
|
||||
#else
|
||||
typedef struct gs_freertos_task_t * gs_thread_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
Type used to declare thread stack buffer for gs_thread_create_with_stack.
|
||||
*/
|
||||
typedef uint32_t gs_stack_type_t;
|
||||
|
||||
/**
|
||||
Thread priorities.
|
||||
These values are mapped to platform specific values.
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
Idle (lowest) priority.
|
||||
Typical use: Not much - runs when nothing else runs.
|
||||
FreeRTOS: Idle thread.
|
||||
*/
|
||||
GS_THREAD_PRIORITY_IDLE = 5,
|
||||
/**
|
||||
Low priority.
|
||||
Typical use: Service applications, e.g. servicing requests from the outside.
|
||||
GOMspace: housekeeping, GOSH.
|
||||
*/
|
||||
GS_THREAD_PRIORITY_LOW = 10,
|
||||
/**
|
||||
Normal priority.
|
||||
Typical use: Control - the primary application(s).
|
||||
*/
|
||||
GS_THREAD_PRIORITY_NORMAL = 15,
|
||||
/**
|
||||
High priority.
|
||||
Typical use: Drivers off loading data from hardware to software buffers.
|
||||
GOMspace: csp_route_task.
|
||||
*/
|
||||
GS_THREAD_PRIORITY_HIGH = 20,
|
||||
/**
|
||||
High priority.
|
||||
Typical use: Very time critical threads. No long, time consuming processing.
|
||||
FreeRTOS: Timer thread.
|
||||
*/
|
||||
GS_THREAD_PRIORITY_CRITICAL = 25,
|
||||
} gs_thread_priority_t;
|
||||
|
||||
/**
|
||||
Thread function.
|
||||
*/
|
||||
typedef void * (*gs_thread_func_t)(void * parameter);
|
||||
|
||||
/**
|
||||
Create thread as joinable.
|
||||
@note only supported on linux. The thread must be joined to free all resources.
|
||||
*/
|
||||
#define GS_THREAD_CREATE_JOINABLE 0x0001
|
||||
|
||||
/**
|
||||
Create thread (or task on some platforms).
|
||||
|
||||
pthread/Posix supports exit value and join, FreeRTOS supports neither (perhaps in the future), so API is designed after Posix.
|
||||
|
||||
FreeRTOS: a thread must always terminate with a call to gs_thread_exit().
|
||||
linux: a thread is by default created detached, unless #GS_THREAD_CREATE_JOINABLE is specified.
|
||||
|
||||
@param[in] name name of thread. Ignored on Linux.
|
||||
@param[in] func function for thread to execute.
|
||||
@param[in] parameter parameter parsed to the thread function.
|
||||
@param[in] stack_size number of bytes to allocate for stack - not used/supported on all platforms. Ignored on Linux.
|
||||
@param[in] priority thread priority. Ignored on Linux.
|
||||
@param[in] flags flags to control creation, see #GS_THREAD_CREATE_JOINABLE.
|
||||
@param[out] handle handle to the created thread, use NULL if not wanted.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_thread_create(const char * const name,
|
||||
gs_thread_func_t func,
|
||||
void * parameter,
|
||||
size_t stack_size,
|
||||
gs_thread_priority_t priority,
|
||||
uint32_t flags,
|
||||
gs_thread_t * handle);
|
||||
|
||||
/**
|
||||
Create thread (or task on some platforms) with user supplied buffer for stack.
|
||||
|
||||
pthread/Posix supports exit value and join, FreeRTOS supports neither (perhaps in the future), so API is designed after Posix.
|
||||
|
||||
FreeRTOS: a thread must always terminate with a call to gs_thread_exit().
|
||||
FreeRTOS v9.0 must be compiled with configSUPPORT_STATIC_ALLOCTION set to 1 - otherwise warning log is printed and user supplied
|
||||
stack buffer is discarded
|
||||
linux: a thread is by default created detached, unless #GS_THREAD_CREATE_JOINABLE is specified.
|
||||
stack_buf is ignored.
|
||||
|
||||
@param[in] name name of thread. Ignored on Linux.
|
||||
@param[in] func function for thread to execute.
|
||||
@param[in] parameter parameter parsed to the thread function.
|
||||
@param[in] stack_size size of the user supplied stack buffer - not used/supported on all platforms. Ignored on Linux.
|
||||
@param[in] stack_buf User supplied stack buffer - not used/supported on all platforms. Ignored on Linux.
|
||||
@param[in] priority thread priority. Ignored on Linux.
|
||||
@param[in] flags flags to control creation, see #GS_THREAD_CREATE_JOINABLE.
|
||||
@param[out] handle handle to the created thread, use NULL if not wanted.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_thread_create_with_stack(const char * const name,
|
||||
gs_thread_func_t func,
|
||||
void * parameter,
|
||||
size_t stack_size,
|
||||
gs_stack_type_t *stack_buf,
|
||||
gs_thread_priority_t priority,
|
||||
uint32_t flags,
|
||||
gs_thread_t * handle);
|
||||
|
||||
/**
|
||||
Exit current thread.
|
||||
@param[in] exit_value exit value.
|
||||
*/
|
||||
void gs_thread_exit(void * exit_value) __attribute__ ((noreturn));
|
||||
|
||||
/**
|
||||
Sleep for X milli-seconds.
|
||||
@note FreeRTOS: minimum sleep time depends on ticks per milli-second. A thread is suspended minimum 1 tick - unless \a time_ms is 0, in which case yield is called.
|
||||
@deprecated use gs_time_sleep_ms()
|
||||
@param[in] time_ms milli-seconds to sleep.
|
||||
*/
|
||||
void gs_thread_sleep_ms(uint32_t time_ms);
|
||||
|
||||
/**
|
||||
Join with a terminated thread.
|
||||
|
||||
@note Only supported on Linux and primarily used for testing.
|
||||
@note This is not based on pthread_cancel(), so the user must have signaled the thread to stop - otherwise this will hang forever.
|
||||
|
||||
@param[in] thread handle.
|
||||
@param[out] return_retval return value from thread, use NULL if not wanted.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_thread_join(gs_thread_t thread, void ** return_retval);
|
||||
|
||||
/**
|
||||
Block thread forever.
|
||||
|
||||
Primarily used in Linux applications main() to block main thread.
|
||||
*/
|
||||
void gs_thread_block(void) __attribute__ ((noreturn));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
95
gomspace/libutil/include/gs/util/time.h
Normal file
95
gomspace/libutil/include/gs/util/time.h
Normal file
@ -0,0 +1,95 @@
|
||||
#ifndef GS_UTIL_TIME_H
|
||||
#define GS_UTIL_TIME_H
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
Releative time.
|
||||
*/
|
||||
|
||||
#include <gs/util/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Converts minutes to seconds.
|
||||
*/
|
||||
#define GS_TIME_MINS_TO_SECS(m) (m * 60)
|
||||
|
||||
/**
|
||||
Converts hours to seconds.
|
||||
*/
|
||||
#define GS_TIME_HOURS_TO_SECS(h) (h * GS_TIME_MINS_TO_SECS(60))
|
||||
|
||||
/**
|
||||
Converts days to seconds.
|
||||
*/
|
||||
#define GS_TIME_DAYS_TO_SECS(d) (d * GS_TIME_HOURS_TO_SECS(24))
|
||||
|
||||
/**
|
||||
Return relative time (milli seconds).
|
||||
@note This will eventually wrap on all platforms - platform must wrap on 32 bit.
|
||||
@return relativ milli seconds
|
||||
*/
|
||||
uint32_t gs_time_rel_ms(void);
|
||||
|
||||
/**
|
||||
Return relative time (milli seconds).
|
||||
@note This will eventually wrap on all platforms - platform must wrap on 32 bit.
|
||||
@return relativ milli seconds
|
||||
*/
|
||||
uint32_t gs_time_rel_ms_isr(void);
|
||||
|
||||
/**
|
||||
Returns seconds since process started.
|
||||
@note On some platforms (e.g. Linux), first call will set offset and
|
||||
first call it therefor not thread-safe.
|
||||
@return seconds since boot (or process startup).
|
||||
*/
|
||||
uint32_t gs_time_uptime(void);
|
||||
|
||||
/**
|
||||
Return time difference, compensating for time wrap due to 32 bit.
|
||||
@note the function can not detect multiple time wraps, so function using it should
|
||||
take action within 32 bit time.
|
||||
@param[in] ref_ms reference time.
|
||||
@param[in] now_ms current time.
|
||||
@returns ms difference, compensating for time wrapping (if now_ms is less than ref_ms).
|
||||
*/
|
||||
uint32_t gs_time_diff_ms(uint32_t ref_ms, uint32_t now_ms);
|
||||
|
||||
/**
|
||||
Sleep for X milli-seconds.
|
||||
No busy waiting.
|
||||
@note FreeRTOS: minimum sleep time depends on ticks per second. Suspends execution for minimum 1 tick - unless \a time is 0, in which case yield is called.
|
||||
@param[in] time_ms milli-seconds to sleep.
|
||||
*/
|
||||
void gs_time_sleep_ms(uint32_t time_ms);
|
||||
|
||||
/**
|
||||
Sleep X milli-seconds relative to reference.
|
||||
|
||||
This sleep function uses a reference \a ref_ms to compensate for variance in processing time.
|
||||
|
||||
No busy waiting.
|
||||
|
||||
@param[in,out] ref_ms time reference.
|
||||
@param[in] sleep_ms how many milli-seconds to sleep - relative to reference.
|
||||
@return \a true if sleep time relative to last reference couldn't be done (reference reset), \a false if normal sleep was done.
|
||||
*/
|
||||
bool gs_time_sleep_until_ms(uint32_t * ref_ms, uint32_t sleep_ms);
|
||||
|
||||
/**
|
||||
Sleep for X nano-seconds.
|
||||
No busy waiting.
|
||||
@note FreeRTOS: minimum sleep time depends on ticks per second. Suspends execution for minimum 1 tick - unless \a time is 0, in which case yield is called.
|
||||
@param[in] time_ns nano-seconds to sleep.
|
||||
*/
|
||||
void gs_time_sleep_ns(uint64_t time_ns);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
73
gomspace/libutil/include/gs/util/timestamp.h
Normal file
73
gomspace/libutil/include/gs/util/timestamp.h
Normal file
@ -0,0 +1,73 @@
|
||||
#ifndef GS_UTIL_TIMESTAMP_H
|
||||
#define GS_UTIL_TIMESTAMP_H
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
Timestamp utilities, for add, subtract, compare, copy, etc.
|
||||
*/
|
||||
|
||||
#include <gs/util/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Number of nano seconds per second.
|
||||
*/
|
||||
#define GS_TIMESTAMP_NSEC_PER_SEC 1000000000
|
||||
|
||||
/**
|
||||
Portable time structure.
|
||||
|
||||
Stanadard timespec_t is non-portable, so this structure must be used instead
|
||||
*/
|
||||
typedef struct {
|
||||
/** Seconds. */
|
||||
uint32_t tv_sec;
|
||||
/** Nano seconds. */
|
||||
uint32_t tv_nsec;
|
||||
} gs_timestamp_t;
|
||||
|
||||
/**
|
||||
@deprecated Use gs_timestamp_t
|
||||
*/
|
||||
typedef gs_timestamp_t timestamp_t;
|
||||
|
||||
/**
|
||||
Add 2 timestamp's (t1 = t1 + t2).
|
||||
@param[in,out] t1 timestamp
|
||||
@param[in] t2 timestamp.
|
||||
@return 0 on success, otherwise -1
|
||||
*/
|
||||
int timestamp_add(gs_timestamp_t * t1, const gs_timestamp_t * t2);
|
||||
|
||||
/**
|
||||
Subtract 2 timestamp's (t1 = t1 - t2)
|
||||
@param[in,out] t1 timestamp
|
||||
@param[in] t2 timestamp.
|
||||
@return 0 on success, otherwise -1
|
||||
*/
|
||||
int timestamp_diff(gs_timestamp_t * t1, const gs_timestamp_t * t2);
|
||||
|
||||
/**
|
||||
Check if t2 is greate than t1.
|
||||
@param[in] t1 time to compare
|
||||
@param[in] t2 time to compare
|
||||
@return 1 if t2 > t1, else 0. -1 on bad arguments.
|
||||
*/
|
||||
int timestamp_ge(const gs_timestamp_t * t1, const gs_timestamp_t * t2);
|
||||
|
||||
/**
|
||||
Copy timestamp.
|
||||
@param[in] from from timestamp
|
||||
@param[out] to to timestamp
|
||||
@return 0 on success, otherwise -1
|
||||
*/
|
||||
int timestamp_copy(const gs_timestamp_t * from, gs_timestamp_t * to);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
114
gomspace/libutil/include/gs/util/types.h
Normal file
114
gomspace/libutil/include/gs/util/types.h
Normal file
@ -0,0 +1,114 @@
|
||||
#ifndef GS_UTIL_TYPES_H
|
||||
#define GS_UTIL_TYPES_H
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
Base type definitions and functions.
|
||||
|
||||
In some rare cases, it is impossible to make code that works on all platforms. In these cases the following defines may be used to
|
||||
exclude/include code:
|
||||
| define | Platform |
|
||||
| :----: | :---- |
|
||||
| \_\_AVR\_\_ | 8 bit, e.g. atmega1281, atmega2560, attiny25, attiny44, attiny84 |
|
||||
| \_\_linux\_\_ | 32/64 bit, Linux based |
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include <stdint.h> // intXX_t
|
||||
#include <stdbool.h> // bool
|
||||
#include <stddef.h> // size_t
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Control static declaration at compile time.
|
||||
Allows unit tests to access internal functions or variables.
|
||||
@note Static declared variables are initialized to zero by the compiler - BUT if you use GS_NO_STATIC instead of static, they will not be initialized.
|
||||
*/
|
||||
#if GS_NO_STATIC
|
||||
#define GS_STATIC
|
||||
#else
|
||||
#define GS_STATIC static
|
||||
#endif
|
||||
|
||||
/**
|
||||
Convert integer to pointer.
|
||||
*/
|
||||
#define GS_TYPES_INT2PTR(value) ((void*)(intptr_t)(value))
|
||||
|
||||
/**
|
||||
Convert integer to pointer.
|
||||
*/
|
||||
#define GS_TYPES_UINT2PTR(value) ((void*)(uintptr_t)(value))
|
||||
|
||||
/**
|
||||
Convert pointer to integer.
|
||||
*/
|
||||
#define GS_TYPES_PTR2INT(value) ((intptr_t)(void*)(value))
|
||||
|
||||
/**
|
||||
Convert pointer to integer.
|
||||
*/
|
||||
#define GS_TYPES_PTR2UINT(value) ((uintptr_t)(void*)(value))
|
||||
|
||||
/**
|
||||
Assert on 'value'.
|
||||
|
||||
Example:
|
||||
GS_STATIC_ASSERT(sizeof(int) >= 2, int_must_be_at_least_16bit);
|
||||
fails if size of (int) is less than 2 bytes.
|
||||
*/
|
||||
#define GS_STATIC_ASSERT(condition, name) typedef char name[(condition) ? 1 : -1]
|
||||
|
||||
/**
|
||||
Context switch state.
|
||||
Used by FreeRTOS when waking a higher priority task/thread from within an ISR.
|
||||
The actual struct is defined in libembed.
|
||||
*/
|
||||
typedef struct gs_context_switch gs_context_switch_t;
|
||||
|
||||
/**
|
||||
Return element count of array.
|
||||
*/
|
||||
#define GS_ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
|
||||
|
||||
/**
|
||||
Address union.
|
||||
*/
|
||||
typedef union {
|
||||
/**
|
||||
Normal address pointer.
|
||||
*/
|
||||
void* p;
|
||||
/**
|
||||
Address pointer as an unsigned value.
|
||||
*/
|
||||
uintptr_t u;
|
||||
} gs_address_t;
|
||||
|
||||
/**
|
||||
@cond HIDDEN_SYMBOLS
|
||||
Compile check size of primitives (just to be sure, that they are what we expect).
|
||||
*/
|
||||
GS_STATIC_ASSERT(sizeof(gs_address_t) == sizeof(void*), unexpected_address_void_pointer_size);
|
||||
GS_STATIC_ASSERT(sizeof(gs_address_t) == sizeof(uintptr_t), unexpected_address_uintptr_size);
|
||||
GS_STATIC_ASSERT(sizeof(bool) == sizeof(uint8_t), unexpected_bool_size);
|
||||
GS_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t), unexpected_float_size);
|
||||
#if (__AVR__)
|
||||
// avr/avr8 is 8 bit
|
||||
GS_STATIC_ASSERT(sizeof(int) == sizeof(int16_t), unexpected_int_size_on_avr8);
|
||||
#else
|
||||
// rest should be 32 or 64 bit
|
||||
GS_STATIC_ASSERT(sizeof(int) == sizeof(int32_t), unexpected_int_size);
|
||||
GS_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t), unexpected_double_size);
|
||||
#endif
|
||||
/** @endcond */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
32
gomspace/libutil/include/gs/util/unistd.h
Normal file
32
gomspace/libutil/include/gs/util/unistd.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef GS_UTIL_UNISTD_H
|
||||
#define GS_UTIL_UNISTD_H
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
GomSpace extensions to standard \a unistd.h.
|
||||
*/
|
||||
|
||||
#include <gs/util/error.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Get current working directory.
|
||||
|
||||
@note Linux uses standard getcwd().
|
||||
|
||||
@param[out] buf user supplied buffer for returning path.
|
||||
@param[in] bufsize size of \a buf.
|
||||
@return #GS_ERROR_NOT_FOUND if no current directory is set.
|
||||
@return #GS_ERROR_RANGE if \a buf is too small
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_getcwd(char * buf, size_t bufsize);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
194
gomspace/libutil/include/gs/util/vmem.h
Normal file
194
gomspace/libutil/include/gs/util/vmem.h
Normal file
@ -0,0 +1,194 @@
|
||||
#ifndef GS_UTIL_VMEM_H
|
||||
#define GS_UTIL_VMEM_H
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
Virtual memory interface.
|
||||
|
||||
The API provides support for accessing different hardware components using a common API, by providing a component specific driver.
|
||||
*/
|
||||
|
||||
#include <gs/util/stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Virtual memory mapping.
|
||||
*/
|
||||
typedef struct gs_vmem gs_vmem_t;
|
||||
|
||||
/**
|
||||
VMEM driver write.
|
||||
|
||||
@param[in] vmem vmem entry.
|
||||
@param[in] to Address where to write data to.
|
||||
@param[in] from Address where to write data from.
|
||||
@param[in] size Number of bytes to write.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
typedef gs_error_t (*gs_vmem_write_function_t)(const gs_vmem_t * vmem, void* to, const void * from, size_t size);
|
||||
|
||||
/**
|
||||
VMEM driver read.
|
||||
|
||||
@param[in] vmem vmem entry.
|
||||
@param[in] to Address where to read data to.
|
||||
@param[in] from Address where to read data from.
|
||||
@param[in] size Number of bytes to read.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
typedef gs_error_t (*gs_vmem_read_function_t)(const gs_vmem_t * vmem, void* to, const void * from, size_t size);
|
||||
|
||||
/**
|
||||
VMEM driver lock.
|
||||
|
||||
@param[in] vmem vmem entry.
|
||||
@param[in] on Enable/Disable lock.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
typedef gs_error_t (*gs_vmem_lock_function_t)(const gs_vmem_t * vmem, bool on);
|
||||
|
||||
/**
|
||||
VMEM driver information.
|
||||
|
||||
Return relevant information for the VMEM driver.
|
||||
|
||||
@param[in] vmem vmem entry.
|
||||
@param[in] buffer user allocated buffer for returning information.
|
||||
@param[in] buffer_size size (length) of \a buffer.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
typedef gs_error_t (*gs_vmem_info_function_t)(const gs_vmem_t * vmem, char * buffer, size_t buffer_size);
|
||||
|
||||
/**
|
||||
VMEM driver interface.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
Write function.
|
||||
*/
|
||||
const gs_vmem_write_function_t write;
|
||||
/**
|
||||
Read function.
|
||||
*/
|
||||
const gs_vmem_read_function_t read;
|
||||
/**
|
||||
Lock function.
|
||||
*/
|
||||
const gs_vmem_lock_function_t lock;
|
||||
/**
|
||||
Information function.
|
||||
*/
|
||||
const gs_vmem_info_function_t info;
|
||||
} gs_vmem_driver_t;
|
||||
|
||||
/**
|
||||
Virtual memory mapping.
|
||||
|
||||
@note Call gs_vmem_set_map() for registering mappings.
|
||||
*/
|
||||
struct gs_vmem {
|
||||
/**
|
||||
Logical name of enry.
|
||||
*/
|
||||
const char *const name;
|
||||
/**
|
||||
Virtual memory start.
|
||||
*/
|
||||
gs_address_t virtmem;
|
||||
/**
|
||||
Physical memory start.
|
||||
This address only makes sense for the VMEM driver.
|
||||
*/
|
||||
gs_address_t physmem;
|
||||
/**
|
||||
Size of memory block.
|
||||
*/
|
||||
const size_t size;
|
||||
/**
|
||||
Driver function.
|
||||
*/
|
||||
const gs_vmem_driver_t* drv;
|
||||
/**
|
||||
Driver data.
|
||||
*/
|
||||
const void* drv_data;
|
||||
};
|
||||
|
||||
/**
|
||||
Set VMEM mapping.
|
||||
Must be done for the API to work.
|
||||
@param[in] map VMEM mapping table, must be terminated with an NULL entry.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_vmem_set_map(const gs_vmem_t * map);
|
||||
|
||||
/**
|
||||
Return VMEM map.
|
||||
*/
|
||||
const gs_vmem_t * gs_vmem_get_map(void);
|
||||
|
||||
/**
|
||||
Print all VMEM entries to stdout.
|
||||
@param[in] out output stream
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_vmem_list(FILE * out);
|
||||
|
||||
/**
|
||||
Get VMEM entry by name.
|
||||
@param[in] name name of VMEM entry.
|
||||
@return VMEM mapping or NULL if not found.
|
||||
*/
|
||||
const gs_vmem_t * gs_vmem_get_by_name(const char * name);
|
||||
|
||||
/**
|
||||
Lock/un-lock VMEM area.
|
||||
@param[in] name name of VMEM entry.
|
||||
@param[in] on Enable/Disable lock.
|
||||
@return GS_ERROR_NOT_FOUND area not found.
|
||||
@return GS_ERROR_NOT_SUPPORTED if locking isn't supported.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_vmem_lock_by_name(const char * name, bool on);
|
||||
|
||||
/**
|
||||
Lock/un-lock all VMEM areas.
|
||||
@param[in] on lock on or off.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_vmem_lock_all(bool on);
|
||||
|
||||
/**
|
||||
memcpy on VMEM.
|
||||
@note if no VMEM entries are found, a normal memcpy is called with the provided pointers.
|
||||
@param[in] to to location.
|
||||
@param[in] from from location.
|
||||
@param[in] size number of bytes to copy.
|
||||
*/
|
||||
void* gs_vmem_cpy(void* to, const void* from, size_t size);
|
||||
|
||||
/**
|
||||
Macro for calling gs_vmem_cpy().
|
||||
*/
|
||||
#define GS_VMEM_CPY(to, from, size) gs_vmem_cpy(to, from, size)
|
||||
|
||||
/**
|
||||
Macro for calling gs_vmem_cpy().
|
||||
@deprecated Use gs_vmem_cpy() directly.
|
||||
*/
|
||||
#define VMEM_CPY(to, from, size) gs_vmem_cpy(to, from, size)
|
||||
|
||||
/**
|
||||
Register VMEM commands.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_vmem_register_commands(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
143
gomspace/libutil/include/gs/util/watchdog/watchdog.h
Normal file
143
gomspace/libutil/include/gs/util/watchdog/watchdog.h
Normal file
@ -0,0 +1,143 @@
|
||||
#ifndef GS_UTIL_WATCHDOG_WATCHDOG_H
|
||||
#define GS_UTIL_WATCHDOG_WATCHDOG_H
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
Software watchdog client interface.
|
||||
|
||||
The software watchdog (SWWD) enables having multiple instances of a Watchdog.
|
||||
The software watchdog manages the HW watchdog, and will ultimately
|
||||
trigger the HW watchdog, if one or more clients are not servicing the
|
||||
software watchdog.
|
||||
*/
|
||||
|
||||
#include <gs/util/drivers/watchdog/device.h>
|
||||
#include <gs/util/log.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Software Watchdog handle
|
||||
*/
|
||||
typedef struct gs_swwd_hdl gs_swwd_hdl_t;
|
||||
|
||||
/**
|
||||
Software watchdog callback function.
|
||||
|
||||
Called by the SWWD upon timeout.
|
||||
@param[in] userdata user data provided on gs_swwd_register()
|
||||
*/
|
||||
typedef void (*gs_swwd_callback_function_t)(void * userdata);
|
||||
|
||||
/**
|
||||
Watchdog timeout action.
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
Reset system on timeout (stops touching the hardware watchdog).
|
||||
Once the watchdog has timeout, the watchdog cannot be re-activated.
|
||||
*/
|
||||
GS_SWWD_TIMEOUT_ACTION_RESET = 0,
|
||||
/**
|
||||
Log 'warning' on timeout, but otherwise ignore the timeout.
|
||||
The watchdog can re-activated by touching the watchdog again.
|
||||
*/
|
||||
GS_SWWD_TIMEOUT_ACTION_LOG = 1,
|
||||
} gs_swwd_timeout_action_t;
|
||||
|
||||
/**
|
||||
Create the software watchdog back-end.
|
||||
|
||||
Only one SWWD back-end can exist at any given time.
|
||||
|
||||
@param[in] max_clients The maximum number of Software Watchog clients supported.
|
||||
@param[in] dev The HW Watchdog device to use.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_swwd_create(uint32_t max_clients, gs_watchdog_device_t *dev);
|
||||
|
||||
/**
|
||||
Destroy the Software Watchdog back-end (and stop the SWWD monitor task if started).
|
||||
|
||||
@param[in] timeout_s Maximum number of seconds to allow this operation to complete.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_swwd_destroy(uint32_t timeout_s);
|
||||
|
||||
/**
|
||||
Check for expired software watchdog clients. This function is only to be used if the
|
||||
SWWD monitor task is not started. Otherwise the SWWD task will handle this in the back-
|
||||
ground. I.e:
|
||||
- In passive mode this function must be called periodically to check for expired
|
||||
clients, and service the HW watchdog.
|
||||
- In active mode this function is called in background by the SWWD monitor task.
|
||||
|
||||
The interval between these checks should be much less that the HW watchdog
|
||||
timeout period, to ensure that the HW Watchdog is correctly serviced.
|
||||
Calling this e.g. every 1-3 seconds will be a good default.
|
||||
|
||||
@param[out] num_expired The number of SW Watchog clients currently expired.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_swwd_check_expired_clients(uint32_t *num_expired);
|
||||
|
||||
/**
|
||||
Register/create a new software watchdog instance
|
||||
|
||||
@param[out] wdt_handle A reference to software watchdog handle
|
||||
@param[in] timeout Timeout in seconds.
|
||||
@param[in] callback Callback function which is called on timeout. NULL if unused.
|
||||
@param[in] userdata Pointer to user data used in the callback function. Ignored if callback is NULL.
|
||||
@param[in] client_name A descriptive name given by the user in order to identify the watchdog/client - the pointer must remain valid as long as the watchdog is registered.
|
||||
@param[in] action what action to take, when/if the watchdog times out.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_swwd_register_with_action(gs_swwd_hdl_t ** wdt_handle, uint32_t timeout, gs_swwd_callback_function_t callback, void * userdata, const char *client_name, gs_swwd_timeout_action_t action);
|
||||
|
||||
/**
|
||||
Register/create a software watchdog with action \a reset on timeout.
|
||||
|
||||
@param[out] wdt_handle A reference to software watchdog handle
|
||||
@param[in] timeout Timeout in seconds before the software watchdog fires.
|
||||
@param[in] callback Callback function which is called on timeout. NULL if unused.
|
||||
@param[in] userdata Pointer to user data used in the callback function. Ignored if callback is NULL.
|
||||
@param[in] client_name A descriptive name given by the user in order to identify the watchdog/client - the pointer must remain valid as long as the watchdog is registered.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
static inline gs_error_t gs_swwd_register(gs_swwd_hdl_t ** wdt_handle, uint32_t timeout, gs_swwd_callback_function_t callback, void * userdata, const char *client_name)
|
||||
{
|
||||
return gs_swwd_register_with_action(wdt_handle, timeout, callback, userdata, client_name, GS_SWWD_TIMEOUT_ACTION_RESET);
|
||||
}
|
||||
|
||||
/**
|
||||
De-Register a Software Watchdog instance
|
||||
|
||||
@param[in] wdt_handle A software watchdog handle
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_swwd_deregister(gs_swwd_hdl_t ** wdt_handle);
|
||||
|
||||
/**
|
||||
Touch Software Watchdog to reset the timer
|
||||
|
||||
@param[in] wdt_handle A software watchdog handle
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_swwd_touch(gs_swwd_hdl_t * wdt_handle);
|
||||
|
||||
/**
|
||||
Set timeout of the Software Watchdog.
|
||||
|
||||
@param[in] wdt_handle A software watchdog handle
|
||||
@param[in] timeout Timeout in seconds before the SWWD fires.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_swwd_set_timeout(gs_swwd_hdl_t * wdt_handle, uint32_t timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
45
gomspace/libutil/include/gs/util/watchdog/watchdog_task.h
Normal file
45
gomspace/libutil/include/gs/util/watchdog/watchdog_task.h
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef GS_UTIL_WATCHDOG_WATCHDOG_TASK_H
|
||||
#define GS_UTIL_WATCHDOG_WATCHDOG_TASK_H
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
Software Watchdog server/task interface
|
||||
|
||||
The Software Watchdog task implements the core (backend) functionality of the the software watchdog.
|
||||
The Client API for the SW watchdog is implemented in watchdog.h
|
||||
|
||||
@note This API is not thread safe!
|
||||
*/
|
||||
|
||||
#include <gs/util/watchdog/watchdog.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Start the Software Watchdog monitor task if the SWWD is to be used as a
|
||||
separate task (active mode).
|
||||
In this case the SWWD task will monitor expired clients in the background
|
||||
and the polling API gs_swwd_check_expired_clients() needs not to be called by
|
||||
the user.
|
||||
|
||||
@return_gs_error_t
|
||||
*/
|
||||
|
||||
gs_error_t gs_swwd_monitor_task_start();
|
||||
|
||||
/**
|
||||
Stops the Software Watchdog monitor task
|
||||
|
||||
@param[in] timeout_s Maximum number of seconds to allow this operation to complete.
|
||||
|
||||
@return_gs_error_t
|
||||
*/
|
||||
gs_error_t gs_swwd_monitor_task_stop(uint32_t timeout_s);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
62
gomspace/libutil/include/gs/util/zip/zip.h
Normal file
62
gomspace/libutil/include/gs/util/zip/zip.h
Normal file
@ -0,0 +1,62 @@
|
||||
#ifndef LIBUTIL_ZIP_ZIP_UTILS_H
|
||||
#define LIBUTIL_ZIP_ZIP_UTILS_H
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
Compress/decompress API based on zlib compressed data format specification standards.
|
||||
*/
|
||||
|
||||
#include <gs/util/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
Compress file.
|
||||
|
||||
@param[in] src file to be compressed.
|
||||
@param[out] dest compressed output file.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
int gs_zip_compress_file(const char *src, const char *dest);
|
||||
|
||||
/**
|
||||
Decompress file.
|
||||
|
||||
@param[in] src file to be secompressed.
|
||||
@param[out] dest decompressed output file.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
int gs_zip_decompress_file(const char *src, const char *dest);
|
||||
|
||||
/**
|
||||
Compress data.
|
||||
|
||||
@param[in] src pointer to the data to be compressed.
|
||||
@param[in] src_len size of the data.
|
||||
@param[out] dest pointer to the compressed data.
|
||||
@param[out] dest_len pointer to the size of the compressed data.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
int gs_zip_compress_data(const unsigned char *src, uint32_t src_len, unsigned char *dest, uint32_t *dest_len);
|
||||
|
||||
/**
|
||||
Decompress data.
|
||||
|
||||
@param[in] src pointer to the data to be decompressed.
|
||||
@param[in] src_len size of the data.
|
||||
@param[out] dest pointer to the decompressed data.
|
||||
@param[out] dest_len size of the destination memory area.
|
||||
@param[out] decomp_len pointer to the size of the decompressed data.
|
||||
@return_gs_error_t
|
||||
*/
|
||||
int gs_zip_decompress_data(const unsigned char *src, uint32_t src_len, unsigned char *dest, uint32_t dest_len, uint32_t *decomp_len);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* LIBUTIL_ZIP_ZIP_UTILS_H */
|
61
gomspace/libutil/src/base16.c
Normal file
61
gomspace/libutil/src/base16.c
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <gs/util/base16.h>
|
||||
#include <gs/util/log.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void base16_encode(const uint8_t * raw, size_t len, char *encoded)
|
||||
{
|
||||
const uint8_t *raw_bytes = raw;
|
||||
char *encoded_bytes = encoded;
|
||||
size_t remaining = len;
|
||||
|
||||
for (; remaining--; encoded_bytes += 2)
|
||||
snprintf(encoded_bytes, 3, "%02X", *(raw_bytes++));
|
||||
|
||||
}
|
||||
|
||||
int base16_decode(const char *encoded, uint8_t *raw)
|
||||
{
|
||||
uint8_t *raw_bytes = raw;
|
||||
if (encoded) {
|
||||
const char *encoded_bytes = encoded;
|
||||
char buf[3];
|
||||
char *endp;
|
||||
|
||||
while (encoded_bytes[0]) {
|
||||
if (!encoded_bytes[1]) {
|
||||
log_error("Base16-encoded string \"%s\" has invalid length\n",
|
||||
encoded);
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
memcpy(buf, encoded_bytes, 2);
|
||||
buf[2] = '\0';
|
||||
*(raw_bytes++) = (uint8_t) strtoul(buf, &endp, 16);
|
||||
if (*endp != '\0') {
|
||||
log_error("Base16-encoded string \"%s\" has invalid byte \"%s\"\n",
|
||||
encoded, buf);
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
encoded_bytes += 2;
|
||||
}
|
||||
}
|
||||
return (int)(raw_bytes - raw);
|
||||
}
|
73
gomspace/libutil/src/bindings/python/pyutil.c
Normal file
73
gomspace/libutil/src/bindings/python/pyutil.c
Normal file
@ -0,0 +1,73 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <Python.h>
|
||||
#include <gs/util/clock.h>
|
||||
#include <gs/util/error.h>
|
||||
|
||||
#if PY_MAJOR_VERSION == 3
|
||||
#define IS_PY3
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
static PyObject* pyutil_get_clock_time(PyObject *self, PyObject *args) {
|
||||
gs_timestamp_t ts;
|
||||
gs_clock_get_time(&ts);
|
||||
return Py_BuildValue("II", ts.tv_sec, ts.tv_nsec);
|
||||
}
|
||||
|
||||
|
||||
static PyObject* pyutil_error_string(PyObject *self, PyObject *args)
|
||||
{
|
||||
int error;
|
||||
if (!PyArg_ParseTuple(args, "i", &error))
|
||||
{
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return Py_BuildValue("s", gs_error_string(error));
|
||||
}
|
||||
|
||||
static PyMethodDef methods[] = {
|
||||
|
||||
/* helpers */
|
||||
{"get_clock_time", pyutil_get_clock_time, METH_NOARGS, ""},
|
||||
{"error_string", pyutil_error_string, METH_VARARGS, ""},
|
||||
|
||||
/* sentinel */
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
#ifdef IS_PY3
|
||||
static struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"libgsutil_py3",
|
||||
NULL, /* module documentation, may be NULL */
|
||||
-1, /* size of per-interpreter state of the module,
|
||||
or -1 if the module keeps state in global variables. */
|
||||
methods,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef IS_PY3
|
||||
PyMODINIT_FUNC PyInit_libgsutil_py3(void) {
|
||||
#else
|
||||
PyMODINIT_FUNC initlibgsutil_py2(void) {
|
||||
#endif
|
||||
|
||||
#ifdef IS_PY3
|
||||
PyObject* m = PyModule_Create(&moduledef);
|
||||
#else
|
||||
Py_InitModule("libgsutil_py2", methods);
|
||||
#endif
|
||||
|
||||
#ifdef IS_PY3
|
||||
return m;
|
||||
#endif
|
||||
}
|
||||
|
128
gomspace/libutil/src/bytebuffer.c
Normal file
128
gomspace/libutil/src/bytebuffer.c
Normal file
@ -0,0 +1,128 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/bytebuffer.h>
|
||||
#include <gs/util/check.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define GS_BYTEBUFFER_F_FAILED 0x01
|
||||
#define GS_BYTEBUFFER_F_OVERRUN 0x02
|
||||
|
||||
gs_error_t gs_bytebuffer_init(gs_bytebuffer_t * bb, void * buffer, size_t buffer_size)
|
||||
{
|
||||
GS_CHECK_HANDLE(bb != NULL);
|
||||
memset(bb, 0, sizeof(*bb));
|
||||
if (buffer) {
|
||||
if (buffer_size < 2) {
|
||||
// must always have room for NUL termination.
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
bb->buffer = buffer;
|
||||
bb->size = buffer_size;
|
||||
} else {
|
||||
// dry run - don't insert anything in buffer, but increment used
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
void gs_bytebuffer_vprintf(gs_bytebuffer_t * bb, const char * format, va_list ap)
|
||||
{
|
||||
int res;
|
||||
if (bb->buffer == NULL) {
|
||||
// dry run
|
||||
char buf[3];
|
||||
res = vsnprintf(buf, 0, format, ap);
|
||||
if (res >= 0) {
|
||||
bb->used += res;
|
||||
}
|
||||
} else {
|
||||
const size_t free_bytes = gs_bytebuffer_get_free(bb);
|
||||
res = vsnprintf((char*)&bb->buffer[bb->used], free_bytes, format, ap);
|
||||
if (res > 0) {
|
||||
if ((size_t)res >= free_bytes) {
|
||||
// over run
|
||||
bb->flags |= GS_BYTEBUFFER_F_OVERRUN;
|
||||
bb->used = bb->size;
|
||||
bb->buffer[bb->size - 1] = 0;
|
||||
} else {
|
||||
bb->used += res;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (res < 0) {
|
||||
bb->flags |= GS_BYTEBUFFER_F_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
void gs_bytebuffer_printf(gs_bytebuffer_t * bb, const char * format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
gs_bytebuffer_vprintf(bb, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void gs_bytebuffer_append(gs_bytebuffer_t * bb, const void * data, size_t length)
|
||||
{
|
||||
if (bb->buffer == NULL) {
|
||||
// dry run
|
||||
bb->used += length;
|
||||
} else {
|
||||
const size_t free_bytes = gs_bytebuffer_get_free(bb);
|
||||
if (free_bytes >= length) {
|
||||
memcpy(&bb->buffer[bb->used], data, length);
|
||||
bb->used += length;
|
||||
} else {
|
||||
memcpy(&bb->buffer[bb->used], data, free_bytes);
|
||||
bb->flags |= GS_BYTEBUFFER_F_OVERRUN;
|
||||
bb->used += free_bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gs_bytebuffer_append_string(gs_bytebuffer_t * bb, const char * string)
|
||||
{
|
||||
if (gs_string_empty(string) == false) {
|
||||
gs_bytebuffer_append(bb, string, strlen(string));
|
||||
}
|
||||
}
|
||||
|
||||
void gs_bytebuffer_append_string_max(gs_bytebuffer_t * bb, const char * string, size_t max_length)
|
||||
{
|
||||
if (gs_string_empty(string) == false) {
|
||||
gs_bytebuffer_append(bb, string, strnlen(string, max_length));
|
||||
}
|
||||
}
|
||||
|
||||
char * gs_bytebuffer_get_as_string(gs_bytebuffer_t * bb, gs_error_t * error)
|
||||
{
|
||||
if (bb && bb->buffer) {
|
||||
// handle NUL termination
|
||||
if (bb->used < bb->size) {
|
||||
bb->buffer[bb->used] = 0;
|
||||
} else {
|
||||
// overrun - truncation buffer
|
||||
bb->flags |= GS_BYTEBUFFER_F_OVERRUN;
|
||||
bb->buffer[bb->used - 1] = 0;
|
||||
}
|
||||
if (error) {
|
||||
*error = gs_bytebuffer_get_state(bb);
|
||||
}
|
||||
return (char*) bb->buffer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gs_error_t gs_bytebuffer_get_state(gs_bytebuffer_t * bb)
|
||||
{
|
||||
if (bb) {
|
||||
if (bb->flags & GS_BYTEBUFFER_F_FAILED) {
|
||||
return GS_ERROR_DATA;
|
||||
}
|
||||
if (bb->flags & GS_BYTEBUFFER_F_OVERRUN) {
|
||||
return GS_ERROR_OVERFLOW;
|
||||
}
|
||||
return GS_OK;
|
||||
}
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
323
gomspace/libutil/src/byteorder.c
Normal file
323
gomspace/libutil/src/byteorder.c
Normal file
@ -0,0 +1,323 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/byteorder.h>
|
||||
#include <conf_util.h>
|
||||
|
||||
/* Convert 16-bit number from host byte order to network byte order */
|
||||
extern inline uint16_t __attribute__ ((__const__)) util_hton16(uint16_t h16) {
|
||||
#if UTIL_BIG_ENDIAN
|
||||
return h16;
|
||||
|
||||
#elif UTIL_LITTLE_ENDIAN
|
||||
return (uint16_t)(((h16 & 0xff00) >> 8) |
|
||||
((h16 & 0x00ff) << 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Convert 16-bit number from network byte order to host byte order */
|
||||
extern inline uint16_t __attribute__ ((__const__)) util_ntoh16(uint16_t n16) {
|
||||
return util_hton16(n16);
|
||||
}
|
||||
|
||||
/* Convert 32-bit number from host byte order to network byte order */
|
||||
extern inline uint32_t __attribute__ ((__const__)) util_hton32(uint32_t h32) {
|
||||
#if UTIL_BIG_ENDIAN
|
||||
return h32;
|
||||
|
||||
#elif UTIL_LITTLE_ENDIAN
|
||||
return (((h32 & 0xff000000) >> 24) |
|
||||
((h32 & 0x000000ff) << 24) |
|
||||
((h32 & 0x0000ff00) << 8) |
|
||||
((h32 & 0x00ff0000) >> 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Convert 32-bit number from network byte order to host byte order */
|
||||
extern inline uint32_t __attribute__ ((__const__)) util_ntoh32(uint32_t n32) {
|
||||
return util_hton32(n32);
|
||||
}
|
||||
|
||||
/* Convert 64-bit number from host byte order to network byte order */
|
||||
extern inline uint64_t __attribute__ ((__const__)) util_hton64(uint64_t h64) {
|
||||
#if UTIL_BIG_ENDIAN
|
||||
return h64;
|
||||
|
||||
#elif UTIL_LITTLE_ENDIAN
|
||||
return (((h64 & 0xff00000000000000LL) >> 56) |
|
||||
((h64 & 0x00000000000000ffLL) << 56) |
|
||||
((h64 & 0x00ff000000000000LL) >> 40) |
|
||||
((h64 & 0x000000000000ff00LL) << 40) |
|
||||
((h64 & 0x0000ff0000000000LL) >> 24) |
|
||||
((h64 & 0x0000000000ff0000LL) << 24) |
|
||||
((h64 & 0x000000ff00000000LL) >> 8) |
|
||||
((h64 & 0x00000000ff000000LL) << 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Convert 64-bit number from host byte order to network byte order */
|
||||
extern inline uint64_t __attribute__ ((__const__)) util_ntoh64(uint64_t n64) {
|
||||
return util_hton64(n64);
|
||||
}
|
||||
|
||||
/* Convert float from host byte order to network byte order */
|
||||
extern inline float __attribute__ ((__const__)) util_htonflt(float f) {
|
||||
#if UTIL_BIG_ENDIAN
|
||||
return f;
|
||||
|
||||
#elif UTIL_LITTLE_ENDIAN
|
||||
union v {
|
||||
float f;
|
||||
uint32_t i;
|
||||
};
|
||||
union v val;
|
||||
val.f = f;
|
||||
val.i = util_hton32(val.i);
|
||||
return val.f;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Convert float from host byte order to network byte order */
|
||||
extern inline float __attribute__ ((__const__)) util_ntohflt(float f) {
|
||||
return util_htonflt(f);
|
||||
}
|
||||
|
||||
/* Convert double from host byte order to network byte order */
|
||||
extern inline double __attribute__ ((__const__)) util_htondbl(double d) {
|
||||
#if UTIL_BIG_ENDIAN
|
||||
return d;
|
||||
|
||||
#elif UTIL_LITTLE_ENDIAN
|
||||
union v {
|
||||
double d;
|
||||
uint64_t i;
|
||||
};
|
||||
union v val;
|
||||
val.d = d;
|
||||
val.i = util_hton64(val.i);
|
||||
return val.d;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Convert float from host byte order to network byte order */
|
||||
extern inline double __attribute__ ((__const__)) util_ntohdbl(double d) {
|
||||
return util_htondbl(d);
|
||||
}
|
||||
|
||||
/* Convert 16-bit number from host byte order to big endian byte order */
|
||||
extern inline uint16_t __attribute__ ((__const__)) util_htobe16(uint16_t h16) {
|
||||
return util_hton16(h16);
|
||||
}
|
||||
|
||||
/* Convert 16-bit number from host byte order to little endian byte order */
|
||||
extern inline uint16_t __attribute__ ((__const__)) util_htole16(uint16_t h16) {
|
||||
#if UTIL_LITTLE_ENDIAN
|
||||
return h16;
|
||||
|
||||
#elif UTIL_BIG_ENDIAN
|
||||
return (uint16_t)(((h16 & 0xff00) >> 8) |
|
||||
((h16 & 0x00ff) << 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Convert 16-bit number from big endian byte order to little endian byte order */
|
||||
extern inline uint16_t __attribute__ ((__const__)) util_betoh16(uint16_t be16) {
|
||||
return util_ntoh16(be16);
|
||||
}
|
||||
|
||||
/* Convert 16-bit number from little endian byte order to host byte order */
|
||||
extern inline uint16_t __attribute__ ((__const__)) util_letoh16(uint16_t le16) {
|
||||
return util_htole16(le16);
|
||||
}
|
||||
|
||||
/* Convert 32-bit number from host byte order to big endian byte order */
|
||||
extern inline uint32_t __attribute__ ((__const__)) util_htobe32(uint32_t h32) {
|
||||
return util_hton32(h32);
|
||||
}
|
||||
|
||||
/* Convert 32-bit number from little endian byte order to host byte order */
|
||||
extern inline uint32_t __attribute__ ((__const__)) util_htole32(uint32_t h32) {
|
||||
#if UTIL_LITTLE_ENDIAN
|
||||
return h32;
|
||||
|
||||
#elif UTIL_BIG_ENDIAN
|
||||
return (((h32 & 0xff000000) >> 24) |
|
||||
((h32 & 0x000000ff) << 24) |
|
||||
((h32 & 0x0000ff00) << 8) |
|
||||
((h32 & 0x00ff0000) >> 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Convert 32-bit number from big endian byte order to host byte order */
|
||||
extern inline uint32_t __attribute__ ((__const__)) util_betoh32(uint32_t be32) {
|
||||
return util_ntoh32(be32);
|
||||
}
|
||||
|
||||
/* Convert 32-bit number from little endian byte order to host byte order */
|
||||
extern inline uint32_t __attribute__ ((__const__)) util_letoh32(uint32_t le32) {
|
||||
return util_htole32(le32);
|
||||
}
|
||||
|
||||
/* Convert 64-bit number from host byte order to big endian byte order */
|
||||
extern inline uint64_t __attribute__ ((__const__)) util_htobe64(uint64_t h64) {
|
||||
return util_hton64(h64);
|
||||
}
|
||||
|
||||
/* Convert 64-bit number from host byte order to little endian byte order */
|
||||
extern inline uint64_t __attribute__ ((__const__)) util_htole64(uint64_t h64) {
|
||||
#if UTIL_LITTLE_ENDIAN
|
||||
return h64;
|
||||
|
||||
#elif UTIL_BIG_ENDIAN
|
||||
return (((h64 & 0xff00000000000000LL) >> 56) |
|
||||
((h64 & 0x00000000000000ffLL) << 56) |
|
||||
((h64 & 0x00ff000000000000LL) >> 40) |
|
||||
((h64 & 0x000000000000ff00LL) << 40) |
|
||||
((h64 & 0x0000ff0000000000LL) >> 24) |
|
||||
((h64 & 0x0000000000ff0000LL) << 24) |
|
||||
((h64 & 0x000000ff00000000LL) >> 8) |
|
||||
((h64 & 0x00000000ff000000LL) << 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Convert 64-bit number from big endian byte order to host byte order */
|
||||
extern inline uint64_t __attribute__ ((__const__)) util_betoh64(uint64_t be64) {
|
||||
return util_ntoh64(be64);
|
||||
}
|
||||
|
||||
/* Convert 64-bit number from little endian byte order to host byte order */
|
||||
extern inline uint64_t __attribute__ ((__const__)) util_letoh64(uint64_t le64) {
|
||||
return util_htole64(le64);
|
||||
}
|
||||
|
||||
/* Convert 16-bit number from host byte order to network byte order */
|
||||
extern inline uint16_t __attribute__ ((__const__)) util_htons(uint16_t h16) {
|
||||
return util_hton16(h16);
|
||||
}
|
||||
|
||||
/* Convert 16-bit number from network byte order to host byte order */
|
||||
extern inline uint16_t __attribute__ ((__const__)) util_ntohs(uint16_t n16) {
|
||||
return util_ntoh16(n16);
|
||||
}
|
||||
|
||||
/* Convert 32-bit number from host byte order to network byte order */
|
||||
extern inline uint32_t __attribute__ ((__const__)) util_htonl(uint32_t h32) {
|
||||
return util_hton32(h32);
|
||||
}
|
||||
|
||||
/* Convert 32-bit number from network byte order to host byte order */
|
||||
extern inline uint32_t __attribute__ ((__const__)) util_ntohl(uint32_t n32) {
|
||||
return util_ntoh32(n32);
|
||||
}
|
||||
|
||||
#define BYTEORDER_ARRAY(convert, from, to, count) { \
|
||||
for (unsigned int i = 0; i < count; ++i, ++from, ++to) { \
|
||||
*to = convert(*from); \
|
||||
} \
|
||||
}
|
||||
|
||||
void util_hton16_array(const uint16_t * from, uint16_t * to, size_t count)
|
||||
{
|
||||
BYTEORDER_ARRAY(util_hton16, from, to, count);
|
||||
}
|
||||
|
||||
void util_hton32_array(const uint32_t * from, uint32_t * to, size_t count)
|
||||
{
|
||||
BYTEORDER_ARRAY(util_hton32, from, to, count);
|
||||
}
|
||||
|
||||
void util_hton64_array(const uint64_t * from, uint64_t * to, size_t count)
|
||||
{
|
||||
BYTEORDER_ARRAY(util_hton64, from, to, count);
|
||||
}
|
||||
|
||||
void util_ntoh16_array(const uint16_t * from, uint16_t * to, size_t count)
|
||||
{
|
||||
BYTEORDER_ARRAY(util_ntoh16, from, to, count);
|
||||
}
|
||||
|
||||
void util_ntoh32_array(const uint32_t * from, uint32_t * to, size_t count)
|
||||
{
|
||||
BYTEORDER_ARRAY(util_ntoh32, from, to, count);
|
||||
}
|
||||
|
||||
void util_ntoh64_array(const uint64_t * from, uint64_t * to, size_t count)
|
||||
{
|
||||
BYTEORDER_ARRAY(util_ntoh64, from, to, count);
|
||||
}
|
||||
|
||||
void util_htonflt_array(const float * from, float * to, size_t count)
|
||||
{
|
||||
BYTEORDER_ARRAY(util_htonflt, from, to, count);
|
||||
}
|
||||
|
||||
void util_ntohflt_array(const float * from, float * to, size_t count)
|
||||
{
|
||||
BYTEORDER_ARRAY(util_ntohflt, from, to, count);
|
||||
}
|
||||
|
||||
void util_htondbl_array(const double * from, double * to, size_t count)
|
||||
{
|
||||
BYTEORDER_ARRAY(util_htondbl, from, to, count);
|
||||
}
|
||||
|
||||
void util_ntohdbl_array(const double * from, double * to, size_t count)
|
||||
{
|
||||
BYTEORDER_ARRAY(util_ntohdbl, from, to, count);
|
||||
}
|
||||
|
||||
uint16_t gs_bswap_16(uint16_t value)
|
||||
{
|
||||
return (uint16_t)(((value & 0xff00) >> 8) |
|
||||
((value & 0x00ff) << 8));
|
||||
}
|
||||
|
||||
void gs_bswap_16_array(const uint16_t * from, uint16_t * to, size_t count)
|
||||
{
|
||||
BYTEORDER_ARRAY(gs_bswap_16, from, to, count);
|
||||
}
|
||||
|
||||
uint32_t gs_bswap_32(uint32_t value)
|
||||
{
|
||||
return (((value & 0xff000000) >> 24) |
|
||||
((value & 0x000000ff) << 24) |
|
||||
((value & 0x0000ff00) << 8) |
|
||||
((value & 0x00ff0000) >> 8));
|
||||
}
|
||||
|
||||
void gs_bswap_32_array(const uint32_t * from, uint32_t * to, size_t count)
|
||||
{
|
||||
BYTEORDER_ARRAY(gs_bswap_32, from, to, count);
|
||||
}
|
||||
|
||||
uint64_t gs_bswap_64(uint64_t value)
|
||||
{
|
||||
return (((value & 0xff00000000000000LL) >> 56) |
|
||||
((value & 0x00000000000000ffLL) << 56) |
|
||||
((value & 0x00ff000000000000LL) >> 40) |
|
||||
((value & 0x000000000000ff00LL) << 40) |
|
||||
((value & 0x0000ff0000000000LL) >> 24) |
|
||||
((value & 0x0000000000ff0000LL) << 24) |
|
||||
((value & 0x000000ff00000000LL) >> 8) |
|
||||
((value & 0x00000000ff000000LL) << 8));
|
||||
}
|
||||
|
||||
void gs_bswap_64_array(const uint64_t * from, uint64_t * to, size_t count)
|
||||
{
|
||||
BYTEORDER_ARRAY(gs_bswap_64, from, to, count);
|
||||
}
|
||||
|
||||
float gs_bswap_float(float value)
|
||||
{
|
||||
union v {
|
||||
float f;
|
||||
uint32_t i;
|
||||
} val;
|
||||
val.f = value;
|
||||
val.i = gs_bswap_32(val.i);
|
||||
return val.f;
|
||||
}
|
||||
|
||||
void gs_bswap_float_array(const float * from, float * to, size_t count)
|
||||
{
|
||||
BYTEORDER_ARRAY(gs_bswap_float, from, to, count);
|
||||
}
|
113
gomspace/libutil/src/clock.c
Normal file
113
gomspace/libutil/src/clock.c
Normal file
@ -0,0 +1,113 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/clock.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#if !__AVR__
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
gs_error_t gs_clock_to_iso8601_string2(uint32_t utc_sec, char * buf, size_t buf_size)
|
||||
{
|
||||
if ((buf == NULL) || (buf_size == 0)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
#if __AVR__
|
||||
int res = snprintf(buf, buf_size, "%"PRIu32"Z", utc_sec);
|
||||
if ((res < 0) || ((size_t)res >= buf_size)) {
|
||||
buf[buf_size - 1] = 0;
|
||||
return GS_ERROR_RANGE;
|
||||
}
|
||||
#else
|
||||
const time_t time_seconds = (time_t) utc_sec;
|
||||
struct tm tm_buf;
|
||||
struct tm * tm = gmtime_r(&time_seconds, &tm_buf);
|
||||
if (tm == NULL) {
|
||||
int res = snprintf(buf, buf_size, "%ldZ", time_seconds);
|
||||
if ((res < 0) || ((size_t)res >= buf_size)) {
|
||||
buf[buf_size - 1] = 0;
|
||||
}
|
||||
return GS_ERROR_DATA;
|
||||
}
|
||||
|
||||
// ISO8601 timestamp: 2017-03-30T06:20:45Z
|
||||
int res = snprintf(buf, buf_size, "%04d-%02d-%02dT%02d:%02d:%02dZ",
|
||||
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
if ((res < 0) || ((size_t)res >= buf_size)) {
|
||||
buf[buf_size - 1] = 0;
|
||||
return GS_ERROR_RANGE;
|
||||
}
|
||||
#endif
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_clock_to_iso8601_string(const gs_timestamp_t * utc_time, char * buf, size_t buf_size)
|
||||
{
|
||||
if (utc_time == NULL) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
return gs_clock_to_iso8601_string2(utc_time->tv_sec, buf, buf_size);
|
||||
}
|
||||
|
||||
gs_error_t gs_clock_from_string(const char * str, gs_timestamp_t * ts)
|
||||
{
|
||||
if (!str || !str[0] || !ts) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
// check for <seconds>.<nano-seconds>
|
||||
{
|
||||
uint32_t sec;
|
||||
uint32_t nsec;
|
||||
int res = sscanf(str, "%" SCNu32 ".%" SCNu32, &sec, &nsec);
|
||||
if (res == 2) {
|
||||
ts->tv_sec = sec;
|
||||
ts->tv_nsec = nsec;
|
||||
return GS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
#if !__AVR__
|
||||
// check for ISO8601
|
||||
{
|
||||
struct tm tm;
|
||||
memset(&tm, 0, sizeof(tm)); // no daylight saving
|
||||
//int res = sscanf(str, "%" SCNd32 "-%" SCNd32 "-%" SCNd32 "T%" SCNd32 ":%" SCNd32 ":%" SCNd32 "Z",
|
||||
int res = sscanf(str, "%d-%d-%dT%d:%d:%dZ",
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
|
||||
&tm.tm_hour, &tm.tm_min, &tm.tm_sec);
|
||||
if ((res == 6) &&
|
||||
(tm.tm_year >= 1970) && (tm.tm_year <= 2038) &&
|
||||
(tm.tm_mon >= 1) && (tm.tm_mon <= 12) &&
|
||||
(tm.tm_mday >= 1) && (tm.tm_mday <= 31) &&
|
||||
(tm.tm_hour >= 0) && (tm.tm_hour <= 23) &&
|
||||
(tm.tm_min >= 0) && (tm.tm_min <= 59) &&
|
||||
(tm.tm_sec >= 0) && (tm.tm_sec <= 60))
|
||||
{
|
||||
tm.tm_year -= 1900;
|
||||
tm.tm_mon -= 1;
|
||||
|
||||
#if __linux__
|
||||
// not posix compliant
|
||||
time_t sec = timegm(&tm);
|
||||
#else
|
||||
// embedded platforms do not have timezones/daylight-saving - so standard mktime works
|
||||
time_t sec = mktime(&tm);
|
||||
#endif
|
||||
if (sec >= 0) {
|
||||
ts->tv_sec = (uint32_t) sec;
|
||||
ts->tv_nsec = 0;
|
||||
return GS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return GS_ERROR_DATA;
|
||||
}
|
79
gomspace/libutil/src/crc32.c
Normal file
79
gomspace/libutil/src/crc32.c
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* efone - Distributed internet phone system.
|
||||
*
|
||||
* (c) 1999,2000 Krzysztof Dabrowski
|
||||
* (c) 1999,2000 ElysiuM deeZine
|
||||
*
|
||||
* 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 (at your option) any later version.
|
||||
*
|
||||
* Based on implementation by Finn Yannick Jacobs
|
||||
*/
|
||||
|
||||
#include <gs/util/crc32.h>
|
||||
#include <gs/util/pgm.h>
|
||||
|
||||
static const uint32_t crc_tab[256] GS_PGM_OBJECT = {
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
|
||||
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
|
||||
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
|
||||
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
|
||||
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
|
||||
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
|
||||
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
|
||||
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
|
||||
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
|
||||
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
|
||||
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
|
||||
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
|
||||
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
|
||||
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
|
||||
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
|
||||
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
|
||||
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
|
||||
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
|
||||
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
|
||||
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
|
||||
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
|
||||
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
||||
};
|
||||
|
||||
#define CALC_CRC32_STEP(crc,byte) (((crc >> 8) & 0x00FFFFFF) ^ GS_PGM_UINT32_BY_PTR(&crc_tab[(crc ^ byte) & (uint32_t) 0xFF]))
|
||||
|
||||
uint32_t gs_crc32_init(void)
|
||||
{
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
uint32_t gs_crc32_update(uint32_t crc, const void * block, size_t length)
|
||||
{
|
||||
if (block && length) {
|
||||
const uint8_t * u8 = block;
|
||||
for (unsigned int i = 0; i < length; i++) {
|
||||
crc = CALC_CRC32_STEP(crc, *u8++);
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
uint32_t gs_crc32_finalize(uint32_t crc)
|
||||
{
|
||||
return (crc ^ 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
uint32_t gs_crc32(const void * block, size_t length)
|
||||
{
|
||||
return gs_crc32_finalize(gs_crc32_update(gs_crc32_init(), block, length));
|
||||
}
|
68
gomspace/libutil/src/crc8.c
Normal file
68
gomspace/libutil/src/crc8.c
Normal file
@ -0,0 +1,68 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/crc8.h>
|
||||
#include <gs/util/pgm.h>
|
||||
|
||||
static const uint8_t crc_tab[256] GS_PGM_OBJECT = {
|
||||
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
|
||||
0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
|
||||
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
|
||||
0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
|
||||
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
|
||||
0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
|
||||
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
|
||||
0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
|
||||
0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
|
||||
0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
|
||||
0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
|
||||
0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
|
||||
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
|
||||
0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
|
||||
0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
|
||||
0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
|
||||
0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
|
||||
0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
|
||||
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
|
||||
0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
|
||||
0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
|
||||
0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
|
||||
0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
|
||||
0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
|
||||
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
|
||||
0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
|
||||
0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
|
||||
0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
|
||||
0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
|
||||
0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
|
||||
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
|
||||
0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
|
||||
};
|
||||
|
||||
|
||||
#define CALC_CRC8_STEP(crc,byte) ((crc >> 8) ^ GS_PGM_UINT8_BY_PTR(&crc_tab[(crc ^ byte) & (uint8_t) 0xFF]))
|
||||
|
||||
uint8_t gs_crc8_init(void)
|
||||
{
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
uint8_t gs_crc8_update(uint8_t crc, const void * block, size_t length)
|
||||
{
|
||||
if (block && length) {
|
||||
const uint8_t * u8 = block;
|
||||
for (unsigned int i = 0; i < length; i++) {
|
||||
crc = CALC_CRC8_STEP(crc, *u8++);
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
uint8_t gs_crc8_finalize(uint8_t crc)
|
||||
{
|
||||
return (crc ^ 0x00);
|
||||
}
|
||||
|
||||
uint8_t gs_crc8(const void * block, size_t length)
|
||||
{
|
||||
return gs_crc8_finalize(gs_crc8_update(gs_crc8_init(), block, length));
|
||||
}
|
6
gomspace/libutil/src/drivers/can/can.c
Normal file
6
gomspace/libutil/src/drivers/can/can.c
Normal file
@ -0,0 +1,6 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/drivers/can/can.h>
|
||||
|
||||
// deifne common log group.
|
||||
GS_LOG_GROUP(gs_can_log, "can", GS_LOG_CAT_DRIVER, GS_LOG_DEFAULT_MASK);
|
6
gomspace/libutil/src/drivers/i2c/i2c.c
Normal file
6
gomspace/libutil/src/drivers/i2c/i2c.c
Normal file
@ -0,0 +1,6 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/drivers/i2c/common.h>
|
||||
|
||||
// define common log group.
|
||||
GS_LOG_GROUP(gs_i2c_log, "i2c", GS_LOG_CAT_DRIVER, GS_LOG_DEFAULT_MASK);
|
6
gomspace/libutil/src/drivers/spi/spi.c
Normal file
6
gomspace/libutil/src/drivers/spi/spi.c
Normal file
@ -0,0 +1,6 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/drivers/spi/common.h>
|
||||
|
||||
// define common log group.
|
||||
GS_LOG_GROUP(gs_spi_log, "spi", GS_LOG_CAT_DRIVER, GS_LOG_DEFAULT_MASK);
|
37
gomspace/libutil/src/drivers/sys/memory.c
Normal file
37
gomspace/libutil/src/drivers/sys/memory.c
Normal file
@ -0,0 +1,37 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/drivers/sys/memory.h>
|
||||
#include <gs/util/check.h>
|
||||
|
||||
static const char * long_to_string(char * buf, size_t buf_size, long lvalue)
|
||||
{
|
||||
if (lvalue >= 0) {
|
||||
snprintf(buf, buf_size, "%ld", lvalue);
|
||||
return buf;
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
gs_error_t gs_mem_get_ram_stat(gs_mem_ram_type_t type, gs_mem_ram_stat_t * ram_stat)
|
||||
{
|
||||
if (type == GS_MEM_RAM_TYPE_INTERNAL) {
|
||||
return gs_mem_get_int_ram_stat(ram_stat);
|
||||
} else if (type == GS_MEM_RAM_TYPE_EXTERNAL) {
|
||||
return gs_mem_get_ext_ram_stat(ram_stat);
|
||||
}
|
||||
|
||||
/* Unsupported memory type */
|
||||
return GS_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
gs_error_t gs_mem_print_ram_stat(gs_mem_ram_stat_t * ram_stat, FILE * out)
|
||||
{
|
||||
GS_CHECK_ARG(ram_stat != NULL);
|
||||
char buf[20];
|
||||
|
||||
fprintf(out, "Total: %s\r\n", long_to_string(buf, sizeof(buf), ram_stat->total));
|
||||
fprintf(out, "Max available: %s\r\n", long_to_string(buf, sizeof(buf), ram_stat->max_available));
|
||||
fprintf(out, "Min available: %s\r\n", long_to_string(buf, sizeof(buf), ram_stat->min_available));
|
||||
fprintf(out, "Available: %s\r\n", long_to_string(buf, sizeof(buf), ram_stat->available));
|
||||
return GS_OK;
|
||||
}
|
106
gomspace/libutil/src/error.c
Normal file
106
gomspace/libutil/src/error.c
Normal file
@ -0,0 +1,106 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#define GS_UTIL_DEPRECATED_ERROR_CODES 1
|
||||
|
||||
#include <gs/util/error.h>
|
||||
#include <gs/util/string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __AVR__
|
||||
const char * gs_error_string(int error)
|
||||
{
|
||||
/**
|
||||
avr: const strings are not automatically stored in program space (see gs/util/pgm.h), and if stored
|
||||
in program space, they require special formatting in logs (i.e. "%S").
|
||||
So we settle for simple error string, with the error nnumber - no need to change log/(s)printf etc.
|
||||
@note: solution is not 100% thread/task safe.
|
||||
*/
|
||||
static char buffer[15]; // large enough to always keep zero termination, due to no thread/task lock
|
||||
snprintf(buffer, sizeof(buffer), "%d", error);
|
||||
return buffer;
|
||||
}
|
||||
#else
|
||||
const char * gs_error_string(int error)
|
||||
{
|
||||
switch (error) {
|
||||
case GS_OK: return "GS_OK(0)";
|
||||
case GS_ERROR_PERM: return GS_DEF2STRING(GS_ERROR_PERM) "(-1)";
|
||||
case GS_ERROR_INTR: return GS_DEF2STRING(GS_ERROR_INTR) "(-4)";
|
||||
case GS_ERROR_IO: return GS_DEF2STRING(GS_ERROR_IO) "(-5)";
|
||||
case GS_ERROR_AGAIN: return GS_DEF2STRING(GS_ERROR_AGAIN) "(-11)";
|
||||
case GS_ERROR_ALLOC: return GS_DEF2STRING(GS_ERROR_ALLOC) "(-12)";
|
||||
case GS_ERROR_ACCESS: return GS_DEF2STRING(GS_ERROR_ACCESS) "(-13)";
|
||||
case GS_ERROR_BUSY: return GS_DEF2STRING(GS_ERROR_BUSY) "(-16)";
|
||||
case GS_ERROR_EXIST: return GS_DEF2STRING(GS_ERROR_EXIST) "(-17)";
|
||||
case GS_ERROR_ARG: return GS_DEF2STRING(GS_ERROR_ARG) "(-22)";
|
||||
case GS_ERROR_NOT_IMPLEMENTED: return GS_DEF2STRING(GS_ERROR_NOT_IMPLEMENTED) "(-38)";
|
||||
case GS_ERROR_OVERFLOW: return GS_DEF2STRING(GS_ERROR_OVERFLOW) "(-75)";
|
||||
case GS_ERROR_NOT_SUPPORTED: return GS_DEF2STRING(GS_ERROR_NOT_SUPPORTED) "(-95)";
|
||||
case GS_ERROR_IN_USE: return GS_DEF2STRING(GS_ERROR_IN_USE) "(-98)";
|
||||
case GS_ERROR_CONNECTION_RESET: return GS_DEF2STRING(GS_ERROR_CONNECTION_RESET) "(-104)";
|
||||
case GS_ERROR_NO_BUFFERS: return GS_DEF2STRING(GS_ERROR_NO_BUFFERS) "(-105)";
|
||||
case GS_ERROR_TIMEOUT: return GS_DEF2STRING(GS_ERROR_TIMEOUT) "(-110)";
|
||||
case GS_ERROR_ALREADY_IN_PROGRESS: return GS_DEF2STRING(GS_ERROR_ALREADY_IN_PROGRESS) "(-114)";
|
||||
|
||||
case GS_ERROR_HANDLE: return GS_DEF2STRING(GS_ERROR_HANDLE) "(-2000)";
|
||||
case GS_ERROR_NOT_FOUND: return GS_DEF2STRING(GS_ERROR_NOT_FOUND) "(-2001)";
|
||||
case GS_ERROR_FULL: return GS_DEF2STRING(GS_ERROR_FULL) "(-2002)";
|
||||
case GS_ERROR_RANGE: return GS_DEF2STRING(GS_ERROR_RANGE) "(-2003)";
|
||||
case GS_ERROR_DATA: return GS_DEF2STRING(GS_ERROR_DATA) "(-2004)";
|
||||
case GS_ERROR_UNKNOWN: return GS_DEF2STRING(GS_ERROR_UNKNOWN) "(-2005)";
|
||||
case GS_ERROR_NO_DATA: return GS_DEF2STRING(GS_ERROR_NO_DATA) "(-2006)";
|
||||
case GS_ERROR_STALE: return GS_DEF2STRING(GS_ERROR_STALE) "(-2007)";
|
||||
case GS_ERROR_TYPE: return GS_DEF2STRING(GS_ERROR_TYPE) "(-2008)";
|
||||
case GS_ERROR_AMBIGUOUS: return GS_DEF2STRING(GS_ERROR_AMBIGUOUS) "(-2009)";
|
||||
case GS_ERROR_STATE: return GS_DEF2STRING(GS_ERROR_STATE) "(-2010)";
|
||||
}
|
||||
|
||||
// as fallback we use standard POSIX error string
|
||||
const int posix_error = abs(error);
|
||||
return strerror(posix_error);
|
||||
}
|
||||
#endif
|
||||
|
||||
gs_error_t gs_error(int error)
|
||||
{
|
||||
return (abs(error) * -1);
|
||||
}
|
||||
|
||||
#ifndef __AVR__
|
||||
const char * error_string(int code)
|
||||
{
|
||||
switch (code) {
|
||||
case E_NO_ERR:
|
||||
return "No error";
|
||||
case E_NO_DEVICE:
|
||||
return "No device";
|
||||
case E_MALLOC_FAIL:
|
||||
return "Malloc fail";
|
||||
case E_THREAD_FAIL:
|
||||
return "Thread failure";
|
||||
case E_NO_QUEUE:
|
||||
return "No such queue";
|
||||
case E_INVALID_BUF_SIZE:
|
||||
return "Invalid buffer size";
|
||||
case E_INVALID_PARAM:
|
||||
return "Invalid paramater";
|
||||
case E_NO_SS:
|
||||
return "No such subsystem";
|
||||
case E_GARBLED_BUFFER:
|
||||
return "Rubbish in buffer";
|
||||
case E_FLASH_ERROR:
|
||||
return "FLASH error";
|
||||
case E_BOOT_SER:
|
||||
return "Thread boot fail: serial driver";
|
||||
case E_BOOT_DEBUG:
|
||||
return "Thread boot fail: debug console";
|
||||
case E_BOOT_FLASH:
|
||||
return "Thread boot fail: flash driver";
|
||||
case E_NO_BUFFER:
|
||||
return "No buffer";
|
||||
default:
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
#endif
|
77
gomspace/libutil/src/fletcher.c
Normal file
77
gomspace/libutil/src/fletcher.c
Normal file
@ -0,0 +1,77 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/fletcher.h>
|
||||
#include <gs/util/string.h>
|
||||
#include <gs/util/pgm.h>
|
||||
|
||||
uint16_t gs_fletcher16_memcpy(const void * data_in, size_t count, void * (*memcpyfcn)(void *, const void *, size_t))
|
||||
{
|
||||
if (memcpyfcn == NULL) {
|
||||
memcpyfcn = &memcpy;
|
||||
}
|
||||
|
||||
uint16_t sum1 = 0;
|
||||
uint16_t sum2 = 0;
|
||||
|
||||
if (data_in && count) {
|
||||
const uint8_t * data = data_in;
|
||||
for (unsigned int idx = 0; idx < count; ++idx) {
|
||||
uint8_t byte;
|
||||
(*memcpyfcn)(&byte, &data[idx], 1);
|
||||
sum1 = (uint16_t)((sum1 + byte) % 255);
|
||||
sum2 = (uint16_t)((sum2 + sum1) % 255);
|
||||
}
|
||||
}
|
||||
return (uint16_t)((sum2 << 8) | sum1);
|
||||
}
|
||||
|
||||
uint16_t gs_fletcher16_P(const void * data_in, size_t count)
|
||||
{
|
||||
uint16_t sum1 = 0;
|
||||
uint16_t sum2 = 0;
|
||||
|
||||
if (data_in && count) {
|
||||
const uint8_t * data = data_in;
|
||||
for (unsigned int idx = 0; idx < count; ++idx) {
|
||||
sum1 = (uint16_t)((sum1 + GS_PGM_UINT8_BY_PTR(data++)) % 255);
|
||||
sum2 = (uint16_t)((sum2 + sum1) % 255);
|
||||
}
|
||||
}
|
||||
return (uint16_t)((sum2 << 8) | sum1);
|
||||
}
|
||||
|
||||
uint16_t gs_fletcher16(const void * data_in, size_t size)
|
||||
{
|
||||
uint16_t sum1 = 0;
|
||||
uint16_t sum2 = 0;
|
||||
|
||||
if (data_in && size) {
|
||||
const uint8_t * data = data_in;
|
||||
for (unsigned int idx = 0; idx < size; ++idx) {
|
||||
sum1 = (uint16_t)((sum1 + (*data++)) % 255);
|
||||
sum2 = (uint16_t)((sum2 + sum1) % 255);
|
||||
}
|
||||
}
|
||||
return (uint16_t)((sum2 << 8) | sum1);
|
||||
}
|
||||
|
||||
void gs_fletcher16_init(gs_fletcher16_t * f16)
|
||||
{
|
||||
f16->sum1 = f16->sum2 = 0;
|
||||
}
|
||||
|
||||
void gs_fletcher16_update(gs_fletcher16_t * f16, const void * data_in, size_t size)
|
||||
{
|
||||
if (f16 && data_in && size) {
|
||||
const uint8_t * data = data_in;
|
||||
for (unsigned int idx = 0; idx < size; ++idx) {
|
||||
f16->sum1 = (uint16_t)((f16->sum1 + (*data++)) % 255);
|
||||
f16->sum2 = (uint16_t)((f16->sum2 + f16->sum1) % 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t gs_fletcher16_finalize(gs_fletcher16_t * f16)
|
||||
{
|
||||
return (uint16_t)((f16->sum2 << 8) | f16->sum1);
|
||||
}
|
111
gomspace/libutil/src/function_scheduler.c
Normal file
111
gomspace/libutil/src/function_scheduler.c
Normal file
@ -0,0 +1,111 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/function_scheduler.h>
|
||||
|
||||
#include <gs/util/check.h>
|
||||
#include <gs/util/time.h>
|
||||
#include <gs/util/minmax.h>
|
||||
#include <gs/util/check.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
typedef struct {
|
||||
// function to call
|
||||
gs_function_scheduler_function_t function;
|
||||
// function's user data
|
||||
void * user_data;
|
||||
// timeout in mS
|
||||
uint32_t timeout_ms;
|
||||
// last execution time in mS
|
||||
uint32_t last_exec_ms;
|
||||
} gs_function_scheduler_entry_t;
|
||||
|
||||
struct gs_function_scheduler {
|
||||
// Max timeout in mS
|
||||
uint32_t max_timeout_ms;
|
||||
// allocated entries
|
||||
unsigned int max_entries;
|
||||
// entries
|
||||
gs_function_scheduler_entry_t * entries;
|
||||
};
|
||||
|
||||
GS_CHECK_STATIC_ASSERT(sizeof(int) >= 2, int_must_be_at_least_16bit);
|
||||
|
||||
gs_error_t gs_function_scheduler_create(uint32_t max_timeout_ms, unsigned int max_entries, gs_function_scheduler_t ** return_scheduler)
|
||||
{
|
||||
GS_CHECK_ARG(max_timeout_ms <= INT_MAX);
|
||||
GS_CHECK_ARG(max_entries > 0);
|
||||
GS_CHECK_ARG(return_scheduler != NULL);
|
||||
|
||||
gs_function_scheduler_entry_t * entries = calloc(max_entries, sizeof(*entries));
|
||||
if (entries == NULL) {
|
||||
return GS_ERROR_ALLOC;
|
||||
}
|
||||
|
||||
gs_function_scheduler_t * scheduler = calloc(1, sizeof(*scheduler));
|
||||
if (scheduler == NULL) {
|
||||
free (entries);
|
||||
return GS_ERROR_ALLOC;
|
||||
}
|
||||
|
||||
scheduler->max_timeout_ms = max_timeout_ms;
|
||||
scheduler->entries = entries;
|
||||
scheduler->max_entries = max_entries;
|
||||
|
||||
*return_scheduler = scheduler;
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_function_scheduler_destroy(gs_function_scheduler_t * scheduler)
|
||||
{
|
||||
GS_CHECK_HANDLE(scheduler);
|
||||
free(scheduler->entries);
|
||||
free(scheduler);
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
GS_CHECK_HANDLE(scheduler != NULL);
|
||||
GS_CHECK_ARG(func != NULL);
|
||||
|
||||
gs_function_scheduler_entry_t * entry = scheduler->entries;
|
||||
for (unsigned int i = 0; i < scheduler->max_entries; ++i, ++entry) {
|
||||
if (entry->function == NULL) {
|
||||
entry->function = func;
|
||||
entry->user_data = user_data;
|
||||
entry->timeout_ms = first_timeout_ms;
|
||||
entry->last_exec_ms = gs_time_rel_ms();
|
||||
return GS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return GS_ERROR_FULL;
|
||||
}
|
||||
|
||||
int gs_function_scheduler_execute_ms(gs_function_scheduler_t * scheduler)
|
||||
{
|
||||
uint32_t timeout_ms = 5000; // max timeout to ensure gs_time_rel_ms() works correctly (wrapping more than once is bad)
|
||||
|
||||
if (scheduler) {
|
||||
timeout_ms = scheduler->max_timeout_ms;
|
||||
uint32_t now_ms = gs_time_rel_ms();
|
||||
|
||||
gs_function_scheduler_entry_t * entry = scheduler->entries;
|
||||
for (unsigned int i = 0; i < scheduler->max_entries; ++i, ++entry) {
|
||||
if (entry->function) {
|
||||
uint32_t elapsed = gs_time_diff_ms(entry->last_exec_ms, now_ms);
|
||||
if (elapsed >= entry->timeout_ms) {
|
||||
entry->timeout_ms = (entry->function)(entry->user_data);
|
||||
entry->last_exec_ms = now_ms = gs_time_rel_ms();
|
||||
elapsed = 0;
|
||||
}
|
||||
timeout_ms = gs_min(timeout_ms, (entry->timeout_ms - elapsed));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (int)((timeout_ms < INT_MAX) ? timeout_ms : INT_MAX);
|
||||
}
|
754
gomspace/libutil/src/gosh/command.c
Normal file
754
gomspace/libutil/src/gosh/command.c
Normal file
@ -0,0 +1,754 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include "command_local.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <gs/util/vmem.h> // register commands
|
||||
#include <gs/util/log.h> // register commands
|
||||
#include <gs/util/string.h>
|
||||
#include <gs/util/clock.h>
|
||||
#include "../lock.h"
|
||||
|
||||
#define MAX_ARGC 30
|
||||
|
||||
#ifdef __AVR__
|
||||
#include <avr/pgmspace.h>
|
||||
#define cmd_strcmp strcmp_P
|
||||
#define cmd_strncmp strncmp_P
|
||||
#define cmd_strlen strlen_P
|
||||
#define cmd_strcpy strcpy_P
|
||||
#define cmd_read_ptr(ptr) ((void *) pgm_read_word(ptr))
|
||||
#define cmd_read_int(ptr) pgm_read_word(ptr)
|
||||
#else
|
||||
#define cmd_strcmp strcmp
|
||||
#define cmd_strncmp strncmp
|
||||
#define cmd_strlen strlen
|
||||
#define cmd_strcpy strcpy
|
||||
#define cmd_read_ptr(ptr) *ptr
|
||||
#define cmd_read_int(ptr) *ptr
|
||||
#endif
|
||||
|
||||
// define common command log group.
|
||||
static GS_LOG_GROUP(gs_command_log, "command", GS_LOG_CAT_COMMAND, LOG_DEFAULT_MASK | LOG_INFO_MASK);
|
||||
#define LOG_DEFAULT gs_command_log
|
||||
|
||||
/**
|
||||
Compile check that size of gs_command_t is multiplum of 4.
|
||||
*/
|
||||
GS_STATIC_ASSERT((sizeof(gs_command_t) % 4) == 0, gs_command_t_is_not_a_multiplum_of_4);
|
||||
|
||||
// Private context
|
||||
typedef struct process_context {
|
||||
// command context - must be first, as it is used to access private context (same address)
|
||||
gs_command_context_t context;
|
||||
// process function
|
||||
gs_error_t (*process)(const gs_command_t * const cmds, int cmd_count, int arg_offset, struct process_context * pc);
|
||||
// command error
|
||||
gs_error_t error;
|
||||
// only exact match (space after last argument)
|
||||
bool requires_exact_match;
|
||||
// first command match
|
||||
const gs_command_t * cmd;
|
||||
// number of hits when hunting commands, completion etc.
|
||||
unsigned int hits;
|
||||
// complete result
|
||||
struct {
|
||||
char * line;
|
||||
size_t token_start;
|
||||
} complete;
|
||||
} private_context_t;
|
||||
|
||||
// command block
|
||||
typedef struct gs_command_block {
|
||||
//! Pointer to command block.
|
||||
const gs_command_t * commands;
|
||||
//! Number of commands in command block.
|
||||
size_t count;
|
||||
//! Reference to next command block.
|
||||
struct gs_command_block * next;
|
||||
} gs_command_block_t;
|
||||
|
||||
// commands
|
||||
static gs_command_block_t g_commands;
|
||||
|
||||
// minimum stack size in bytes.
|
||||
static size_t g_stack_size;
|
||||
|
||||
// command logger callback
|
||||
static gs_command_log_t g_command_logger = NULL;
|
||||
static void * g_command_logger_ctx = NULL;
|
||||
|
||||
static gs_error_t command_stdio_set_result(gs_command_context_t *ctx, const char *group, const char *key, const char *value)
|
||||
{
|
||||
static const char* printed_group_header = NULL;
|
||||
/* Print Group header if Group string is non-empty */
|
||||
if ((group != NULL) && (group[0] != '\0')) {
|
||||
if (printed_group_header != group) {
|
||||
fprintf(ctx->out, "%s:\r\n", group);
|
||||
printed_group_header = group;
|
||||
}
|
||||
}
|
||||
/* Print "<key>: <value>" if key string is non-empty */
|
||||
if (key != NULL) {
|
||||
if (key[0] != '\0') {
|
||||
if ((group != NULL) && (group[0] != '\0')) {
|
||||
fprintf(ctx->out, " %s: ", key);
|
||||
} else {
|
||||
fprintf(ctx->out, "%s: ", key);
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(ctx->out, "%s\r\n", value);
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_command_stdio_flush(gs_command_context_t *ctx)
|
||||
{
|
||||
fflush(ctx->out);
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_command_stdio_wait_for_key(gs_command_context_t *ctx, int *ch, int timeout_ms)
|
||||
{
|
||||
return gs_stdio_getchar_timed(timeout_ms, ch);
|
||||
}
|
||||
|
||||
static const gs_command_io_functions_t stdio_functions = {
|
||||
.set_result = command_stdio_set_result,
|
||||
.flush = gs_command_stdio_flush,
|
||||
.wait_for_key = gs_command_stdio_wait_for_key
|
||||
};
|
||||
|
||||
const char * gs_command_args(gs_command_context_t *ctx)
|
||||
{
|
||||
if (ctx->argc > 1) {
|
||||
// find first matching argument (= starts with) - this is not 100% and doesn't handle arguments with spaces (quoted)
|
||||
const char * arg = ctx->command_line;
|
||||
while (arg && arg[0]) {
|
||||
if (strncmp(arg, ctx->argv[1], strlen(ctx->argv[1])) == 0) {
|
||||
return arg;
|
||||
}
|
||||
// skip argument
|
||||
for (; *arg && (*arg != ' '); ++arg);
|
||||
// skip spaces
|
||||
// cppcheck-suppress redundantCondition
|
||||
for (; *arg && (*arg == ' '); ++arg);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool gs_command_build_argv(char *line, int *argc, char **argv, int max_argc)
|
||||
{
|
||||
// Skip spaces
|
||||
for (; line && *line && isspace((unsigned int)*line); ++line);
|
||||
|
||||
*argc = 0;
|
||||
argv[*argc] = line;
|
||||
|
||||
char quote = 0;
|
||||
|
||||
while (*line) {
|
||||
// check for quote's: ' or "
|
||||
if ((*line == '\'') || (*line == '\"')) {
|
||||
if (quote == 0) {
|
||||
quote = *line;
|
||||
argv[*argc]++;
|
||||
} else if (quote == *line) {
|
||||
quote = 0;
|
||||
*line = '\0';
|
||||
}
|
||||
}
|
||||
// check for whitespace and no quotes active
|
||||
else if (isspace((unsigned int)*line) && quote == 0) {
|
||||
/* Delete space */
|
||||
*line++ = '\0';
|
||||
|
||||
// skip spaces
|
||||
for (; *line && isspace((unsigned int)*line); ++line);
|
||||
|
||||
/* If there is more data, we have another argument */
|
||||
if (*line) {
|
||||
(*argc)++;
|
||||
if (*argc >= max_argc) {
|
||||
return false;
|
||||
}
|
||||
argv[*argc] = line;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
line++;
|
||||
}
|
||||
|
||||
(*argc)++;
|
||||
if (*argc >= max_argc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// According to C11 section 5.1.2.2.1, argv[argc] must be NULL
|
||||
argv[*argc] = NULL;
|
||||
|
||||
// Check for invalid number of quotes
|
||||
return (quote == 0) ? true : false;
|
||||
}
|
||||
|
||||
static inline gs_error_t command_logger(const char *cmd_line, gs_error_t ret, gs_error_t cmd_ret, gs_timestamp_t ts, gs_timestamp_t te)
|
||||
{
|
||||
gs_lock_lock();
|
||||
gs_command_log_t logger = g_command_logger;
|
||||
void * log_ctx = g_command_logger_ctx;
|
||||
gs_lock_unlock();
|
||||
|
||||
if (logger) {
|
||||
return logger(cmd_line, ret, cmd_ret, ts, te, log_ctx);
|
||||
}
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static gs_error_t command_execute(const gs_command_t * const cmds, int cmd_count, int arg_offset, private_context_t * pc)
|
||||
{
|
||||
for (int i = 0; i < cmd_count; i++) {
|
||||
const gs_command_t * cmd = &cmds[i];
|
||||
|
||||
if (cmd_strcmp(pc->context.argv[arg_offset], cmd->name) == 0) {
|
||||
// check for sub-commands
|
||||
const gs_command_t * list = (void*) cmd_read_ptr(&cmd->chain.list);
|
||||
if (list) {
|
||||
++arg_offset;
|
||||
if (arg_offset >= pc->context.argc) {
|
||||
return GS_ERROR_TYPE;
|
||||
}
|
||||
return command_execute(list, cmd_read_int(&cmd->chain.count), arg_offset, pc);
|
||||
}
|
||||
|
||||
gs_command_handler_t handler = (void *) cmd_read_ptr(&cmd->handler);
|
||||
if (handler == NULL) {
|
||||
return GS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
pc->context.argc -= arg_offset;
|
||||
pc->context.argv = &pc->context.argv[arg_offset];
|
||||
pc->context.command = cmd;
|
||||
|
||||
// check arguments - if specified
|
||||
if (cmd->mandatory_args || cmd->optional_args) {
|
||||
const int min_args = (cmd->mandatory_args == GS_COMMAND_NO_ARGS) ? 0 : cmd->mandatory_args;
|
||||
const int args = (pc->context.argc - 1);
|
||||
if (args < min_args) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
if (args > (min_args + cmd->optional_args)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
pc->error = handler(&pc->context);
|
||||
return GS_OK; // command was excecuted
|
||||
}
|
||||
}
|
||||
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
static gs_error_t command_process(private_context_t * pc)
|
||||
{
|
||||
const char * command = gs_string_skip_leading_spaces(pc->context.command_line);
|
||||
|
||||
// Make copy of string, because command parser mangles destroys it
|
||||
const size_t command_len = strlen(command);
|
||||
char command_copy[command_len + 1];
|
||||
strcpy(command_copy, command);
|
||||
|
||||
if (command_len && command[command_len-1] == ' ') {
|
||||
pc->requires_exact_match = true;
|
||||
}
|
||||
|
||||
pc->context.optsp = 1;
|
||||
pc->context.optind = 1;
|
||||
pc->context.optopt = '?';
|
||||
pc->context.command_line = command;
|
||||
|
||||
// split into arguments
|
||||
char *argv[MAX_ARGC + 1];
|
||||
if (gs_command_build_argv(command_copy, &pc->context.argc, argv, MAX_ARGC + 1) == false) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
pc->context.argv = argv;
|
||||
|
||||
gs_error_t error = GS_ERROR_NOT_FOUND;
|
||||
for (const gs_command_block_t * block = &g_commands; block && (error == GS_ERROR_NOT_FOUND); block = block->next) {
|
||||
if (block->commands) {
|
||||
error = (pc->process)(block->commands, block->count, 0, pc);
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_command_run(const char * command, gs_error_t * return_command_result)
|
||||
{
|
||||
return gs_command_execute_stdio(command, return_command_result);
|
||||
}
|
||||
|
||||
gs_error_t gs_command_execute_stdio(const char * command, gs_error_t * return_command_result)
|
||||
{
|
||||
return gs_command_execute(command, return_command_result, stdout, &stdio_functions, NULL);
|
||||
}
|
||||
|
||||
gs_error_t gs_command_execute(const char * command, gs_error_t * return_command_result, FILE *out,
|
||||
const gs_command_io_functions_t *iof, void *iof_ctx)
|
||||
{
|
||||
command = gs_string_skip_leading_spaces(command);
|
||||
GS_CHECK_ARG(gs_string_empty(command) == false);
|
||||
|
||||
private_context_t pc = {
|
||||
.process = command_execute,
|
||||
.error = GS_OK,
|
||||
.context = {
|
||||
.command_line = command,
|
||||
.out = out,
|
||||
.io_functions = iof,
|
||||
.io_ctx = iof_ctx,
|
||||
}
|
||||
};
|
||||
gs_timestamp_t tm_start, tm_end;
|
||||
gs_clock_get_time(&tm_start);
|
||||
gs_error_t error = command_process(&pc);
|
||||
gs_clock_get_time(&tm_end);
|
||||
command_logger(pc.context.command_line, error, pc.error, tm_start, tm_end);
|
||||
if ((error == GS_OK) && return_command_result) {
|
||||
*return_command_result = pc.error;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_command_set_output(gs_command_context_t *ctx, const char* group, const char* key, const char* value)
|
||||
{
|
||||
GS_CHECK_ARG(ctx);
|
||||
|
||||
if (ctx->io_functions && ctx->io_functions->set_result) {
|
||||
return ctx->io_functions->set_result(ctx, group, key, value);
|
||||
}
|
||||
|
||||
/* If no IO-function set - ignore the data and send Success */
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_command_set_output_printf(gs_command_context_t *ctx, const char* group, const char* key, const char * format, ...)
|
||||
{
|
||||
GS_CHECK_ARG(ctx);
|
||||
|
||||
if (ctx->io_functions && ctx->io_functions->set_result)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
char value[256];
|
||||
int size = vsnprintf(value, sizeof(value), format, args);
|
||||
va_end(args);
|
||||
|
||||
/* Don't allow to set truncated results - Return error in this case */
|
||||
if (size >= (int)sizeof(value)) {
|
||||
return GS_ERROR_ALLOC;
|
||||
}
|
||||
|
||||
return ctx->io_functions->set_result(ctx, group, key, value);
|
||||
}
|
||||
|
||||
/* If no IO-function set - ignore the data and send Success */
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_command_flush_output(gs_command_context_t *ctx)
|
||||
{
|
||||
GS_CHECK_ARG(ctx);
|
||||
|
||||
if (ctx->io_functions && ctx->io_functions->flush) {
|
||||
return ctx->io_functions->flush(ctx);
|
||||
}
|
||||
|
||||
/* If no IO-function set - ignore the data and send Success */
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
bool gs_command_wait_any_key(gs_command_context_t *ctx, int timeout_ms)
|
||||
{
|
||||
int ch;
|
||||
gs_error_t ret = gs_command_wait_key(ctx, &ch, timeout_ms);
|
||||
|
||||
if (ret == GS_ERROR_TIMEOUT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Ensure that a commands handler will not stall if IO function if not available etc.
|
||||
False will only be returned in case of a positive timeout */
|
||||
return true;
|
||||
}
|
||||
|
||||
gs_error_t gs_command_wait_key(gs_command_context_t *ctx, int* ch, int timeout_ms)
|
||||
{
|
||||
if (ctx && ctx->io_functions && ctx->io_functions->wait_for_key)
|
||||
{
|
||||
return ctx->io_functions->wait_for_key(ctx, ch, timeout_ms);
|
||||
}
|
||||
|
||||
/* If no IO-function set set return GS_ERROR_HANDLE */
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
unsigned int gs_command_completer_add_token(gs_command_context_t * ctx, const char * token, bool exact)
|
||||
{
|
||||
private_context_t * pc = (private_context_t *) ctx;
|
||||
char * line = &pc->complete.line[pc->complete.token_start];
|
||||
|
||||
if (token == NULL) {
|
||||
// mark any pending partial token as exact
|
||||
if ((line[0] == 0) || (pc->hits != 1)) {
|
||||
return pc->hits;
|
||||
}
|
||||
exact = true;
|
||||
}
|
||||
|
||||
if (exact) {
|
||||
if (token) {
|
||||
strcpy(line, token);
|
||||
}
|
||||
strcat(line, " ");
|
||||
pc->complete.token_start = strlen(pc->complete.line);
|
||||
pc->hits = 1;
|
||||
} else {
|
||||
if (pc->hits == 0) {
|
||||
strcpy(line, token);
|
||||
} else {
|
||||
for (; *line && *token && (*line == *token); ++line, ++token);
|
||||
*line = 0;
|
||||
}
|
||||
++pc->hits;
|
||||
}
|
||||
|
||||
return pc->hits;
|
||||
}
|
||||
|
||||
static unsigned int command_complete_add(private_context_t * pc, const gs_command_t * cmd, bool exact)
|
||||
{
|
||||
if (cmd) {
|
||||
pc->cmd = cmd;
|
||||
return gs_command_completer_add_token(&pc->context, cmd->name, exact);
|
||||
} else {
|
||||
return gs_command_completer_add_token(&pc->context, NULL, exact);
|
||||
}
|
||||
}
|
||||
|
||||
static gs_error_t command_complete(const gs_command_t * const cmds, int cmd_count, int arg_offset, private_context_t * pc)
|
||||
{
|
||||
if (arg_offset > 0) {
|
||||
// command we are looking for must be in this block
|
||||
pc->hits = 0;
|
||||
}
|
||||
pc->cmd = NULL;
|
||||
bool exact_match = false;
|
||||
|
||||
for (int i = 0; i < cmd_count; i++) {
|
||||
const gs_command_t * cmd = &cmds[i];
|
||||
|
||||
if (cmd_read_int(&cmd->mode) & GS_COMMAND_FLAG_HIDDEN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (gs_string_empty(pc->context.argv[arg_offset])) {
|
||||
// exceeding known token(s) - partial match
|
||||
command_complete_add(pc, cmd, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pc->requires_exact_match || ((arg_offset+1) < pc->context.argc)) {
|
||||
// must be an exact match
|
||||
if (cmd_strcmp(pc->context.argv[arg_offset], cmd->name) == 0) {
|
||||
command_complete_add(pc, cmd, true);
|
||||
exact_match = true;
|
||||
break;
|
||||
}
|
||||
} else if (cmd_strncmp(pc->context.argv[arg_offset], cmd->name,
|
||||
strlen(pc->context.argv[arg_offset])) == 0) {
|
||||
// partial match
|
||||
command_complete_add(pc, cmd, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (exact_match || ((arg_offset > 0) && (pc->hits == 1))) {
|
||||
command_complete_add(pc, NULL, true);
|
||||
|
||||
if (strlen(pc->complete.line) > strlen(pc->context.command_line)) {
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
if (pc->cmd->chain.list) {
|
||||
return command_complete(pc->cmd->chain.list, pc->cmd->chain.count, arg_offset+1, pc);
|
||||
}
|
||||
|
||||
// command arguments
|
||||
pc->context.argc -= arg_offset;
|
||||
pc->context.argv = &pc->context.argv[arg_offset];
|
||||
pc->context.command = pc->cmd;
|
||||
|
||||
// add the "already" completed ones
|
||||
int arg_to_complete = 1;
|
||||
for (; arg_to_complete < (pc->context.argc - 1); ++arg_to_complete) {
|
||||
gs_command_completer_add_token(&pc->context, pc->context.argv[arg_to_complete], true);
|
||||
}
|
||||
// add the last - if its completed (space after)
|
||||
if ((arg_to_complete < pc->context.argc) && pc->requires_exact_match) {
|
||||
// cppcheck-suppress unreadVariable - not used on __AVR__ because it doesn't support 'completer'
|
||||
gs_command_completer_add_token(&pc->context, pc->context.argv[arg_to_complete], true);
|
||||
++arg_to_complete;
|
||||
}
|
||||
|
||||
#if (__AVR__ == 0)
|
||||
if (pc->cmd->completer) {
|
||||
pc->hits = 0;
|
||||
(pc->cmd->completer)(&pc->context, arg_to_complete);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
pc->hits = 1; // no completer - assume single hit
|
||||
}
|
||||
|
||||
return GS_OK; // only used for breaking loop
|
||||
}
|
||||
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
gs_error_t gs_command_complete(char *line, size_t max_line_length, FILE* out)
|
||||
{
|
||||
const size_t line_len = strlen(line);
|
||||
char buffer[max_line_length];
|
||||
buffer[0] = 0;
|
||||
private_context_t pc = {
|
||||
.process = command_complete,
|
||||
.context = {
|
||||
.command_line = line,
|
||||
.out = out,
|
||||
},
|
||||
.complete = {
|
||||
.line = buffer,
|
||||
},
|
||||
};
|
||||
command_process(&pc);
|
||||
gs_command_completer_add_token(&pc.context, NULL, true);
|
||||
if (strlen(buffer) > line_len ) {
|
||||
strcpy(line, buffer);
|
||||
}
|
||||
switch (pc.hits) {
|
||||
case 0:
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
case 1:
|
||||
return GS_OK;
|
||||
default:
|
||||
return GS_ERROR_AMBIGUOUS;
|
||||
}
|
||||
}
|
||||
|
||||
static void command_help_print(const gs_command_t * const cmd, private_context_t * pc)
|
||||
{
|
||||
if (pc->hits == 1) {
|
||||
if (cmd->help) {
|
||||
fprintf(pc->context.out, "%s\r\n", cmd->help);
|
||||
}
|
||||
if (cmd->chain.count == 0) {
|
||||
fprintf(pc->context.out, "usage: %s %s\r\n", cmd->name, cmd->usage ? cmd->usage : "");
|
||||
} else {
|
||||
for (unsigned int i = 0; i < cmd->chain.count; ++i) {
|
||||
const gs_command_t * scmd = &cmd->chain.list[i];
|
||||
|
||||
if (scmd->mode & GS_COMMAND_FLAG_HIDDEN) {
|
||||
continue;
|
||||
}
|
||||
fprintf(pc->context.out, " %-19s %s\r\n", scmd->name, scmd->help ? scmd->help : "");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fprintf(pc->context.out, " %-19s %s\r\n", cmd->name, cmd->help ? cmd->help : "");
|
||||
}
|
||||
}
|
||||
|
||||
static void command_help_hit(const gs_command_t * const cmd, private_context_t * pc)
|
||||
{
|
||||
pc->error = GS_OK;
|
||||
++pc->hits;
|
||||
if (pc->hits == 1) {
|
||||
// single hit so far - hold off printing until we know if we get more
|
||||
pc->cmd = cmd;
|
||||
} else {
|
||||
if (pc->cmd) {
|
||||
command_help_print(pc->cmd, pc);
|
||||
pc->cmd = NULL;
|
||||
}
|
||||
command_help_print(cmd, pc);
|
||||
}
|
||||
}
|
||||
|
||||
static gs_error_t command_help(const gs_command_t * const cmds, int cmd_count, int arg_offset, private_context_t * pc)
|
||||
{
|
||||
for (int i = 0; i < cmd_count; i++) {
|
||||
const gs_command_t * cmd = &cmds[i];
|
||||
|
||||
if (cmd_read_int(&cmd->mode) & GS_COMMAND_FLAG_HIDDEN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pc->requires_exact_match || ((arg_offset+1) < pc->context.argc)) {
|
||||
// must be an exact match
|
||||
if (cmd_strcmp(pc->context.argv[arg_offset], cmd->name) == 0) {
|
||||
const gs_command_t * list = (void*) cmd_read_ptr(&cmd->chain.list);
|
||||
if (list && ((arg_offset+1) < pc->context.argc)) {
|
||||
return command_help(list, cmd_read_int(&cmd->chain.count), arg_offset+1, pc);
|
||||
}
|
||||
command_help_hit(cmd, pc);
|
||||
}
|
||||
|
||||
} else if (cmd_strncmp(pc->context.argv[arg_offset], cmd->name,
|
||||
strlen(pc->context.argv[arg_offset])) == 0) {
|
||||
command_help_hit(cmd, pc);
|
||||
}
|
||||
}
|
||||
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
gs_error_t gs_command_show_help(const char * command, FILE* out)
|
||||
{
|
||||
private_context_t pc = {
|
||||
.process = command_help,
|
||||
.error = GS_ERROR_NOT_FOUND,
|
||||
.context = {
|
||||
.command_line = command,
|
||||
.out = out,
|
||||
}
|
||||
};
|
||||
gs_error_t error = command_process(&pc);
|
||||
if (pc.cmd) {
|
||||
command_help_print(pc.cmd, &pc);
|
||||
error = GS_OK;
|
||||
} else if ((error == GS_ERROR_NOT_FOUND) && pc.hits) {
|
||||
error = GS_OK;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_command_register(const gs_command_t * commands, size_t count)
|
||||
{
|
||||
GS_CHECK_ARG(commands != NULL);
|
||||
GS_CHECK_ARG(count > 0);
|
||||
|
||||
gs_error_t error = GS_OK;
|
||||
|
||||
gs_lock_lock();
|
||||
{
|
||||
// check if command block already installed
|
||||
gs_command_block_t * last_block = NULL;
|
||||
for (gs_command_block_t * block = &g_commands; block; block = block->next) {
|
||||
if (block->commands) {
|
||||
const gs_command_t * cmd = block->commands;
|
||||
// loop through because it may be in the linked blocks
|
||||
for (size_t i = 0; i < block->count; ++i, ++cmd) {
|
||||
if (cmd == commands) {
|
||||
error = GS_ERROR_EXIST;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
last_block = block;
|
||||
}
|
||||
|
||||
if (error == GS_OK) {
|
||||
gs_command_block_t * block = calloc(1, sizeof(*block));
|
||||
if (block) {
|
||||
// Insert command last, so lock isn't needed when accessing commands
|
||||
block->commands = commands;
|
||||
block->count = count;
|
||||
block->next = NULL;
|
||||
last_block->next = block;
|
||||
} else {
|
||||
error = GS_ERROR_ALLOC;
|
||||
}
|
||||
}
|
||||
}
|
||||
gs_lock_unlock();
|
||||
|
||||
return (error != GS_ERROR_EXIST) ? error : GS_OK;
|
||||
}
|
||||
|
||||
size_t gs_command_get_stack_size(void)
|
||||
{
|
||||
return g_stack_size;
|
||||
}
|
||||
|
||||
gs_error_t gs_command_init_no_commands(size_t stack_size)
|
||||
{
|
||||
g_stack_size = stack_size;
|
||||
|
||||
gs_error_t error = gs_lock_init();
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_log_group_register(gs_command_log);
|
||||
|
||||
#if (__linux__ == 0)
|
||||
// look up static linked commands - only embedded (= none linux) systems
|
||||
gs_command_block_t * block = &g_commands;
|
||||
extern volatile unsigned int __command_start __attribute__ ((__weak__));
|
||||
extern volatile unsigned int __command_end __attribute__ ((__weak__));
|
||||
if (&__command_start) {
|
||||
block->count = ((ptrdiff_t)&__command_end - (ptrdiff_t)&__command_start) / sizeof(gs_command_t);
|
||||
block->commands = (gs_command_t *) &__command_start;
|
||||
}
|
||||
#endif
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_command_init(size_t stack_size)
|
||||
{
|
||||
gs_error_t error = gs_command_init_no_commands(stack_size);
|
||||
if (error == GS_OK) {
|
||||
// register default commands
|
||||
gs_command_register_default_commands();
|
||||
gs_log_register_commands();
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
(void)log_ctx;
|
||||
|
||||
timestamp_diff(&t_end, &t_start);
|
||||
if (ret == GS_OK) {
|
||||
log_info_group(gs_command_log, "'%s' returned '%s' ["
|
||||
"t: <%04"PRIu32".%06"PRIu32">, dt: <%01"PRIu32".%06"PRIu32">]",
|
||||
cmd_line, gs_error_string(cmd_ret),
|
||||
t_start.tv_sec, t_start.tv_nsec/1000, t_end.tv_sec, t_end.tv_nsec/1000);
|
||||
} else {
|
||||
log_info_group(gs_command_log, "'%s' could not be run, returned '%s' ["
|
||||
"t: <%04"PRIu32".%06"PRIu32">]",
|
||||
cmd_line, gs_error_string(ret),
|
||||
t_start.tv_sec, t_start.tv_nsec/1000);
|
||||
}
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_command_register_logger(gs_command_log_t log_cb, void *log_ctx)
|
||||
{
|
||||
gs_lock_lock();
|
||||
g_command_logger = log_cb;
|
||||
g_command_logger_ctx = log_ctx;
|
||||
gs_lock_unlock();
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
35
gomspace/libutil/src/gosh/command_local.h
Normal file
35
gomspace/libutil/src/gosh/command_local.h
Normal file
@ -0,0 +1,35 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/gosh/command.h>
|
||||
|
||||
/**
|
||||
Command I/O function - flush stdout.
|
||||
*/
|
||||
gs_error_t gs_command_stdio_flush(gs_command_context_t *ctx);
|
||||
|
||||
/**
|
||||
Command I/O function - wait for a key.
|
||||
*/
|
||||
gs_error_t gs_command_stdio_wait_for_key(gs_command_context_t *ctx, int *ch, int timeout_ms);
|
||||
|
||||
/**
|
||||
Complete command.
|
||||
@param[in] line command line to complete
|
||||
@param[in] max \a length (size)
|
||||
@param[in] out output stream, e.g. stdout
|
||||
*/
|
||||
gs_error_t gs_command_complete(char *line, size_t max_line_length, FILE* out);
|
||||
|
||||
/**
|
||||
Show help.
|
||||
@param line command line to show help for.
|
||||
@param out output stream, e.g. stdout
|
||||
*/
|
||||
gs_error_t gs_command_show_help(const char * command, FILE * out);
|
||||
|
||||
/**
|
||||
Change console mode.
|
||||
@param[in] mode console mode, 'cci'
|
||||
@return_gs_error_t
|
||||
*/
|
||||
int gs_console_change_mode(const char * mode);
|
758
gomspace/libutil/src/gosh/console.c
Normal file
758
gomspace/libutil/src/gosh/console.c
Normal file
@ -0,0 +1,758 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
The console interface provides support for executing commands over stdout (typically a serial port).
|
||||
|
||||
The connection can run in 2 modes:
|
||||
- normal, standard GOSH interface (Human Machine Interface), echo characters, prompt, etc.
|
||||
- cci, Computer Computer Interface. Simple text interface, but with tagged output format - easier to parse by a computer.
|
||||
*/
|
||||
#include "console_local.h"
|
||||
#include "command_local.h"
|
||||
|
||||
#include <gs/util/string.h>
|
||||
#include <gs/util/stdio.h>
|
||||
#include <gs/util/thread.h>
|
||||
#include <gs/util/mutex.h>
|
||||
#include <gs/util/time.h>
|
||||
#include <gs/util/log/appender/console.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <conf_util.h> // console defines set through Waf options
|
||||
|
||||
#if (__linux__)
|
||||
#include <gs/util/linux/signal.h>
|
||||
#include <gs/util/linux/command_line.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
/* Max history length (elements) */
|
||||
#ifndef GS_CONSOLE_HISTORY_LEN
|
||||
#define GS_CONSOLE_HISTORY_LEN 10
|
||||
#endif
|
||||
|
||||
/* Max input length */
|
||||
#ifndef GS_CONSOLE_INPUT_LEN
|
||||
#define GS_CONSOLE_INPUT_LEN 100
|
||||
#endif
|
||||
|
||||
#define CONTROL(X) ((X) - '@')
|
||||
|
||||
typedef enum {
|
||||
CONSOLE_NORMAL = 0,
|
||||
CONSOLE_ESCAPE = 1,
|
||||
CONSOLE_PRE_ESCAPE = 2,
|
||||
} console_escape_t;
|
||||
|
||||
static const char hash_prompt[] = "\033[1;30m # ";
|
||||
|
||||
static const char backspace_char = '\b';
|
||||
static const char space_char = ' ';
|
||||
static const char cr_char = '\r';
|
||||
static const char nl_char = '\n';
|
||||
|
||||
static const char * user_prompt = "gosh";
|
||||
|
||||
static console_escape_t escape = CONSOLE_NORMAL;
|
||||
|
||||
#if (GS_CONSOLE_HISTORY_LEN > 0)
|
||||
static int history_elements;
|
||||
static int history_cur;
|
||||
static int history_browse;
|
||||
static char history[GS_CONSOLE_HISTORY_LEN][GS_CONSOLE_INPUT_LEN+1];
|
||||
#endif
|
||||
|
||||
static int size;
|
||||
static int pos;
|
||||
static char buf[GS_CONSOLE_INPUT_LEN+1];
|
||||
static gs_thread_t console_thread;
|
||||
|
||||
#if (__linux__)
|
||||
static bool termios_changed;
|
||||
static struct termios old_stdin;
|
||||
static struct termios old_stdout;
|
||||
#endif
|
||||
|
||||
static gs_mutex_t g_cci_lock; // Lock for protecting stdout for async output, e.g. log messages
|
||||
static gs_error_t command_io_cci_set_result(gs_command_context_t *ctx, const char *group, const char *key, const char *value);
|
||||
static const gs_command_io_functions_t cci_io_functions = {
|
||||
.set_result = command_io_cci_set_result,
|
||||
.flush = gs_command_stdio_flush,
|
||||
.wait_for_key = gs_command_stdio_wait_for_key,
|
||||
};
|
||||
#define CCI_START_TAG "[X["
|
||||
#define CCI_END_TAG "]X]"
|
||||
|
||||
static void gs_console_write(const char *str, int length)
|
||||
{
|
||||
for (int i = 0; i < length; i++) {
|
||||
putchar(str[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void gs_console_prompt(void)
|
||||
{
|
||||
static const char col_start[] = "\033[1;32m";
|
||||
static const char col_end[] = "\033[0m";
|
||||
|
||||
gs_console_write(col_start, sizeof(col_start) - 1);
|
||||
gs_console_write(user_prompt, strlen(user_prompt));
|
||||
gs_console_write(hash_prompt, sizeof(hash_prompt) - 1);
|
||||
gs_console_write(col_end, sizeof(col_end) - 1);
|
||||
}
|
||||
|
||||
void gs_console_set_prompt(const char * _prompt)
|
||||
{
|
||||
if (gs_string_empty(_prompt) == false) {
|
||||
user_prompt = _prompt;
|
||||
}
|
||||
}
|
||||
|
||||
static void gs_console_reset(void)
|
||||
{
|
||||
pos = size = 0;
|
||||
buf[pos] = 0;
|
||||
gs_console_prompt();
|
||||
}
|
||||
|
||||
static void gs_console_rewind(void)
|
||||
{
|
||||
int plen = strlen(hash_prompt) + strlen(user_prompt);
|
||||
gs_console_write(&cr_char, 1);
|
||||
while (size-- + plen) {
|
||||
gs_console_write(&space_char, 1);
|
||||
}
|
||||
pos = size = 0;
|
||||
gs_console_write(&cr_char, 1);
|
||||
}
|
||||
|
||||
void gs_console_clear(void)
|
||||
{
|
||||
static const char clear[] = "\033[H\033[2J";
|
||||
gs_console_write(clear, sizeof(clear) - 1);
|
||||
gs_console_rewind();
|
||||
gs_console_reset();
|
||||
}
|
||||
|
||||
void gs_console_update(void)
|
||||
{
|
||||
gs_console_rewind();
|
||||
gs_console_prompt();
|
||||
pos = size = strlen(buf);
|
||||
gs_console_write(buf, size);
|
||||
}
|
||||
|
||||
#if (GS_CONSOLE_HISTORY_LEN > 0)
|
||||
|
||||
static void gs_console_history_add(void)
|
||||
{
|
||||
strncpy(history[history_cur], buf, GS_CONSOLE_INPUT_LEN);
|
||||
history[history_cur][GS_CONSOLE_INPUT_LEN] = 0;
|
||||
|
||||
history_browse = 0;
|
||||
history_cur = (history_cur + 1) % GS_CONSOLE_HISTORY_LEN;
|
||||
|
||||
if (history_elements < GS_CONSOLE_HISTORY_LEN) {
|
||||
history_elements++;
|
||||
}
|
||||
}
|
||||
|
||||
static void gs_console_last_line(void)
|
||||
{
|
||||
if (history_elements < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (history_browse >= history_elements) {
|
||||
return;
|
||||
}
|
||||
|
||||
gs_console_rewind();
|
||||
history_browse++;
|
||||
strcpy(buf, history[(history_cur - history_browse + GS_CONSOLE_HISTORY_LEN) % GS_CONSOLE_HISTORY_LEN]);
|
||||
gs_console_update();
|
||||
}
|
||||
|
||||
static void gs_console_next_line(void)
|
||||
{
|
||||
if (history_elements < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (history_browse < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
gs_console_rewind();
|
||||
history_browse--;
|
||||
if (history_browse > 0) {
|
||||
strcpy(buf, history[(history_cur - history_browse + GS_CONSOLE_HISTORY_LEN) % GS_CONSOLE_HISTORY_LEN]);
|
||||
} else {
|
||||
buf[0] = '\0';
|
||||
}
|
||||
gs_console_update();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void gs_console_forward_char(void)
|
||||
{
|
||||
if (pos < size) {
|
||||
gs_console_write(&buf[pos], 1);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
static void gs_console_end_of_line(void)
|
||||
{
|
||||
while (pos < size) {
|
||||
gs_console_forward_char();
|
||||
}
|
||||
}
|
||||
|
||||
static void gs_console_backward_char(void)
|
||||
{
|
||||
if (pos > 0) {
|
||||
pos--;
|
||||
gs_console_write(&backspace_char, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void gs_console_beginning_of_line(void)
|
||||
{
|
||||
while (pos) {
|
||||
gs_console_backward_char();
|
||||
}
|
||||
}
|
||||
|
||||
static void gs_console_newline(void)
|
||||
{
|
||||
gs_console_write(&cr_char, 1);
|
||||
gs_console_write(&nl_char, 1);
|
||||
}
|
||||
|
||||
static bool gs_command_not_empty(const char *ibuf)
|
||||
{
|
||||
while (*ibuf) {
|
||||
if (!isblank((int) *ibuf++)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void show_help(const char * command)
|
||||
{
|
||||
gs_error_t error = gs_command_show_help(command, stdout);
|
||||
if (error) {
|
||||
printf("Could not show help for \'%s\': %s (%d)\r\n", command, gs_error_string(error), error);
|
||||
}
|
||||
}
|
||||
|
||||
static void gs_console_execute(void)
|
||||
{
|
||||
gs_console_newline();
|
||||
buf[GS_CONSOLE_INPUT_LEN] = 0; // ensure 0 termination
|
||||
if (size > 0 && gs_command_not_empty(buf)) {
|
||||
#if (GS_CONSOLE_HISTORY_LEN > 0)
|
||||
gs_console_history_add();
|
||||
#endif
|
||||
gs_error_t result = GS_OK;
|
||||
gs_error_t error = gs_command_execute_stdio(buf, &result);
|
||||
if (error == GS_ERROR_TYPE) {
|
||||
show_help(buf);
|
||||
} else if (error == GS_ERROR_NOT_FOUND) {
|
||||
printf("Unknown command \'%s\'\r\n", buf);
|
||||
} else if (error == GS_ERROR_ARG) {
|
||||
show_help(buf);
|
||||
} else if (error) {
|
||||
printf("Command \'%s\' did not execute: %s (%d)\r\n", buf, gs_error_string(error), error);
|
||||
} else if (result == GS_ERROR_ARG) {
|
||||
show_help(buf);
|
||||
} else if (result) {
|
||||
printf("Command \'%s\' executed, but returned error: %s (%d)\r\n", buf, gs_error_string(result), result);
|
||||
}
|
||||
}
|
||||
gs_console_reset();
|
||||
}
|
||||
|
||||
static void gs_console_complete(void)
|
||||
{
|
||||
/* We don't expand in the middle of a line */
|
||||
if (size != pos) {
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t old_buf_len = strlen(buf);
|
||||
gs_error_t ret = gs_command_complete(buf, sizeof(buf), stdout);
|
||||
if ((ret == GS_OK) && (old_buf_len == strlen(buf))) {
|
||||
// completed (again) and no change - show help
|
||||
ret = GS_ERROR_AMBIGUOUS;
|
||||
}
|
||||
switch (ret) {
|
||||
case GS_ERROR_AMBIGUOUS:
|
||||
gs_console_newline();
|
||||
show_help(buf);
|
||||
gs_console_update();
|
||||
break;
|
||||
case GS_OK:
|
||||
gs_console_update();
|
||||
break;
|
||||
default:
|
||||
case GS_ERROR_NOT_FOUND:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void gs_console_insert(char c)
|
||||
{
|
||||
int i;
|
||||
int diff = size - pos;
|
||||
|
||||
if (size >= GS_CONSOLE_INPUT_LEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
memmove(&buf[pos + 1], &buf[pos], diff);
|
||||
buf[pos] = c;
|
||||
|
||||
gs_console_write(&buf[pos], diff + 1);
|
||||
for (i = 0; i < diff; i++) {
|
||||
gs_console_write(&backspace_char, 1);
|
||||
}
|
||||
|
||||
size++;
|
||||
pos++;
|
||||
buf[size] = '\0';
|
||||
}
|
||||
|
||||
static void gs_console_insert_overwrite(char c)
|
||||
{
|
||||
buf[pos++] = c;
|
||||
|
||||
if (pos > size) {
|
||||
size++;
|
||||
}
|
||||
|
||||
gs_console_write(&c, 1);
|
||||
}
|
||||
|
||||
static void gs_console_delete(void)
|
||||
{
|
||||
int i;
|
||||
int diff = size - pos;
|
||||
|
||||
/* Nothing to delete */
|
||||
if (size == pos) {
|
||||
return;
|
||||
}
|
||||
|
||||
size--;
|
||||
memmove(&buf[pos], &buf[pos + 1], diff - 1);
|
||||
buf[size] = '\0';
|
||||
|
||||
gs_console_write(&buf[pos], diff - 1);
|
||||
gs_console_write(&space_char, 1);
|
||||
for (i = 0; i < diff; i++) {
|
||||
gs_console_write(&backspace_char, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void gs_console_backspace(void)
|
||||
{
|
||||
if (pos < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
gs_console_backward_char();
|
||||
gs_console_delete();
|
||||
}
|
||||
|
||||
static void gs_console_kill_line(void)
|
||||
{
|
||||
int i;
|
||||
int diff;
|
||||
|
||||
diff = size - pos;
|
||||
|
||||
if (diff == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < diff; i++) {
|
||||
gs_console_write(&space_char, 1);
|
||||
}
|
||||
for (i = 0; i < diff; i++) {
|
||||
gs_console_write(&backspace_char, 1);
|
||||
}
|
||||
|
||||
memset(&buf[pos], 0, diff);
|
||||
size = pos;
|
||||
}
|
||||
|
||||
static void gs_console_kill_line_from_beginning(void)
|
||||
{
|
||||
gs_console_beginning_of_line();
|
||||
gs_console_kill_line();
|
||||
}
|
||||
|
||||
static void gs_console_backward_kill_word(void)
|
||||
{
|
||||
while (pos > 0 && buf[pos - 1] == ' ') {
|
||||
gs_console_backspace();
|
||||
}
|
||||
while (pos > 0 && buf[pos - 1] != ' ') {
|
||||
gs_console_backspace();
|
||||
}
|
||||
}
|
||||
|
||||
static void gs_console_transpose_chars(void)
|
||||
{
|
||||
char c1, c2;
|
||||
|
||||
if (size < 2 || pos < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pos == size) {
|
||||
c1 = buf[pos - 1];
|
||||
c2 = buf[pos - 2];
|
||||
|
||||
gs_console_backward_char();
|
||||
gs_console_backward_char();
|
||||
gs_console_insert_overwrite(c1);
|
||||
gs_console_insert_overwrite(c2);
|
||||
} else {
|
||||
c1 = buf[pos];
|
||||
c2 = buf[pos - 1];
|
||||
|
||||
gs_console_backward_char();
|
||||
gs_console_insert_overwrite(c1);
|
||||
gs_console_insert_overwrite(c2);
|
||||
}
|
||||
}
|
||||
|
||||
static void gs_console_normal(char c)
|
||||
{
|
||||
switch (c) {
|
||||
case CONTROL('A'):
|
||||
gs_console_beginning_of_line();
|
||||
break;
|
||||
case CONTROL('B'):
|
||||
gs_console_backward_char();
|
||||
break;
|
||||
case CONTROL('C'):
|
||||
// Either ignored or handled through signals
|
||||
break;
|
||||
case CONTROL('D'):
|
||||
gs_console_delete();
|
||||
break;
|
||||
case CONTROL('E'):
|
||||
gs_console_end_of_line();
|
||||
break;
|
||||
case CONTROL('F'):
|
||||
gs_console_forward_char();
|
||||
break;
|
||||
case CONTROL('K'):
|
||||
gs_console_kill_line();
|
||||
break;
|
||||
case CONTROL('L'):
|
||||
gs_console_clear();
|
||||
break;
|
||||
#if (GS_CONSOLE_HISTORY_LEN > 0)
|
||||
case CONTROL('N'):
|
||||
gs_console_next_line();
|
||||
break;
|
||||
case CONTROL('P'):
|
||||
gs_console_last_line();
|
||||
break;
|
||||
#endif
|
||||
case CONTROL('T'):
|
||||
gs_console_transpose_chars();
|
||||
break;
|
||||
case CONTROL('U'):
|
||||
gs_console_kill_line_from_beginning();
|
||||
break;
|
||||
case CONTROL('W'):
|
||||
gs_console_backward_kill_word();
|
||||
break;
|
||||
case CONTROL('Z'):
|
||||
// We cannot suspend
|
||||
break;
|
||||
case CONTROL('H'):
|
||||
case 0x7f:
|
||||
gs_console_backspace();
|
||||
break;
|
||||
case '\r':
|
||||
case '\n':
|
||||
gs_console_execute();
|
||||
break;
|
||||
case '\t':
|
||||
gs_console_complete();
|
||||
break;
|
||||
case '\033':
|
||||
escape = CONSOLE_ESCAPE;
|
||||
break;
|
||||
default:
|
||||
if (escape == CONSOLE_ESCAPE) {
|
||||
if ((c == '[') || (c == 'O')) {
|
||||
c = getchar();
|
||||
if (c == 'F')
|
||||
gs_console_end_of_line();
|
||||
if (c == 'H')
|
||||
gs_console_beginning_of_line();
|
||||
#if (GS_CONSOLE_HISTORY_LEN > 0)
|
||||
if (c == 'A')
|
||||
gs_console_last_line();
|
||||
if (c == 'B')
|
||||
gs_console_next_line();
|
||||
#endif
|
||||
if (c == 'C')
|
||||
gs_console_forward_char();
|
||||
if (c == 'D')
|
||||
gs_console_backward_char();
|
||||
if (c == '1')
|
||||
if (getchar() == '~')
|
||||
gs_console_beginning_of_line();
|
||||
if (c == '3')
|
||||
if (getchar() == '~')
|
||||
gs_console_delete();
|
||||
}
|
||||
escape = CONSOLE_NORMAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isprint((unsigned char) c)) {
|
||||
gs_console_insert(c);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gs_error_t command_io_cci_set_result(gs_command_context_t *ctx, const char *group, const char *key, const char *value)
|
||||
{
|
||||
gs_mutex_lock(g_cci_lock);
|
||||
{
|
||||
printf(CCI_START_TAG "cmd_res,%s,%s,%s" CCI_END_TAG, group, key, value);
|
||||
}
|
||||
gs_mutex_unlock(g_cci_lock);
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static void gs_console_cci_log(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)
|
||||
{
|
||||
va_list my_va;
|
||||
va_copy(my_va, va);
|
||||
|
||||
gs_mutex_lock(g_cci_lock);
|
||||
{
|
||||
printf(CCI_START_TAG "log,%04"PRIu32".%06"PRIu32",%c,%s,", ts->tv_sec, ts->tv_nsec / 1000, gs_log_level_to_char(level), group->name);
|
||||
vprintf(format, my_va);
|
||||
printf(CCI_END_TAG "\r\n");
|
||||
}
|
||||
gs_mutex_unlock(g_cci_lock);
|
||||
|
||||
va_end(my_va);
|
||||
}
|
||||
|
||||
static void gs_console_cci(char c)
|
||||
{
|
||||
switch (c) {
|
||||
case CONTROL('C'):
|
||||
case CONTROL('L'):
|
||||
size = 0;
|
||||
buf[0] = 0;
|
||||
break;
|
||||
case '\r':
|
||||
case '\n':
|
||||
buf[GS_CONSOLE_INPUT_LEN] = 0; // ensure 0 termination
|
||||
if (size > 0 && gs_command_not_empty(buf)) {
|
||||
static unsigned int seq; // simple sequence number keep incrementing
|
||||
|
||||
gs_mutex_lock(g_cci_lock);
|
||||
++seq;
|
||||
printf(CCI_START_TAG "cmd_exec_begin,%u,%s" CCI_END_TAG "\r\n", seq, buf);
|
||||
gs_mutex_unlock(g_cci_lock);
|
||||
|
||||
gs_error_t result = GS_OK;
|
||||
gs_error_t error = gs_command_execute(buf, &result, stdout, &cci_io_functions, NULL);
|
||||
|
||||
gs_mutex_lock(g_cci_lock);
|
||||
printf(CCI_START_TAG "cmd_exec_end,%u,%d,%d" CCI_END_TAG "\r\n", seq, error, result);
|
||||
gs_mutex_unlock(g_cci_lock);
|
||||
}
|
||||
size = 0;
|
||||
buf[0] = 0;
|
||||
break;
|
||||
default:
|
||||
if (isprint((unsigned char) c) && (size < GS_CONSOLE_INPUT_LEN)) {
|
||||
buf[size++] = c;
|
||||
buf[size] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Currrent mode handler, switch by sending command
|
||||
static void (*console_handler)(char c) = gs_console_normal;
|
||||
|
||||
int gs_console_change_mode(const char * mode)
|
||||
{
|
||||
if (strcasecmp(mode, "cci") == 0) {
|
||||
gs_error_t error = GS_OK;
|
||||
if (console_handler != gs_console_cci) {
|
||||
error = gs_mutex_create(&g_cci_lock);
|
||||
if (error == GS_OK) {
|
||||
gs_log_appender_console_set_cb(gs_console_cci_log);
|
||||
console_handler = gs_console_cci; // change console handler
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
return GS_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static void * gs_console_thread(void * param)
|
||||
{
|
||||
gs_console_reset();
|
||||
while (1) {
|
||||
char c = getchar();
|
||||
console_handler(c);
|
||||
}
|
||||
|
||||
gs_thread_exit(NULL);
|
||||
}
|
||||
|
||||
gs_error_t gs_console_exit(void)
|
||||
{
|
||||
#if (__linux__)
|
||||
if (termios_changed) {
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &old_stdin);
|
||||
tcsetattr(STDOUT_FILENO, TCSANOW, &old_stdout);
|
||||
}
|
||||
#endif
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
#if (__linux__)
|
||||
static inline void exithandler(void)
|
||||
{
|
||||
printf("\n");
|
||||
gs_console_exit();
|
||||
}
|
||||
#endif
|
||||
|
||||
static gs_error_t gs_console_init2(uint32_t flags)
|
||||
{
|
||||
#if (__linux__)
|
||||
// save current stdio setting, for restoring when terminating process
|
||||
tcgetattr(STDIN_FILENO, &old_stdin);
|
||||
tcgetattr(STDOUT_FILENO, &old_stdout);
|
||||
|
||||
// change stdin settings
|
||||
{
|
||||
struct termios new = old_stdin;
|
||||
new.c_iflag &= ~(IGNCR | ICRNL);
|
||||
new.c_lflag &= ~(ECHO | ICANON | IEXTEN);
|
||||
new.c_cc[VTIME]=0;
|
||||
new.c_cc[VMIN]=1;
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &new);
|
||||
}
|
||||
// change stdout settings
|
||||
{
|
||||
struct termios new = old_stdout;
|
||||
new.c_iflag &= ~(IGNCR | ICRNL);
|
||||
new.c_lflag &= ~(ECHO | ICANON | IEXTEN);
|
||||
new.c_cc[VTIME]=0;
|
||||
new.c_cc[VMIN]=1;
|
||||
tcsetattr(STDOUT_FILENO, TCSANOW, &new);
|
||||
}
|
||||
|
||||
termios_changed = true;
|
||||
|
||||
// add exit-handler to restore original termianl settings
|
||||
atexit(exithandler);
|
||||
|
||||
// install signal handlers to ensure terminal settings are restored
|
||||
if ((flags & GS_CONSOLE_F_NO_SIGNAL_HANDLER) == 0) {
|
||||
// install signal handler(s) to ensure atexit() is called
|
||||
gs_signal_catch(SIGTERM, NULL);
|
||||
|
||||
if (gs_command_line_ignore_ctrlc() == false) {
|
||||
gs_signal_catch(SIGINT, NULL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (__AVR__ == 0)
|
||||
/** This is very important on AVR32 */
|
||||
setvbuf(stdin, NULL, _IONBF, 0);
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
#endif
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_console_init()
|
||||
{
|
||||
return gs_console_init2(0);
|
||||
}
|
||||
|
||||
static gs_error_t _console_create_thread(gs_thread_priority_t priority, gs_thread_t * handle, uint32_t thread_create_flags)
|
||||
{
|
||||
gs_error_t error = gs_thread_create("CONSOLE",
|
||||
gs_console_thread, NULL,
|
||||
gs_command_get_stack_size(),
|
||||
priority,
|
||||
thread_create_flags,
|
||||
handle);
|
||||
if (error == GS_OK) {
|
||||
// give thread a few moments to print prompt
|
||||
gs_time_sleep_ms(20);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_error_t gs_console_create_thread(gs_thread_t * handle)
|
||||
{
|
||||
return _console_create_thread(GS_THREAD_PRIORITY_LOW, handle, 0);
|
||||
}
|
||||
|
||||
gs_error_t gs_console_create_thread_with_priority(gs_thread_priority_t priority, gs_thread_t * handle)
|
||||
{
|
||||
return _console_create_thread(priority, handle, 0);
|
||||
}
|
||||
|
||||
gs_error_t gs_console_start(const char * prompt, uint32_t flags)
|
||||
{
|
||||
if (console_thread) {
|
||||
return GS_ERROR_EXIST;
|
||||
}
|
||||
|
||||
gs_console_init2(flags);
|
||||
gs_console_set_prompt(prompt);
|
||||
|
||||
return _console_create_thread(GS_THREAD_PRIORITY_LOW, &console_thread, GS_THREAD_CREATE_JOINABLE);
|
||||
}
|
||||
|
||||
gs_error_t gs_console_stop(void)
|
||||
{
|
||||
if (console_thread == 0) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
#if (__linux__)
|
||||
if (pthread_cancel(console_thread) != 0) {
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
gs_error_t error = gs_thread_join(console_thread, NULL);
|
||||
if (error == GS_OK) {
|
||||
console_thread = 0;
|
||||
}
|
||||
return error;
|
||||
#else
|
||||
return GS_ERROR_NOT_SUPPORTED;
|
||||
#endif
|
||||
}
|
10
gomspace/libutil/src/gosh/console_local.h
Normal file
10
gomspace/libutil/src/gosh/console_local.h
Normal file
@ -0,0 +1,10 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/gosh/console.h>
|
||||
|
||||
/**
|
||||
Change console mode.
|
||||
@param[in] mode console mode, 'rgosh', 'normal'
|
||||
@return_gs_error_t
|
||||
*/
|
||||
int gs_console_change_mode(const char * mode);
|
277
gomspace/libutil/src/gosh/default_commands.c
Normal file
277
gomspace/libutil/src/gosh/default_commands.c
Normal file
@ -0,0 +1,277 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include "command_local.h"
|
||||
#include "console_local.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
#include <gs/util/stdio.h>
|
||||
#include <gs/util/clock.h>
|
||||
#include <gs/util/time.h>
|
||||
#include <gs/util/string.h>
|
||||
#include <conf_util.h>
|
||||
|
||||
static int cmd_help(gs_command_context_t * context)
|
||||
{
|
||||
return gs_command_show_help(gs_command_args(context), context->out);
|
||||
}
|
||||
|
||||
static int cmd_sleep(gs_command_context_t * context)
|
||||
{
|
||||
uint32_t sleep_ms;
|
||||
gs_error_t error = gs_string_to_uint32(context->argv[1], &sleep_ms);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
gs_time_sleep_ms(sleep_ms);
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static int cmd_watch(gs_command_context_t * context, bool check_error)
|
||||
{
|
||||
uint32_t sleep_ms;
|
||||
gs_error_t error = gs_string_to_uint32(context->argv[1], &sleep_ms);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
fprintf(context->out, "Execution delay: %" PRIu32 "\r\n", sleep_ms);
|
||||
|
||||
char * new_command = strstr(gs_command_args(context), " ");
|
||||
if (new_command == NULL) {
|
||||
return GS_ERROR_ARG;
|
||||
} else {
|
||||
new_command = new_command + 1;
|
||||
}
|
||||
|
||||
fprintf(context->out, "Command: %s\r\n", new_command);
|
||||
|
||||
while(1) {
|
||||
|
||||
gs_error_t cmd_result;
|
||||
error = gs_command_execute(new_command, &cmd_result, context->out, context->io_functions, context->io_ctx);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
if (check_error && cmd_result) {
|
||||
return cmd_result;
|
||||
}
|
||||
|
||||
if (gs_stdio_getchar_timed(sleep_ms, NULL) != GS_ERROR_TIMEOUT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static int cmd_watch_nocheck(gs_command_context_t * context)
|
||||
{
|
||||
return cmd_watch(context, false);
|
||||
}
|
||||
|
||||
static int cmd_watch_check(gs_command_context_t * context)
|
||||
{
|
||||
return cmd_watch(context, true);
|
||||
}
|
||||
|
||||
#define CONTROL(X) ((X) - '@')
|
||||
|
||||
static int cmd_batch(gs_command_context_t * ctx)
|
||||
{
|
||||
char c;
|
||||
int quit = 0, execute = 0;
|
||||
unsigned int batch_size = 100;
|
||||
unsigned int batch_input = 0;
|
||||
unsigned int batch_count = 0;
|
||||
char * batch[20] = {};
|
||||
printf("Type each command followed by enter, hit ctrl+e to end typing, ctrl+x to cancel:\r\n");
|
||||
|
||||
/* Wait for ^q to quit. */
|
||||
while (quit == 0) {
|
||||
|
||||
/* Get character */
|
||||
c = getchar();
|
||||
|
||||
switch (c) {
|
||||
|
||||
/* CTRL + X */
|
||||
case 0x18:
|
||||
quit = 1;
|
||||
break;
|
||||
|
||||
/* CTRL + E */
|
||||
case 0x05:
|
||||
execute = 1;
|
||||
quit = 1;
|
||||
break;
|
||||
|
||||
/* Backspace */
|
||||
case CONTROL('H'):
|
||||
case 0x7f:
|
||||
if (batch_input > 0) {
|
||||
putchar('\b');
|
||||
putchar(' ');
|
||||
putchar('\b');
|
||||
batch_input--;
|
||||
}
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
putchar('\r');
|
||||
putchar('\n');
|
||||
if ((batch[batch_count] != NULL) && (batch_input < batch_size))
|
||||
batch[batch_count][batch_input++] = '\r';
|
||||
if ((batch[batch_count] != NULL) && (batch_input < batch_size))
|
||||
batch[batch_count][batch_input++] = '\0';
|
||||
batch_count++;
|
||||
batch_input = 0;
|
||||
if (batch_count == 20)
|
||||
quit = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
putchar(c);
|
||||
if (batch[batch_count] == NULL) {
|
||||
batch[batch_count] = calloc(GS_CONSOLE_INPUT_LEN+1, 1);
|
||||
}
|
||||
|
||||
if ((batch[batch_count] != NULL) && (batch_input < batch_size))
|
||||
batch[batch_count][batch_input++] = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (execute) {
|
||||
printf("\r\n");
|
||||
for (unsigned int i = 0; i <= batch_count; i++) {
|
||||
if (batch[i])
|
||||
printf("[%02u] %s\r\n", i, batch[i]);
|
||||
}
|
||||
printf("Press ctrl+e to execute, or any key to abort\r\n");
|
||||
c = getchar();
|
||||
if (c != 0x05)
|
||||
execute = 0;
|
||||
}
|
||||
|
||||
/* Run/Free batch job */
|
||||
for (unsigned int i = 0; i <= batch_count; i++) {
|
||||
if (execute && batch[i]) {
|
||||
printf("EXEC [%02u] %s\r\n", i, batch[i]);
|
||||
gs_command_run(batch[i], NULL);
|
||||
}
|
||||
free(batch[i]);
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
static int cmd_exit(gs_command_context_t * context)
|
||||
{
|
||||
gs_console_exit();
|
||||
exit(EXIT_SUCCESS);
|
||||
return GS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int cmd_clock(gs_command_context_t * ctx)
|
||||
{
|
||||
if (ctx->argc > 1) {
|
||||
gs_timestamp_t ts;
|
||||
gs_error_t error = gs_clock_from_string(ctx->argv[1], &ts);
|
||||
if (error) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
error = gs_clock_set_time(&ts);
|
||||
if (error) {
|
||||
fprintf(ctx->out, "Failed to set time, error=%s\r\n", gs_error_string(error));
|
||||
return GS_ERROR_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
timestamp_t clock;
|
||||
gs_clock_get_monotonic(&clock);
|
||||
fprintf(ctx->out, "monotonic: %10"PRIu32".%09"PRIu32" sec\r\n", clock.tv_sec, clock.tv_nsec);
|
||||
gs_command_set_output_printf(ctx, "", "monotonic", "%10"PRIu32".%09"PRIu32"", clock.tv_sec, clock.tv_nsec);
|
||||
|
||||
char tbuf[25];
|
||||
gs_clock_get_time(&clock);
|
||||
gs_clock_to_iso8601_string(&clock, tbuf, sizeof(tbuf));
|
||||
fprintf(ctx->out, "realtime: %10"PRIu32".%09"PRIu32" sec -> %s\r\n", clock.tv_sec, clock.tv_nsec, tbuf);
|
||||
gs_command_set_output_printf(ctx, "", "realtime", "%10"PRIu32".%09"PRIu32"", clock.tv_sec, clock.tv_nsec);
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static int cmd_console_mode(gs_command_context_t * ctx)
|
||||
{
|
||||
return gs_console_change_mode(ctx->argv[1]);
|
||||
}
|
||||
|
||||
static const gs_command_t GS_COMMAND_ROOT cmd_default[] = {
|
||||
{
|
||||
.name = "help",
|
||||
.help = "Show help",
|
||||
.usage = "[command[ subcommand[ arg ...]]]",
|
||||
.handler = cmd_help,
|
||||
.optional_args = 100,
|
||||
},{
|
||||
.name = "sleep",
|
||||
.help = "Sleep X ms",
|
||||
.usage = "<mS>",
|
||||
.handler = cmd_sleep,
|
||||
.mandatory_args = 1,
|
||||
},{
|
||||
.name = "watch",
|
||||
.help = "Run commands at intervals (abort with key)",
|
||||
.usage = "<interval mS> <command> [arg ...]",
|
||||
.handler = cmd_watch_nocheck,
|
||||
.mandatory_args = 2,
|
||||
.optional_args = 100,
|
||||
},{
|
||||
.name = "watch_check",
|
||||
.help = "Like 'watch', but abort if command fails",
|
||||
.usage = "<interval mS> <command [arg ...]>",
|
||||
.handler = cmd_watch_check,
|
||||
.mandatory_args = 2,
|
||||
.optional_args = 100,
|
||||
},{
|
||||
.name = "batch",
|
||||
.help = "Run multiple commands",
|
||||
.handler = cmd_batch,
|
||||
.mode = GS_COMMAND_FLAG_HIDDEN,
|
||||
},{
|
||||
.name = "clock",
|
||||
.help = "Get/set system clock",
|
||||
.usage = "[<sec.nsec> | <YYYY-MM-DDTHH:MM:SSZ>]",
|
||||
.handler = cmd_clock,
|
||||
.optional_args = 1,
|
||||
},{
|
||||
.name = "console_mode",
|
||||
.help = "Console mode(s): cci",
|
||||
.usage = "<mode>",
|
||||
.handler = cmd_console_mode,
|
||||
.mode = GS_COMMAND_FLAG_HIDDEN,
|
||||
.mandatory_args = 1,
|
||||
},
|
||||
#if defined(__linux__)
|
||||
{
|
||||
.name = "exit",
|
||||
.help = "Exit program",
|
||||
.handler = cmd_exit,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
gs_error_t gs_command_register_default_commands(void)
|
||||
{
|
||||
return GS_COMMAND_REGISTER(cmd_default);
|
||||
}
|
55
gomspace/libutil/src/gosh/getopt.c
Normal file
55
gomspace/libutil/src/gosh/getopt.c
Normal file
@ -0,0 +1,55 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/gosh/command.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int gs_command_getopt(gs_command_context_t *ctx, const char *opts)
|
||||
{
|
||||
int c;
|
||||
char *cp;
|
||||
|
||||
if (ctx->optsp == 1) {
|
||||
if (ctx->optind >= ctx->argc ||
|
||||
ctx->argv[ctx->optind][0] != '-' ||
|
||||
ctx->argv[ctx->optind][1] == '\0') {
|
||||
return EOF;
|
||||
} else if (!strcmp(ctx->argv[ctx->optind], "--")) {
|
||||
ctx->optind++;
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->optopt = c = ctx->argv[ctx->optind][ctx->optsp];
|
||||
if (c == ':' || (cp = strchr(opts, c)) == NULL) {
|
||||
printf("illegal option -- %c\r\n", c);
|
||||
if (ctx->argv[ctx->optind][++ctx->optsp] == '\0') {
|
||||
ctx->optind++;
|
||||
ctx->optsp = 1;
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
|
||||
if (*++cp == ':') {
|
||||
if (ctx->argv[ctx->optind][ctx->optsp+1] != '\0') {
|
||||
ctx->optarg = &ctx->argv[ctx->optind++][ctx->optsp+1];
|
||||
} else if(++ctx->optind >= ctx->argc) {
|
||||
printf("option requires an argument -- %c\r\n", c);
|
||||
ctx->optsp = 1;
|
||||
return '?';
|
||||
} else {
|
||||
ctx->optarg = ctx->argv[ctx->optind++];
|
||||
}
|
||||
ctx->optsp = 1;
|
||||
} else {
|
||||
if (ctx->argv[ctx->optind][++ctx->optsp] == '\0') {
|
||||
ctx->optsp = 1;
|
||||
ctx->optind++;
|
||||
}
|
||||
ctx->optarg = NULL;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
92
gomspace/libutil/src/hexdump.c
Normal file
92
gomspace/libutil/src/hexdump.c
Normal file
@ -0,0 +1,92 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/hexdump.h>
|
||||
#include <gs/util/string.h>
|
||||
#include <gs/util/stdio.h>
|
||||
#include <gs/util/log.h>
|
||||
|
||||
static void print_disp_addr02(FILE * out, uintptr_t disp_addr)
|
||||
{
|
||||
fprintf(out, "0x%02"PRIx32" : ", (uint32_t) disp_addr);
|
||||
}
|
||||
static void print_disp_addr04(FILE * out, uintptr_t disp_addr)
|
||||
{
|
||||
fprintf(out, "0x%04"PRIx32" : ", (uint32_t) disp_addr);
|
||||
}
|
||||
static void print_disp_addrxx(FILE * out, uintptr_t disp_addr)
|
||||
{
|
||||
#if defined(PRIx64)
|
||||
fprintf(out, "0x%08"PRIx64" : ", (uint64_t) disp_addr);
|
||||
#else
|
||||
fprintf(out, "0x%08"PRIx32" : ", (uint32_t) disp_addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void gs_hexdump_to_stream(const void * in_src, size_t len, const void * in_disp_addr, FILE* out)
|
||||
{
|
||||
volatile const uint8_t * src = in_src;
|
||||
uintptr_t disp_addr = GS_TYPES_PTR2UINT(in_disp_addr);
|
||||
const uintptr_t end_disp_addr = disp_addr + len;
|
||||
|
||||
// work-rounds for not printing NIL (if address 0), align addresses, not supporting %zx, %*x or %08p on all platforms
|
||||
void (*print_addr)(FILE * out, uintptr_t disp_addr);
|
||||
if (end_disp_addr <= 0xff) {
|
||||
print_addr = print_disp_addr02;
|
||||
} else if (end_disp_addr <= 0xffff) {
|
||||
print_addr = print_disp_addr04;
|
||||
} else {
|
||||
print_addr = print_disp_addrxx;
|
||||
}
|
||||
|
||||
print_addr(out, disp_addr);
|
||||
|
||||
size_t i = 0;
|
||||
size_t j = 0;
|
||||
size_t k = 0;
|
||||
char text[17];
|
||||
for(; i < len; ++i) {
|
||||
const uint8_t ch = *src++;
|
||||
++disp_addr;
|
||||
|
||||
// hex
|
||||
fprintf(out, "%02x ", ch);
|
||||
++j;
|
||||
if (j == 8) {
|
||||
fprintf(out, " ");
|
||||
}
|
||||
|
||||
// printable
|
||||
if ((ch < 32) || (ch > 126)) {
|
||||
text[k] = '.';
|
||||
} else {
|
||||
text[k] = (char) ch;
|
||||
}
|
||||
++k;
|
||||
text[k] = 0;
|
||||
|
||||
// newline?
|
||||
if(j >= 16) {
|
||||
fprintf(out, "|%-16.16s|\r\n", text);
|
||||
j = 0;
|
||||
k = 0;
|
||||
text[k] = 0;
|
||||
|
||||
if (i < (len - 1)) {
|
||||
print_addr(out, disp_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((i == 0) || (i % 16)) {
|
||||
if (j) {
|
||||
// something was printed - show textual
|
||||
for (; j < 16; j++) {
|
||||
if (j == 7) {
|
||||
fprintf(out, " ");
|
||||
}
|
||||
fprintf(out, " ");
|
||||
}
|
||||
fprintf(out, "|%-16.16s|", text);
|
||||
}
|
||||
fprintf(out, "\r\n");
|
||||
}
|
||||
}
|
34
gomspace/libutil/src/linux/argp.c
Normal file
34
gomspace/libutil/src/linux/argp.c
Normal file
@ -0,0 +1,34 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/linux/argp.h>
|
||||
#include <gs/util/linux/exitcode.h>
|
||||
#include <gs/util/string.h>
|
||||
|
||||
void gs_argp_parse(const struct argp * argp,
|
||||
int argc, char ** argv,
|
||||
unsigned int flags, int * return_arg_index,
|
||||
const char * revision)
|
||||
{
|
||||
if (gs_string_empty(revision) == false) {
|
||||
argp_program_version = revision;
|
||||
}
|
||||
|
||||
int arg_index = 0;
|
||||
int res = argp_parse(argp, argc, argv, 0, &arg_index, 0);
|
||||
if (res) {
|
||||
printf("Failed to parse argument/option (result: %d)\n", res);
|
||||
exit(GS_EXITCODE_USAGE);
|
||||
}
|
||||
|
||||
if ((return_arg_index == NULL) && (arg_index < argc)) {
|
||||
// application doesn't expect unhandled arguments
|
||||
for (int i = arg_index; i < argc; ++i) {
|
||||
printf("Unhandled/unknown argument: [%s]\n", argv[i]);
|
||||
}
|
||||
exit(GS_EXITCODE_USAGE);
|
||||
}
|
||||
|
||||
if (return_arg_index) {
|
||||
*return_arg_index = arg_index;
|
||||
}
|
||||
}
|
68
gomspace/libutil/src/linux/clock.c
Normal file
68
gomspace/libutil/src/linux/clock.c
Normal file
@ -0,0 +1,68 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/clock.h>
|
||||
#include <gs/util/rtc.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
void gs_clock_get_time(gs_timestamp_t * time)
|
||||
{
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
|
||||
time->tv_sec = (uint32_t) now.tv_sec;
|
||||
time->tv_nsec = (uint32_t) now.tv_nsec;
|
||||
}
|
||||
|
||||
gs_error_t gs_clock_set_time(const gs_timestamp_t * time)
|
||||
{
|
||||
struct timespec now;
|
||||
now.tv_sec = time->tv_sec;
|
||||
now.tv_nsec = time->tv_nsec;
|
||||
|
||||
int res = clock_settime(CLOCK_REALTIME, &now);
|
||||
if (res != 0) {
|
||||
return gs_error(errno);
|
||||
}
|
||||
|
||||
gs_error_t error = GS_OK;
|
||||
if (gs_rtc_supported() == GS_OK) {
|
||||
error = gs_rtc_set_time(time);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void gs_clock_get_monotonic(gs_timestamp_t * time)
|
||||
{
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
time->tv_sec = (uint32_t) now.tv_sec;
|
||||
time->tv_nsec = (uint32_t) now.tv_nsec;
|
||||
}
|
||||
|
||||
uint64_t gs_clock_get_nsec(void)
|
||||
{
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
return (((uint64_t)now.tv_sec) * GS_TIMESTAMP_NSEC_PER_SEC) + ((uint64_t)now.tv_nsec);
|
||||
}
|
||||
|
||||
/**
|
||||
Required by libcsp.
|
||||
Proto-typed in ./libcsp/include/csp/arch/csp_clock.h, but with different argumet!
|
||||
|
||||
__attribute__((weak)) extern void clock_get_time(csp_timestamp_t * time);
|
||||
__attribute__((weak)) extern void clock_set_time(csp_timestamp_t * time);
|
||||
*/
|
||||
void clock_get_time(gs_timestamp_t * time)
|
||||
{
|
||||
gs_clock_get_time(time);
|
||||
}
|
||||
|
||||
void clock_set_time(const gs_timestamp_t * time)
|
||||
{
|
||||
gs_clock_set_time(time);
|
||||
}
|
76
gomspace/libutil/src/linux/command_line.c
Normal file
76
gomspace/libutil/src/linux/command_line.c
Normal file
@ -0,0 +1,76 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/linux/command_line.h>
|
||||
#include <gs/util/linux/signal.h>
|
||||
#include <gs/util/string.h>
|
||||
|
||||
#define KEY_IGNORE_CTRLC 200
|
||||
|
||||
static bool ignore_ctrlc;
|
||||
|
||||
static int parser(int key, char *arg, struct argp_state *state)
|
||||
{
|
||||
switch (key) {
|
||||
case KEY_IGNORE_CTRLC:
|
||||
ignore_ctrlc = true;
|
||||
gs_signal_ignore(SIGINT);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
argp_help(state->root_argp, state->out_stream, ARGP_HELP_STD_HELP, state->name);
|
||||
exit(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct argp_option options[] = {
|
||||
{
|
||||
.name = "ignore-ctrlc",
|
||||
.key = KEY_IGNORE_CTRLC,
|
||||
.doc = "Ignore/disable CTRL-C"
|
||||
},
|
||||
{0}
|
||||
};
|
||||
|
||||
static const struct argp argp_console = {.options = options, .parser = parser};
|
||||
|
||||
const struct argp_child gs_console_command_line_ignore_ctrlc_argp = {.argp = &argp_console};
|
||||
|
||||
bool gs_command_line_ignore_ctrlc(void)
|
||||
{
|
||||
return ignore_ctrlc;
|
||||
}
|
||||
|
||||
static const struct argp_option help_options[] = {
|
||||
{
|
||||
.name = "help",
|
||||
.key = 'h',
|
||||
.doc = "Give this help list"
|
||||
},
|
||||
{0}
|
||||
};
|
||||
|
||||
static const struct argp gs_argp_help = {.options = help_options, .parser = parser};
|
||||
|
||||
const struct argp_child gs_help_command_line_argp = {.argp = &gs_argp_help};
|
||||
|
||||
const char * gs_command_line_program_name(const char * argv)
|
||||
{
|
||||
if (gs_string_empty(argv) == false) {
|
||||
const char * name = strrchr(argv, '/');
|
||||
if (name) {
|
||||
// skip slash
|
||||
++name;
|
||||
if (gs_string_empty(name) == false) {
|
||||
return name;
|
||||
}
|
||||
} else {
|
||||
return argv;
|
||||
}
|
||||
}
|
||||
return "<program>";
|
||||
}
|
28
gomspace/libutil/src/linux/cwd.c
Normal file
28
gomspace/libutil/src/linux/cwd.c
Normal file
@ -0,0 +1,28 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/unistd.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
gs_error_t gs_getcwd(char * buf, size_t bufsize)
|
||||
{
|
||||
if (buf && bufsize) {
|
||||
char * wd = getcwd(buf, bufsize);
|
||||
if (wd) {
|
||||
return GS_OK;
|
||||
}
|
||||
switch(errno) {
|
||||
case ENAMETOOLONG:
|
||||
case ERANGE:
|
||||
return GS_ERROR_RANGE;
|
||||
|
||||
case EACCES:
|
||||
case ENOENT:
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return GS_ERROR_ARG;
|
||||
}
|
22
gomspace/libutil/src/linux/delay.c
Normal file
22
gomspace/libutil/src/linux/delay.c
Normal file
@ -0,0 +1,22 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/delay.h>
|
||||
#include <unistd.h>
|
||||
#include <gs/util/time.h>
|
||||
|
||||
void gs_delay_us(uint32_t time_us)
|
||||
{
|
||||
uint64_t ns = time_us;
|
||||
ns *= 1000LL;
|
||||
gs_time_sleep_ns(ns);
|
||||
}
|
||||
|
||||
uint16_t gs_delay_ts_get(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gs_delay_from_ts(uint16_t ts, uint16_t delay)
|
||||
{
|
||||
|
||||
}
|
308
gomspace/libutil/src/linux/drivers/can/can.c
Normal file
308
gomspace/libutil/src/linux/drivers/can/can.c
Normal file
@ -0,0 +1,308 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/drivers/can/can.h>
|
||||
#include <gs/util/thread.h>
|
||||
#include <gs/util/log.h>
|
||||
#include <gs/util/string.h>
|
||||
#include <gs/util/time.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/raw.h>
|
||||
|
||||
typedef struct {
|
||||
// true if handle is in use
|
||||
bool inuse;
|
||||
|
||||
// opened socket
|
||||
int can_socket;
|
||||
|
||||
// receiver thread
|
||||
gs_thread_t rxthread;
|
||||
|
||||
// received data callback
|
||||
gs_can_rxdata_callback_t rx_callback;
|
||||
void * user_data;
|
||||
|
||||
} gs_can_handle_t;
|
||||
|
||||
#define MAX_CAN_HANDLES 10
|
||||
static gs_can_handle_t can_handles[MAX_CAN_HANDLES];
|
||||
|
||||
static int gs_can_alloc_handle(void)
|
||||
{
|
||||
int handle_id;
|
||||
for (handle_id = 0; (handle_id < MAX_CAN_HANDLES) && (can_handles[handle_id].inuse == true); ++handle_id);
|
||||
|
||||
if (handle_id < MAX_CAN_HANDLES) {
|
||||
gs_can_handle_t * handle = &can_handles[handle_id];
|
||||
memset(handle, 0, sizeof(*handle));
|
||||
handle->inuse = true;
|
||||
handle->can_socket = -1;
|
||||
}
|
||||
|
||||
return handle_id;
|
||||
}
|
||||
|
||||
static inline gs_can_handle_t * gs_can_handle(uint8_t hdl)
|
||||
{
|
||||
if (hdl >= MAX_CAN_HANDLES) {
|
||||
return NULL;
|
||||
}
|
||||
if (can_handles[hdl].inuse == false) {
|
||||
return NULL;
|
||||
}
|
||||
return &can_handles[hdl];
|
||||
}
|
||||
|
||||
static void * gs_can_rx_thread(void * parameter)
|
||||
{
|
||||
int hdl = (int) GS_TYPES_PTR2INT(parameter);
|
||||
|
||||
log_debug("%s: running, hdl: %d", __FUNCTION__, hdl);
|
||||
|
||||
gs_can_handle_t * handle = gs_can_handle(hdl);
|
||||
if (handle == NULL) {
|
||||
log_error("%s: CAN handle: %d is invalid or not opened", __FUNCTION__, hdl);
|
||||
gs_thread_exit(NULL);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
/* Read CAN frame */
|
||||
struct can_frame frame;
|
||||
ssize_t nbytes = read(handle->can_socket, &frame, sizeof(frame));
|
||||
if (nbytes < 0) {
|
||||
log_error("%s: read() on socket failed, error: %s", __FUNCTION__, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nbytes != sizeof(frame)) {
|
||||
log_warning("%s: read() returned incomplete CAN frame of %d bytes - ignoring frame", __FUNCTION__, (int) nbytes);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Frame type */
|
||||
if (frame.can_id & (CAN_ERR_FLAG | CAN_RTR_FLAG)) {
|
||||
/* Drop error and remote frames */
|
||||
log_warning("%s: discarding ERR/RTR frame, can_id: 0x%x", __FUNCTION__, frame.can_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool extId = (frame.can_id & CAN_EFF_FLAG) ? true : false;
|
||||
if (extId) {
|
||||
frame.can_id &= CAN_EFF_MASK;
|
||||
} else {
|
||||
frame.can_id &= CAN_SFF_MASK;
|
||||
}
|
||||
handle->rx_callback(hdl, frame.can_id, extId, frame.data, frame.can_dlc, gs_time_rel_ms(), handle->user_data, false);
|
||||
}
|
||||
|
||||
/* We should never reach this point */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gs_error_t gs_can_send(uint8_t hdl, uint32_t canMsgId, bool extended, const void * data, size_t data_size, int timeout_ms)
|
||||
{
|
||||
gs_can_handle_t * handle = gs_can_handle(hdl);
|
||||
if (handle == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
if ((data == NULL) || (data_size > 8)) {
|
||||
log_error("%s: invalid data: %p, data_size: %u", __FUNCTION__, (void*) data, (unsigned int) data_size);
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
struct can_frame frame;
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
frame.can_id = canMsgId;
|
||||
if (extended) {
|
||||
frame.can_id |= CAN_EFF_FLAG;
|
||||
}
|
||||
|
||||
memcpy(frame.data, data, data_size);
|
||||
|
||||
frame.can_dlc = (uint8_t) data_size;
|
||||
|
||||
const int DELAY_MS = 10;
|
||||
while (write(handle->can_socket, &frame, sizeof(frame)) != sizeof(frame)) {
|
||||
if ((timeout_ms > 0) && (errno == ENOBUFS)) {
|
||||
// Wait a bit and try again
|
||||
gs_thread_sleep_ms(DELAY_MS);
|
||||
timeout_ms -= DELAY_MS;
|
||||
} else {
|
||||
gs_error_t gserror = gs_error(errno);
|
||||
log_error("%s: write() failed, error: %s", __FUNCTION__, gs_error_string(gserror));
|
||||
return gserror;
|
||||
}
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_can_send_standard(uint8_t hdl, uint32_t canMsgId, const void * data, size_t data_size, int timeout_ms)
|
||||
{
|
||||
return gs_can_send(hdl, canMsgId, false, data, data_size, timeout_ms);
|
||||
}
|
||||
|
||||
gs_error_t gs_can_send_extended(uint8_t hdl, uint32_t canExtMsgId, const void * data, size_t data_size, int timeout_ms)
|
||||
{
|
||||
return gs_can_send(hdl, canExtMsgId, true, data, data_size, timeout_ms);
|
||||
}
|
||||
|
||||
static void gs_can_close(gs_can_handle_t * handle)
|
||||
{
|
||||
if (handle->can_socket >= 0) {
|
||||
close(handle->can_socket);
|
||||
}
|
||||
|
||||
// free instance - must be the last thing done, no lock needed
|
||||
handle->inuse = false;
|
||||
}
|
||||
|
||||
gs_error_t gs_can_open(const char * ifname, int * return_handle)
|
||||
{
|
||||
if ((ifname == NULL) || (ifname[0] == 0) || (return_handle == NULL)) {
|
||||
log_error("%s: invalid CAN interface name", __FUNCTION__);
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
int handle_id = gs_can_alloc_handle();
|
||||
if (handle_id >= MAX_CAN_HANDLES) {
|
||||
log_error("%s: no free handles", __FUNCTION__);
|
||||
return GS_ERROR_FULL;
|
||||
}
|
||||
gs_can_handle_t * handle = &can_handles[handle_id];
|
||||
|
||||
/* Create socket */
|
||||
if ((handle->can_socket = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
|
||||
gs_error_t gserror = gs_error(errno);
|
||||
log_error("%s: socket() failed, error: %s", __FUNCTION__, gs_error_string(gserror));
|
||||
gs_can_close(handle);
|
||||
return gserror;
|
||||
}
|
||||
|
||||
/* Locate interface */
|
||||
struct ifreq ifr;
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
|
||||
if (ioctl(handle->can_socket, SIOCGIFINDEX, &ifr) < 0) {
|
||||
gs_error_t gserror = gs_error(errno);
|
||||
log_error("%s: ioctl(ifname: [%s]) failed, error: %s", __FUNCTION__, ifr.ifr_name, gs_error_string(gserror));
|
||||
gs_can_close(handle);
|
||||
return gserror;
|
||||
}
|
||||
|
||||
/* Bind the socket to CAN interface */
|
||||
struct sockaddr_can addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.can_family = AF_CAN;
|
||||
addr.can_ifindex = ifr.ifr_ifindex;
|
||||
if (bind(handle->can_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
gs_error_t gserror = gs_error(errno);
|
||||
log_error("%s: bind() failed, error: %s", __FUNCTION__, gs_error_string(gserror));
|
||||
gs_can_close(handle);
|
||||
return gserror;
|
||||
}
|
||||
|
||||
*return_handle = handle_id;
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static gs_error_t gs_can_set_filter_mask(uint8_t hdl, bool extended, uint32_t canMsgId, uint32_t mask, gs_can_rxdata_callback_t rx_callback, void * user_data)
|
||||
{
|
||||
gs_can_handle_t * handle = gs_can_handle(hdl);
|
||||
if (handle == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
if (extended) {
|
||||
if ((canMsgId > CAN_EFF_MASK) || (mask > CAN_EFF_MASK)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
} else {
|
||||
if ((canMsgId > CAN_SFF_MASK) || (mask > CAN_SFF_MASK)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
handle->rx_callback = rx_callback;
|
||||
handle->user_data = user_data;
|
||||
|
||||
struct can_filter filter;
|
||||
filter.can_id = canMsgId;
|
||||
filter.can_mask = mask;
|
||||
if (extended == false) {
|
||||
filter.can_mask |= (CAN_EFF_MASK & ~CAN_SFF_MASK);
|
||||
}
|
||||
|
||||
if (setsockopt(handle->can_socket, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, sizeof(filter)) < 0) {
|
||||
gs_error_t gserror = gs_error(errno);
|
||||
log_error("%s: setsockopt(id: 0x%x, mask: 0x%x) failed, error: %s", __FUNCTION__, canMsgId, mask, gs_error_string(gserror));
|
||||
return gserror;
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_can_set_standard_filter_mask(uint8_t hdl, uint32_t canMsgId, uint32_t mask, gs_can_rxdata_callback_t rx_callback, void * user_data)
|
||||
{
|
||||
return gs_can_set_filter_mask(hdl, false, canMsgId, mask, rx_callback, user_data);
|
||||
}
|
||||
|
||||
gs_error_t gs_can_set_extended_filter_mask(uint8_t hdl, uint32_t canExtMsgId, uint32_t mask, gs_can_rxdata_callback_t rx_callback, void * user_data)
|
||||
{
|
||||
return gs_can_set_filter_mask(hdl, true, canExtMsgId, mask, rx_callback, user_data);
|
||||
}
|
||||
|
||||
gs_error_t gs_can_start(uint8_t hdl)
|
||||
{
|
||||
gs_can_handle_t * handle = gs_can_handle(hdl);
|
||||
if (handle == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
if (handle->rxthread) {
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
/* Create receiver thread */
|
||||
gs_error_t gserror = gs_thread_create("rxcan", gs_can_rx_thread, GS_TYPES_INT2PTR(hdl), 0, GS_THREAD_PRIORITY_HIGH, 0, &handle->rxthread);
|
||||
if (gserror) {
|
||||
log_error("s: gs_thread_create() failed, error: %s", gs_error_string(gserror));
|
||||
return gserror;
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_can_stop(uint8_t hdl)
|
||||
{
|
||||
gs_can_handle_t * handle = gs_can_handle(hdl);
|
||||
if (handle == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
return GS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
gs_error_t gs_can_error_state(uint8_t hdl, bool * restart_required)
|
||||
{
|
||||
gs_can_handle_t * handle = gs_can_handle(hdl);
|
||||
if (handle == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
if (restart_required) {
|
||||
*restart_required = false;
|
||||
}
|
||||
|
||||
// missing error state check on CAN layer
|
||||
|
||||
return GS_OK;
|
||||
}
|
102
gomspace/libutil/src/linux/drivers/gpio/gpio.c
Normal file
102
gomspace/libutil/src/linux/drivers/gpio/gpio.c
Normal file
@ -0,0 +1,102 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/check.h>
|
||||
#include <gs/util/linux/drivers/gpio/gpio.h>
|
||||
|
||||
#define MAX_DRIVERS 20
|
||||
|
||||
typedef struct {
|
||||
gs_gpio_driver_entry_t entry;
|
||||
bool in_use;
|
||||
} gs_gpio_driver_handle_t;
|
||||
|
||||
static gs_gpio_driver_handle_t gpio_drivers[MAX_DRIVERS];
|
||||
static uint8_t max_index_in_use = 0;
|
||||
|
||||
|
||||
static inline gs_gpio_driver_entry_t * gs_find_driver_entry(gs_gpio_t * gpio)
|
||||
{
|
||||
gs_gpio_driver_handle_t * handle;
|
||||
for (int i = max_index_in_use; i >= 0; i--) {
|
||||
handle = &gpio_drivers[i];
|
||||
if (((gpio->pin == handle->entry.pin) || (handle->entry.pin == GS_GPIO_ALL_PINS)) &&
|
||||
((gpio->port == handle->entry.port) || (handle->entry.port == GS_GPIO_ALL_PORTS)) &&
|
||||
(handle->in_use == true)) {
|
||||
return &handle->entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_get(gs_gpio_t gpio, bool *value)
|
||||
{
|
||||
gs_gpio_driver_entry_t * driver_entry = gs_find_driver_entry(&gpio);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->get_handler) {
|
||||
return driver_entry->driver->get_handler(gpio, value, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
bool gs_gpio_get_nc(gs_gpio_t gpio)
|
||||
{
|
||||
gs_gpio_driver_entry_t * driver_entry = gs_find_driver_entry(&gpio);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->get_nc_handler) {
|
||||
return driver_entry->driver->get_nc_handler(gpio, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_set(gs_gpio_t gpio, bool value)
|
||||
{
|
||||
gs_gpio_driver_entry_t * driver_entry = gs_find_driver_entry(&gpio);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->set_handler) {
|
||||
return driver_entry->driver->set_handler(gpio, value, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
void gs_gpio_set_nc(gs_gpio_t gpio, bool value)
|
||||
{
|
||||
gs_gpio_driver_entry_t * driver_entry = gs_find_driver_entry(&gpio);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->set_nc_handler) {
|
||||
driver_entry->driver->set_nc_handler(gpio, value, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_init_as_interrupt(gs_gpio_t gpio, const gs_interrupt_conf_t * conf)
|
||||
{
|
||||
gs_gpio_driver_entry_t * driver_entry = gs_find_driver_entry(&gpio);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->init_as_interrupt_handler) {
|
||||
return driver_entry->driver->init_as_interrupt_handler(gpio, conf, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_register_driver(const gs_gpio_driver_entry_t * driver_entry)
|
||||
{
|
||||
GS_CHECK_ARG(driver_entry != NULL);
|
||||
GS_CHECK_ARG(driver_entry->driver != NULL);
|
||||
|
||||
gs_gpio_driver_handle_t * handle;
|
||||
for (uint8_t i = 0; i < MAX_DRIVERS; i++) {
|
||||
handle = &gpio_drivers[i];
|
||||
if (handle->in_use == false) {
|
||||
handle->entry = *driver_entry;
|
||||
handle->in_use = true;
|
||||
max_index_in_use = i;
|
||||
return GS_OK;
|
||||
}
|
||||
}
|
||||
/* Not enough space in buffer */
|
||||
return GS_ERROR_FULL;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user