319 lines
7.5 KiB
C
319 lines
7.5 KiB
C
/*
|
|
* cpu_linux.c - contains driver for linux
|
|
*
|
|
* Copyright (c) 2012-2023 emotas embedded communication GmbH
|
|
*-------------------------------------------------------------------
|
|
* $Id: cpu_linux.c 54592 2024-07-08 13:01:02Z 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(
|
|
CO_LINE_TYPE CO_LINE /**< can line */
|
|
)
|
|
{
|
|
/* enable CAN interrupts */
|
|
(void)(CO_LINE);
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/**
|
|
* \brief codrvCanDisableInterrupt - disable the CAN interrupt
|
|
*
|
|
*/
|
|
void codrvCanDisableInterrupt(
|
|
CO_LINE_TYPE CO_LINE /**< can line */
|
|
)
|
|
{
|
|
/* disable CAN interrupts */
|
|
(void)(CO_LINE);
|
|
}
|
|
|
|
|
|
#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__) */
|