blob: 7760f06f17fee1f2d4176d63309278fee5cc93c3 [file] [log] [blame]
Brian Silverman8fce7482020-01-05 13:18:21 -08001/*----------------------------------------------------------------------------*/
Austin Schuh1e69f942020-11-14 15:06:14 -08002/* Copyright (c) 2017-2020 FIRST. All Rights Reserved. */
Brian Silverman8fce7482020-01-05 13:18:21 -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/condition_variable.h>
13
14#include "AnalogInternal.h"
15#include "DigitalInternal.h"
16#include "ErrorsInternal.h"
17#include "HALInitializer.h"
18#include "MockHooksInternal.h"
19#include "PortsInternal.h"
20#include "hal/AnalogTrigger.h"
21#include "hal/Errors.h"
22#include "hal/Value.h"
23#include "hal/handles/HandlesInternal.h"
24#include "hal/handles/LimitedHandleResource.h"
25#include "hal/handles/UnlimitedHandleResource.h"
26#include "mockdata/AnalogInDataInternal.h"
27#include "mockdata/DIODataInternal.h"
28
29#ifdef _WIN32
30#pragma warning(disable : 4996 4018 6297 26451 4334)
31#endif
32
33using namespace hal;
34
35enum WaitResult {
36 Timeout = 0x0,
37 RisingEdge = 0x1,
38 FallingEdge = 0x100,
39 Both = 0x101,
40};
41
42namespace {
43struct Interrupt {
44 bool isAnalog;
45 HAL_Handle portHandle;
46 uint8_t index;
47 HAL_AnalogTriggerType trigType;
48 bool watcher;
49 int64_t risingTimestamp;
50 int64_t fallingTimestamp;
51 bool previousState;
52 bool fireOnUp;
53 bool fireOnDown;
54 int32_t callbackId;
55
56 void* callbackParam;
57 HAL_InterruptHandlerFunction callbackFunction;
58};
59
60struct SynchronousWaitData {
61 HAL_InterruptHandle interruptHandle{HAL_kInvalidHandle};
62 wpi::condition_variable waitCond;
63 HAL_Bool waitPredicate{false};
64};
65} // namespace
66
67static LimitedHandleResource<HAL_InterruptHandle, Interrupt, kNumInterrupts,
68 HAL_HandleEnum::Interrupt>* interruptHandles;
69
70typedef HAL_Handle SynchronousWaitDataHandle;
71static UnlimitedHandleResource<SynchronousWaitDataHandle, SynchronousWaitData,
72 HAL_HandleEnum::Vendor>*
73 synchronousInterruptHandles;
74
75namespace hal {
76namespace init {
77void InitializeInterrupts() {
78 static LimitedHandleResource<HAL_InterruptHandle, Interrupt, kNumInterrupts,
79 HAL_HandleEnum::Interrupt>
80 iH;
81 interruptHandles = &iH;
82 static UnlimitedHandleResource<SynchronousWaitDataHandle, SynchronousWaitData,
83 HAL_HandleEnum::Vendor>
84 siH;
85 synchronousInterruptHandles = &siH;
86}
87} // namespace init
88} // namespace hal
89
90extern "C" {
91HAL_InterruptHandle HAL_InitializeInterrupts(HAL_Bool watcher,
92 int32_t* status) {
93 hal::init::CheckInit();
94 HAL_InterruptHandle handle = interruptHandles->Allocate();
95 if (handle == HAL_kInvalidHandle) {
96 *status = NO_AVAILABLE_RESOURCES;
97 return HAL_kInvalidHandle;
98 }
99 auto anInterrupt = interruptHandles->Get(handle);
100 if (anInterrupt == nullptr) { // would only occur on thread issue.
101 *status = HAL_HANDLE_ERROR;
102 return HAL_kInvalidHandle;
103 }
104
105 anInterrupt->index = getHandleIndex(handle);
106 anInterrupt->callbackId = -1;
107
108 anInterrupt->watcher = watcher;
109
110 return handle;
111}
112void* HAL_CleanInterrupts(HAL_InterruptHandle interruptHandle,
113 int32_t* status) {
114 HAL_DisableInterrupts(interruptHandle, status);
115 auto anInterrupt = interruptHandles->Get(interruptHandle);
116 interruptHandles->Free(interruptHandle);
117 if (anInterrupt == nullptr) {
118 return nullptr;
119 }
120 return anInterrupt->callbackParam;
121}
122
123static void ProcessInterruptDigitalSynchronous(const char* name, void* param,
124 const struct HAL_Value* value) {
125 // void* is a SynchronousWaitDataHandle.
126 // convert to uintptr_t first, then to handle
127 uintptr_t handleTmp = reinterpret_cast<uintptr_t>(param);
128 SynchronousWaitDataHandle handle =
129 static_cast<SynchronousWaitDataHandle>(handleTmp);
130 auto interruptData = synchronousInterruptHandles->Get(handle);
131 if (interruptData == nullptr) return;
132 auto interrupt = interruptHandles->Get(interruptData->interruptHandle);
133 if (interrupt == nullptr) return;
134 // Have a valid interrupt
135 if (value->type != HAL_Type::HAL_BOOLEAN) return;
136 bool retVal = value->data.v_boolean;
137 // If no change in interrupt, return;
138 if (retVal == interrupt->previousState) return;
139 // If its a falling change, and we dont fire on falling return
140 if (interrupt->previousState && !interrupt->fireOnDown) return;
141 // If its a rising change, and we dont fire on rising return.
142 if (!interrupt->previousState && !interrupt->fireOnUp) return;
143
144 interruptData->waitPredicate = true;
145
146 // Pulse interrupt
147 interruptData->waitCond.notify_all();
148}
149
150static double GetAnalogTriggerValue(HAL_Handle triggerHandle,
151 HAL_AnalogTriggerType type,
152 int32_t* status) {
153 return HAL_GetAnalogTriggerOutput(triggerHandle, type, status);
154}
155
156static void ProcessInterruptAnalogSynchronous(const char* name, void* param,
157 const struct HAL_Value* value) {
158 // void* is a SynchronousWaitDataHandle.
159 // convert to uintptr_t first, then to handle
160 uintptr_t handleTmp = reinterpret_cast<uintptr_t>(param);
161 SynchronousWaitDataHandle handle =
162 static_cast<SynchronousWaitDataHandle>(handleTmp);
163 auto interruptData = synchronousInterruptHandles->Get(handle);
164 if (interruptData == nullptr) return;
165 auto interrupt = interruptHandles->Get(interruptData->interruptHandle);
166 if (interrupt == nullptr) return;
167 // Have a valid interrupt
168 if (value->type != HAL_Type::HAL_DOUBLE) return;
169 int32_t status = 0;
170 bool retVal = GetAnalogTriggerValue(interrupt->portHandle,
171 interrupt->trigType, &status);
172 if (status != 0) {
173 // Interrupt and Cancel
174 interruptData->waitPredicate = true;
175 // Pulse interrupt
176 interruptData->waitCond.notify_all();
177 }
178 // If no change in interrupt, return;
179 if (retVal == interrupt->previousState) return;
180 // If its a falling change, and we dont fire on falling return
181 if (interrupt->previousState && !interrupt->fireOnDown) return;
182 // If its a rising change, and we dont fire on rising return.
183 if (!interrupt->previousState && !interrupt->fireOnUp) return;
184
185 interruptData->waitPredicate = true;
186
187 // Pulse interrupt
188 interruptData->waitCond.notify_all();
189}
190
191static int64_t WaitForInterruptDigital(HAL_InterruptHandle handle,
192 Interrupt* interrupt, double timeout,
193 bool ignorePrevious) {
194 auto data = std::make_shared<SynchronousWaitData>();
195
196 auto dataHandle = synchronousInterruptHandles->Allocate(data);
197 if (dataHandle == HAL_kInvalidHandle) {
198 // Error allocating data
199 return WaitResult::Timeout;
200 }
201
202 // auto data = synchronousInterruptHandles->Get(dataHandle);
203 data->waitPredicate = false;
204 data->interruptHandle = handle;
205
206 int32_t status = 0;
207
208 int32_t digitalIndex = GetDigitalInputChannel(interrupt->portHandle, &status);
209
210 if (status != 0) return WaitResult::Timeout;
211
212 interrupt->previousState = SimDIOData[digitalIndex].value;
213
214 int32_t uid = SimDIOData[digitalIndex].value.RegisterCallback(
215 &ProcessInterruptDigitalSynchronous,
216 reinterpret_cast<void*>(static_cast<uintptr_t>(dataHandle)), false);
217
218 bool timedOut = false;
219
220 wpi::mutex waitMutex;
221
222 auto timeoutTime =
223 std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
224
225 {
226 std::unique_lock lock(waitMutex);
227 while (!data->waitPredicate) {
228 if (data->waitCond.wait_until(lock, timeoutTime) ==
229 std::cv_status::timeout) {
230 timedOut = true;
231 break;
232 }
233 }
234 }
235
236 // Cancel our callback
237 SimDIOData[digitalIndex].value.CancelCallback(uid);
238 (void)synchronousInterruptHandles->Free(dataHandle);
239
240 // Check for what to return
241 if (timedOut) return WaitResult::Timeout;
242 // True => false, Falling
243 if (interrupt->previousState) {
244 // Set our return value and our timestamps
245 interrupt->fallingTimestamp = hal::GetFPGATime();
246 return 1 << (8 + interrupt->index);
247 } else {
248 interrupt->risingTimestamp = hal::GetFPGATime();
249 return 1 << (interrupt->index);
250 }
251}
252
253static int64_t WaitForInterruptAnalog(HAL_InterruptHandle handle,
254 Interrupt* interrupt, double timeout,
255 bool ignorePrevious) {
256 auto data = std::make_shared<SynchronousWaitData>();
257
258 auto dataHandle = synchronousInterruptHandles->Allocate(data);
259 if (dataHandle == HAL_kInvalidHandle) {
260 // Error allocating data
261 return WaitResult::Timeout;
262 }
263
264 data->waitPredicate = false;
265 data->interruptHandle = handle;
266
267 int32_t status = 0;
268 interrupt->previousState = GetAnalogTriggerValue(
269 interrupt->portHandle, interrupt->trigType, &status);
270
271 if (status != 0) return WaitResult::Timeout;
272
273 int32_t analogIndex =
274 GetAnalogTriggerInputIndex(interrupt->portHandle, &status);
275
276 if (status != 0) return WaitResult::Timeout;
277
278 int32_t uid = SimAnalogInData[analogIndex].voltage.RegisterCallback(
279 &ProcessInterruptAnalogSynchronous,
280 reinterpret_cast<void*>(static_cast<uintptr_t>(dataHandle)), false);
281
282 bool timedOut = false;
283
284 wpi::mutex waitMutex;
285
286 auto timeoutTime =
287 std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
288
289 {
290 std::unique_lock lock(waitMutex);
291 while (!data->waitPredicate) {
292 if (data->waitCond.wait_until(lock, timeoutTime) ==
293 std::cv_status::timeout) {
294 timedOut = true;
295 break;
296 }
297 }
298 }
299
300 // Cancel our callback
301 SimAnalogInData[analogIndex].voltage.CancelCallback(uid);
302 (void)synchronousInterruptHandles->Free(dataHandle);
303
304 // Check for what to return
305 if (timedOut) return WaitResult::Timeout;
306 // True => false, Falling
307 if (interrupt->previousState) {
308 // Set our return value and our timestamps
309 interrupt->fallingTimestamp = hal::GetFPGATime();
310 return 1 << (8 + interrupt->index);
311 } else {
312 interrupt->risingTimestamp = hal::GetFPGATime();
313 return 1 << (interrupt->index);
314 }
315}
316
317int64_t HAL_WaitForInterrupt(HAL_InterruptHandle interruptHandle,
318 double timeout, HAL_Bool ignorePrevious,
319 int32_t* status) {
320 auto interrupt = interruptHandles->Get(interruptHandle);
321 if (interrupt == nullptr) {
322 *status = HAL_HANDLE_ERROR;
323 return WaitResult::Timeout;
324 }
325
326 // Check to make sure we are actually an interrupt in synchronous mode
327 if (!interrupt->watcher) {
328 *status = NiFpga_Status_InvalidParameter;
329 return WaitResult::Timeout;
330 }
331
332 if (interrupt->isAnalog) {
333 return WaitForInterruptAnalog(interruptHandle, interrupt.get(), timeout,
334 ignorePrevious);
335 } else {
336 return WaitForInterruptDigital(interruptHandle, interrupt.get(), timeout,
337 ignorePrevious);
338 }
339}
340
341static void ProcessInterruptDigitalAsynchronous(const char* name, void* param,
342 const struct HAL_Value* value) {
343 // void* is a HAL handle
344 // convert to uintptr_t first, then to handle
345 uintptr_t handleTmp = reinterpret_cast<uintptr_t>(param);
346 HAL_InterruptHandle handle = static_cast<HAL_InterruptHandle>(handleTmp);
347 auto interrupt = interruptHandles->Get(handle);
348 if (interrupt == nullptr) return;
349 // Have a valid interrupt
350 if (value->type != HAL_Type::HAL_BOOLEAN) return;
351 bool retVal = value->data.v_boolean;
352 // If no change in interrupt, return;
353 if (retVal == interrupt->previousState) return;
354 int32_t mask = 0;
355 if (interrupt->previousState) {
356 interrupt->previousState = retVal;
357 interrupt->fallingTimestamp = hal::GetFPGATime();
358 mask = 1 << (8 + interrupt->index);
359 if (!interrupt->fireOnDown) return;
360 } else {
361 interrupt->previousState = retVal;
362 interrupt->risingTimestamp = hal::GetFPGATime();
363 mask = 1 << (interrupt->index);
364 if (!interrupt->fireOnUp) return;
365 }
366
367 // run callback
368 auto callback = interrupt->callbackFunction;
369 if (callback == nullptr) return;
370 callback(mask, interrupt->callbackParam);
371}
372
373static void ProcessInterruptAnalogAsynchronous(const char* name, void* param,
374 const struct HAL_Value* value) {
375 // void* is a HAL handle
376 // convert to intptr_t first, then to handle
377 uintptr_t handleTmp = reinterpret_cast<uintptr_t>(param);
378 HAL_InterruptHandle handle = static_cast<HAL_InterruptHandle>(handleTmp);
379 auto interrupt = interruptHandles->Get(handle);
380 if (interrupt == nullptr) return;
381 // Have a valid interrupt
382 if (value->type != HAL_Type::HAL_DOUBLE) return;
383 int32_t status = 0;
384 bool retVal = GetAnalogTriggerValue(interrupt->portHandle,
385 interrupt->trigType, &status);
386 if (status != 0) return;
387 // If no change in interrupt, return;
388 if (retVal == interrupt->previousState) return;
389 int mask = 0;
390 if (interrupt->previousState) {
391 interrupt->previousState = retVal;
392 interrupt->fallingTimestamp = hal::GetFPGATime();
393 if (!interrupt->fireOnDown) return;
394 mask = 1 << (8 + interrupt->index);
395 } else {
396 interrupt->previousState = retVal;
397 interrupt->risingTimestamp = hal::GetFPGATime();
398 if (!interrupt->fireOnUp) return;
399 mask = 1 << (interrupt->index);
400 }
401
402 // run callback
403 auto callback = interrupt->callbackFunction;
404 if (callback == nullptr) return;
405 callback(mask, interrupt->callbackParam);
406}
407
408static void EnableInterruptsDigital(HAL_InterruptHandle handle,
409 Interrupt* interrupt) {
410 int32_t status = 0;
411 int32_t digitalIndex = GetDigitalInputChannel(interrupt->portHandle, &status);
412 if (status != 0) return;
413
414 interrupt->previousState = SimDIOData[digitalIndex].value;
415
416 int32_t uid = SimDIOData[digitalIndex].value.RegisterCallback(
417 &ProcessInterruptDigitalAsynchronous,
418 reinterpret_cast<void*>(static_cast<uintptr_t>(handle)), false);
419 interrupt->callbackId = uid;
420}
421
422static void EnableInterruptsAnalog(HAL_InterruptHandle handle,
423 Interrupt* interrupt) {
424 int32_t status = 0;
425 int32_t analogIndex =
426 GetAnalogTriggerInputIndex(interrupt->portHandle, &status);
427 if (status != 0) return;
428
429 status = 0;
430 interrupt->previousState = GetAnalogTriggerValue(
431 interrupt->portHandle, interrupt->trigType, &status);
432 if (status != 0) return;
433
434 int32_t uid = SimAnalogInData[analogIndex].voltage.RegisterCallback(
435 &ProcessInterruptAnalogAsynchronous,
436 reinterpret_cast<void*>(static_cast<uintptr_t>(handle)), false);
437 interrupt->callbackId = uid;
438}
439
440void HAL_EnableInterrupts(HAL_InterruptHandle interruptHandle,
441 int32_t* status) {
442 auto interrupt = interruptHandles->Get(interruptHandle);
443 if (interrupt == nullptr) {
444 *status = HAL_HANDLE_ERROR;
445 return;
446 }
447
448 // If we have not had a callback set, error out
449 if (interrupt->callbackFunction == nullptr) {
450 *status = INCOMPATIBLE_STATE;
451 return;
452 }
453
454 // EnableInterrupts has already been called
455 if (interrupt->callbackId >= 0) {
456 // We can double enable safely.
457 return;
458 }
459
460 if (interrupt->isAnalog) {
461 EnableInterruptsAnalog(interruptHandle, interrupt.get());
462 } else {
463 EnableInterruptsDigital(interruptHandle, interrupt.get());
464 }
465}
466void HAL_DisableInterrupts(HAL_InterruptHandle interruptHandle,
467 int32_t* status) {
468 auto interrupt = interruptHandles->Get(interruptHandle);
469 if (interrupt == nullptr) {
470 *status = HAL_HANDLE_ERROR;
471 return;
472 }
473
474 // No need to disable if we are already disabled
475 if (interrupt->callbackId < 0) return;
476
477 if (interrupt->isAnalog) {
478 // Do analog
479 int32_t status = 0;
480 int32_t analogIndex =
481 GetAnalogTriggerInputIndex(interrupt->portHandle, &status);
482 if (status != 0) return;
483 SimAnalogInData[analogIndex].voltage.CancelCallback(interrupt->callbackId);
484 } else {
485 int32_t status = 0;
486 int32_t digitalIndex =
487 GetDigitalInputChannel(interrupt->portHandle, &status);
488 if (status != 0) return;
489 SimDIOData[digitalIndex].value.CancelCallback(interrupt->callbackId);
490 }
491 interrupt->callbackId = -1;
492}
493int64_t HAL_ReadInterruptRisingTimestamp(HAL_InterruptHandle interruptHandle,
494 int32_t* status) {
495 auto interrupt = interruptHandles->Get(interruptHandle);
496 if (interrupt == nullptr) {
497 *status = HAL_HANDLE_ERROR;
498 return 0;
499 }
500
501 return interrupt->risingTimestamp;
502}
503int64_t HAL_ReadInterruptFallingTimestamp(HAL_InterruptHandle interruptHandle,
504 int32_t* status) {
505 auto interrupt = interruptHandles->Get(interruptHandle);
506 if (interrupt == nullptr) {
507 *status = HAL_HANDLE_ERROR;
508 return 0;
509 }
510
511 return interrupt->fallingTimestamp;
512}
513void HAL_RequestInterrupts(HAL_InterruptHandle interruptHandle,
514 HAL_Handle digitalSourceHandle,
515 HAL_AnalogTriggerType analogTriggerType,
516 int32_t* status) {
517 auto interrupt = interruptHandles->Get(interruptHandle);
518 if (interrupt == nullptr) {
519 *status = HAL_HANDLE_ERROR;
520 return;
521 }
522
523 bool routingAnalogTrigger = false;
524 uint8_t routingChannel = 0;
525 uint8_t routingModule = 0;
526 bool success =
527 remapDigitalSource(digitalSourceHandle, analogTriggerType, routingChannel,
528 routingModule, routingAnalogTrigger);
529 if (!success) {
530 *status = HAL_HANDLE_ERROR;
531 return;
532 }
533
534 interrupt->isAnalog = routingAnalogTrigger;
535 interrupt->trigType = analogTriggerType;
536 interrupt->portHandle = digitalSourceHandle;
537}
538void HAL_AttachInterruptHandler(HAL_InterruptHandle interruptHandle,
539 HAL_InterruptHandlerFunction handler,
540 void* param, int32_t* status) {
541 auto interrupt = interruptHandles->Get(interruptHandle);
542 if (interrupt == nullptr) {
543 *status = HAL_HANDLE_ERROR;
544 return;
545 }
546
547 interrupt->callbackFunction = handler;
548 interrupt->callbackParam = param;
549}
550
551void HAL_AttachInterruptHandlerThreaded(HAL_InterruptHandle interruptHandle,
552 HAL_InterruptHandlerFunction handler,
553 void* param, int32_t* status) {
554 HAL_AttachInterruptHandler(interruptHandle, handler, param, status);
555}
556
557void HAL_SetInterruptUpSourceEdge(HAL_InterruptHandle interruptHandle,
558 HAL_Bool risingEdge, HAL_Bool fallingEdge,
559 int32_t* status) {
560 auto interrupt = interruptHandles->Get(interruptHandle);
561 if (interrupt == nullptr) {
562 *status = HAL_HANDLE_ERROR;
563 return;
564 }
565
566 interrupt->fireOnDown = fallingEdge;
567 interrupt->fireOnUp = risingEdge;
568}
Austin Schuh1e69f942020-11-14 15:06:14 -0800569
570void HAL_ReleaseWaitingInterrupt(HAL_InterruptHandle interruptHandle,
571 int32_t* status) {
572 // Requires a fairly large rewrite to get working
573}
Brian Silverman8fce7482020-01-05 13:18:21 -0800574} // extern "C"