blob: 2184b5aec52a15c4d55fd11095ae5da5e8dbce8c [file] [log] [blame]
Brian Silverman1a675112016-02-20 20:42:49 -05001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2016. All Rights Reserved. */
3/* 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
Brian Silverman26e4e522015-12-17 01:56:40 -05008#include "HAL/Notifier.hpp"
9#include "ChipObject.h"
10#include "HAL/HAL.hpp"
11#include "HAL/cpp/priority_mutex.h"
12#include <atomic>
13#include <cstdlib>
14#include <mutex>
15
16static const uint32_t kTimerInterruptNumber = 28;
17
18static priority_mutex notifierInterruptMutex;
19static priority_recursive_mutex notifierMutex;
20static tAlarm *notifierAlarm = nullptr;
21static tInterruptManager *notifierManager = nullptr;
Brian Silverman1a675112016-02-20 20:42:49 -050022static uint64_t closestTrigger = UINT64_MAX;
Brian Silverman26e4e522015-12-17 01:56:40 -050023struct Notifier {
24 Notifier *prev, *next;
25 void *param;
Brian Silverman1a675112016-02-20 20:42:49 -050026 void (*process)(uint64_t, void*);
27 uint64_t triggerTime = UINT64_MAX;
Brian Silverman26e4e522015-12-17 01:56:40 -050028};
29static Notifier *notifiers = nullptr;
30static std::atomic_flag notifierAtexitRegistered = ATOMIC_FLAG_INIT;
31static std::atomic_int notifierRefCount{0};
32
33static void alarmCallback(uint32_t, void*)
34{
35 std::unique_lock<priority_recursive_mutex> sync(notifierMutex);
36
37 int32_t status = 0;
Brian Silverman1a675112016-02-20 20:42:49 -050038 uint64_t currentTime = 0;
Brian Silverman26e4e522015-12-17 01:56:40 -050039
40 // the hardware disables itself after each alarm
Brian Silverman1a675112016-02-20 20:42:49 -050041 closestTrigger = UINT64_MAX;
Brian Silverman26e4e522015-12-17 01:56:40 -050042
43 // process all notifiers
44 Notifier *notifier = notifiers;
45 while (notifier) {
Brian Silverman1a675112016-02-20 20:42:49 -050046 if (notifier->triggerTime != UINT64_MAX) {
Brian Silverman26e4e522015-12-17 01:56:40 -050047 if (currentTime == 0)
48 currentTime = getFPGATime(&status);
49 if (notifier->triggerTime < currentTime) {
Brian Silverman1a675112016-02-20 20:42:49 -050050 notifier->triggerTime = UINT64_MAX;
Brian Silverman26e4e522015-12-17 01:56:40 -050051 auto process = notifier->process;
52 auto param = notifier->param;
53 sync.unlock();
54 process(currentTime, param);
55 sync.lock();
56 } else if (notifier->triggerTime < closestTrigger) {
57 updateNotifierAlarm(notifier, notifier->triggerTime, &status);
58 }
59 }
60 notifier = notifier->next;
61 }
62}
63
64static void cleanupNotifierAtExit() {
65 notifierAlarm = nullptr;
66 notifierManager = nullptr;
67}
68
Brian Silverman1a675112016-02-20 20:42:49 -050069extern "C" {
70
71void* initializeNotifier(void (*process)(uint64_t, void*), void *param, int32_t *status)
Brian Silverman26e4e522015-12-17 01:56:40 -050072{
Brian Silverman1a675112016-02-20 20:42:49 -050073 if (!process) {
Brian Silverman26e4e522015-12-17 01:56:40 -050074 *status = NULL_PARAMETER;
75 return nullptr;
76 }
77 if (!notifierAtexitRegistered.test_and_set())
78 std::atexit(cleanupNotifierAtExit);
79 if (notifierRefCount.fetch_add(1) == 0) {
80 std::lock_guard<priority_mutex> sync(notifierInterruptMutex);
81 // create manager and alarm if not already created
82 if (!notifierManager) {
83 notifierManager = new tInterruptManager(1 << kTimerInterruptNumber, false, status);
84 notifierManager->registerHandler(alarmCallback, NULL, status);
85 notifierManager->enable(status);
86 }
87 if (!notifierAlarm) notifierAlarm = tAlarm::create(status);
88 }
89
90 std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
91 // create notifier structure and add to list
92 Notifier* notifier = new Notifier();
93 notifier->prev = nullptr;
94 notifier->next = notifiers;
95 if (notifier->next) notifier->next->prev = notifier;
96 notifier->param = param;
Brian Silverman1a675112016-02-20 20:42:49 -050097 notifier->process = process;
Brian Silverman26e4e522015-12-17 01:56:40 -050098 notifiers = notifier;
99 return notifier;
100}
101
102void cleanNotifier(void* notifier_pointer, int32_t *status)
103{
104 {
105 std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
106 Notifier* notifier = (Notifier*)notifier_pointer;
107
108 // remove from list and delete
109 if (notifier->prev) notifier->prev->next = notifier->next;
110 if (notifier->next) notifier->next->prev = notifier->prev;
111 if (notifiers == notifier) notifiers = notifier->next;
112 delete notifier;
113 }
114
115 if (notifierRefCount.fetch_sub(1) == 1) {
116 std::lock_guard<priority_mutex> sync(notifierInterruptMutex);
117 // if this was the last notifier, clean up alarm and manager
118 if (notifierAlarm) {
119 notifierAlarm->writeEnable(false, status);
120 delete notifierAlarm;
121 notifierAlarm = nullptr;
122 }
123 if (notifierManager) {
124 notifierManager->disable(status);
125 delete notifierManager;
126 notifierManager = nullptr;
127 }
128 }
129}
130
Brian Silverman1a675112016-02-20 20:42:49 -0500131void* getNotifierParam(void* notifier_pointer, int32_t *status)
132{
133 return ((Notifier*)notifier_pointer)->param;
134}
135
136void updateNotifierAlarm(void* notifier_pointer, uint64_t triggerTime, int32_t *status)
Brian Silverman26e4e522015-12-17 01:56:40 -0500137{
138 std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
139
140 Notifier* notifier = (Notifier*)notifier_pointer;
141 notifier->triggerTime = triggerTime;
Brian Silverman1a675112016-02-20 20:42:49 -0500142 bool wasActive = (closestTrigger != UINT64_MAX);
Brian Silverman26e4e522015-12-17 01:56:40 -0500143
Brian Silverman1a675112016-02-20 20:42:49 -0500144 if (!notifierInterruptMutex.try_lock() || notifierRefCount == 0 ||
145 !notifierAlarm)
146 return;
Brian Silverman26e4e522015-12-17 01:56:40 -0500147
Brian Silverman1a675112016-02-20 20:42:49 -0500148 // Update alarm time if closer than current.
Brian Silverman26e4e522015-12-17 01:56:40 -0500149 if (triggerTime < closestTrigger) {
150 closestTrigger = triggerTime;
Brian Silverman1a675112016-02-20 20:42:49 -0500151 // Simply truncate the hardware trigger time to 32-bit.
152 notifierAlarm->writeTriggerTime((uint32_t)triggerTime, status);
Brian Silverman26e4e522015-12-17 01:56:40 -0500153 }
154 // Enable the alarm. The hardware disables itself after each alarm.
155 if (!wasActive) notifierAlarm->writeEnable(true, status);
156
157 notifierInterruptMutex.unlock();
158}
Brian Silverman1a675112016-02-20 20:42:49 -0500159
160void stopNotifierAlarm(void* notifier_pointer, int32_t *status)
161{
162 std::lock_guard<priority_recursive_mutex> sync(notifierMutex);
163 Notifier* notifier = (Notifier*)notifier_pointer;
164 notifier->triggerTime = UINT64_MAX;
165}
166
167} // extern "C"