blob: e55316d14abec9716898f86c1cc9ebdff64184c9 [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "hal/Interrupts.h"
6
7#include <memory>
8
9#include <wpi/condition_variable.h>
10
11#include "AnalogInternal.h"
12#include "DigitalInternal.h"
13#include "ErrorsInternal.h"
14#include "HALInitializer.h"
15#include "MockHooksInternal.h"
16#include "PortsInternal.h"
17#include "hal/AnalogTrigger.h"
18#include "hal/Errors.h"
19#include "hal/Value.h"
20#include "hal/handles/HandlesInternal.h"
21#include "hal/handles/LimitedHandleResource.h"
22#include "hal/handles/UnlimitedHandleResource.h"
23#include "mockdata/AnalogInDataInternal.h"
24#include "mockdata/DIODataInternal.h"
25
26#ifdef _WIN32
27#pragma warning(disable : 4996 4018 6297 26451 4334)
28#endif
29
30using namespace hal;
31
32enum WaitResult {
33 Timeout = 0x0,
34 RisingEdge = 0x1,
35 FallingEdge = 0x100,
36 Both = 0x101,
37};
38
39namespace {
40struct Interrupt {
41 bool isAnalog;
42 HAL_Handle portHandle;
43 uint8_t index;
44 HAL_AnalogTriggerType trigType;
Brian Silverman8fce7482020-01-05 13:18:21 -080045 int64_t risingTimestamp;
46 int64_t fallingTimestamp;
47 bool previousState;
48 bool fireOnUp;
49 bool fireOnDown;
50 int32_t callbackId;
Brian Silverman8fce7482020-01-05 13:18:21 -080051};
52
53struct SynchronousWaitData {
54 HAL_InterruptHandle interruptHandle{HAL_kInvalidHandle};
55 wpi::condition_variable waitCond;
56 HAL_Bool waitPredicate{false};
57};
58} // namespace
59
60static LimitedHandleResource<HAL_InterruptHandle, Interrupt, kNumInterrupts,
61 HAL_HandleEnum::Interrupt>* interruptHandles;
62
Austin Schuh812d0d12021-11-04 20:16:48 -070063using SynchronousWaitDataHandle = HAL_Handle;
Brian Silverman8fce7482020-01-05 13:18:21 -080064static UnlimitedHandleResource<SynchronousWaitDataHandle, SynchronousWaitData,
65 HAL_HandleEnum::Vendor>*
66 synchronousInterruptHandles;
67
Austin Schuh812d0d12021-11-04 20:16:48 -070068namespace hal::init {
Brian Silverman8fce7482020-01-05 13:18:21 -080069void InitializeInterrupts() {
70 static LimitedHandleResource<HAL_InterruptHandle, Interrupt, kNumInterrupts,
71 HAL_HandleEnum::Interrupt>
72 iH;
73 interruptHandles = &iH;
74 static UnlimitedHandleResource<SynchronousWaitDataHandle, SynchronousWaitData,
75 HAL_HandleEnum::Vendor>
76 siH;
77 synchronousInterruptHandles = &siH;
78}
Austin Schuh812d0d12021-11-04 20:16:48 -070079} // namespace hal::init
Brian Silverman8fce7482020-01-05 13:18:21 -080080
81extern "C" {
Austin Schuh812d0d12021-11-04 20:16:48 -070082HAL_InterruptHandle HAL_InitializeInterrupts(int32_t* status) {
Brian Silverman8fce7482020-01-05 13:18:21 -080083 hal::init::CheckInit();
84 HAL_InterruptHandle handle = interruptHandles->Allocate();
85 if (handle == HAL_kInvalidHandle) {
86 *status = NO_AVAILABLE_RESOURCES;
87 return HAL_kInvalidHandle;
88 }
89 auto anInterrupt = interruptHandles->Get(handle);
90 if (anInterrupt == nullptr) { // would only occur on thread issue.
91 *status = HAL_HANDLE_ERROR;
92 return HAL_kInvalidHandle;
93 }
94
95 anInterrupt->index = getHandleIndex(handle);
96 anInterrupt->callbackId = -1;
97
Brian Silverman8fce7482020-01-05 13:18:21 -080098 return handle;
99}
Austin Schuh812d0d12021-11-04 20:16:48 -0700100void HAL_CleanInterrupts(HAL_InterruptHandle interruptHandle) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800101 interruptHandles->Free(interruptHandle);
Brian Silverman8fce7482020-01-05 13:18:21 -0800102}
103
104static void ProcessInterruptDigitalSynchronous(const char* name, void* param,
105 const struct HAL_Value* value) {
106 // void* is a SynchronousWaitDataHandle.
107 // convert to uintptr_t first, then to handle
108 uintptr_t handleTmp = reinterpret_cast<uintptr_t>(param);
109 SynchronousWaitDataHandle handle =
110 static_cast<SynchronousWaitDataHandle>(handleTmp);
111 auto interruptData = synchronousInterruptHandles->Get(handle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700112 if (interruptData == nullptr) {
113 return;
114 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800115 auto interrupt = interruptHandles->Get(interruptData->interruptHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700116 if (interrupt == nullptr) {
117 return;
118 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800119 // Have a valid interrupt
Austin Schuh812d0d12021-11-04 20:16:48 -0700120 if (value->type != HAL_Type::HAL_BOOLEAN) {
121 return;
122 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800123 bool retVal = value->data.v_boolean;
Austin Schuh812d0d12021-11-04 20:16:48 -0700124 auto previousState = interrupt->previousState;
125 interrupt->previousState = retVal;
Brian Silverman8fce7482020-01-05 13:18:21 -0800126 // If no change in interrupt, return;
Austin Schuh812d0d12021-11-04 20:16:48 -0700127 if (retVal == previousState) {
128 return;
129 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800130 // If its a falling change, and we dont fire on falling return
Austin Schuh812d0d12021-11-04 20:16:48 -0700131 if (previousState && !interrupt->fireOnDown) {
132 return;
133 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800134 // If its a rising change, and we dont fire on rising return.
Austin Schuh812d0d12021-11-04 20:16:48 -0700135 if (!previousState && !interrupt->fireOnUp) {
136 return;
137 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800138
139 interruptData->waitPredicate = true;
140
141 // Pulse interrupt
142 interruptData->waitCond.notify_all();
143}
144
145static double GetAnalogTriggerValue(HAL_Handle triggerHandle,
146 HAL_AnalogTriggerType type,
147 int32_t* status) {
148 return HAL_GetAnalogTriggerOutput(triggerHandle, type, status);
149}
150
151static void ProcessInterruptAnalogSynchronous(const char* name, void* param,
152 const struct HAL_Value* value) {
153 // void* is a SynchronousWaitDataHandle.
154 // convert to uintptr_t first, then to handle
155 uintptr_t handleTmp = reinterpret_cast<uintptr_t>(param);
156 SynchronousWaitDataHandle handle =
157 static_cast<SynchronousWaitDataHandle>(handleTmp);
158 auto interruptData = synchronousInterruptHandles->Get(handle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700159 if (interruptData == nullptr) {
160 return;
161 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800162 auto interrupt = interruptHandles->Get(interruptData->interruptHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700163 if (interrupt == nullptr) {
164 return;
165 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800166 // Have a valid interrupt
Austin Schuh812d0d12021-11-04 20:16:48 -0700167 if (value->type != HAL_Type::HAL_DOUBLE) {
168 return;
169 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800170 int32_t status = 0;
171 bool retVal = GetAnalogTriggerValue(interrupt->portHandle,
172 interrupt->trigType, &status);
173 if (status != 0) {
174 // Interrupt and Cancel
175 interruptData->waitPredicate = true;
176 // Pulse interrupt
177 interruptData->waitCond.notify_all();
178 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700179 auto previousState = interrupt->previousState;
180 interrupt->previousState = retVal;
Brian Silverman8fce7482020-01-05 13:18:21 -0800181 // If no change in interrupt, return;
Austin Schuh812d0d12021-11-04 20:16:48 -0700182 if (retVal == previousState) {
183 return;
184 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800185 // If its a falling change, and we dont fire on falling return
Austin Schuh812d0d12021-11-04 20:16:48 -0700186 if (previousState && !interrupt->fireOnDown) {
187 return;
188 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800189 // If its a rising change, and we dont fire on rising return.
Austin Schuh812d0d12021-11-04 20:16:48 -0700190 if (!previousState && !interrupt->fireOnUp) {
191 return;
192 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800193
194 interruptData->waitPredicate = true;
195
196 // Pulse interrupt
197 interruptData->waitCond.notify_all();
198}
199
200static int64_t WaitForInterruptDigital(HAL_InterruptHandle handle,
201 Interrupt* interrupt, double timeout,
202 bool ignorePrevious) {
203 auto data = std::make_shared<SynchronousWaitData>();
204
205 auto dataHandle = synchronousInterruptHandles->Allocate(data);
206 if (dataHandle == HAL_kInvalidHandle) {
207 // Error allocating data
208 return WaitResult::Timeout;
209 }
210
211 // auto data = synchronousInterruptHandles->Get(dataHandle);
212 data->waitPredicate = false;
213 data->interruptHandle = handle;
214
215 int32_t status = 0;
216
217 int32_t digitalIndex = GetDigitalInputChannel(interrupt->portHandle, &status);
218
Austin Schuh812d0d12021-11-04 20:16:48 -0700219 if (status != 0) {
220 return WaitResult::Timeout;
221 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800222
223 interrupt->previousState = SimDIOData[digitalIndex].value;
224
225 int32_t uid = SimDIOData[digitalIndex].value.RegisterCallback(
226 &ProcessInterruptDigitalSynchronous,
227 reinterpret_cast<void*>(static_cast<uintptr_t>(dataHandle)), false);
228
229 bool timedOut = false;
230
231 wpi::mutex waitMutex;
232
233 auto timeoutTime =
234 std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
235
236 {
237 std::unique_lock lock(waitMutex);
238 while (!data->waitPredicate) {
239 if (data->waitCond.wait_until(lock, timeoutTime) ==
240 std::cv_status::timeout) {
241 timedOut = true;
242 break;
243 }
244 }
245 }
246
247 // Cancel our callback
248 SimDIOData[digitalIndex].value.CancelCallback(uid);
249 (void)synchronousInterruptHandles->Free(dataHandle);
250
251 // Check for what to return
Austin Schuh812d0d12021-11-04 20:16:48 -0700252 if (timedOut) {
253 return WaitResult::Timeout;
254 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800255 // True => false, Falling
256 if (interrupt->previousState) {
257 // Set our return value and our timestamps
258 interrupt->fallingTimestamp = hal::GetFPGATime();
259 return 1 << (8 + interrupt->index);
260 } else {
261 interrupt->risingTimestamp = hal::GetFPGATime();
262 return 1 << (interrupt->index);
263 }
264}
265
266static int64_t WaitForInterruptAnalog(HAL_InterruptHandle handle,
267 Interrupt* interrupt, double timeout,
268 bool ignorePrevious) {
269 auto data = std::make_shared<SynchronousWaitData>();
270
271 auto dataHandle = synchronousInterruptHandles->Allocate(data);
272 if (dataHandle == HAL_kInvalidHandle) {
273 // Error allocating data
274 return WaitResult::Timeout;
275 }
276
277 data->waitPredicate = false;
278 data->interruptHandle = handle;
279
280 int32_t status = 0;
281 interrupt->previousState = GetAnalogTriggerValue(
282 interrupt->portHandle, interrupt->trigType, &status);
283
Austin Schuh812d0d12021-11-04 20:16:48 -0700284 if (status != 0) {
285 return WaitResult::Timeout;
286 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800287
288 int32_t analogIndex =
289 GetAnalogTriggerInputIndex(interrupt->portHandle, &status);
290
Austin Schuh812d0d12021-11-04 20:16:48 -0700291 if (status != 0) {
292 return WaitResult::Timeout;
293 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800294
295 int32_t uid = SimAnalogInData[analogIndex].voltage.RegisterCallback(
296 &ProcessInterruptAnalogSynchronous,
297 reinterpret_cast<void*>(static_cast<uintptr_t>(dataHandle)), false);
298
299 bool timedOut = false;
300
301 wpi::mutex waitMutex;
302
303 auto timeoutTime =
304 std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
305
306 {
307 std::unique_lock lock(waitMutex);
308 while (!data->waitPredicate) {
309 if (data->waitCond.wait_until(lock, timeoutTime) ==
310 std::cv_status::timeout) {
311 timedOut = true;
312 break;
313 }
314 }
315 }
316
317 // Cancel our callback
318 SimAnalogInData[analogIndex].voltage.CancelCallback(uid);
319 (void)synchronousInterruptHandles->Free(dataHandle);
320
321 // Check for what to return
Austin Schuh812d0d12021-11-04 20:16:48 -0700322 if (timedOut) {
323 return WaitResult::Timeout;
324 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800325 // True => false, Falling
326 if (interrupt->previousState) {
327 // Set our return value and our timestamps
328 interrupt->fallingTimestamp = hal::GetFPGATime();
329 return 1 << (8 + interrupt->index);
330 } else {
331 interrupt->risingTimestamp = hal::GetFPGATime();
332 return 1 << (interrupt->index);
333 }
334}
335
336int64_t HAL_WaitForInterrupt(HAL_InterruptHandle interruptHandle,
337 double timeout, HAL_Bool ignorePrevious,
338 int32_t* status) {
339 auto interrupt = interruptHandles->Get(interruptHandle);
340 if (interrupt == nullptr) {
341 *status = HAL_HANDLE_ERROR;
342 return WaitResult::Timeout;
343 }
344
Brian Silverman8fce7482020-01-05 13:18:21 -0800345 if (interrupt->isAnalog) {
346 return WaitForInterruptAnalog(interruptHandle, interrupt.get(), timeout,
347 ignorePrevious);
348 } else {
349 return WaitForInterruptDigital(interruptHandle, interrupt.get(), timeout,
350 ignorePrevious);
351 }
352}
353
Brian Silverman8fce7482020-01-05 13:18:21 -0800354int64_t HAL_ReadInterruptRisingTimestamp(HAL_InterruptHandle interruptHandle,
355 int32_t* status) {
356 auto interrupt = interruptHandles->Get(interruptHandle);
357 if (interrupt == nullptr) {
358 *status = HAL_HANDLE_ERROR;
359 return 0;
360 }
361
362 return interrupt->risingTimestamp;
363}
364int64_t HAL_ReadInterruptFallingTimestamp(HAL_InterruptHandle interruptHandle,
365 int32_t* status) {
366 auto interrupt = interruptHandles->Get(interruptHandle);
367 if (interrupt == nullptr) {
368 *status = HAL_HANDLE_ERROR;
369 return 0;
370 }
371
372 return interrupt->fallingTimestamp;
373}
374void HAL_RequestInterrupts(HAL_InterruptHandle interruptHandle,
375 HAL_Handle digitalSourceHandle,
376 HAL_AnalogTriggerType analogTriggerType,
377 int32_t* status) {
378 auto interrupt = interruptHandles->Get(interruptHandle);
379 if (interrupt == nullptr) {
380 *status = HAL_HANDLE_ERROR;
381 return;
382 }
383
384 bool routingAnalogTrigger = false;
385 uint8_t routingChannel = 0;
386 uint8_t routingModule = 0;
387 bool success =
388 remapDigitalSource(digitalSourceHandle, analogTriggerType, routingChannel,
389 routingModule, routingAnalogTrigger);
390 if (!success) {
391 *status = HAL_HANDLE_ERROR;
392 return;
393 }
394
395 interrupt->isAnalog = routingAnalogTrigger;
396 interrupt->trigType = analogTriggerType;
397 interrupt->portHandle = digitalSourceHandle;
398}
Brian Silverman8fce7482020-01-05 13:18:21 -0800399
400void HAL_SetInterruptUpSourceEdge(HAL_InterruptHandle interruptHandle,
401 HAL_Bool risingEdge, HAL_Bool fallingEdge,
402 int32_t* status) {
403 auto interrupt = interruptHandles->Get(interruptHandle);
404 if (interrupt == nullptr) {
405 *status = HAL_HANDLE_ERROR;
406 return;
407 }
408
409 interrupt->fireOnDown = fallingEdge;
410 interrupt->fireOnUp = risingEdge;
411}
Austin Schuh1e69f942020-11-14 15:06:14 -0800412
413void HAL_ReleaseWaitingInterrupt(HAL_InterruptHandle interruptHandle,
414 int32_t* status) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700415 auto interrupt = interruptHandles->Get(interruptHandle);
416 if (interrupt == nullptr) {
417 *status = HAL_HANDLE_ERROR;
418 return;
419 }
420
421 synchronousInterruptHandles->ForEach(
422 [interruptHandle](SynchronousWaitDataHandle handle,
423 SynchronousWaitData* data) {
424 if (data->interruptHandle == interruptHandle) {
425 data->waitPredicate = true;
426 data->waitCond.notify_all();
427 }
428 });
Austin Schuh1e69f942020-11-14 15:06:14 -0800429}
Brian Silverman8fce7482020-01-05 13:18:21 -0800430} // extern "C"