/* * 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 #include #include #include #include #include #include /* header of project specific types ---------------------------------------------------------------------------*/ #include #include #include #include /* 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__) */