Brian Silverman | 26e4e52 | 2015-12-17 01:56:40 -0500 | [diff] [blame^] | 1 | #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 | |
| 9 | static const uint32_t kTimerInterruptNumber = 28; |
| 10 | |
| 11 | static priority_mutex notifierInterruptMutex; |
| 12 | static priority_recursive_mutex notifierMutex; |
| 13 | static tAlarm *notifierAlarm = nullptr; |
| 14 | static tInterruptManager *notifierManager = nullptr; |
| 15 | static uint32_t closestTrigger = UINT32_MAX; |
| 16 | struct Notifier { |
| 17 | Notifier *prev, *next; |
| 18 | void *param; |
| 19 | void (*process)(uint32_t, void*); |
| 20 | uint32_t triggerTime = UINT32_MAX; |
| 21 | }; |
| 22 | static Notifier *notifiers = nullptr; |
| 23 | static std::atomic_flag notifierAtexitRegistered = ATOMIC_FLAG_INIT; |
| 24 | static std::atomic_int notifierRefCount{0}; |
| 25 | |
| 26 | static 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 | |
| 57 | static void cleanupNotifierAtExit() { |
| 58 | notifierAlarm = nullptr; |
| 59 | notifierManager = nullptr; |
| 60 | } |
| 61 | |
| 62 | void* 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 | |
| 93 | void 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 | |
| 122 | void 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 | } |