blob: b0b2071abb446069ad68e06782e224d0e8d29158 [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
67} // namespace
68
69static void threadedInterruptHandler(uint32_t mask, void* param) {
70 static_cast<InterruptThreadOwner*>(param)->Notify(mask);
71}
72
73struct Interrupt {
74 std::unique_ptr<tInterrupt> anInterrupt;
75 std::unique_ptr<tInterruptManager> manager;
76 std::unique_ptr<InterruptThreadOwner> threadOwner = nullptr;
77 void* param = nullptr;
78};
79
80static 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 Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800121 anInterrupt->manager->disable(status);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800122 void* param = anInterrupt->param;
123 return param;
124}
125
126int64_t HAL_WaitForInterrupt(HAL_InterruptHandle interruptHandle,
127 double timeout, HAL_Bool ignorePrevious,
128 int32_t* status) {
129 uint32_t result;
130 auto anInterrupt = interruptHandles->Get(interruptHandle);
131 if (anInterrupt == nullptr) {
132 *status = HAL_HANDLE_ERROR;
133 return 0;
134 }
135
136 result = anInterrupt->manager->watch(static_cast<int32_t>(timeout * 1e3),
137 ignorePrevious, status);
138
139 // Don't report a timeout as an error - the return code is enough to tell
140 // that a timeout happened.
141 if (*status == -NiFpga_Status_IrqTimeout) {
142 *status = NiFpga_Status_Success;
143 }
144
145 return result;
146}
147
148void HAL_EnableInterrupts(HAL_InterruptHandle interruptHandle,
149 int32_t* status) {
150 auto anInterrupt = interruptHandles->Get(interruptHandle);
151 if (anInterrupt == nullptr) {
152 *status = HAL_HANDLE_ERROR;
153 return;
154 }
155 anInterrupt->manager->enable(status);
156}
157
158void HAL_DisableInterrupts(HAL_InterruptHandle interruptHandle,
159 int32_t* status) {
160 auto anInterrupt = interruptHandles->Get(interruptHandle);
161 if (anInterrupt == nullptr) {
162 *status = HAL_HANDLE_ERROR;
163 return;
164 }
165 anInterrupt->manager->disable(status);
166}
167
168int64_t HAL_ReadInterruptRisingTimestamp(HAL_InterruptHandle interruptHandle,
169 int32_t* status) {
170 auto anInterrupt = interruptHandles->Get(interruptHandle);
171 if (anInterrupt == nullptr) {
172 *status = HAL_HANDLE_ERROR;
173 return 0;
174 }
175 uint32_t timestamp = anInterrupt->anInterrupt->readRisingTimeStamp(status);
176 return timestamp;
177}
178
179int64_t HAL_ReadInterruptFallingTimestamp(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->readFallingTimeStamp(status);
187 return timestamp;
188}
189
190void HAL_RequestInterrupts(HAL_InterruptHandle interruptHandle,
191 HAL_Handle digitalSourceHandle,
192 HAL_AnalogTriggerType analogTriggerType,
193 int32_t* status) {
194 auto anInterrupt = interruptHandles->Get(interruptHandle);
195 if (anInterrupt == nullptr) {
196 *status = HAL_HANDLE_ERROR;
197 return;
198 }
199 anInterrupt->anInterrupt->writeConfig_WaitForAck(false, status);
200 bool routingAnalogTrigger = false;
201 uint8_t routingChannel = 0;
202 uint8_t routingModule = 0;
203 bool success =
204 remapDigitalSource(digitalSourceHandle, analogTriggerType, routingChannel,
205 routingModule, routingAnalogTrigger);
206 if (!success) {
207 *status = HAL_HANDLE_ERROR;
208 return;
209 }
210 anInterrupt->anInterrupt->writeConfig_Source_AnalogTrigger(
211 routingAnalogTrigger, status);
212 anInterrupt->anInterrupt->writeConfig_Source_Channel(routingChannel, status);
213 anInterrupt->anInterrupt->writeConfig_Source_Module(routingModule, status);
214}
215
216void HAL_AttachInterruptHandler(HAL_InterruptHandle interruptHandle,
217 HAL_InterruptHandlerFunction handler,
218 void* param, int32_t* status) {
219 auto anInterrupt = interruptHandles->Get(interruptHandle);
220 if (anInterrupt == nullptr) {
221 *status = HAL_HANDLE_ERROR;
222 return;
223 }
224 anInterrupt->manager->registerHandler(handler, param, status);
225 anInterrupt->param = param;
226}
227
228void HAL_AttachInterruptHandlerThreaded(HAL_InterruptHandle interrupt_handle,
229 HAL_InterruptHandlerFunction handler,
230 void* param, int32_t* status) {
231 auto anInterrupt = interruptHandles->Get(interrupt_handle);
232 if (anInterrupt == nullptr) {
233 *status = HAL_HANDLE_ERROR;
234 return;
235 }
236
237 anInterrupt->threadOwner = std::make_unique<InterruptThreadOwner>();
238 anInterrupt->threadOwner->Start();
239 anInterrupt->threadOwner->SetFunc(handler, param);
240
241 HAL_AttachInterruptHandler(interrupt_handle, threadedInterruptHandler,
242 anInterrupt->threadOwner.get(), status);
243
244 if (*status != 0) {
245 anInterrupt->threadOwner = nullptr;
246 }
247 anInterrupt->param = param;
248}
249
250void HAL_SetInterruptUpSourceEdge(HAL_InterruptHandle interruptHandle,
251 HAL_Bool risingEdge, HAL_Bool fallingEdge,
252 int32_t* status) {
253 auto anInterrupt = interruptHandles->Get(interruptHandle);
254 if (anInterrupt == nullptr) {
255 *status = HAL_HANDLE_ERROR;
256 return;
257 }
258 anInterrupt->anInterrupt->writeConfig_RisingEdge(risingEdge, status);
259 anInterrupt->anInterrupt->writeConfig_FallingEdge(fallingEdge, status);
260}
261
262} // extern "C"