blob: ef53dce0c56655805b4b0ebb921f90130b66e101 [file] [log] [blame]
#include <sysLib.h>
#include <usrLib.h>
#include <sigevent.h>
#include "WPILib/Task.h"
#include "aos/common/logging/logging.h"
#include "aos/crio/motor_server/MotorServer.h"
extern "C" {
// A really simple function implemented in a .c file because the header that
// declares the variable doesn't work in C++.
// Returns the value of the global variable excExtendedVectors declared by
// <usrConfig.h>.
int aos_getExcExtendedVectors();
}
namespace aos {
namespace crio {
// Conceptually a TimerNotifier*[]. Used by the signal handler so that it can
// figure out which instance it's supposed to be using.
// Doesn't need a lock because a value is put in here before the signal handler
// is set up, so everything that might have concurrency issues will be reads
// which will happen after the set to each index.
// Declared like this instead of as an actual array because its size might be
// determined dynamically (SIGRTMAX and SIGRTMIN can be function calls).
extern void **const timer_notifiers;
template<typename T>
InterruptBridge<T>::InterruptBridge(Handler handler,
T *param, int priority)
: handler_(handler),
param_(param),
task_(new Task("_NotifierLooper", reinterpret_cast<FUNCPTR>(HandlerLoop),
priority)),
sem_(semBCreate(SEM_Q_PRIORITY, SEM_EMPTY)) {
// Basically, makes sure that it does -mlongcall for calling interrupt handler
// functions.
// See <http://www.vxdev.com/docs/vx55man/vxworks/ppc/powerpc.html#91321> for
// details about why this is important.
if (!aos_getExcExtendedVectors()) {
LOG(FATAL, "extended-call interrupts are not enabled\n");
}
}
template<typename T>
InterruptBridge<T>::~InterruptBridge() {
// Can't call Stop() because that calls StopNotifications(), which is virtual,
// so you can't call it after the subclass's destructor has run.
semDelete(sem_);
}
template<typename T>
void InterruptBridge<T>::StartTask() {
// The inner cast to InterruptBridge<T>* is so that Loop knows what to
// cast the void* to (the implementation of polymorphism means that
// casting the pointer might actually change its value).
task_->Start(reinterpret_cast<UINT32>(
static_cast<InterruptBridge<T> *>(this)));
}
template<typename T>
void InterruptBridge<T>::Stop() {
StopNotifications();
task_->Stop();
}
template<typename T>
void InterruptBridge<T>::HandlerLoop(void *self_in) {
InterruptBridge<T> *const self = static_cast<InterruptBridge<T> *>(self_in);
while (true) {
semTake(self->sem_, WAIT_FOREVER);
self->handler_(self->param_);
}
}
template<typename T>
PeriodicNotifier<T>::PeriodicNotifier(
typename InterruptBridge<T>::Handler handler,
T *param, int priority)
: InterruptBridge<T>(handler, param, priority),
period_(-1, -1) {}
template<typename T>
void PeriodicNotifier<T>::StartPeriodic(time::Time period) {
period_ = period;
this->StartTask();
StartNotifications();
}
template<typename T>
TimerNotifier<T>::TimerNotifier(typename InterruptBridge<T>::Handler handler,
T *param, int unique, int priority)
: PeriodicNotifier<T>(handler, param, priority),
signal_(SIGRTMIN + unique) {
timer_notifiers[signal_] = static_cast<void *>(this);
struct sigaction sa;
sa.sa_flags = 0;
sa.sa_handler = StaticNotify;
sigemptyset(&sa.sa_mask);
if (sigaction(signal_, &sa, &old_sa_) != OK) {
LOG(FATAL, "sigaction(%d, %p, %p) failed with %d: %s\n",
signal_, &sa, &old_sa_, errno, strerror(errno));
}
sigevent event;
event.sigev_notify = SIGEV_TASK_SIGNAL;
event.sigev_signo = signal_;
event.sigev_value.sival_ptr = this;
if (timer_create(CLOCK_REALTIME, &event, &timer_) != OK) {
LOG(FATAL, "timer_create(CLOCK_REALTIME, %p, %p) failed with %d: %s\n",
&event, &timer_, errno, strerror(errno));
}
}
template<typename T>
TimerNotifier<T>::~TimerNotifier() {
if (timer_delete(timer_) != OK) {
LOG(FATAL, "timer_delete(%p) failed with %d: %s\n", timer_,
errno, strerror(errno));
}
if (sigaction(signal_, &old_sa_, NULL) != OK) {
LOG(FATAL, "sigaction(%d, %p, NULL) failed with %d: %s\n",
signal_, &old_sa_, errno, strerror(errno));
}
StopNotifications();
}
template<typename T>
void TimerNotifier<T>::StartNotifications() {
itimerspec timer_spec;
timer_spec.it_value.tv_sec = 0;
timer_spec.it_value.tv_nsec = 1; // 0 would mean to disarm the timer
timer_spec.it_interval = this->period().ToTimespec();
if (timer_settime(timer_, 0, &timer_spec, NULL) != OK) {
LOG(FATAL, "timer_settime(%p, 0, %p, NULL) failed with %d: %s\n",
timer_, &timer_spec, errno, strerror(errno));
}
}
template<typename T>
void TimerNotifier<T>::StopNotifications() {
timer_cancel(timer_);
}
template<typename T>
WDInterruptNotifier<T>::WDInterruptNotifier(
typename InterruptBridge<T>::Handler handler, T *param, int priority)
: PeriodicNotifier<T>(handler, param, priority), wd_(wdCreate()) {
if (wd_ == NULL) {
LOG(FATAL, "wdCreate() failed with %d: %s\n", errno, strerror(errno));
}
}
template<typename T>
WDInterruptNotifier<T>::~WDInterruptNotifier() {
if (wdDelete(wd_) != OK) {
LOG(FATAL, "wdDelete(%p) failed with %d: %s\n",
wd_, errno, strerror(errno));
}
StopNotifications();
}
template<typename T>
void WDInterruptNotifier<T>::StartNotifications() {
delay_ = this->period().ToTicks();
if (wdStart(wd_,
1, // run it really soon
(FUNCPTR)StaticNotify,
(UINT32)this) != OK) {
LOG(FATAL, "wdStart(%p) failed with %d: %s", wd_, errno, strerror(errno));
}
}
template<typename T>
void WDInterruptNotifier<T>::StopNotifications() {
wdCancel(wd_);
}
// THESE GET CALLED AT INTERRUPT LEVEL. BE CAREFUL CHANGING THEM!!
//
// It might be tempting to use floating point math here, but DON'T!
//
// Also, do NOT use 64-bit integers (at least not without checking the assembly
// code again). GCC optimizes those using the floating point registers (see
// <http://compgroups.net/comp.os.vxworks/int64-where-gcc-ppc-and-vxworks-don-t-play-we/1110156>
// for an example of that being a problem).
//
// The assembly code comments are in there to make looking at assembly code
// dumps to verify that there aren't any floating point instructions easier.
// brians did that on 10/14/12 to his then-uncommited code and didn't find any.
// For future reference, here is a list of powerpc instruction mnemonics (what
// g++ -S gives) that do not involve any floating point:
// lwz
// lis
// mflr
// la
// stwu
// stw
// mr
// mtctr
// bctrl
// addi
// mtlr
// blr
// cmpwi
// bne
// li
#define ASM_COMMENT(str) __asm__("# " str)
template<typename T>
void WDInterruptNotifier<T>::StaticNotify(void *self_in) {
ASM_COMMENT("beginning of WDInterruptNotifier::StaticNotify");
WDInterruptNotifier<T> *const self =
static_cast<WDInterruptNotifier<T> *>(self_in);
wdStart(self->wd_,
self->delay_,
(FUNCPTR)StaticNotify,
(UINT32)self); // call the same thing again
self->Notify();
ASM_COMMENT("end of WDInterruptNotifier::StaticNotify");
}
template<typename T>
void TimerNotifier<T>::StaticNotify(int signum) {
ASM_COMMENT("beginning of TimerNotifier::StaticNotify");
TimerNotifier<T> *const self =
static_cast<TimerNotifier<T> *>(timer_notifiers[signum]);
self->Notify();
ASM_COMMENT("end of TimerNotifier::StaticNotify");
}
template<typename T>
void InterruptBridge<T>::Notify() {
ASM_COMMENT("beginning of InterruptBridge::Notify");
semGive(sem_);
ASM_COMMENT("end of InterruptBridge::Notify");
}
} // namespace crio
} // namespace aos