blob: c30e8d1048a35f20b8f90d36e73ea82482ed450a [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -08002/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */
Brian Silverman41cdd3e2019-01-19 19:48:58 -08003/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "hal/Notifier.h"
9
10#include <atomic>
11#include <cstdlib> // For std::atexit()
12#include <memory>
13
14#include <wpi/condition_variable.h>
15#include <wpi/mutex.h>
16
17#include "HALInitializer.h"
18#include "hal/ChipObject.h"
19#include "hal/Errors.h"
20#include "hal/HAL.h"
21#include "hal/handles/UnlimitedHandleResource.h"
22
23using namespace hal;
24
25static constexpr int32_t kTimerInterruptNumber = 28;
26
27static wpi::mutex notifierMutex;
28static std::unique_ptr<tAlarm> notifierAlarm;
29static std::unique_ptr<tInterruptManager> notifierManager;
30static uint64_t closestTrigger{UINT64_MAX};
31
32namespace {
33
34struct Notifier {
35 uint64_t triggerTime = UINT64_MAX;
36 uint64_t triggeredTime = UINT64_MAX;
37 bool active = true;
38 wpi::mutex mutex;
39 wpi::condition_variable cond;
40};
41
42} // namespace
43
44static std::atomic_flag notifierAtexitRegistered{ATOMIC_FLAG_INIT};
45static std::atomic_int notifierRefCount{0};
46
47using namespace hal;
48
49class NotifierHandleContainer
50 : public UnlimitedHandleResource<HAL_NotifierHandle, Notifier,
51 HAL_HandleEnum::Notifier> {
52 public:
53 ~NotifierHandleContainer() {
54 ForEach([](HAL_NotifierHandle handle, Notifier* notifier) {
55 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080056 std::scoped_lock lock(notifier->mutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080057 notifier->triggerTime = UINT64_MAX;
58 notifier->triggeredTime = 0;
59 notifier->active = false;
60 }
61 notifier->cond.notify_all(); // wake up any waiting threads
62 });
63 }
64};
65
66static NotifierHandleContainer* notifierHandles;
67
68static void alarmCallback(uint32_t, void*) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080069 std::scoped_lock lock(notifierMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080070 int32_t status = 0;
71 uint64_t currentTime = 0;
72
73 // the hardware disables itself after each alarm
74 closestTrigger = UINT64_MAX;
75
76 // process all notifiers
77 notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
78 if (notifier->triggerTime == UINT64_MAX) return;
79 if (currentTime == 0) currentTime = HAL_GetFPGATime(&status);
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080080 std::unique_lock lock(notifier->mutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080081 if (notifier->triggerTime < currentTime) {
82 notifier->triggerTime = UINT64_MAX;
83 notifier->triggeredTime = currentTime;
84 lock.unlock();
85 notifier->cond.notify_all();
86 } else if (notifier->triggerTime < closestTrigger) {
87 closestTrigger = notifier->triggerTime;
88 }
89 });
90
91 if (notifierAlarm && closestTrigger != UINT64_MAX) {
92 // Simply truncate the hardware trigger time to 32-bit.
93 notifierAlarm->writeTriggerTime(static_cast<uint32_t>(closestTrigger),
94 &status);
95 // Enable the alarm. The hardware disables itself after each alarm.
96 notifierAlarm->writeEnable(true, &status);
97 }
98}
99
100static void cleanupNotifierAtExit() {
101 notifierAlarm = nullptr;
102 notifierManager = nullptr;
103}
104
105namespace hal {
106namespace init {
107void InitializeNotifier() {
108 static NotifierHandleContainer nH;
109 notifierHandles = &nH;
110}
111} // namespace init
112} // namespace hal
113
114extern "C" {
115
116HAL_NotifierHandle HAL_InitializeNotifier(int32_t* status) {
117 hal::init::CheckInit();
118 if (!notifierAtexitRegistered.test_and_set())
119 std::atexit(cleanupNotifierAtExit);
120
121 if (notifierRefCount.fetch_add(1) == 0) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800122 std::scoped_lock lock(notifierMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800123 // create manager and alarm if not already created
124 if (!notifierManager) {
125 notifierManager = std::make_unique<tInterruptManager>(
126 1 << kTimerInterruptNumber, false, status);
127 notifierManager->registerHandler(alarmCallback, nullptr, status);
128 notifierManager->enable(status);
129 }
130 if (!notifierAlarm) notifierAlarm.reset(tAlarm::create(status));
131 }
132
133 std::shared_ptr<Notifier> notifier = std::make_shared<Notifier>();
134 HAL_NotifierHandle handle = notifierHandles->Allocate(notifier);
135 if (handle == HAL_kInvalidHandle) {
136 *status = HAL_HANDLE_ERROR;
137 return HAL_kInvalidHandle;
138 }
139 return handle;
140}
141
James Kuszmaul4b81d302019-12-14 20:53:14 -0800142void HAL_SetNotifierName(HAL_NotifierHandle notifierHandle, const char* name,
143 int32_t* status) {}
144
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800145void HAL_StopNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
146 auto notifier = notifierHandles->Get(notifierHandle);
147 if (!notifier) return;
148
149 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800150 std::scoped_lock lock(notifier->mutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800151 notifier->triggerTime = UINT64_MAX;
152 notifier->triggeredTime = 0;
153 notifier->active = false;
154 }
155 notifier->cond.notify_all(); // wake up any waiting threads
156}
157
158void HAL_CleanNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
159 auto notifier = notifierHandles->Free(notifierHandle);
160 if (!notifier) return;
161
162 // Just in case HAL_StopNotifier() wasn't called...
163 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800164 std::scoped_lock lock(notifier->mutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800165 notifier->triggerTime = UINT64_MAX;
166 notifier->triggeredTime = 0;
167 notifier->active = false;
168 }
169 notifier->cond.notify_all();
170
171 if (notifierRefCount.fetch_sub(1) == 1) {
172 // if this was the last notifier, clean up alarm and manager
173 // the notifier can call back into our callback, so don't hold the lock
174 // here (the atomic fetch_sub will prevent multiple parallel entries
175 // into this function)
176
177 // Cleaning up the manager takes up to a second to complete, so don't do
178 // that here. Fix it more permanently in 2019...
179
180 // if (notifierAlarm) notifierAlarm->writeEnable(false, status);
181 // if (notifierManager) notifierManager->disable(status);
182
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800183 // std::scoped_lock lock(notifierMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800184 // notifierAlarm = nullptr;
185 // notifierManager = nullptr;
186 // closestTrigger = UINT64_MAX;
187 }
188}
189
190void HAL_UpdateNotifierAlarm(HAL_NotifierHandle notifierHandle,
191 uint64_t triggerTime, int32_t* status) {
192 auto notifier = notifierHandles->Get(notifierHandle);
193 if (!notifier) return;
194
195 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800196 std::scoped_lock lock(notifier->mutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800197 notifier->triggerTime = triggerTime;
198 notifier->triggeredTime = UINT64_MAX;
199 }
200
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800201 std::scoped_lock lock(notifierMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800202 // Update alarm time if closer than current.
203 if (triggerTime < closestTrigger) {
204 bool wasActive = (closestTrigger != UINT64_MAX);
205 closestTrigger = triggerTime;
206 // Simply truncate the hardware trigger time to 32-bit.
207 notifierAlarm->writeTriggerTime(static_cast<uint32_t>(closestTrigger),
208 status);
209 // Enable the alarm.
210 if (!wasActive) notifierAlarm->writeEnable(true, status);
211 }
212}
213
214void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle,
215 int32_t* status) {
216 auto notifier = notifierHandles->Get(notifierHandle);
217 if (!notifier) return;
218
219 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800220 std::scoped_lock lock(notifier->mutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800221 notifier->triggerTime = UINT64_MAX;
222 }
223}
224
225uint64_t HAL_WaitForNotifierAlarm(HAL_NotifierHandle notifierHandle,
226 int32_t* status) {
227 auto notifier = notifierHandles->Get(notifierHandle);
228 if (!notifier) return 0;
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800229 std::unique_lock lock(notifier->mutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800230 notifier->cond.wait(lock, [&] {
231 return !notifier->active || notifier->triggeredTime != UINT64_MAX;
232 });
233 return notifier->active ? notifier->triggeredTime : 0;
234}
235
236} // extern "C"