blob: 08b0989668695a2d6a562879b4213000686df6f5 [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -08002/* Copyright (c) 2018-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/CANAPI.h"
9
10#include <atomic>
11#include <ctime>
12
13#include <wpi/DenseMap.h>
14
15#include "CANAPIInternal.h"
16#include "HALInitializer.h"
17#include "hal/CAN.h"
18#include "hal/Errors.h"
19#include "hal/HAL.h"
20#include "hal/handles/UnlimitedHandleResource.h"
21
22using namespace hal;
23
24namespace {
25struct Receives {
26 uint64_t lastTimeStamp;
27 uint8_t data[8];
28 uint8_t length;
29};
30
31struct CANStorage {
32 HAL_CANManufacturer manufacturer;
33 HAL_CANDeviceType deviceType;
34 uint8_t deviceId;
35 wpi::mutex mapMutex;
36 wpi::SmallDenseMap<int32_t, int32_t> periodicSends;
37 wpi::SmallDenseMap<int32_t, Receives> receives;
38};
39} // namespace
40
41static UnlimitedHandleResource<HAL_CANHandle, CANStorage, HAL_HandleEnum::CAN>*
42 canHandles;
43
44static uint32_t GetPacketBaseTime() {
45 int status = 0;
46 auto basetime = HAL_GetFPGATime(&status);
47 // us to ms
48 return (basetime / 1000ull) & 0xFFFFFFFF;
49}
50
51namespace hal {
52namespace init {
53void InitializeCANAPI() {
54 static UnlimitedHandleResource<HAL_CANHandle, CANStorage, HAL_HandleEnum::CAN>
55 cH;
56 canHandles = &cH;
57}
58} // namespace init
59namespace can {
60int32_t GetCANModuleFromHandle(HAL_CANHandle handle, int32_t* status) {
61 auto can = canHandles->Get(handle);
62 if (!can) {
63 *status = HAL_HANDLE_ERROR;
64 return -1;
65 }
66 return can->deviceId;
67}
68} // namespace can
69} // namespace hal
70
71static int32_t CreateCANId(CANStorage* storage, int32_t apiId) {
72 int32_t createdId = 0;
73 createdId |= (static_cast<int32_t>(storage->deviceType) & 0x1F) << 24;
74 createdId |= (static_cast<int32_t>(storage->manufacturer) & 0xFF) << 16;
75 createdId |= (apiId & 0x3FF) << 6;
76 createdId |= (storage->deviceId & 0x3F);
77 return createdId;
78}
79
80HAL_CANHandle HAL_InitializeCAN(HAL_CANManufacturer manufacturer,
81 int32_t deviceId, HAL_CANDeviceType deviceType,
82 int32_t* status) {
83 hal::init::CheckInit();
84 auto can = std::make_shared<CANStorage>();
85
86 auto handle = canHandles->Allocate(can);
87
88 if (handle == HAL_kInvalidHandle) {
89 *status = NO_AVAILABLE_RESOURCES;
90 return HAL_kInvalidHandle;
91 }
92
93 can->deviceId = deviceId;
94 can->deviceType = deviceType;
95 can->manufacturer = manufacturer;
96
97 return handle;
98}
99
100void HAL_CleanCAN(HAL_CANHandle handle) {
101 auto data = canHandles->Free(handle);
102
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800103 std::scoped_lock lock(data->mapMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800104
105 for (auto&& i : data->periodicSends) {
106 int32_t s = 0;
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800107 auto id = CreateCANId(data.get(), i.first);
108 HAL_CAN_SendMessage(id, nullptr, 0, HAL_CAN_SEND_PERIOD_STOP_REPEATING, &s);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800109 i.second = -1;
110 }
111}
112
113void HAL_WriteCANPacket(HAL_CANHandle handle, const uint8_t* data,
114 int32_t length, int32_t apiId, int32_t* status) {
115 auto can = canHandles->Get(handle);
116 if (!can) {
117 *status = HAL_HANDLE_ERROR;
118 return;
119 }
120 auto id = CreateCANId(can.get(), apiId);
121
122 HAL_CAN_SendMessage(id, data, length, HAL_CAN_SEND_PERIOD_NO_REPEAT, status);
123
124 if (*status != 0) {
125 return;
126 }
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800127 std::scoped_lock lock(can->mapMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800128 can->periodicSends[apiId] = -1;
129}
130
131void HAL_WriteCANPacketRepeating(HAL_CANHandle handle, const uint8_t* data,
132 int32_t length, int32_t apiId,
133 int32_t repeatMs, int32_t* status) {
134 auto can = canHandles->Get(handle);
135 if (!can) {
136 *status = HAL_HANDLE_ERROR;
137 return;
138 }
139 auto id = CreateCANId(can.get(), apiId);
140
141 HAL_CAN_SendMessage(id, data, length, repeatMs, status);
142
143 if (*status != 0) {
144 return;
145 }
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800146 std::scoped_lock lock(can->mapMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800147 can->periodicSends[apiId] = repeatMs;
148}
149
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800150void HAL_WriteCANRTRFrame(HAL_CANHandle handle, int32_t length, int32_t apiId,
151 int32_t* status) {
152 auto can = canHandles->Get(handle);
153 if (!can) {
154 *status = HAL_HANDLE_ERROR;
155 return;
156 }
157 auto id = CreateCANId(can.get(), apiId);
158 id |= HAL_CAN_IS_FRAME_REMOTE;
159 uint8_t data[8];
160 std::memset(data, 0, sizeof(data));
161
162 HAL_CAN_SendMessage(id, data, length, HAL_CAN_SEND_PERIOD_NO_REPEAT, status);
163
164 if (*status != 0) {
165 return;
166 }
167 std::scoped_lock lock(can->mapMutex);
168 can->periodicSends[apiId] = -1;
169}
170
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800171void HAL_StopCANPacketRepeating(HAL_CANHandle handle, int32_t apiId,
172 int32_t* status) {
173 auto can = canHandles->Get(handle);
174 if (!can) {
175 *status = HAL_HANDLE_ERROR;
176 return;
177 }
178 auto id = CreateCANId(can.get(), apiId);
179
180 HAL_CAN_SendMessage(id, nullptr, 0, HAL_CAN_SEND_PERIOD_STOP_REPEATING,
181 status);
182
183 if (*status != 0) {
184 return;
185 }
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800186 std::scoped_lock lock(can->mapMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800187 can->periodicSends[apiId] = -1;
188}
189
190void HAL_ReadCANPacketNew(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
191 int32_t* length, uint64_t* receivedTimestamp,
192 int32_t* status) {
193 auto can = canHandles->Get(handle);
194 if (!can) {
195 *status = HAL_HANDLE_ERROR;
196 return;
197 }
198
199 uint32_t messageId = CreateCANId(can.get(), apiId);
200 uint8_t dataSize = 0;
201 uint32_t ts = 0;
202 HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
203
204 if (*status == 0) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800205 std::scoped_lock lock(can->mapMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800206 auto& msg = can->receives[messageId];
207 msg.length = dataSize;
208 msg.lastTimeStamp = ts;
209 // The NetComm call placed in data, copy into the msg
210 std::memcpy(msg.data, data, dataSize);
211 }
212 *length = dataSize;
213 *receivedTimestamp = ts;
214}
215
216void HAL_ReadCANPacketLatest(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
217 int32_t* length, uint64_t* receivedTimestamp,
218 int32_t* status) {
219 auto can = canHandles->Get(handle);
220 if (!can) {
221 *status = HAL_HANDLE_ERROR;
222 return;
223 }
224
225 uint32_t messageId = CreateCANId(can.get(), apiId);
226 uint8_t dataSize = 0;
227 uint32_t ts = 0;
228 HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
229
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800230 std::scoped_lock lock(can->mapMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800231 if (*status == 0) {
232 // fresh update
233 auto& msg = can->receives[messageId];
234 msg.length = dataSize;
235 *length = dataSize;
236 msg.lastTimeStamp = ts;
237 *receivedTimestamp = ts;
238 // The NetComm call placed in data, copy into the msg
239 std::memcpy(msg.data, data, dataSize);
240 } else {
241 auto i = can->receives.find(messageId);
242 if (i != can->receives.end()) {
243 // Read the data from the stored message into the output
244 std::memcpy(data, i->second.data, i->second.length);
245 *length = i->second.length;
246 *receivedTimestamp = i->second.lastTimeStamp;
247 *status = 0;
248 }
249 }
250}
251
252void HAL_ReadCANPacketTimeout(HAL_CANHandle handle, int32_t apiId,
253 uint8_t* data, int32_t* length,
254 uint64_t* receivedTimestamp, int32_t timeoutMs,
255 int32_t* status) {
256 auto can = canHandles->Get(handle);
257 if (!can) {
258 *status = HAL_HANDLE_ERROR;
259 return;
260 }
261
262 uint32_t messageId = CreateCANId(can.get(), apiId);
263 uint8_t dataSize = 0;
264 uint32_t ts = 0;
265 HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
266
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800267 std::scoped_lock lock(can->mapMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800268 if (*status == 0) {
269 // fresh update
270 auto& msg = can->receives[messageId];
271 msg.length = dataSize;
272 *length = dataSize;
273 msg.lastTimeStamp = ts;
274 *receivedTimestamp = ts;
275 // The NetComm call placed in data, copy into the msg
276 std::memcpy(msg.data, data, dataSize);
277 } else {
278 auto i = can->receives.find(messageId);
279 if (i != can->receives.end()) {
280 // Found, check if new enough
281 uint32_t now = GetPacketBaseTime();
282 if (now - i->second.lastTimeStamp > static_cast<uint32_t>(timeoutMs)) {
283 // Timeout, return bad status
284 *status = HAL_CAN_TIMEOUT;
285 return;
286 }
287 // Read the data from the stored message into the output
288 std::memcpy(data, i->second.data, i->second.length);
289 *length = i->second.length;
290 *receivedTimestamp = i->second.lastTimeStamp;
291 *status = 0;
292 }
293 }
294}
295
296void HAL_ReadCANPeriodicPacket(HAL_CANHandle handle, int32_t apiId,
297 uint8_t* data, int32_t* length,
298 uint64_t* receivedTimestamp, int32_t timeoutMs,
299 int32_t periodMs, int32_t* status) {
300 auto can = canHandles->Get(handle);
301 if (!can) {
302 *status = HAL_HANDLE_ERROR;
303 return;
304 }
305
306 uint32_t messageId = CreateCANId(can.get(), apiId);
307
308 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800309 std::scoped_lock lock(can->mapMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800310 auto i = can->receives.find(messageId);
311 if (i != can->receives.end()) {
312 // Found, check if new enough
313 uint32_t now = GetPacketBaseTime();
314 if (now - i->second.lastTimeStamp < static_cast<uint32_t>(periodMs)) {
315 *status = 0;
316 // Read the data from the stored message into the output
317 std::memcpy(data, i->second.data, i->second.length);
318 *length = i->second.length;
319 *receivedTimestamp = i->second.lastTimeStamp;
320 return;
321 }
322 }
323 }
324
325 uint8_t dataSize = 0;
326 uint32_t ts = 0;
327 HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
328
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800329 std::scoped_lock lock(can->mapMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800330 if (*status == 0) {
331 // fresh update
332 auto& msg = can->receives[messageId];
333 msg.length = dataSize;
334 *length = dataSize;
335 msg.lastTimeStamp = ts;
336 *receivedTimestamp = ts;
337 // The NetComm call placed in data, copy into the msg
338 std::memcpy(msg.data, data, dataSize);
339 } else {
340 auto i = can->receives.find(messageId);
341 if (i != can->receives.end()) {
342 // Found, check if new enough
343 uint32_t now = GetPacketBaseTime();
344 if (now - i->second.lastTimeStamp > static_cast<uint32_t>(timeoutMs)) {
345 // Timeout, return bad status
346 *status = HAL_CAN_TIMEOUT;
347 return;
348 }
349 // Read the data from the stored message into the output
350 std::memcpy(data, i->second.data, i->second.length);
351 *length = i->second.length;
352 *receivedTimestamp = i->second.lastTimeStamp;
353 *status = 0;
354 }
355 }
356}