blob: cc00ab82ace144927657d6f6d05858eb5d2b84c4 [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#include <sysLib.h>
2#include <usrLib.h>
3#include <sigevent.h>
4
5#include "WPILib/Task.h"
6
brians56032682013-03-02 21:00:17 +00007#include "aos/common/logging/logging.h"
brians343bc112013-02-10 01:53:46 +00008#include "aos/common/time.h"
9
10extern "C" {
11// A really simple function implemented in a .c file because the header that
12// declares the variable doesn't work in C++.
13// Returns the value of the global variable excExtendedVectors declared by
14// <usrConfig.h>.
15int aos_getExcExtendedVectors();
16}
17
18namespace aos {
19namespace crio {
20
21// Conceptually a TimerNotifier*[]. Used by the signal handler so that it can
22// figure out which instance it's supposed to be using.
23// Doesn't need a lock because a value is put in here before the signal handler
24// is set up, so everything that might have concurrency issues will be reads
25// which will happen after the set to each index.
26// Declared like this instead of as an actual array because its size might be
27// determined dynamically (SIGRTMAX and SIGRTMIN can be function calls).
28extern void **const timer_notifiers;
29
30template<typename T>
31InterruptBridge<T>::InterruptBridge(Handler handler,
32 T *param, int priority)
33 : handler_(handler),
34 param_(param),
35 task_(new Task("_NotifierLooper", reinterpret_cast<FUNCPTR>(HandlerLoop),
36 priority)),
37 sem_(semBCreate(SEM_Q_PRIORITY, SEM_EMPTY)) {
38 // Basically, makes sure that it does -mlongcall for calling interrupt handler
39 // functions.
40 // See <http://www.vxdev.com/docs/vx55man/vxworks/ppc/powerpc.html#91321> for
41 // details about why this is important.
42 if (!aos_getExcExtendedVectors()) {
43 LOG(FATAL, "extended-call interrupts are not enabled\n");
44 }
45}
46
47template<typename T>
48InterruptBridge<T>::~InterruptBridge() {
49 // Can't call Stop() because that calls StopNotifications(), which is virtual,
50 // so you can't call it after the subclass's destructor has run.
51 semDelete(sem_);
52}
53
54template<typename T>
55void InterruptBridge<T>::StartTask() {
56 // The inner cast to InterruptBridge<T>* is so that Loop knows what to
57 // cast the void* to (the implementation of polymorphism means that
58 // casting the pointer might actually change its value).
59 task_->Start(reinterpret_cast<UINT32>(
60 static_cast<InterruptBridge<T> *>(this)));
61}
62
63template<typename T>
64void InterruptBridge<T>::Stop() {
65 StopNotifications();
66 task_->Stop();
67}
68
69template<typename T>
70void InterruptBridge<T>::HandlerLoop(void *self_in) {
71 InterruptBridge<T> *const self = static_cast<InterruptBridge<T> *>(self_in);
72 while (true) {
73 semTake(self->sem_, WAIT_FOREVER);
74 self->handler_(self->param_);
75 }
76}
77
78template<typename T>
79PeriodicNotifier<T>::PeriodicNotifier(
80 typename InterruptBridge<T>::Handler handler,
81 T *param, int priority)
82 : InterruptBridge<T>(handler, param, priority) {}
83
84template<typename T>
85void PeriodicNotifier<T>::StartPeriodic(double period) {
86 this->StartTask();
87 StartNotifications(period);
88}
89
90template<typename T>
91TimerNotifier<T>::TimerNotifier(typename InterruptBridge<T>::Handler handler,
92 T *param, int unique, int priority)
93 : PeriodicNotifier<T>(handler, param, priority),
94 signal_(SIGRTMIN + unique) {
95 timer_notifiers[signal_] = static_cast<void *>(this);
96
97 struct sigaction sa;
98 sa.sa_flags = 0;
99 sa.sa_handler = StaticNotify;
100 sigemptyset(&sa.sa_mask);
101 if (sigaction(signal_, &sa, &old_sa_) != OK) {
102 LOG(FATAL, "sigaction(%d, %p, %p) failed with %d: %s\n",
103 signal_, &sa, &old_sa_, errno, strerror(errno));
104 }
105
106 sigevent event;
107 event.sigev_notify = SIGEV_TASK_SIGNAL;
108 event.sigev_signo = signal_;
109 event.sigev_value.sival_ptr = this;
110 if (timer_create(CLOCK_REALTIME, &event, &timer_) != OK) {
111 LOG(FATAL, "timer_create(CLOCK_REALTIME, %p, %p) failed with %d: %s\n",
112 &event, &timer_, errno, strerror(errno));
113 }
114}
115
116template<typename T>
117TimerNotifier<T>::~TimerNotifier() {
118 if (timer_delete(timer_) != OK) {
119 LOG(FATAL, "timer_delete(%p) failed with %d: %s\n", timer_,
120 errno, strerror(errno));
121 }
122 if (sigaction(signal_, &old_sa_, NULL) != OK) {
123 LOG(FATAL, "sigaction(%d, %p, NULL) failed with %d: %s\n",
124 signal_, &old_sa_, errno, strerror(errno));
125 }
126 StopNotifications();
127}
128
129template<typename T>
130void TimerNotifier<T>::StartNotifications(double period) {
131 itimerspec timer_spec;
132 timer_spec.it_value.tv_sec = 0;
133 timer_spec.it_value.tv_nsec = 1; // 0 would mean to disarm the timer
134 timer_spec.it_interval = time::Time::InSeconds(period).ToTimespec();
135 if (timer_settime(timer_, 0, &timer_spec, NULL) != OK) {
136 LOG(FATAL, "timer_settime(%p, 0, %p, NULL) failed with %d: %s\n",
137 timer_, &timer_spec, errno, strerror(errno));
138 }
139}
140
141template<typename T>
142void TimerNotifier<T>::StopNotifications() {
143 timer_cancel(timer_);
144}
145
146template<typename T>
147WDInterruptNotifier<T>::WDInterruptNotifier(
148 typename InterruptBridge<T>::Handler handler, T *param, int priority)
149 : PeriodicNotifier<T>(handler, param, priority), wd_(wdCreate()) {
150 if (wd_ == NULL) {
151 LOG(FATAL, "wdCreate() failed with %d: %s\n", errno, strerror(errno));
152 }
153}
154
155template<typename T>
156WDInterruptNotifier<T>::~WDInterruptNotifier() {
157 if (wdDelete(wd_) != OK) {
158 LOG(FATAL, "wdDelete(%p) failed with %d: %s\n",
159 wd_, errno, strerror(errno));
160 }
161 StopNotifications();
162}
163
164template<typename T>
165void WDInterruptNotifier<T>::StartNotifications(double period) {
166 delay_ = time::Time::InSeconds(period).ToTicks();
167
168 if (wdStart(wd_,
169 1, // run it really soon
170 (FUNCPTR)StaticNotify,
171 (UINT32)this) != OK) {
172 LOG(FATAL, "wdStart(%p) failed with %d: %s", wd_, errno, strerror(errno));
173 }
174}
175
176template<typename T>
177void WDInterruptNotifier<T>::StopNotifications() {
178 wdCancel(wd_);
179}
180
181// THESE GET CALLED AT INTERRUPT LEVEL. BE CAREFUL CHANGING THEM!!
182//
183// It might be tempting to use floating point math here, but DON'T!
184//
185// Also, do NOT use 64-bit integers (at least not without checking the assembly
186// code again). GCC optimizes those using the floating point registers (see
187// <http://compgroups.net/comp.os.vxworks/int64-where-gcc-ppc-and-vxworks-don-t-play-we/1110156>
188// for an example of that being a problem).
189//
190// The assembly code comments are in there to make looking at assembly code
191// dumps to verify that there aren't any floating point instructions easier.
192// brians did that on 10/14/12 to his then-uncommited code and didn't find any.
193// For future reference, here is a list of powerpc instruction mnemonics (what
194// g++ -S gives) that do not involve any floating point:
195// lwz
196// lis
197// mflr
198// la
199// stwu
200// stw
201// mr
202// mtctr
203// bctrl
204// addi
205// mtlr
206// blr
207// cmpwi
208// bne
209// li
brians343bc112013-02-10 01:53:46 +0000210#define ASM_COMMENT(str) __asm__("# " str)
211template<typename T>
212void WDInterruptNotifier<T>::StaticNotify(void *self_in) {
213 ASM_COMMENT("beginning of WDInterruptNotifier::StaticNotify");
214 WDInterruptNotifier<T> *const self =
215 static_cast<WDInterruptNotifier<T> *>(self_in);
216 wdStart(self->wd_,
217 self->delay_,
218 (FUNCPTR)StaticNotify,
219 (UINT32)self); // call the same thing again
220 self->Notify();
221 ASM_COMMENT("end of WDInterruptNotifier::StaticNotify");
222}
223
224template<typename T>
225void TimerNotifier<T>::StaticNotify(int signum) {
226 ASM_COMMENT("beginning of TimerNotifier::StaticNotify");
227 TimerNotifier<T> *const self =
228 static_cast<TimerNotifier<T> *>(timer_notifiers[signum]);
229 self->Notify();
230 ASM_COMMENT("end of TimerNotifier::StaticNotify");
231}
232
233template<typename T>
234void InterruptBridge<T>::Notify() {
235 ASM_COMMENT("beginning of InterruptBridge::Notify");
236 semGive(sem_);
237 ASM_COMMENT("end of InterruptBridge::Notify");
238}
239
240} // namespace crio
241} // namespace aos