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