blob: c410deab3d64577fbd87898ad923402f95c87153 [file] [log] [blame]
Brian Silverman8fce7482020-01-05 13:18:21 -08001/*----------------------------------------------------------------------------*/
Austin Schuh1e69f942020-11-14 15:06:14 -08002/* Copyright (c) 2018-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/CANAPI.h"
9
Brian Silverman8fce7482020-01-05 13:18:21 -080010#include <wpi/DenseMap.h>
11
12#include "CANAPIInternal.h"
13#include "HALInitializer.h"
14#include "hal/CAN.h"
15#include "hal/Errors.h"
Austin Schuh1e69f942020-11-14 15:06:14 -080016#include "hal/HALBase.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080017#include "hal/handles/UnlimitedHandleResource.h"
18
19using namespace hal;
20
21namespace {
22struct Receives {
23 uint64_t lastTimeStamp;
24 uint8_t data[8];
25 uint8_t length;
26};
27
28struct CANStorage {
29 HAL_CANManufacturer manufacturer;
30 HAL_CANDeviceType deviceType;
31 uint8_t deviceId;
32 wpi::mutex mapMutex;
33 wpi::SmallDenseMap<int32_t, int32_t> periodicSends;
34 wpi::SmallDenseMap<int32_t, Receives> receives;
35};
36} // namespace
37
38static UnlimitedHandleResource<HAL_CANHandle, CANStorage, HAL_HandleEnum::CAN>*
39 canHandles;
40
41static uint32_t GetPacketBaseTime() {
42 int status = 0;
43 auto basetime = HAL_GetFPGATime(&status);
44 // us to ms
45 return (basetime / 1000ull) & 0xFFFFFFFF;
46}
47
48namespace hal {
49namespace init {
50void InitializeCANAPI() {
51 static UnlimitedHandleResource<HAL_CANHandle, CANStorage, HAL_HandleEnum::CAN>
52 cH;
53 canHandles = &cH;
54}
55} // namespace init
56namespace can {
57int32_t GetCANModuleFromHandle(HAL_CANHandle handle, int32_t* status) {
58 auto can = canHandles->Get(handle);
59 if (!can) {
60 *status = HAL_HANDLE_ERROR;
61 return -1;
62 }
63 return can->deviceId;
64}
65} // namespace can
66} // namespace hal
67
68static int32_t CreateCANId(CANStorage* storage, int32_t apiId) {
69 int32_t createdId = 0;
70 createdId |= (static_cast<int32_t>(storage->deviceType) & 0x1F) << 24;
71 createdId |= (static_cast<int32_t>(storage->manufacturer) & 0xFF) << 16;
72 createdId |= (apiId & 0x3FF) << 6;
73 createdId |= (storage->deviceId & 0x3F);
74 return createdId;
75}
76
77HAL_CANHandle HAL_InitializeCAN(HAL_CANManufacturer manufacturer,
78 int32_t deviceId, HAL_CANDeviceType deviceType,
79 int32_t* status) {
80 hal::init::CheckInit();
81 auto can = std::make_shared<CANStorage>();
82
83 auto handle = canHandles->Allocate(can);
84
85 if (handle == HAL_kInvalidHandle) {
86 *status = NO_AVAILABLE_RESOURCES;
87 return HAL_kInvalidHandle;
88 }
89
90 can->deviceId = deviceId;
91 can->deviceType = deviceType;
92 can->manufacturer = manufacturer;
93
94 return handle;
95}
96
97void HAL_CleanCAN(HAL_CANHandle handle) {
98 auto data = canHandles->Free(handle);
99
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
278 uint32_t now = GetPacketBaseTime();
279 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
310 uint32_t now = GetPacketBaseTime();
311 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
340 uint32_t now = GetPacketBaseTime();
341 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}