blob: 78d518c38c5cde473483e34807a02b07e0c3a08c [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/Interrupts.h"
9
10#include <memory>
11
12#include <wpi/SafeThread.h>
13
14#include "DigitalInternal.h"
15#include "HALInitializer.h"
16#include "PortsInternal.h"
17#include "hal/ChipObject.h"
18#include "hal/Errors.h"
19#include "hal/handles/HandlesInternal.h"
20#include "hal/handles/LimitedHandleResource.h"
21
22using namespace hal;
23
24namespace {
25// Safe thread to allow callbacks to run on their own thread
26class InterruptThread : public wpi::SafeThread {
27 public:
28 void Main() {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080029 std::unique_lock lock(m_mutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080030 while (m_active) {
31 m_cond.wait(lock, [&] { return !m_active || m_notify; });
32 if (!m_active) break;
33 m_notify = false;
34 HAL_InterruptHandlerFunction handler = m_handler;
35 uint32_t mask = m_mask;
36 void* param = m_param;
37 lock.unlock(); // don't hold mutex during callback execution
38 handler(mask, param);
39 lock.lock();
40 }
41 }
42
43 bool m_notify = false;
44 HAL_InterruptHandlerFunction m_handler;
45 void* m_param;
46 uint32_t m_mask;
47};
48
49class InterruptThreadOwner : public wpi::SafeThreadOwner<InterruptThread> {
50 public:
51 void SetFunc(HAL_InterruptHandlerFunction handler, void* param) {
52 auto thr = GetThread();
53 if (!thr) return;
54 thr->m_handler = handler;
55 thr->m_param = param;
56 }
57
58 void Notify(uint32_t mask) {
59 auto thr = GetThread();
60 if (!thr) return;
61 thr->m_mask = mask;
62 thr->m_notify = true;
63 thr->m_cond.notify_one();
64 }
65};
66
Brian Silverman41cdd3e2019-01-19 19:48:58 -080067struct Interrupt {
68 std::unique_ptr<tInterrupt> anInterrupt;
69 std::unique_ptr<tInterruptManager> manager;
70 std::unique_ptr<InterruptThreadOwner> threadOwner = nullptr;
71 void* param = nullptr;
72};
73
James Kuszmaul4b81d302019-12-14 20:53:14 -080074} // namespace
75
76static void threadedInterruptHandler(uint32_t mask, void* param) {
77 static_cast<InterruptThreadOwner*>(param)->Notify(mask);
78}
79
Brian Silverman41cdd3e2019-01-19 19:48:58 -080080static LimitedHandleResource<HAL_InterruptHandle, Interrupt, kNumInterrupts,
81 HAL_HandleEnum::Interrupt>* interruptHandles;
82
83namespace hal {
84namespace init {
85void InitialzeInterrupts() {
86 static LimitedHandleResource<HAL_InterruptHandle, Interrupt, kNumInterrupts,
87 HAL_HandleEnum::Interrupt>
88 iH;
89 interruptHandles = &iH;
90}
91} // namespace init
92} // namespace hal
93
94extern "C" {
95
96HAL_InterruptHandle HAL_InitializeInterrupts(HAL_Bool watcher,
97 int32_t* status) {
98 hal::init::CheckInit();
99 HAL_InterruptHandle handle = interruptHandles->Allocate();
100 if (handle == HAL_kInvalidHandle) {
101 *status = NO_AVAILABLE_RESOURCES;
102 return HAL_kInvalidHandle;
103 }
104 auto anInterrupt = interruptHandles->Get(handle);
105 uint32_t interruptIndex = static_cast<uint32_t>(getHandleIndex(handle));
106 // Expects the calling leaf class to allocate an interrupt index.
107 anInterrupt->anInterrupt.reset(tInterrupt::create(interruptIndex, status));
108 anInterrupt->anInterrupt->writeConfig_WaitForAck(false, status);
109 anInterrupt->manager = std::make_unique<tInterruptManager>(
110 (1u << interruptIndex) | (1u << (interruptIndex + 8u)), watcher, status);
111 return handle;
112}
113
114void* HAL_CleanInterrupts(HAL_InterruptHandle interruptHandle,
115 int32_t* status) {
116 auto anInterrupt = interruptHandles->Get(interruptHandle);
117 interruptHandles->Free(interruptHandle);
118 if (anInterrupt == nullptr) {
119 return nullptr;
120 }
James Kuszmaul4b81d302019-12-14 20:53:14 -0800121
122 if (anInterrupt->manager->isEnabled(status)) {
123 anInterrupt->manager->disable(status);
124 }
125
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800126 void* param = anInterrupt->param;
127 return param;
128}
129
130int64_t HAL_WaitForInterrupt(HAL_InterruptHandle interruptHandle,
131 double timeout, HAL_Bool ignorePrevious,
132 int32_t* status) {
133 uint32_t result;
134 auto anInterrupt = interruptHandles->Get(interruptHandle);
135 if (anInterrupt == nullptr) {
136 *status = HAL_HANDLE_ERROR;
137 return 0;
138 }
139
140 result = anInterrupt->manager->watch(static_cast<int32_t>(timeout * 1e3),
141 ignorePrevious, status);
142
143 // Don't report a timeout as an error - the return code is enough to tell
144 // that a timeout happened.
145 if (*status == -NiFpga_Status_IrqTimeout) {
146 *status = NiFpga_Status_Success;
147 }
148
149 return result;
150}
151
152void HAL_EnableInterrupts(HAL_InterruptHandle interruptHandle,
153 int32_t* status) {
154 auto anInterrupt = interruptHandles->Get(interruptHandle);
155 if (anInterrupt == nullptr) {
156 *status = HAL_HANDLE_ERROR;
157 return;
158 }
James Kuszmaul4b81d302019-12-14 20:53:14 -0800159
160 if (!anInterrupt->manager->isEnabled(status)) {
161 anInterrupt->manager->enable(status);
162 }
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800163}
164
165void HAL_DisableInterrupts(HAL_InterruptHandle interruptHandle,
166 int32_t* status) {
167 auto anInterrupt = interruptHandles->Get(interruptHandle);
168 if (anInterrupt == nullptr) {
169 *status = HAL_HANDLE_ERROR;
170 return;
171 }
James Kuszmaul4b81d302019-12-14 20:53:14 -0800172 if (anInterrupt->manager->isEnabled(status)) {
173 anInterrupt->manager->disable(status);
174 }
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800175}
176
177int64_t HAL_ReadInterruptRisingTimestamp(HAL_InterruptHandle interruptHandle,
178 int32_t* status) {
179 auto anInterrupt = interruptHandles->Get(interruptHandle);
180 if (anInterrupt == nullptr) {
181 *status = HAL_HANDLE_ERROR;
182 return 0;
183 }
184 uint32_t timestamp = anInterrupt->anInterrupt->readRisingTimeStamp(status);
185 return timestamp;
186}
187
188int64_t HAL_ReadInterruptFallingTimestamp(HAL_InterruptHandle interruptHandle,
189 int32_t* status) {
190 auto anInterrupt = interruptHandles->Get(interruptHandle);
191 if (anInterrupt == nullptr) {
192 *status = HAL_HANDLE_ERROR;
193 return 0;
194 }
195 uint32_t timestamp = anInterrupt->anInterrupt->readFallingTimeStamp(status);
196 return timestamp;
197}
198
199void HAL_RequestInterrupts(HAL_InterruptHandle interruptHandle,
200 HAL_Handle digitalSourceHandle,
201 HAL_AnalogTriggerType analogTriggerType,
202 int32_t* status) {
203 auto anInterrupt = interruptHandles->Get(interruptHandle);
204 if (anInterrupt == nullptr) {
205 *status = HAL_HANDLE_ERROR;
206 return;
207 }
208 anInterrupt->anInterrupt->writeConfig_WaitForAck(false, status);
209 bool routingAnalogTrigger = false;
210 uint8_t routingChannel = 0;
211 uint8_t routingModule = 0;
212 bool success =
213 remapDigitalSource(digitalSourceHandle, analogTriggerType, routingChannel,
214 routingModule, routingAnalogTrigger);
215 if (!success) {
216 *status = HAL_HANDLE_ERROR;
217 return;
218 }
219 anInterrupt->anInterrupt->writeConfig_Source_AnalogTrigger(
220 routingAnalogTrigger, status);
221 anInterrupt->anInterrupt->writeConfig_Source_Channel(routingChannel, status);
222 anInterrupt->anInterrupt->writeConfig_Source_Module(routingModule, status);
223}
224
225void HAL_AttachInterruptHandler(HAL_InterruptHandle interruptHandle,
226 HAL_InterruptHandlerFunction handler,
227 void* param, int32_t* status) {
228 auto anInterrupt = interruptHandles->Get(interruptHandle);
229 if (anInterrupt == nullptr) {
230 *status = HAL_HANDLE_ERROR;
231 return;
232 }
233 anInterrupt->manager->registerHandler(handler, param, status);
234 anInterrupt->param = param;
235}
236
237void HAL_AttachInterruptHandlerThreaded(HAL_InterruptHandle interrupt_handle,
238 HAL_InterruptHandlerFunction handler,
239 void* param, int32_t* status) {
240 auto anInterrupt = interruptHandles->Get(interrupt_handle);
241 if (anInterrupt == nullptr) {
242 *status = HAL_HANDLE_ERROR;
243 return;
244 }
245
246 anInterrupt->threadOwner = std::make_unique<InterruptThreadOwner>();
247 anInterrupt->threadOwner->Start();
248 anInterrupt->threadOwner->SetFunc(handler, param);
249
250 HAL_AttachInterruptHandler(interrupt_handle, threadedInterruptHandler,
251 anInterrupt->threadOwner.get(), status);
252
253 if (*status != 0) {
254 anInterrupt->threadOwner = nullptr;
255 }
256 anInterrupt->param = param;
257}
258
259void HAL_SetInterruptUpSourceEdge(HAL_InterruptHandle interruptHandle,
260 HAL_Bool risingEdge, HAL_Bool fallingEdge,
261 int32_t* status) {
262 auto anInterrupt = interruptHandles->Get(interruptHandle);
263 if (anInterrupt == nullptr) {
264 *status = HAL_HANDLE_ERROR;
265 return;
266 }
267 anInterrupt->anInterrupt->writeConfig_RisingEdge(risingEdge, status);
268 anInterrupt->anInterrupt->writeConfig_FallingEdge(fallingEdge, status);
269}
270
271} // extern "C"