blob: aedaaeafba6b3b90f27e00fb8c9cd01445d5d435 [file] [log] [blame]
#ifndef AOS_CRIO_SHARED_LIBS_INTERRUPT_BRIDGE_H_
#define AOS_CRIO_SHARED_LIBS_INTERRUPT_BRIDGE_H_
#include <wdLib.h>
#include <semLib.h>
#include <timers.h>
#include <signal.h>
#include "aos/common/scoped_ptr.h"
#include "aos/common/time.h"
#include "aos/common/macros.h"
class Task;
namespace aos {
namespace crio {
// Handles creating a separate Task at a high priority with a semaphore to
// link it to something else that provides timing information (likely in an ISR
// context).
template<typename T>
class InterruptBridge {
public:
// The default priority. Here as a constant for subclasses to use as a default
// too.
static const int kDefaultPriority = 96;
typedef void (*Handler)(T *param);
// Stops calling the handler at all until a Start function is called again.
void Stop();
protected:
// < 95 priority does not work, and < 100 isn't guaranteed to work
InterruptBridge(Handler handler, T *param, int priority);
// Subclasses should call StopNotifications.
virtual ~InterruptBridge();
// Subclasses should call this whenever the Notifier triggers.
// It is safe to call from an ISR.
void Notify();
// Starts the actual Task.
void StartTask();
private:
const Handler handler_;
T *const param_;
// Subclasses should do whatever they have to to stop calling Notify().
virtual void StopNotifications() = 0;
// The function that the Task runs.
// Loops forever, waiting for sem_ and then calling handler_.
static void HandlerLoop(void *self_in);
const scoped_ptr<Task> task_;
// For synchronizing between the Task and Notify().
SEM_ID sem_;
DISALLOW_COPY_AND_ASSIGN(InterruptBridge<T>);
};
// An accurate version of WPILib's Notifier class.
template<typename T>
class PeriodicNotifier : public InterruptBridge<T> {
public:
// Period is how much to wait each time between running the handler.
void StartPeriodic(time::Time period);
// DEPRECATED(brians): use the overload that takes a time::Time
void StartPeriodic(double seconds) {
StartPeriodic(time::Time::InSeconds(seconds));
}
// After StartPeriodic is called, returns whether or not the subclass can
// actually call the callback exactly on those intervals or whether it will
// call it on some rounded amount.
//
// Default implementation assumes that the subclass has sysClockRateGet()
// resolution. Override if this is not true.
virtual bool IsExact() {
return period_ == time::Time::InTicks(period_.ToTicks());
}
time::Time period() { return period_; }
protected:
PeriodicNotifier(typename InterruptBridge<T>::Handler handler, T *param,
int priority);
virtual ~PeriodicNotifier() {}
private:
virtual void StopNotifications() = 0;
// Subclasses should do whatever they have to to start calling Notify() every
// period_.
virtual void StartNotifications() = 0;
time::Time period_;
};
// This one works accurately, but it has the potential to drift over time.
// It has only sysClockRateGet() resolution.
template<typename T>
class WDInterruptNotifier : public PeriodicNotifier<T> {
public:
WDInterruptNotifier(typename InterruptBridge<T>::Handler handler,
T *param = NULL,
int priority = InterruptBridge<T>::kDefaultPriority);
virtual ~WDInterruptNotifier();
private:
// The argument is the general callback parameter which will be a pointer to
// an instance. This function calls Notify() on that instance.
static void StaticNotify(void *self_in);
virtual void StopNotifications();
virtual void StartNotifications();
WDOG_ID wd_;
int delay_; // what to pass to wdStart
DISALLOW_COPY_AND_ASSIGN(WDInterruptNotifier<T>);
};
// Vxworks (really really) should take care of making sure that this one
// doesn't drift over time, but IT DOESN'T!! Brian found the implementation
// online (file timerLib.c), and it's just doing the same thing as
// WDInterruptNotifier, except with extra conversions and overhead to implement
// the POSIX standard. There really is no reason to use this.
// It has only sysClockRateGet() resolution.
template<typename T>
class TimerNotifier : public PeriodicNotifier<T> {
public:
// unique should be different for all instances created in the same Task. It
// can range from 0 to (SIGRTMAX - SIGRTMIN). This instance will use the
// signal (SIGRTMIN + unique), so nothing else should use that signal.
TimerNotifier(typename InterruptBridge<T>::Handler handler,
T *param = NULL,
int unique = 0,
int priority = InterruptBridge<T>::kDefaultPriority);
virtual ~TimerNotifier();
private:
// The first argument is the signal number that is being triggered. This
// function looks up a pointer to an instance in timer_notifiers using that
// and calls Notify() on that instance.
static void StaticNotify(int signum);
virtual void StopNotifications();
virtual void StartNotifications();
timer_t timer_;
// Which signal timer_ will notify on.
const int signal_;
// What the action for signal_ was before we started messing with it.
struct sigaction old_sa_;
DISALLOW_COPY_AND_ASSIGN(TimerNotifier<T>);
};
} // namespace crio
} // namespace aos
#include "aos/crio/shared_libs/interrupt_bridge-tmpl.h"
#endif // AOS_CRIO_SHARED_LIBS_INTERRUPT_BRIDGE_H_