NorthStar-Endurance-TestBench/north-star_emotas-stack/codrv_sl/common/cpu_linux.c

317 lines
7.4 KiB
C

/*
* cpu_linux.c - contains driver for linux
*
* Copyright (c) 2012-2023 emotas embedded communication GmbH
*-------------------------------------------------------------------
* $Id: cpu_linux.c 53392 2024-04-15 10:37:08Z hil $
*
*
*-------------------------------------------------------------------
*
*
*/
#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__)
/********************************************************************/
/**
* \file
* \brief cpu functionality for linux system
*
* LINUX_SIGNAL_TIMER
* signal 34 interrupt functions like usleep() or sleep()
*
* LINUX_THREAD_TIMER
* compile with -pthread
* Please note, that the Thread Timer accuracy is depend from the cpu load.
* In our tests a 1 second Stack timer jitter between 0.78s and 1.24s.
*/
/* header of standard C - libraries
---------------------------------------------------------------------------*/
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <pthread.h>
/* header of project specific types
---------------------------------------------------------------------------*/
#include <gen_define.h>
#include <co_datatype.h>
#include <co_timer.h>
#include <co_drv.h>
/* constant definitions
---------------------------------------------------------------------------*/
/* set default */
#if defined(LINUX_THREAD_TIMER) || defined(THREAD_SPAWN)
#else /* defined(LINUX_THREAD_TIMER) || defined(THREAD_SPAWN) */
# define LINUX_SIGNAL_TIMER 1
#endif /* defined(LINUX_THREAD_TIMER) || defined(THREAD_SPAWN) */
#define SIG SIGRTMIN
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
} while (0)
/* local defined data types
---------------------------------------------------------------------------*/
/* list of external used functions, if not in headers
---------------------------------------------------------------------------*/
/* list of global defined functions
---------------------------------------------------------------------------*/
void codrvHardwareInit(void);
/* list of local defined functions
---------------------------------------------------------------------------*/
#ifdef LINUX_SIGNAL_TIMER
static void timerInt (int sig, siginfo_t *si, void *uc);
#endif /* LINUX_SIGNAL_TIMER */
#ifdef THREAD_SPAWN
static void timerIntSpawnedThread(sigval_t sival);
#endif /* THREAD_SPAWN */
/* external variables
---------------------------------------------------------------------------*/
/* global variables
---------------------------------------------------------------------------*/
/* local defined variables
---------------------------------------------------------------------------*/
/***************************************************************************/
/**
* \brief codrvHardwareInit - hardware initialization
*
* This function initialize the hardware, incl. Clock and CAN hardware.
*/
void codrvHardwareInit(void)
{
/* normally nothing to do */
}
/***************************************************************************/
/**
* \brief codrvCanEnableInterrupt - enable the CAN interrupt
*
*/
void codrvCanEnableInterrupt(
void /* no parameter */
)
{
/* enable CAN interrupts */
}
/***************************************************************************/
/**
* \brief codrvCanDisableInterrupt - disable the CAN interrupt
*
*/
void codrvCanDisableInterrupt(
void /* no parameter */
)
{
/* disable CAN interrupts */
}
#ifdef LINUX_SIGNAL_TIMER
/***************************************************************************/
/**
* \brief codrvTimerSetup - init Timer
*
* \param
* \results
* nothing
*/
RET_T codrvTimerSetup(
UNSIGNED32 timerInterval
)
{
timer_t timerid;
struct sigevent sev;
struct sigaction sa;
struct itimerspec its;
/* Establish handler for timer signal */
printf("Establishing handler for signal %d\n", SIG);
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timerInt;
sigemptyset(&sa.sa_mask);
if (sigaction(SIG, &sa, NULL) == -1) {
return(RET_INTERNAL_ERROR);
}
/* Create the timer */
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIG;
sev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) {
return(RET_INTERNAL_ERROR);
}
printf("timer ID is 0x%lx\n", (long) timerid);
/* Start the timer (value is in usec */
its.it_value.tv_sec = timerInterval / 1000000;
its.it_value.tv_nsec = (timerInterval % 1000000) * 1000;
its.it_interval.tv_sec = its.it_value.tv_sec;
its.it_interval.tv_nsec = its.it_value.tv_nsec;
if (timer_settime(timerid, 0, &its, NULL) == -1) {
return(RET_INTERNAL_ERROR);
}
return(RET_OK);
}
/***************************************************************************/
/**
* \internal
*
* \brief timerInt - timer interrupt
*
* \param
* \results
* nothing
*/
void timerInt (
int sig,
siginfo_t *si,
void *uc
)
{
(void)sig;
(void)si;
(void)uc;
// printf("Caught signal %d\n", sig);
coTimerTick();
}
#endif /* LINUX_SIGNAL_TIMER */
#ifdef LINUX_THREAD_TIMER
void *coTimerThreadFn(void *x_void_ptr);
/***************************************************************************/
/**
* \brief codrvTimerSetup - init Timer
*
* \param
* \results
* nothing
*/
RET_T codrvTimerSetup(
UNSIGNED32 timerInterval
)
{
static __useconds_t interval = 0;
interval = timerInterval;
pthread_t inc_x_thread = 0;
if (inc_x_thread != 0) {
printf("ERROR: resetting the CANopen timre not (yet) supported\n");
return(RET_INTERNAL_ERROR);
}
pthread_create(&inc_x_thread, NULL, coTimerThreadFn, &interval);
return(RET_OK);
}
/* thread timer callback */
void *coTimerThreadFn(void *x_void_ptr)
{
struct timeval tv;
static uint64_t l_time_usec_last=0;
__useconds_t interval = *(__useconds_t*)x_void_ptr;
while(1) {
gettimeofday(&tv, NULL);
uint64_t l_time_usec = (uint64_t)(tv.tv_sec) * (uint64_t)1000000ul + (uint64_t)(tv.tv_usec);
if (l_time_usec_last == 0) {
/* initialization */
l_time_usec_last = l_time_usec;
}
if (l_time_usec_last > l_time_usec) {
/* time decrease - was system time changed? */
l_time_usec_last = l_time_usec;
coTimerTick();
} else
if ((l_time_usec - l_time_usec_last) / (uint64_t)interval > 10u) {
/* too many time - was system time changed? */
l_time_usec_last = l_time_usec;
coTimerTick();
}
while (l_time_usec > (l_time_usec_last + (uint64_t)interval)) {
coTimerTick();
l_time_usec_last += (uint64_t)interval;
}
usleep(interval);
}
return(x_void_ptr);
}
#endif /* LINUX_THREAD_TIMER */
#ifdef THREAD_SPAWN
RET_T codrvTimerSetup(
UNSIGNED32 timerInterval
)
{
timer_t timerid;
struct sigevent sev;
struct itimerspec its;
/* Create the timer */
sev.sigev_notify = SIGEV_THREAD;
sev.sigev_signo = 0;
sev.sigev_value.sival_int = 0;
sev.sigev_notify_function = timerIntSpawnedThread;
sev.sigev_notify_attributes = 0;
if (timer_create(CLOCK_MONOTONIC, &sev, &timerid) == -1) {
return(RET_INTERNAL_ERROR);
}
printf("timer ID is 0x%lx\n", (long) timerid);
/* Start the timer (value is in usec */
its.it_value.tv_sec = timerInterval / 1000000;
its.it_value.tv_nsec = (timerInterval % 1000000) * 1000;
its.it_interval.tv_sec = its.it_value.tv_sec;
its.it_interval.tv_nsec = its.it_value.tv_nsec;
if (timer_settime(timerid, 0, &its, NULL) == -1) {
return(RET_INTERNAL_ERROR);
}
return(RET_OK);
}
static void timerIntSpawnedThread(
sigval_t sival
)
{
coTimerTick();
}
#endif /* THREAD_SPAWN */
#endif /* defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__) */