save failed integration state

This commit is contained in:
2020-11-26 10:24:23 +01:00
parent 77970418d8
commit 17fc4b0de1
194 changed files with 45450 additions and 2 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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 */