blob: c2c6c4c202a910b928fe374e6ef1f66772efb497 [file] [log] [blame]
Brian Silverman26e4e522015-12-17 01:56:40 -05001#include "HAL/Notifier.hpp"
2#include "ChipObject.h"
3#include "HAL/HAL.hpp"
4#include "HAL/cpp/priority_mutex.h"
5#include <atomic>
6#include <cstdlib>
7#include <mutex>
8
9static const uint32_t kTimerInterruptNumber = 28;
10
11static priority_mutex notifierInterruptMutex;
12static priority_recursive_mutex notifierMutex;
13static tAlarm *notifierAlarm = nullptr;
14static tInterruptManager *notifierManager = nullptr;
15static uint32_t closestTrigger = UINT32_MAX;
16struct Notifier {
17 Notifier *prev, *next;
18 void *param;
19 void (*process)(uint32_t, void*);
20 uint32_t triggerTime = UINT32_MAX;
21};
22static Notifier *notifiers = nullptr;
23static std::atomic_flag notifierAtexitRegistered = ATOMIC_FLAG_INIT;
24static std::atomic_int notifierRefCount{0};
25
26static void alarmCallback(uint32_t, void*)
27{
28 std::unique_lock<priority_recursive_mutex> sync(notifierMutex);
29
30 int32_t status = 0;
31 uint32_t currentTime = 0;
32
33 // the hardware disables itself after each alarm
34 closestTrigger = UINT32_MAX;
35
36 // process all notifiers
37 Notifier *notifier = notifiers;
38 while (notifier) {
39 if (notifier->triggerTime != UINT32_MAX) {
40 if (currentTime == 0)
41 currentTime = getFPGATime(&status);
42 if (notifier->triggerTime < currentTime) {
43 notifier->triggerTime = UINT32_MAX;
44 auto process = notifier->process;
45 auto param = notifier->param;
46 sync.unlock();
47 process(currentTime, param);
48 sync.lock();
49 } else if (notifier->triggerTime < closestTrigger) {
50 updateNotifierAlarm(notifier, notifier->triggerTime, &status);
51 }
52 }
53 notifier = notifier->next;
54 }
55}
56
57static void cleanupNotifierAtExit() {
58 notifierAlarm = nullptr;
59 notifierManager = nullptr;
60}
61
62void* initializeNotifier(void (*ProcessQueue)(uint32_t, void*), void *param, int32_t *status)
63{
64 if (!ProcessQueue) {
65 *status = NULL_PARAMETER;
66 return nullptr;
67 }
68 if (!notifierAtexitRegistered.test_and_set())
69 std::atexit(cleanupNotifierAtExit);
70 if (notifierRefCount.fetch_add(1) == 0) {
71 std::lock_guard<priority_mutex> sync(notifierInterruptMutex);
72 // create manager and alarm if not already created
73 if (!notifierManager) {
74 notifierManager = new tInterruptManager(1 << kTimerInterruptNumber, false, status);
75 notifierManager->registerHandler(alarmCallback, NULL, status);
76 notifierManager->enable(status);
77 }
78 if (!notifierAlarm) notifierAlarm = tAlarm::create(status);
79 }
80
81 std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
82 // create notifier structure and add to list
83 Notifier* notifier = new Notifier();
84 notifier->prev = nullptr;
85 notifier->next = notifiers;
86 if (notifier->next) notifier->next->prev = notifier;
87 notifier->param = param;
88 notifier->process = ProcessQueue;
89 notifiers = notifier;
90 return notifier;
91}
92
93void cleanNotifier(void* notifier_pointer, int32_t *status)
94{
95 {
96 std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
97 Notifier* notifier = (Notifier*)notifier_pointer;
98
99 // remove from list and delete
100 if (notifier->prev) notifier->prev->next = notifier->next;
101 if (notifier->next) notifier->next->prev = notifier->prev;
102 if (notifiers == notifier) notifiers = notifier->next;
103 delete notifier;
104 }
105
106 if (notifierRefCount.fetch_sub(1) == 1) {
107 std::lock_guard<priority_mutex> sync(notifierInterruptMutex);
108 // if this was the last notifier, clean up alarm and manager
109 if (notifierAlarm) {
110 notifierAlarm->writeEnable(false, status);
111 delete notifierAlarm;
112 notifierAlarm = nullptr;
113 }
114 if (notifierManager) {
115 notifierManager->disable(status);
116 delete notifierManager;
117 notifierManager = nullptr;
118 }
119 }
120}
121
122void updateNotifierAlarm(void* notifier_pointer, uint32_t triggerTime, int32_t *status)
123{
124 std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
125
126 Notifier* notifier = (Notifier*)notifier_pointer;
127 notifier->triggerTime = triggerTime;
128 bool wasActive = (closestTrigger != UINT32_MAX);
129
130 if (!notifierInterruptMutex.try_lock() || notifierRefCount == 0 ||
131 !notifierAlarm)
132 return;
133
134 // Update alarm time if closer than current.
135 if (triggerTime < closestTrigger) {
136 closestTrigger = triggerTime;
137 notifierAlarm->writeTriggerTime(triggerTime, status);
138 }
139 // Enable the alarm. The hardware disables itself after each alarm.
140 if (!wasActive) notifierAlarm->writeEnable(true, status);
141
142 notifierInterruptMutex.unlock();
143}