save failed integration state
This commit is contained in:
34
gomspace/libutil/src/linux/argp.c
Normal file
34
gomspace/libutil/src/linux/argp.c
Normal file
@ -0,0 +1,34 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/linux/argp.h>
|
||||
#include <gs/util/linux/exitcode.h>
|
||||
#include <gs/util/string.h>
|
||||
|
||||
void gs_argp_parse(const struct argp * argp,
|
||||
int argc, char ** argv,
|
||||
unsigned int flags, int * return_arg_index,
|
||||
const char * revision)
|
||||
{
|
||||
if (gs_string_empty(revision) == false) {
|
||||
argp_program_version = revision;
|
||||
}
|
||||
|
||||
int arg_index = 0;
|
||||
int res = argp_parse(argp, argc, argv, 0, &arg_index, 0);
|
||||
if (res) {
|
||||
printf("Failed to parse argument/option (result: %d)\n", res);
|
||||
exit(GS_EXITCODE_USAGE);
|
||||
}
|
||||
|
||||
if ((return_arg_index == NULL) && (arg_index < argc)) {
|
||||
// application doesn't expect unhandled arguments
|
||||
for (int i = arg_index; i < argc; ++i) {
|
||||
printf("Unhandled/unknown argument: [%s]\n", argv[i]);
|
||||
}
|
||||
exit(GS_EXITCODE_USAGE);
|
||||
}
|
||||
|
||||
if (return_arg_index) {
|
||||
*return_arg_index = arg_index;
|
||||
}
|
||||
}
|
68
gomspace/libutil/src/linux/clock.c
Normal file
68
gomspace/libutil/src/linux/clock.c
Normal file
@ -0,0 +1,68 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/clock.h>
|
||||
#include <gs/util/rtc.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
void gs_clock_get_time(gs_timestamp_t * time)
|
||||
{
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
|
||||
time->tv_sec = (uint32_t) now.tv_sec;
|
||||
time->tv_nsec = (uint32_t) now.tv_nsec;
|
||||
}
|
||||
|
||||
gs_error_t gs_clock_set_time(const gs_timestamp_t * time)
|
||||
{
|
||||
struct timespec now;
|
||||
now.tv_sec = time->tv_sec;
|
||||
now.tv_nsec = time->tv_nsec;
|
||||
|
||||
int res = clock_settime(CLOCK_REALTIME, &now);
|
||||
if (res != 0) {
|
||||
return gs_error(errno);
|
||||
}
|
||||
|
||||
gs_error_t error = GS_OK;
|
||||
if (gs_rtc_supported() == GS_OK) {
|
||||
error = gs_rtc_set_time(time);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void gs_clock_get_monotonic(gs_timestamp_t * time)
|
||||
{
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
time->tv_sec = (uint32_t) now.tv_sec;
|
||||
time->tv_nsec = (uint32_t) now.tv_nsec;
|
||||
}
|
||||
|
||||
uint64_t gs_clock_get_nsec(void)
|
||||
{
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
return (((uint64_t)now.tv_sec) * GS_TIMESTAMP_NSEC_PER_SEC) + ((uint64_t)now.tv_nsec);
|
||||
}
|
||||
|
||||
/**
|
||||
Required by libcsp.
|
||||
Proto-typed in ./libcsp/include/csp/arch/csp_clock.h, but with different argumet!
|
||||
|
||||
__attribute__((weak)) extern void clock_get_time(csp_timestamp_t * time);
|
||||
__attribute__((weak)) extern void clock_set_time(csp_timestamp_t * time);
|
||||
*/
|
||||
void clock_get_time(gs_timestamp_t * time)
|
||||
{
|
||||
gs_clock_get_time(time);
|
||||
}
|
||||
|
||||
void clock_set_time(const gs_timestamp_t * time)
|
||||
{
|
||||
gs_clock_set_time(time);
|
||||
}
|
76
gomspace/libutil/src/linux/command_line.c
Normal file
76
gomspace/libutil/src/linux/command_line.c
Normal file
@ -0,0 +1,76 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/linux/command_line.h>
|
||||
#include <gs/util/linux/signal.h>
|
||||
#include <gs/util/string.h>
|
||||
|
||||
#define KEY_IGNORE_CTRLC 200
|
||||
|
||||
static bool ignore_ctrlc;
|
||||
|
||||
static int parser(int key, char *arg, struct argp_state *state)
|
||||
{
|
||||
switch (key) {
|
||||
case KEY_IGNORE_CTRLC:
|
||||
ignore_ctrlc = true;
|
||||
gs_signal_ignore(SIGINT);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
argp_help(state->root_argp, state->out_stream, ARGP_HELP_STD_HELP, state->name);
|
||||
exit(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct argp_option options[] = {
|
||||
{
|
||||
.name = "ignore-ctrlc",
|
||||
.key = KEY_IGNORE_CTRLC,
|
||||
.doc = "Ignore/disable CTRL-C"
|
||||
},
|
||||
{0}
|
||||
};
|
||||
|
||||
static const struct argp argp_console = {.options = options, .parser = parser};
|
||||
|
||||
const struct argp_child gs_console_command_line_ignore_ctrlc_argp = {.argp = &argp_console};
|
||||
|
||||
bool gs_command_line_ignore_ctrlc(void)
|
||||
{
|
||||
return ignore_ctrlc;
|
||||
}
|
||||
|
||||
static const struct argp_option help_options[] = {
|
||||
{
|
||||
.name = "help",
|
||||
.key = 'h',
|
||||
.doc = "Give this help list"
|
||||
},
|
||||
{0}
|
||||
};
|
||||
|
||||
static const struct argp gs_argp_help = {.options = help_options, .parser = parser};
|
||||
|
||||
const struct argp_child gs_help_command_line_argp = {.argp = &gs_argp_help};
|
||||
|
||||
const char * gs_command_line_program_name(const char * argv)
|
||||
{
|
||||
if (gs_string_empty(argv) == false) {
|
||||
const char * name = strrchr(argv, '/');
|
||||
if (name) {
|
||||
// skip slash
|
||||
++name;
|
||||
if (gs_string_empty(name) == false) {
|
||||
return name;
|
||||
}
|
||||
} else {
|
||||
return argv;
|
||||
}
|
||||
}
|
||||
return "<program>";
|
||||
}
|
28
gomspace/libutil/src/linux/cwd.c
Normal file
28
gomspace/libutil/src/linux/cwd.c
Normal file
@ -0,0 +1,28 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/unistd.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
gs_error_t gs_getcwd(char * buf, size_t bufsize)
|
||||
{
|
||||
if (buf && bufsize) {
|
||||
char * wd = getcwd(buf, bufsize);
|
||||
if (wd) {
|
||||
return GS_OK;
|
||||
}
|
||||
switch(errno) {
|
||||
case ENAMETOOLONG:
|
||||
case ERANGE:
|
||||
return GS_ERROR_RANGE;
|
||||
|
||||
case EACCES:
|
||||
case ENOENT:
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return GS_ERROR_ARG;
|
||||
}
|
22
gomspace/libutil/src/linux/delay.c
Normal file
22
gomspace/libutil/src/linux/delay.c
Normal file
@ -0,0 +1,22 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/delay.h>
|
||||
#include <unistd.h>
|
||||
#include <gs/util/time.h>
|
||||
|
||||
void gs_delay_us(uint32_t time_us)
|
||||
{
|
||||
uint64_t ns = time_us;
|
||||
ns *= 1000LL;
|
||||
gs_time_sleep_ns(ns);
|
||||
}
|
||||
|
||||
uint16_t gs_delay_ts_get(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gs_delay_from_ts(uint16_t ts, uint16_t delay)
|
||||
{
|
||||
|
||||
}
|
308
gomspace/libutil/src/linux/drivers/can/can.c
Normal file
308
gomspace/libutil/src/linux/drivers/can/can.c
Normal file
@ -0,0 +1,308 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/drivers/can/can.h>
|
||||
#include <gs/util/thread.h>
|
||||
#include <gs/util/log.h>
|
||||
#include <gs/util/string.h>
|
||||
#include <gs/util/time.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/raw.h>
|
||||
|
||||
typedef struct {
|
||||
// true if handle is in use
|
||||
bool inuse;
|
||||
|
||||
// opened socket
|
||||
int can_socket;
|
||||
|
||||
// receiver thread
|
||||
gs_thread_t rxthread;
|
||||
|
||||
// received data callback
|
||||
gs_can_rxdata_callback_t rx_callback;
|
||||
void * user_data;
|
||||
|
||||
} gs_can_handle_t;
|
||||
|
||||
#define MAX_CAN_HANDLES 10
|
||||
static gs_can_handle_t can_handles[MAX_CAN_HANDLES];
|
||||
|
||||
static int gs_can_alloc_handle(void)
|
||||
{
|
||||
int handle_id;
|
||||
for (handle_id = 0; (handle_id < MAX_CAN_HANDLES) && (can_handles[handle_id].inuse == true); ++handle_id);
|
||||
|
||||
if (handle_id < MAX_CAN_HANDLES) {
|
||||
gs_can_handle_t * handle = &can_handles[handle_id];
|
||||
memset(handle, 0, sizeof(*handle));
|
||||
handle->inuse = true;
|
||||
handle->can_socket = -1;
|
||||
}
|
||||
|
||||
return handle_id;
|
||||
}
|
||||
|
||||
static inline gs_can_handle_t * gs_can_handle(uint8_t hdl)
|
||||
{
|
||||
if (hdl >= MAX_CAN_HANDLES) {
|
||||
return NULL;
|
||||
}
|
||||
if (can_handles[hdl].inuse == false) {
|
||||
return NULL;
|
||||
}
|
||||
return &can_handles[hdl];
|
||||
}
|
||||
|
||||
static void * gs_can_rx_thread(void * parameter)
|
||||
{
|
||||
int hdl = (int) GS_TYPES_PTR2INT(parameter);
|
||||
|
||||
log_debug("%s: running, hdl: %d", __FUNCTION__, hdl);
|
||||
|
||||
gs_can_handle_t * handle = gs_can_handle(hdl);
|
||||
if (handle == NULL) {
|
||||
log_error("%s: CAN handle: %d is invalid or not opened", __FUNCTION__, hdl);
|
||||
gs_thread_exit(NULL);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
/* Read CAN frame */
|
||||
struct can_frame frame;
|
||||
ssize_t nbytes = read(handle->can_socket, &frame, sizeof(frame));
|
||||
if (nbytes < 0) {
|
||||
log_error("%s: read() on socket failed, error: %s", __FUNCTION__, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nbytes != sizeof(frame)) {
|
||||
log_warning("%s: read() returned incomplete CAN frame of %d bytes - ignoring frame", __FUNCTION__, (int) nbytes);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Frame type */
|
||||
if (frame.can_id & (CAN_ERR_FLAG | CAN_RTR_FLAG)) {
|
||||
/* Drop error and remote frames */
|
||||
log_warning("%s: discarding ERR/RTR frame, can_id: 0x%x", __FUNCTION__, frame.can_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool extId = (frame.can_id & CAN_EFF_FLAG) ? true : false;
|
||||
if (extId) {
|
||||
frame.can_id &= CAN_EFF_MASK;
|
||||
} else {
|
||||
frame.can_id &= CAN_SFF_MASK;
|
||||
}
|
||||
handle->rx_callback(hdl, frame.can_id, extId, frame.data, frame.can_dlc, gs_time_rel_ms(), handle->user_data, false);
|
||||
}
|
||||
|
||||
/* We should never reach this point */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gs_error_t gs_can_send(uint8_t hdl, uint32_t canMsgId, bool extended, const void * data, size_t data_size, int timeout_ms)
|
||||
{
|
||||
gs_can_handle_t * handle = gs_can_handle(hdl);
|
||||
if (handle == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
if ((data == NULL) || (data_size > 8)) {
|
||||
log_error("%s: invalid data: %p, data_size: %u", __FUNCTION__, (void*) data, (unsigned int) data_size);
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
struct can_frame frame;
|
||||
memset(&frame, 0, sizeof(frame));
|
||||
frame.can_id = canMsgId;
|
||||
if (extended) {
|
||||
frame.can_id |= CAN_EFF_FLAG;
|
||||
}
|
||||
|
||||
memcpy(frame.data, data, data_size);
|
||||
|
||||
frame.can_dlc = (uint8_t) data_size;
|
||||
|
||||
const int DELAY_MS = 10;
|
||||
while (write(handle->can_socket, &frame, sizeof(frame)) != sizeof(frame)) {
|
||||
if ((timeout_ms > 0) && (errno == ENOBUFS)) {
|
||||
// Wait a bit and try again
|
||||
gs_thread_sleep_ms(DELAY_MS);
|
||||
timeout_ms -= DELAY_MS;
|
||||
} else {
|
||||
gs_error_t gserror = gs_error(errno);
|
||||
log_error("%s: write() failed, error: %s", __FUNCTION__, gs_error_string(gserror));
|
||||
return gserror;
|
||||
}
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_can_send_standard(uint8_t hdl, uint32_t canMsgId, const void * data, size_t data_size, int timeout_ms)
|
||||
{
|
||||
return gs_can_send(hdl, canMsgId, false, data, data_size, timeout_ms);
|
||||
}
|
||||
|
||||
gs_error_t gs_can_send_extended(uint8_t hdl, uint32_t canExtMsgId, const void * data, size_t data_size, int timeout_ms)
|
||||
{
|
||||
return gs_can_send(hdl, canExtMsgId, true, data, data_size, timeout_ms);
|
||||
}
|
||||
|
||||
static void gs_can_close(gs_can_handle_t * handle)
|
||||
{
|
||||
if (handle->can_socket >= 0) {
|
||||
close(handle->can_socket);
|
||||
}
|
||||
|
||||
// free instance - must be the last thing done, no lock needed
|
||||
handle->inuse = false;
|
||||
}
|
||||
|
||||
gs_error_t gs_can_open(const char * ifname, int * return_handle)
|
||||
{
|
||||
if ((ifname == NULL) || (ifname[0] == 0) || (return_handle == NULL)) {
|
||||
log_error("%s: invalid CAN interface name", __FUNCTION__);
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
int handle_id = gs_can_alloc_handle();
|
||||
if (handle_id >= MAX_CAN_HANDLES) {
|
||||
log_error("%s: no free handles", __FUNCTION__);
|
||||
return GS_ERROR_FULL;
|
||||
}
|
||||
gs_can_handle_t * handle = &can_handles[handle_id];
|
||||
|
||||
/* Create socket */
|
||||
if ((handle->can_socket = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
|
||||
gs_error_t gserror = gs_error(errno);
|
||||
log_error("%s: socket() failed, error: %s", __FUNCTION__, gs_error_string(gserror));
|
||||
gs_can_close(handle);
|
||||
return gserror;
|
||||
}
|
||||
|
||||
/* Locate interface */
|
||||
struct ifreq ifr;
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
|
||||
if (ioctl(handle->can_socket, SIOCGIFINDEX, &ifr) < 0) {
|
||||
gs_error_t gserror = gs_error(errno);
|
||||
log_error("%s: ioctl(ifname: [%s]) failed, error: %s", __FUNCTION__, ifr.ifr_name, gs_error_string(gserror));
|
||||
gs_can_close(handle);
|
||||
return gserror;
|
||||
}
|
||||
|
||||
/* Bind the socket to CAN interface */
|
||||
struct sockaddr_can addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.can_family = AF_CAN;
|
||||
addr.can_ifindex = ifr.ifr_ifindex;
|
||||
if (bind(handle->can_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
gs_error_t gserror = gs_error(errno);
|
||||
log_error("%s: bind() failed, error: %s", __FUNCTION__, gs_error_string(gserror));
|
||||
gs_can_close(handle);
|
||||
return gserror;
|
||||
}
|
||||
|
||||
*return_handle = handle_id;
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static gs_error_t gs_can_set_filter_mask(uint8_t hdl, bool extended, uint32_t canMsgId, uint32_t mask, gs_can_rxdata_callback_t rx_callback, void * user_data)
|
||||
{
|
||||
gs_can_handle_t * handle = gs_can_handle(hdl);
|
||||
if (handle == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
if (extended) {
|
||||
if ((canMsgId > CAN_EFF_MASK) || (mask > CAN_EFF_MASK)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
} else {
|
||||
if ((canMsgId > CAN_SFF_MASK) || (mask > CAN_SFF_MASK)) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
handle->rx_callback = rx_callback;
|
||||
handle->user_data = user_data;
|
||||
|
||||
struct can_filter filter;
|
||||
filter.can_id = canMsgId;
|
||||
filter.can_mask = mask;
|
||||
if (extended == false) {
|
||||
filter.can_mask |= (CAN_EFF_MASK & ~CAN_SFF_MASK);
|
||||
}
|
||||
|
||||
if (setsockopt(handle->can_socket, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, sizeof(filter)) < 0) {
|
||||
gs_error_t gserror = gs_error(errno);
|
||||
log_error("%s: setsockopt(id: 0x%x, mask: 0x%x) failed, error: %s", __FUNCTION__, canMsgId, mask, gs_error_string(gserror));
|
||||
return gserror;
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_can_set_standard_filter_mask(uint8_t hdl, uint32_t canMsgId, uint32_t mask, gs_can_rxdata_callback_t rx_callback, void * user_data)
|
||||
{
|
||||
return gs_can_set_filter_mask(hdl, false, canMsgId, mask, rx_callback, user_data);
|
||||
}
|
||||
|
||||
gs_error_t gs_can_set_extended_filter_mask(uint8_t hdl, uint32_t canExtMsgId, uint32_t mask, gs_can_rxdata_callback_t rx_callback, void * user_data)
|
||||
{
|
||||
return gs_can_set_filter_mask(hdl, true, canExtMsgId, mask, rx_callback, user_data);
|
||||
}
|
||||
|
||||
gs_error_t gs_can_start(uint8_t hdl)
|
||||
{
|
||||
gs_can_handle_t * handle = gs_can_handle(hdl);
|
||||
if (handle == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
if (handle->rxthread) {
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
/* Create receiver thread */
|
||||
gs_error_t gserror = gs_thread_create("rxcan", gs_can_rx_thread, GS_TYPES_INT2PTR(hdl), 0, GS_THREAD_PRIORITY_HIGH, 0, &handle->rxthread);
|
||||
if (gserror) {
|
||||
log_error("s: gs_thread_create() failed, error: %s", gs_error_string(gserror));
|
||||
return gserror;
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_can_stop(uint8_t hdl)
|
||||
{
|
||||
gs_can_handle_t * handle = gs_can_handle(hdl);
|
||||
if (handle == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
return GS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
gs_error_t gs_can_error_state(uint8_t hdl, bool * restart_required)
|
||||
{
|
||||
gs_can_handle_t * handle = gs_can_handle(hdl);
|
||||
if (handle == NULL) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
if (restart_required) {
|
||||
*restart_required = false;
|
||||
}
|
||||
|
||||
// missing error state check on CAN layer
|
||||
|
||||
return GS_OK;
|
||||
}
|
102
gomspace/libutil/src/linux/drivers/gpio/gpio.c
Normal file
102
gomspace/libutil/src/linux/drivers/gpio/gpio.c
Normal file
@ -0,0 +1,102 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/check.h>
|
||||
#include <gs/util/linux/drivers/gpio/gpio.h>
|
||||
|
||||
#define MAX_DRIVERS 20
|
||||
|
||||
typedef struct {
|
||||
gs_gpio_driver_entry_t entry;
|
||||
bool in_use;
|
||||
} gs_gpio_driver_handle_t;
|
||||
|
||||
static gs_gpio_driver_handle_t gpio_drivers[MAX_DRIVERS];
|
||||
static uint8_t max_index_in_use = 0;
|
||||
|
||||
|
||||
static inline gs_gpio_driver_entry_t * gs_find_driver_entry(gs_gpio_t * gpio)
|
||||
{
|
||||
gs_gpio_driver_handle_t * handle;
|
||||
for (int i = max_index_in_use; i >= 0; i--) {
|
||||
handle = &gpio_drivers[i];
|
||||
if (((gpio->pin == handle->entry.pin) || (handle->entry.pin == GS_GPIO_ALL_PINS)) &&
|
||||
((gpio->port == handle->entry.port) || (handle->entry.port == GS_GPIO_ALL_PORTS)) &&
|
||||
(handle->in_use == true)) {
|
||||
return &handle->entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_get(gs_gpio_t gpio, bool *value)
|
||||
{
|
||||
gs_gpio_driver_entry_t * driver_entry = gs_find_driver_entry(&gpio);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->get_handler) {
|
||||
return driver_entry->driver->get_handler(gpio, value, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
bool gs_gpio_get_nc(gs_gpio_t gpio)
|
||||
{
|
||||
gs_gpio_driver_entry_t * driver_entry = gs_find_driver_entry(&gpio);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->get_nc_handler) {
|
||||
return driver_entry->driver->get_nc_handler(gpio, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_set(gs_gpio_t gpio, bool value)
|
||||
{
|
||||
gs_gpio_driver_entry_t * driver_entry = gs_find_driver_entry(&gpio);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->set_handler) {
|
||||
return driver_entry->driver->set_handler(gpio, value, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
void gs_gpio_set_nc(gs_gpio_t gpio, bool value)
|
||||
{
|
||||
gs_gpio_driver_entry_t * driver_entry = gs_find_driver_entry(&gpio);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->set_nc_handler) {
|
||||
driver_entry->driver->set_nc_handler(gpio, value, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_init_as_interrupt(gs_gpio_t gpio, const gs_interrupt_conf_t * conf)
|
||||
{
|
||||
gs_gpio_driver_entry_t * driver_entry = gs_find_driver_entry(&gpio);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->init_as_interrupt_handler) {
|
||||
return driver_entry->driver->init_as_interrupt_handler(gpio, conf, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_register_driver(const gs_gpio_driver_entry_t * driver_entry)
|
||||
{
|
||||
GS_CHECK_ARG(driver_entry != NULL);
|
||||
GS_CHECK_ARG(driver_entry->driver != NULL);
|
||||
|
||||
gs_gpio_driver_handle_t * handle;
|
||||
for (uint8_t i = 0; i < MAX_DRIVERS; i++) {
|
||||
handle = &gpio_drivers[i];
|
||||
if (handle->in_use == false) {
|
||||
handle->entry = *driver_entry;
|
||||
handle->in_use = true;
|
||||
max_index_in_use = i;
|
||||
return GS_OK;
|
||||
}
|
||||
}
|
||||
/* Not enough space in buffer */
|
||||
return GS_ERROR_FULL;
|
||||
}
|
145
gomspace/libutil/src/linux/drivers/gpio/gpio_sysfs.c
Normal file
145
gomspace/libutil/src/linux/drivers/gpio/gpio_sysfs.c
Normal file
@ -0,0 +1,145 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
@file
|
||||
|
||||
@brief GPIO Implementation for Linux of the GPIO API in libutil.
|
||||
|
||||
The GPIO driver provides a simple interface toward driving HW GPIO's.
|
||||
*/
|
||||
|
||||
#include <gs/util/linux/drivers/gpio/gpio_sysfs.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <gs/util/log.h>
|
||||
|
||||
#include <gs/util/linux/sysfs_helper.h>
|
||||
|
||||
gs_error_t gs_gpio_sysfs_initialize(gs_gpio_t gpio, bool output,bool init_value, bool active_low)
|
||||
{
|
||||
char gpio_pin_str[6];
|
||||
snprintf(gpio_pin_str, sizeof(gpio_pin_str), "%d", gpio.pin);
|
||||
|
||||
/* Try to unexport first */
|
||||
gs_sysfs_write_file("/sys/class/gpio/unexport", gpio_pin_str);
|
||||
|
||||
if (gs_sysfs_write_file("/sys/class/gpio/export", gpio_pin_str) != GS_OK)
|
||||
{
|
||||
log_warning("failed to export GPIO %s: %s", gpio_pin_str, strerror(errno));
|
||||
return GS_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
char gpio_sys_fname[128];
|
||||
snprintf(gpio_sys_fname, sizeof(gpio_sys_fname), "/sys/class/gpio/gpio%d/active_low", gpio.pin);
|
||||
const char * active_low_str = active_low ? "1" : "0";
|
||||
|
||||
if (gs_sysfs_write_file(gpio_sys_fname, active_low_str) != GS_OK)
|
||||
{
|
||||
log_warning("failed to set GPIO %d active_low: %s", gpio.pin, strerror(errno));
|
||||
return GS_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
snprintf(gpio_sys_fname, sizeof(gpio_sys_fname), "/sys/class/gpio/gpio%d/direction", gpio.pin);
|
||||
|
||||
/* Glitch-free output set (high/low makes pin an output and sets value to 1/0 respectively)*/
|
||||
const char * dir = output ? (init_value ? "high" : "low") : "in";
|
||||
|
||||
if (gs_sysfs_write_file(gpio_sys_fname, dir) != GS_OK)
|
||||
{
|
||||
log_warning("failed to set GPIO %d direction: %s", gpio.pin, strerror(errno));
|
||||
return GS_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_sysfs_get(gs_gpio_t gpio, bool *value, void * driver_data)
|
||||
{
|
||||
char gpio_sys_fname[128];
|
||||
snprintf(gpio_sys_fname, sizeof(gpio_sys_fname), "/sys/class/gpio/gpio%d/value", gpio.pin);
|
||||
|
||||
if (access(gpio_sys_fname, R_OK) != 0)
|
||||
{
|
||||
log_error("GPIO %d not initialized - Can't read the input.", gpio.pin);
|
||||
return GS_ERROR_ACCESS;
|
||||
}
|
||||
|
||||
char value_str[10];
|
||||
gs_error_t ret = gs_sysfs_read_file(gpio_sys_fname, value_str, sizeof(value_str));
|
||||
if (ret == GS_OK)
|
||||
{
|
||||
if (strcmp(value_str, "1") == 0)
|
||||
*value = true;
|
||||
else
|
||||
*value = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool gs_gpio_sysfs_get_nc(gs_gpio_t gpio, void * driver_data)
|
||||
{
|
||||
char gpio_sys_fname[128];
|
||||
snprintf(gpio_sys_fname, sizeof(gpio_sys_fname), "/sys/class/gpio/gpio%d/value", gpio.pin);
|
||||
|
||||
if (access(gpio_sys_fname, R_OK) != 0)
|
||||
{
|
||||
log_error("GPIO %d not initialized - Can't read the input.", gpio.pin);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char value_str[10];
|
||||
gs_sysfs_read_file(gpio_sys_fname, value_str, sizeof(value_str));
|
||||
|
||||
if (strncmp(value_str, "1", 10) == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_sysfs_set(gs_gpio_t gpio, bool value, void * driver_data)
|
||||
{
|
||||
const char *value_str = value ? "1" : "0";
|
||||
|
||||
char gpio_sys_fname[128];
|
||||
snprintf(gpio_sys_fname, sizeof(gpio_sys_fname), "/sys/class/gpio/gpio%d/value", gpio.pin);
|
||||
if (access(gpio_sys_fname, W_OK) == 0)
|
||||
{
|
||||
return gs_sysfs_write_file(gpio_sys_fname, value_str);
|
||||
}
|
||||
|
||||
log_error("GPIO %d not initialized - Can't set the output.", gpio.pin);
|
||||
return GS_ERROR_ACCESS;
|
||||
}
|
||||
|
||||
void gs_gpio_sysfs_set_nc(gs_gpio_t gpio, bool value, void * driver_data)
|
||||
{
|
||||
const char *value_str = value ? "1" : "0";
|
||||
|
||||
char gpio_sys_fname[128];
|
||||
snprintf(gpio_sys_fname, sizeof(gpio_sys_fname), "/sys/class/gpio/gpio%d/value", gpio.pin);
|
||||
if (access(gpio_sys_fname, W_OK) == 0)
|
||||
{
|
||||
gs_sysfs_write_file(gpio_sys_fname, value_str);
|
||||
return;
|
||||
}
|
||||
log_error("GPIO %d not initialized - Can't set the output.", gpio.pin);
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_sysfs_init_as_interrupt(gs_gpio_t gpio, const gs_interrupt_conf_t * conf, void * driver_data)
|
||||
{
|
||||
return GS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
const gs_gpio_driver_t gs_gpio_sysfs_driver = {
|
||||
.get_handler = gs_gpio_sysfs_get,
|
||||
.get_nc_handler = gs_gpio_sysfs_get_nc,
|
||||
.set_handler = gs_gpio_sysfs_set,
|
||||
.set_nc_handler = gs_gpio_sysfs_set_nc,
|
||||
.init_as_interrupt_handler = gs_gpio_sysfs_init_as_interrupt,
|
||||
};
|
||||
|
171
gomspace/libutil/src/linux/drivers/gpio/gpio_virtual.c
Normal file
171
gomspace/libutil/src/linux/drivers/gpio/gpio_virtual.c
Normal file
@ -0,0 +1,171 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/linux/drivers/gpio/gpio_virtual.h>
|
||||
|
||||
#define MAX_VPINS 500
|
||||
|
||||
#define FALLING_EDGE_FLAG 0x1
|
||||
#define RISING_EDGE_FLAG 0x2
|
||||
|
||||
typedef struct {
|
||||
gs_gpio_t gpio;
|
||||
bool output;
|
||||
bool value;
|
||||
bool in_use;
|
||||
gs_gpio_isr_t isr;
|
||||
uint8_t edge_flags;
|
||||
uint32_t transistions;
|
||||
} gs_gpio_virtual_t;
|
||||
|
||||
static gs_gpio_virtual_t vpins[MAX_VPINS];
|
||||
|
||||
gs_error_t gs_gpio_virtual_initialize(gs_gpio_t gpio, bool output, bool value)
|
||||
{
|
||||
gs_gpio_virtual_t * pin;
|
||||
for (uint16_t i = 0; i < MAX_VPINS; i++) {
|
||||
pin = &vpins[i];
|
||||
if ((!pin->in_use) || ((pin->gpio.pin == gpio.pin) && (pin->gpio.port == gpio.port))) {
|
||||
pin->gpio = gpio;
|
||||
pin->output = output;
|
||||
pin->value = value;
|
||||
pin->in_use = true;
|
||||
return GS_OK;
|
||||
}
|
||||
}
|
||||
return GS_ERROR_FULL;
|
||||
}
|
||||
|
||||
static gs_gpio_virtual_t * find_vpin(gs_gpio_t * gpio)
|
||||
{
|
||||
gs_gpio_virtual_t * pin;
|
||||
for (uint16_t i = 0; i < MAX_VPINS; i++) {
|
||||
pin = &vpins[i];
|
||||
if (pin->gpio.pin == gpio->pin) {
|
||||
if (pin->gpio.port == gpio->port) {
|
||||
if (pin->in_use) {
|
||||
return pin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_virtual_get(gs_gpio_t gpio, bool *value, void * driver_data)
|
||||
{
|
||||
gs_gpio_virtual_t * pin = find_vpin(&gpio);
|
||||
if (pin) {
|
||||
*value = pin->value;
|
||||
return GS_OK;
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
bool gs_gpio_virtual_get_nc(gs_gpio_t gpio, void * driver_data)
|
||||
{
|
||||
gs_gpio_virtual_t * pin = find_vpin(&gpio);
|
||||
if (pin) {
|
||||
return pin->value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_virtual_set(gs_gpio_t gpio, bool value, void * driver_data)
|
||||
{
|
||||
gs_gpio_virtual_t * pin = find_vpin(&gpio);
|
||||
if (pin) {
|
||||
if (pin->output) {
|
||||
if (pin->value != value) {
|
||||
pin->value = value;
|
||||
pin->transistions++;
|
||||
}
|
||||
return GS_OK;
|
||||
}
|
||||
return GS_ERROR_PERM;
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
void gs_gpio_virtual_set_nc(gs_gpio_t gpio, bool value, void * driver_data)
|
||||
{
|
||||
gs_gpio_virtual_t * pin = find_vpin(&gpio);
|
||||
if (pin) {
|
||||
if (pin->output) {
|
||||
if (pin->value != value) {
|
||||
pin->value = value;
|
||||
pin->transistions++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_virtual_init_as_interrupt(gs_gpio_t gpio, const gs_interrupt_conf_t * conf, void * driver_data)
|
||||
{
|
||||
gs_gpio_virtual_t * pin;
|
||||
for (uint16_t i = 0; i < MAX_VPINS; i++) {
|
||||
pin = &vpins[i];
|
||||
if ((!pin->in_use) || ((pin->gpio.pin == gpio.pin) && (pin->gpio.port == gpio.port))) {
|
||||
pin->gpio = gpio;
|
||||
pin->output = false;
|
||||
pin->value = 0;
|
||||
pin->in_use = true;
|
||||
pin->isr = conf->isr;
|
||||
if (conf->falling_edge) {
|
||||
pin->edge_flags |= FALLING_EDGE_FLAG;
|
||||
}
|
||||
if (conf->rising_edge) {
|
||||
pin->edge_flags |= RISING_EDGE_FLAG;
|
||||
}
|
||||
return GS_OK;
|
||||
}
|
||||
}
|
||||
return GS_ERROR_FULL;
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_virtual_force_set(gs_gpio_t gpio, bool value)
|
||||
{
|
||||
gs_gpio_virtual_t * pin = find_vpin(&gpio);
|
||||
if (pin) {
|
||||
bool old_value = pin->value;
|
||||
if (old_value != value) {
|
||||
pin->value = value;
|
||||
pin->transistions++;
|
||||
if (pin->isr) {
|
||||
if ((old_value == false) && (pin->edge_flags & RISING_EDGE_FLAG)) {
|
||||
pin->isr(NULL);
|
||||
} else if ((old_value == true) && (pin->edge_flags & FALLING_EDGE_FLAG)) {
|
||||
pin->isr(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
return GS_OK;
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
gs_error_t gs_gpio_virtual_get_transistions(gs_gpio_t gpio, uint32_t * transitions)
|
||||
{
|
||||
gs_gpio_virtual_t * pin = find_vpin(&gpio);
|
||||
if (pin) {
|
||||
*transitions = pin->transistions;
|
||||
pin->transistions = 0;
|
||||
return GS_OK;
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
const gs_gpio_driver_t gs_gpio_virtual_driver = {
|
||||
.get_handler = gs_gpio_virtual_get,
|
||||
.get_nc_handler = gs_gpio_virtual_get_nc,
|
||||
.set_handler = gs_gpio_virtual_set,
|
||||
.set_nc_handler = gs_gpio_virtual_set_nc,
|
||||
.init_as_interrupt_handler = gs_gpio_virtual_init_as_interrupt,
|
||||
};
|
||||
|
||||
|
||||
const gs_gpio_driver_entry_t gs_gpio_virtual_driver_entry_all = {
|
||||
.port = GS_GPIO_ALL_PORTS,
|
||||
.pin = GS_GPIO_ALL_PINS,
|
||||
.driver = &gs_gpio_virtual_driver,
|
||||
.driver_data = NULL,
|
||||
};
|
144
gomspace/libutil/src/linux/drivers/i2c/i2c.c
Normal file
144
gomspace/libutil/src/linux/drivers/i2c/i2c.c
Normal file
@ -0,0 +1,144 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <gs/util/check.h>
|
||||
#include <gs/util/linux/drivers/i2c/i2c.h>
|
||||
|
||||
#define MAX_DRIVERS 20
|
||||
#define HIGHEST_I2C_ADDR 127
|
||||
|
||||
typedef struct {
|
||||
gs_i2c_master_driver_entry_t entry;
|
||||
bool in_use;
|
||||
} gs_i2c_master_driver_handle_t;
|
||||
|
||||
typedef struct {
|
||||
gs_i2c_slave_driver_entry_t entry;
|
||||
bool in_use;
|
||||
} gs_i2c_slave_driver_handle_t;
|
||||
|
||||
static gs_i2c_master_driver_handle_t master_drivers[MAX_DRIVERS];
|
||||
static gs_i2c_slave_driver_handle_t slave_drivers[MAX_DRIVERS];
|
||||
|
||||
static uint8_t max_index_master_in_use = 0;
|
||||
static uint8_t max_index_slave_in_use = 0;
|
||||
|
||||
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)
|
||||
{
|
||||
GS_CHECK_RANGE(addr <= HIGHEST_I2C_ADDR);
|
||||
gs_i2c_master_driver_handle_t * handle;
|
||||
for (int i = max_index_master_in_use; i >= 0; i--) {
|
||||
handle = &master_drivers[i];
|
||||
if (((device == handle->entry.device) || (handle->entry.device == GS_I2C_ALL_DEVICES))
|
||||
&& ((addr == handle->entry.addr) || (handle->entry.addr == GS_I2C_ALL_ADDR))
|
||||
&& (handle->in_use == true)) {
|
||||
if (handle->entry.driver->master_transaction_handler) {
|
||||
return handle->entry.driver->master_transaction_handler(device, addr, tx, txlen, rx, rxlen, timeout_ms, handle->entry.driver_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
gs_error_t gs_i2c_master_register_driver(const gs_i2c_master_driver_entry_t * driver_entry)
|
||||
{
|
||||
GS_CHECK_ARG(driver_entry != NULL);
|
||||
GS_CHECK_ARG(driver_entry->driver != NULL);
|
||||
|
||||
GS_CHECK_RANGE((driver_entry->addr == GS_I2C_ALL_ADDR) || (driver_entry->addr <= HIGHEST_I2C_ADDR));
|
||||
|
||||
gs_i2c_master_driver_handle_t * handle;
|
||||
for (uint8_t i = 0; i < MAX_DRIVERS; i++) {
|
||||
handle = &master_drivers[i];
|
||||
if (handle->in_use == false) {
|
||||
handle->entry = *driver_entry;
|
||||
handle->in_use = true;
|
||||
max_index_master_in_use = i;
|
||||
return GS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not enough space in buffer */
|
||||
return GS_ERROR_FULL;
|
||||
}
|
||||
|
||||
static inline gs_i2c_slave_driver_entry_t * gs_slave_find_driver_entry(uint8_t device)
|
||||
{
|
||||
gs_i2c_slave_driver_handle_t * handle;
|
||||
for (int i = max_index_slave_in_use; i >= 0; i--) {
|
||||
handle = &slave_drivers[i];
|
||||
if (((device == handle->entry.device) || (handle->entry.device == GS_I2C_ALL_DEVICES))
|
||||
&& (handle->in_use == true)) {
|
||||
return &handle->entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gs_error_t gs_i2c_slave_start(uint8_t device)
|
||||
{
|
||||
gs_i2c_slave_driver_entry_t * driver_entry = gs_slave_find_driver_entry(device);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->start_handler) {
|
||||
return driver_entry->driver->start_handler(device, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
gs_error_t gs_i2c_slave_set_rx(uint8_t device, gs_i2c_slave_receive_t rx)
|
||||
{
|
||||
gs_i2c_slave_driver_entry_t * driver_entry = gs_slave_find_driver_entry(device);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->set_rx_handler) {
|
||||
return driver_entry->driver->set_rx_handler(device, rx, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
gs_i2c_slave_driver_entry_t * driver_entry = gs_slave_find_driver_entry(device);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->set_get_rx_buf_handler) {
|
||||
return driver_entry->driver->set_get_rx_buf_handler(device, get_rx_buf, buf_length, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
gs_error_t gs_i2c_slave_set_response(uint8_t device, const uint8_t * tx, size_t tx_length)
|
||||
{
|
||||
gs_i2c_slave_driver_entry_t * driver_entry = gs_slave_find_driver_entry(device);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->set_response_handler) {
|
||||
return driver_entry->driver->set_response_handler(device, tx, tx_length, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
gs_error_t gs_i2c_slave_register_driver(const gs_i2c_slave_driver_entry_t * driver_entry)
|
||||
{
|
||||
GS_CHECK_ARG(driver_entry != NULL);
|
||||
GS_CHECK_ARG(driver_entry->driver != NULL);
|
||||
|
||||
gs_i2c_slave_driver_handle_t * handle;
|
||||
for (uint8_t i = 0; i < MAX_DRIVERS; i++) {
|
||||
handle = &slave_drivers[i];
|
||||
if (handle->in_use == false) {
|
||||
handle->entry = *driver_entry;
|
||||
handle->in_use = true;
|
||||
max_index_slave_in_use = i;
|
||||
return GS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not enough space in buffer */
|
||||
return GS_ERROR_FULL;
|
||||
}
|
137
gomspace/libutil/src/linux/drivers/spi/spi.c
Normal file
137
gomspace/libutil/src/linux/drivers/spi/spi.c
Normal file
@ -0,0 +1,137 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <gs/util/check.h>
|
||||
#include <gs/util/linux/drivers/spi/spi.h>
|
||||
|
||||
#define MAX_DRIVERS 20
|
||||
|
||||
typedef struct {
|
||||
gs_spi_master_driver_entry_t entry;
|
||||
bool in_use;
|
||||
} gs_spi_master_driver_handle_t;
|
||||
|
||||
typedef struct {
|
||||
gs_spi_slave_driver_entry_t entry;
|
||||
bool in_use;
|
||||
} gs_spi_slave_driver_handle_t;
|
||||
|
||||
static gs_spi_master_driver_handle_t master_drivers[MAX_DRIVERS];
|
||||
static gs_spi_slave_driver_handle_t slave_drivers[MAX_DRIVERS];
|
||||
|
||||
static uint8_t max_index_master_in_use = 0;
|
||||
static uint8_t max_index_slave_in_use = 0;
|
||||
|
||||
static inline gs_spi_master_driver_entry_t * gs_master_find_driver_entry(uint8_t slave)
|
||||
{
|
||||
gs_spi_master_driver_handle_t * handle;
|
||||
for (int i = max_index_master_in_use; i >= 0; i--) {
|
||||
handle = &master_drivers[i];
|
||||
if (((slave == handle->entry.slave) || (handle->entry.slave == GS_SPI_ALL_SLAVES)) && (handle->in_use == true)) {
|
||||
return &handle->entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gs_error_t gs_spi_master_transaction(uint8_t slave, const void * tx, void * rx, size_t size, int timeout_ms)
|
||||
{
|
||||
gs_spi_master_trans_t trans = {.tx = tx, .rx = rx, .size = size};
|
||||
return gs_spi_master_transactions(slave, &trans, 1, timeout_ms);
|
||||
}
|
||||
|
||||
gs_error_t gs_spi_master_transactions(uint8_t slave, gs_spi_master_trans_t *trans, size_t count, int timeout_ms)
|
||||
{
|
||||
gs_spi_master_driver_entry_t * driver_entry = gs_master_find_driver_entry(slave);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->master_transactions_handler) {
|
||||
return driver_entry->driver->master_transactions_handler(slave, trans, count, timeout_ms, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
gs_error_t gs_spi_master_register_driver(const gs_spi_master_driver_entry_t * driver_entry)
|
||||
{
|
||||
GS_CHECK_ARG(driver_entry != NULL);
|
||||
GS_CHECK_ARG(driver_entry->driver != NULL);
|
||||
|
||||
gs_spi_master_driver_handle_t * handle;
|
||||
for (uint8_t i = 0; i < MAX_DRIVERS; i++) {
|
||||
handle = &master_drivers[i];
|
||||
if (handle->in_use == false) {
|
||||
handle->entry = *driver_entry;
|
||||
handle->in_use = true;
|
||||
max_index_master_in_use = i;
|
||||
return GS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not enough space in buffer */
|
||||
return GS_ERROR_FULL;
|
||||
}
|
||||
|
||||
static inline gs_spi_slave_driver_entry_t * gs_slave_find_driver_entry(uint8_t device)
|
||||
{
|
||||
gs_spi_slave_driver_handle_t * handle;
|
||||
for (int i = max_index_slave_in_use; i >= 0; i--) {
|
||||
handle = &slave_drivers[i];
|
||||
if (((device == handle->entry.device) || (handle->entry.device == GS_SPI_ALL_DEVICES))
|
||||
&& (handle->in_use == true)) {
|
||||
return &handle->entry;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gs_error_t gs_spi_slave_start(uint8_t device)
|
||||
{
|
||||
gs_spi_slave_driver_entry_t * driver_entry = gs_slave_find_driver_entry(device);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->start_handler) {
|
||||
return driver_entry->driver->start_handler(device, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
gs_error_t gs_spi_slave_set_rx(uint8_t device, gs_spi_slave_receive_t rx)
|
||||
{
|
||||
gs_spi_slave_driver_entry_t * driver_entry = gs_slave_find_driver_entry(device);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->set_rx_handler) {
|
||||
return driver_entry->driver->set_rx_handler(device, rx, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
gs_error_t gs_spi_slave_set_response(uint8_t device, size_t offset, const uint8_t * tx, size_t size)
|
||||
{
|
||||
gs_spi_slave_driver_entry_t * driver_entry = gs_slave_find_driver_entry(device);
|
||||
if (driver_entry) {
|
||||
if (driver_entry->driver->set_response_handler) {
|
||||
return driver_entry->driver->set_response_handler(device, offset, tx, size, driver_entry->driver_data);
|
||||
}
|
||||
}
|
||||
return GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
gs_error_t gs_spi_slave_register_driver(const gs_spi_slave_driver_entry_t * driver_entry)
|
||||
{
|
||||
GS_CHECK_ARG(driver_entry != NULL);
|
||||
GS_CHECK_ARG(driver_entry->driver != NULL);
|
||||
|
||||
gs_spi_slave_driver_handle_t * handle;
|
||||
for (uint8_t i = 0; i < MAX_DRIVERS; i++) {
|
||||
handle = &slave_drivers[i];
|
||||
if (handle->in_use == false) {
|
||||
handle->entry = *driver_entry;
|
||||
handle->in_use = true;
|
||||
max_index_slave_in_use = i;
|
||||
return GS_OK;
|
||||
}
|
||||
}
|
||||
/* Not enough space in buffer */
|
||||
return GS_ERROR_FULL;
|
||||
}
|
30
gomspace/libutil/src/linux/drivers/sys/memory.c
Normal file
30
gomspace/libutil/src/linux/drivers/sys/memory.c
Normal file
@ -0,0 +1,30 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <sys/sysinfo.h>
|
||||
#include <gs/util/drivers/sys/memory.h>
|
||||
|
||||
gs_error_t gs_mem_get_int_ram_stat(gs_mem_ram_stat_t * ram_stat)
|
||||
{
|
||||
return GS_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
gs_error_t gs_mem_get_ext_ram_stat(gs_mem_ram_stat_t * ram_stat)
|
||||
{
|
||||
struct sysinfo info;
|
||||
int res = sysinfo(&info);
|
||||
if (res != GS_OK) {
|
||||
return res;
|
||||
}
|
||||
|
||||
ram_stat->total = info.totalram;
|
||||
ram_stat->max_available = -1;
|
||||
ram_stat->min_available = -1;
|
||||
ram_stat->available = info.freeram;
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_mem_ram_type_t gs_mem_get_ram_default()
|
||||
{
|
||||
return GS_MEM_RAM_TYPE_EXTERNAL;
|
||||
}
|
41
gomspace/libutil/src/linux/function.c
Normal file
41
gomspace/libutil/src/linux/function.c
Normal file
@ -0,0 +1,41 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/linux/function.h>
|
||||
#include <gs/util/string.h>
|
||||
|
||||
typedef struct {
|
||||
const char * short_name;
|
||||
const char * long_name;
|
||||
gs_function_t function;
|
||||
} gs_function_register_t;
|
||||
|
||||
static gs_function_register_t registry[10];
|
||||
|
||||
gs_error_t gs_function_register(const char * short_name, const char * long_name, gs_function_t function)
|
||||
{
|
||||
for (unsigned int i = 0; i < GS_ARRAY_SIZE(registry); ++i) {
|
||||
gs_function_register_t * cb = ®istry[i];
|
||||
if ((cb->short_name == NULL) && (cb->long_name == NULL)) {
|
||||
cb->short_name = short_name;
|
||||
cb->long_name = long_name;
|
||||
cb->function = function;
|
||||
return GS_OK;
|
||||
}
|
||||
}
|
||||
return GS_ERROR_FULL;
|
||||
}
|
||||
|
||||
gs_error_t gs_function_invoke(const char * name, void * arg)
|
||||
{
|
||||
for (unsigned int i = 0; i < GS_ARRAY_SIZE(registry); ++i) {
|
||||
gs_function_register_t * cb = ®istry[i];
|
||||
if ((gs_string_empty(cb->short_name) == false) && (strcasecmp(cb->short_name, name) == 0)) {
|
||||
return (cb->function)(arg);
|
||||
}
|
||||
if ((gs_string_empty(cb->long_name) == false) && (strcasecmp(cb->long_name, name) == 0)) {
|
||||
return (cb->function)(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return GS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
59
gomspace/libutil/src/linux/mutex.c
Normal file
59
gomspace/libutil/src/linux/mutex.c
Normal file
@ -0,0 +1,59 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/mutex.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
gs_error_t gs_mutex_create(gs_mutex_t * mutex)
|
||||
{
|
||||
if (mutex == NULL) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
*mutex = malloc(sizeof(pthread_mutex_t));
|
||||
if (*mutex == NULL) {
|
||||
return GS_ERROR_ALLOC;
|
||||
}
|
||||
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
int res = pthread_mutex_init(*mutex, &attr);
|
||||
if (res < 0) {
|
||||
res = gs_error(errno);
|
||||
free(*mutex);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
gs_error_t gs_mutex_destroy(gs_mutex_t mutex)
|
||||
{
|
||||
int res = GS_OK;
|
||||
if (mutex) {
|
||||
res = pthread_mutex_destroy(mutex);
|
||||
if (res < 0) {
|
||||
res = gs_error(errno);
|
||||
}
|
||||
free(mutex);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
gs_error_t gs_mutex_lock(gs_mutex_t mutex)
|
||||
{
|
||||
int res = pthread_mutex_lock(mutex);
|
||||
if (res < 0) {
|
||||
res = gs_error(errno);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
gs_error_t gs_mutex_unlock(gs_mutex_t mutex)
|
||||
{
|
||||
int res = pthread_mutex_unlock(mutex);
|
||||
if (res < 0) {
|
||||
res = gs_error(errno);
|
||||
}
|
||||
return res;
|
||||
}
|
217
gomspace/libutil/src/linux/queue.c
Normal file
217
gomspace/libutil/src/linux/queue.c
Normal file
@ -0,0 +1,217 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
/**
|
||||
* Inspired by c-pthread-queue by Matthew Dickinson
|
||||
* http://code.google.com/p/c-pthread-queue/
|
||||
*/
|
||||
|
||||
#include <gs/util/queue.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define PTHREAD_QUEUE_ARG GS_ERROR_ARG
|
||||
#define PTHREAD_QUEUE_EMPTY GS_ERROR_NOT_FOUND
|
||||
#define PTHREAD_QUEUE_FULL GS_ERROR_FULL
|
||||
#define PTHREAD_QUEUE_TIMEOUT GS_ERROR_TIMEOUT
|
||||
#define PTHREAD_QUEUE_OK GS_OK
|
||||
|
||||
typedef struct gs_pthread_queue {
|
||||
uint8_t * buffer;
|
||||
size_t size;
|
||||
size_t item_size;
|
||||
size_t items;
|
||||
size_t in;
|
||||
size_t out;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond_full;
|
||||
pthread_cond_t cond_empty;
|
||||
} pthread_queue_t;
|
||||
|
||||
static pthread_queue_t * pthread_queue_create(size_t length, size_t item_size)
|
||||
{
|
||||
pthread_queue_t * q = malloc(sizeof(pthread_queue_t));
|
||||
|
||||
if (q != NULL) {
|
||||
q->buffer = malloc(length*item_size);
|
||||
if (q->buffer != NULL) {
|
||||
q->size = length;
|
||||
q->item_size = item_size;
|
||||
q->items = 0;
|
||||
q->in = 0;
|
||||
q->out = 0;
|
||||
if (pthread_mutex_init(&(q->mutex), NULL) || pthread_cond_init(&(q->cond_full), NULL) || pthread_cond_init(&(q->cond_empty), NULL)) {
|
||||
free(q->buffer);
|
||||
free(q);
|
||||
q = NULL;
|
||||
}
|
||||
} else {
|
||||
free(q);
|
||||
q = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
static void pthread_queue_delete(pthread_queue_t * q)
|
||||
{
|
||||
if (q) {
|
||||
free(q->buffer);
|
||||
free(q);
|
||||
}
|
||||
}
|
||||
|
||||
static int pthread_queue_enqueue(pthread_queue_t * queue, const void * value, uint32_t timeout)
|
||||
{
|
||||
/* Calculate timeout */
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts)) {
|
||||
return PTHREAD_QUEUE_ARG;
|
||||
}
|
||||
|
||||
uint32_t sec = timeout / 1000;
|
||||
uint32_t nsec = (timeout - 1000 * sec) * 1000000;
|
||||
|
||||
ts.tv_sec += sec;
|
||||
|
||||
if (ts.tv_nsec + nsec > 1000000000)
|
||||
ts.tv_sec++;
|
||||
|
||||
ts.tv_nsec = (ts.tv_nsec + nsec) % 1000000000;
|
||||
|
||||
/* Get queue lock */
|
||||
pthread_mutex_lock(&(queue->mutex));
|
||||
|
||||
while (queue->items == queue->size) {
|
||||
int ret = -1;
|
||||
if (timeout) {
|
||||
ret = pthread_cond_timedwait(&(queue->cond_full), &(queue->mutex), &ts);
|
||||
}
|
||||
if (ret) {
|
||||
pthread_mutex_unlock(&(queue->mutex));
|
||||
return PTHREAD_QUEUE_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Coby object from input buffer */
|
||||
memcpy(queue->buffer+(queue->in * queue->item_size), value, queue->item_size);
|
||||
queue->items++;
|
||||
queue->in = (queue->in + 1) % queue->size;
|
||||
pthread_mutex_unlock(&(queue->mutex));
|
||||
|
||||
/* Nofify blocked threads */
|
||||
pthread_cond_broadcast(&(queue->cond_empty));
|
||||
|
||||
return PTHREAD_QUEUE_OK;
|
||||
}
|
||||
|
||||
static int pthread_queue_dequeue(pthread_queue_t * queue, void * buf, uint32_t timeout)
|
||||
{
|
||||
/* Calculate timeout */
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts)) {
|
||||
return PTHREAD_QUEUE_ARG;
|
||||
}
|
||||
|
||||
uint32_t sec = timeout / 1000;
|
||||
uint32_t nsec = (timeout - 1000 * sec) * 1000000;
|
||||
|
||||
ts.tv_sec += sec;
|
||||
|
||||
if (ts.tv_nsec + nsec > 1000000000)
|
||||
ts.tv_sec++;
|
||||
|
||||
ts.tv_nsec = (ts.tv_nsec + nsec) % 1000000000;
|
||||
|
||||
/* Get queue lock */
|
||||
pthread_mutex_lock(&(queue->mutex));
|
||||
while (queue->items == 0) {
|
||||
int ret = -1;
|
||||
if (timeout) {
|
||||
ret = pthread_cond_timedwait(&(queue->cond_empty), &(queue->mutex), &ts);
|
||||
}
|
||||
if (ret) {
|
||||
pthread_mutex_unlock(&(queue->mutex));
|
||||
return PTHREAD_QUEUE_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Coby object to output buffer */
|
||||
memcpy(buf, queue->buffer+(queue->out * queue->item_size), queue->item_size);
|
||||
queue->items--;
|
||||
queue->out = (queue->out + 1) % queue->size;
|
||||
pthread_mutex_unlock(&(queue->mutex));
|
||||
|
||||
/* Nofify blocked threads */
|
||||
pthread_cond_broadcast(&(queue->cond_full));
|
||||
|
||||
return PTHREAD_QUEUE_OK;
|
||||
}
|
||||
|
||||
static size_t pthread_queue_items(pthread_queue_t * queue)
|
||||
{
|
||||
pthread_mutex_lock(&(queue->mutex));
|
||||
size_t items = queue->items;
|
||||
pthread_mutex_unlock(&(queue->mutex));
|
||||
return items;
|
||||
}
|
||||
|
||||
gs_error_t gs_queue_create(size_t items, size_t item_size, gs_queue_t * queue)
|
||||
{
|
||||
if (queue == NULL) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
pthread_queue_t * q = pthread_queue_create(items, item_size);
|
||||
if (q == NULL) {
|
||||
return GS_ERROR_ALLOC;
|
||||
}
|
||||
*queue = q;
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_queue_destroy(gs_queue_t queue)
|
||||
{
|
||||
pthread_queue_delete(queue);
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_queue_enqueue(gs_queue_t queue, const void *value, int timeout_ms)
|
||||
{
|
||||
return pthread_queue_enqueue(queue, value, (timeout_ms >= 0) ? timeout_ms : INT_MAX);
|
||||
}
|
||||
|
||||
gs_error_t gs_queue_enqueue_isr(gs_queue_t queue, const void * value, gs_context_switch_t * cswitch)
|
||||
{
|
||||
(void) cswitch;
|
||||
gs_error_t error = gs_queue_enqueue(queue, value, 0);
|
||||
return (error != GS_ERROR_TIMEOUT) ? error : GS_ERROR_FULL;
|
||||
}
|
||||
|
||||
gs_error_t gs_queue_dequeue(gs_queue_t queue, int timeout_ms, void *buf)
|
||||
{
|
||||
return pthread_queue_dequeue(queue, buf, (timeout_ms >= 0) ? timeout_ms : INT_MAX);
|
||||
}
|
||||
|
||||
gs_error_t gs_queue_dequeue_isr(gs_queue_t queue, gs_context_switch_t * cswitch, void *buf)
|
||||
{
|
||||
(void) cswitch;
|
||||
gs_error_t error = gs_queue_dequeue(queue, 0, buf);
|
||||
return (error != GS_ERROR_TIMEOUT) ? error : GS_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
unsigned int gs_queue_size(gs_queue_t queue)
|
||||
{
|
||||
if (queue) {
|
||||
return pthread_queue_items(queue);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int gs_queue_size_isr(gs_queue_t queue)
|
||||
{
|
||||
return gs_queue_size(queue);
|
||||
}
|
78
gomspace/libutil/src/linux/rtc.c
Normal file
78
gomspace/libutil/src/linux/rtc.c
Normal file
@ -0,0 +1,78 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/linux/rtc.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
static gs_error_t gs_rtc_get(void * driver_data, gs_timestamp_t * return_time)
|
||||
{
|
||||
if (return_time == NULL) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
return_time->tv_sec = 0;
|
||||
return_time->tv_nsec = 0;
|
||||
|
||||
int fd = open("/dev/rtc", O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
return gs_error(errno);
|
||||
}
|
||||
|
||||
struct tm tm;
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
int res = ioctl(fd, RTC_RD_TIME, &tm);
|
||||
close(fd);
|
||||
if (res < 0) {
|
||||
return gs_error(errno);
|
||||
}
|
||||
|
||||
time_t time = mktime(&tm);
|
||||
if (time < 0) {
|
||||
return GS_ERROR_DATA;
|
||||
}
|
||||
|
||||
return_time->tv_sec = (uint32_t) time;
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static gs_error_t gs_rtc_set(void * driver_data, const gs_timestamp_t * set_time)
|
||||
{
|
||||
if (set_time == NULL) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
int fd = open("/dev/rtc", O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
return gs_error(errno);
|
||||
}
|
||||
|
||||
const time_t now = set_time->tv_sec;
|
||||
struct tm tm;
|
||||
gmtime_r(&now, &tm);
|
||||
int res = ioctl(fd, RTC_SET_TIME, &tm);
|
||||
close(fd);
|
||||
if (res < 0) {
|
||||
return gs_error(errno);
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_rtc_register_linux(bool get, bool set)
|
||||
{
|
||||
static gs_rtc_driver_t rtc_driver;
|
||||
if (get) {
|
||||
rtc_driver.get_time = gs_rtc_get;
|
||||
}
|
||||
if (set) {
|
||||
rtc_driver.set_time = gs_rtc_set;
|
||||
}
|
||||
return gs_rtc_register(&rtc_driver, NULL);
|
||||
}
|
89
gomspace/libutil/src/linux/sem.c
Normal file
89
gomspace/libutil/src/linux/sem.c
Normal file
@ -0,0 +1,89 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/sem.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
gs_error_t gs_sem_create(unsigned int initialValue, gs_sem_t * sem)
|
||||
{
|
||||
if (sem == NULL) {
|
||||
return GS_ERROR_ARG;
|
||||
}
|
||||
|
||||
*sem = malloc(sizeof(sem_t));
|
||||
if (*sem == NULL) {
|
||||
return GS_ERROR_ALLOC;
|
||||
}
|
||||
|
||||
int res = sem_init(*sem, 0, initialValue);
|
||||
if (res < 0) {
|
||||
res = gs_error(errno);
|
||||
free(*sem);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
gs_error_t gs_sem_destroy(gs_sem_t sem)
|
||||
{
|
||||
int res = GS_OK;
|
||||
if (sem) {
|
||||
res = sem_destroy(sem);
|
||||
if (res < 0) {
|
||||
res = gs_error(errno);
|
||||
}
|
||||
free(sem);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
gs_error_t gs_sem_wait(gs_sem_t sem, int timeout_ms)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (timeout_ms < 0) {
|
||||
res = sem_wait(sem);
|
||||
} else {
|
||||
struct timespec ts;
|
||||
res = clock_gettime(CLOCK_REALTIME, &ts);
|
||||
if (res == 0) {
|
||||
const uint32_t ms = (uint32_t)timeout_ms;
|
||||
uint32_t sec = ms / 1000;
|
||||
uint32_t nsec = (ms - (1000 * sec)) * 1000000;
|
||||
|
||||
ts.tv_sec += sec;
|
||||
|
||||
if (ts.tv_nsec + nsec >= 1000000000) {
|
||||
ts.tv_sec++;
|
||||
}
|
||||
|
||||
ts.tv_nsec = (ts.tv_nsec + nsec) % 1000000000;
|
||||
|
||||
res = sem_timedwait(sem, &ts);
|
||||
}
|
||||
}
|
||||
if (res < 0) {
|
||||
res = gs_error(errno);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
gs_error_t gs_sem_post(gs_sem_t sem)
|
||||
{
|
||||
int res = sem_post(sem);
|
||||
if (res < 0) {
|
||||
res = gs_error(errno);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
gs_error_t gs_sem_post_isr(gs_sem_t sem, gs_context_switch_t * cswitch)
|
||||
{
|
||||
(void) cswitch;
|
||||
int res = sem_post(sem);
|
||||
if (res < 0) {
|
||||
res = gs_error(errno);
|
||||
}
|
||||
return res;
|
||||
}
|
38
gomspace/libutil/src/linux/signal.c
Normal file
38
gomspace/libutil/src/linux/signal.c
Normal file
@ -0,0 +1,38 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/linux/signal.h>
|
||||
#include <gs/util/linux/exitcode.h>
|
||||
|
||||
static void gs_signal_default_handler(int signo, siginfo_t *si, void *context)
|
||||
{
|
||||
exit(GS_EXITCODE_SIGNAL(signo)); // ensure atexit are invoked
|
||||
}
|
||||
|
||||
gs_error_t gs_signal_catch(int signal, gs_signal_handler handler)
|
||||
{
|
||||
if (handler == NULL) {
|
||||
handler = gs_signal_default_handler;
|
||||
}
|
||||
struct sigaction sa = { .sa_flags = SA_SIGINFO,
|
||||
.sa_sigaction = handler};
|
||||
if (sigemptyset(&sa.sa_mask)) {
|
||||
return GS_ERROR_UNKNOWN;
|
||||
}
|
||||
if (sigaction(signal, &sa, NULL)) {
|
||||
return GS_ERROR_UNKNOWN;
|
||||
}
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_signal_ignore(int signal)
|
||||
{
|
||||
struct sigaction sa = { .sa_flags = 0,
|
||||
.sa_handler = SIG_IGN}; // handle signal by ignoring
|
||||
if (sigemptyset(&sa.sa_mask)) {
|
||||
return GS_ERROR_UNKNOWN;
|
||||
}
|
||||
if (sigaction(signal, &sa, NULL)) {
|
||||
return GS_ERROR_UNKNOWN;
|
||||
}
|
||||
return GS_OK;
|
||||
}
|
36
gomspace/libutil/src/linux/stdio.c
Normal file
36
gomspace/libutil/src/linux/stdio.c
Normal file
@ -0,0 +1,36 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
|
||||
gs_error_t gs_stdio_putchar(int ch)
|
||||
{
|
||||
const int res = putchar(ch);
|
||||
if (res < 0) {
|
||||
return GS_ERROR_IO;
|
||||
}
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
gs_error_t gs_stdio_getchar_timed(int timeout_ms, int * ch)
|
||||
{
|
||||
struct pollfd fds = {STDIN_FILENO, POLLIN, 0};
|
||||
const int res = poll(&fds, 1, timeout_ms);
|
||||
|
||||
if (res == 0) {
|
||||
return GS_ERROR_TIMEOUT;
|
||||
}
|
||||
|
||||
if ((res > 0) && (fds.revents & POLLIN)) {
|
||||
int tmp = getchar();
|
||||
if (tmp >= 0) {
|
||||
if (ch) {
|
||||
*ch = tmp;
|
||||
}
|
||||
return GS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return GS_ERROR_IO;
|
||||
}
|
48
gomspace/libutil/src/linux/sysfs_helper.c
Normal file
48
gomspace/libutil/src/linux/sysfs_helper.c
Normal file
@ -0,0 +1,48 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/linux/sysfs_helper.h>
|
||||
#include <gs/util/log.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
gs_error_t gs_sysfs_write_file(const char *path, const char *value)
|
||||
{
|
||||
log_trace("sysfs: write %s to %s", value, path);
|
||||
|
||||
int fd = open(path, O_WRONLY);
|
||||
if (fd < 0) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
size_t len = strlen(value);
|
||||
ssize_t bytes = write(fd, value, len);
|
||||
close(fd);
|
||||
if (bytes < 0) {
|
||||
return GS_ERROR_NO_DATA;
|
||||
}
|
||||
|
||||
return (len == (size_t)bytes) ? GS_OK : GS_ERROR_NO_DATA;
|
||||
}
|
||||
|
||||
gs_error_t gs_sysfs_read_file(const char *path, char *value, size_t len)
|
||||
{
|
||||
log_trace("sysfs: read %s", path);
|
||||
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
return GS_ERROR_HANDLE;
|
||||
}
|
||||
|
||||
ssize_t bytes = read(fd, value, len);
|
||||
close(fd);
|
||||
if (bytes < 0) {
|
||||
return GS_ERROR_DATA;
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
89
gomspace/libutil/src/linux/thread.c
Normal file
89
gomspace/libutil/src/linux/thread.c
Normal file
@ -0,0 +1,89 @@
|
||||
/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/thread.h>
|
||||
#include <unistd.h>
|
||||
#include <gs/util/time.h>
|
||||
|
||||
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 * return_handle)
|
||||
{
|
||||
gs_time_uptime(); // force initialize of static offset
|
||||
|
||||
pthread_attr_t attr;
|
||||
int res = pthread_attr_init(&attr);
|
||||
if (res) {
|
||||
return GS_ERROR_ALLOC;
|
||||
}
|
||||
|
||||
if (flags & GS_THREAD_CREATE_JOINABLE) {
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
||||
} else {
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
}
|
||||
|
||||
gs_thread_t handle;
|
||||
res = pthread_create(&handle, &attr, func, parameter);
|
||||
pthread_attr_destroy(&attr);
|
||||
if (res) {
|
||||
return GS_ERROR_ALLOC;
|
||||
}
|
||||
|
||||
if (return_handle) {
|
||||
*return_handle = handle;
|
||||
}
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
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,
|
||||
gs_thread_priority_t priority,
|
||||
uint32_t flags,
|
||||
gs_thread_t * return_handle)
|
||||
{
|
||||
return gs_thread_create(name, func, parameter, stack_size, priority, flags, return_handle);
|
||||
}
|
||||
|
||||
void gs_thread_exit(void * exitValue)
|
||||
{
|
||||
pthread_exit(exitValue);
|
||||
}
|
||||
|
||||
void gs_thread_sleep_ms(uint32_t time_ms)
|
||||
{
|
||||
gs_time_sleep_ms(time_ms);
|
||||
}
|
||||
|
||||
gs_error_t gs_thread_join(gs_thread_t thread, void ** return_retval)
|
||||
{
|
||||
gs_error_t error = GS_ERROR_ARG;
|
||||
void * retval = 0;
|
||||
if (thread) {
|
||||
int res = pthread_join(thread, &retval);
|
||||
if (res == 0) {
|
||||
error = GS_OK;
|
||||
} else {
|
||||
retval = 0;
|
||||
}
|
||||
}
|
||||
if (return_retval) {
|
||||
*return_retval = retval;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
void gs_thread_block(void)
|
||||
{
|
||||
/* Wait here forever */
|
||||
for (;;) {
|
||||
gs_time_sleep_ms(10000);
|
||||
}
|
||||
}
|
53
gomspace/libutil/src/linux/time.c
Normal file
53
gomspace/libutil/src/linux/time.c
Normal file
@ -0,0 +1,53 @@
|
||||
/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */
|
||||
|
||||
#include <gs/util/time.h>
|
||||
#include <time.h>
|
||||
#include <gs/util/timestamp.h>
|
||||
|
||||
uint32_t gs_time_rel_ms(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (uint32_t)((ts.tv_sec * 1000) + (ts.tv_nsec/1000000));
|
||||
}
|
||||
|
||||
uint32_t gs_time_rel_ms_isr(void)
|
||||
{
|
||||
return gs_time_rel_ms();
|
||||
}
|
||||
|
||||
static uint32_t uptime_offset = 0;
|
||||
uint32_t gs_time_uptime(void)
|
||||
{
|
||||
uint32_t seconds;
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
|
||||
seconds = 0;
|
||||
} else {
|
||||
seconds = (uint32_t) ts.tv_sec;
|
||||
}
|
||||
if (uptime_offset == 0) {
|
||||
uptime_offset = seconds;
|
||||
}
|
||||
return (seconds - uptime_offset);
|
||||
}
|
||||
|
||||
void gs_time_sleep_ns(uint64_t time_ns)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = (time_ns / GS_TIMESTAMP_NSEC_PER_SEC);
|
||||
ts.tv_nsec = (time_ns % GS_TIMESTAMP_NSEC_PER_SEC);
|
||||
|
||||
// improvement: check return code (INTR) and use remaining.
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
void gs_time_sleep_ms(uint32_t time_ms)
|
||||
{
|
||||
uint64_t ns = time_ms;
|
||||
ns *= 1000000LL;
|
||||
gs_time_sleep_ns( ns);
|
||||
}
|
Reference in New Issue
Block a user