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