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