blob: a3e4904ccad8953ad5d62102f62760bb4d913c2a [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 <ctime>
8
9#include <wpi/DenseMap.h>
10
11#include "HALInitializer.h"
12#include "hal/CAN.h"
13#include "hal/Errors.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080014#include "hal/handles/UnlimitedHandleResource.h"
15
16using namespace hal;
17
18namespace {
19struct Receives {
20 uint32_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
38static uint32_t GetPacketBaseTime() {
39 timespec t;
40 clock_gettime(CLOCK_MONOTONIC, &t);
41
42 // Convert t to milliseconds
43 uint64_t ms = t.tv_sec * 1000ull + t.tv_nsec / 1000000ull;
44 return ms & 0xFFFFFFFF;
45}
46
Austin Schuh812d0d12021-11-04 20:16:48 -070047namespace hal::init {
Brian Silverman8fce7482020-01-05 13:18:21 -080048void InitializeCANAPI() {
49 static UnlimitedHandleResource<HAL_CANHandle, CANStorage, HAL_HandleEnum::CAN>
50 cH;
51 canHandles = &cH;
52}
Austin Schuh812d0d12021-11-04 20:16:48 -070053} // namespace hal::init
Brian Silverman8fce7482020-01-05 13:18:21 -080054
55static int32_t CreateCANId(CANStorage* storage, int32_t apiId) {
56 int32_t createdId = 0;
57 createdId |= (static_cast<int32_t>(storage->deviceType) & 0x1F) << 24;
58 createdId |= (static_cast<int32_t>(storage->manufacturer) & 0xFF) << 16;
59 createdId |= (apiId & 0x3FF) << 6;
60 createdId |= (storage->deviceId & 0x3F);
61 return createdId;
62}
63
64extern "C" {
65
66HAL_CANHandle HAL_InitializeCAN(HAL_CANManufacturer manufacturer,
67 int32_t deviceId, HAL_CANDeviceType deviceType,
68 int32_t* status) {
69 hal::init::CheckInit();
70 auto can = std::make_shared<CANStorage>();
71
72 auto handle = canHandles->Allocate(can);
73
74 if (handle == HAL_kInvalidHandle) {
75 *status = NO_AVAILABLE_RESOURCES;
76 return HAL_kInvalidHandle;
77 }
78
79 can->deviceId = deviceId;
80 can->deviceType = deviceType;
81 can->manufacturer = manufacturer;
82
83 return handle;
84}
85
86void HAL_CleanCAN(HAL_CANHandle handle) {
87 auto data = canHandles->Free(handle);
James Kuszmaulcf324122023-01-14 14:07:17 -080088 if (data == nullptr) {
89 return;
90 }
Brian Silverman8fce7482020-01-05 13:18:21 -080091
92 std::scoped_lock lock(data->mapMutex);
93
94 for (auto&& i : data->periodicSends) {
95 int32_t s = 0;
96 auto id = CreateCANId(data.get(), i.first);
97 HAL_CAN_SendMessage(id, nullptr, 0, HAL_CAN_SEND_PERIOD_STOP_REPEATING, &s);
98 i.second = -1;
99 }
100}
101
102void HAL_WriteCANPacket(HAL_CANHandle handle, const uint8_t* data,
103 int32_t length, int32_t apiId, int32_t* status) {
104 auto can = canHandles->Get(handle);
105 if (!can) {
106 *status = HAL_HANDLE_ERROR;
107 return;
108 }
109 auto id = CreateCANId(can.get(), apiId);
110
111 HAL_CAN_SendMessage(id, data, length, HAL_CAN_SEND_PERIOD_NO_REPEAT, status);
112
113 if (*status != 0) {
114 return;
115 }
116 std::scoped_lock lock(can->mapMutex);
117 can->periodicSends[apiId] = -1;
118}
119
120void HAL_WriteCANPacketRepeating(HAL_CANHandle handle, const uint8_t* data,
121 int32_t length, int32_t apiId,
122 int32_t repeatMs, int32_t* status) {
123 auto can = canHandles->Get(handle);
124 if (!can) {
125 *status = HAL_HANDLE_ERROR;
126 return;
127 }
128 auto id = CreateCANId(can.get(), apiId);
129
130 HAL_CAN_SendMessage(id, data, length, repeatMs, status);
131
132 if (*status != 0) {
133 return;
134 }
135 std::scoped_lock lock(can->mapMutex);
136 can->periodicSends[apiId] = repeatMs;
137}
138
139void HAL_WriteCANRTRFrame(HAL_CANHandle handle, int32_t length, int32_t apiId,
140 int32_t* status) {
141 auto can = canHandles->Get(handle);
142 if (!can) {
143 *status = HAL_HANDLE_ERROR;
144 return;
145 }
146 auto id = CreateCANId(can.get(), apiId);
147 id |= HAL_CAN_IS_FRAME_REMOTE;
148 uint8_t data[8];
149 std::memset(data, 0, sizeof(data));
150
151 HAL_CAN_SendMessage(id, data, length, HAL_CAN_SEND_PERIOD_NO_REPEAT, status);
152
153 if (*status != 0) {
154 return;
155 }
156 std::scoped_lock lock(can->mapMutex);
157 can->periodicSends[apiId] = -1;
158}
159
160void HAL_StopCANPacketRepeating(HAL_CANHandle handle, int32_t apiId,
161 int32_t* status) {
162 auto can = canHandles->Get(handle);
163 if (!can) {
164 *status = HAL_HANDLE_ERROR;
165 return;
166 }
167 auto id = CreateCANId(can.get(), apiId);
168
169 HAL_CAN_SendMessage(id, nullptr, 0, HAL_CAN_SEND_PERIOD_STOP_REPEATING,
170 status);
171
172 if (*status != 0) {
173 return;
174 }
175 std::scoped_lock lock(can->mapMutex);
176 can->periodicSends[apiId] = -1;
177}
178
179void HAL_ReadCANPacketNew(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
180 int32_t* length, uint64_t* receivedTimestamp,
181 int32_t* status) {
182 auto can = canHandles->Get(handle);
183 if (!can) {
184 *status = HAL_HANDLE_ERROR;
185 return;
186 }
187
188 uint32_t messageId = CreateCANId(can.get(), apiId);
189 uint8_t dataSize = 0;
190 uint32_t ts = 0;
191 HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
192
193 if (*status == 0) {
194 std::scoped_lock lock(can->mapMutex);
195 auto& msg = can->receives[messageId];
196 msg.length = dataSize;
197 msg.lastTimeStamp = ts;
198 // The NetComm call placed in data, copy into the msg
199 std::memcpy(msg.data, data, dataSize);
200 }
201 *length = dataSize;
202 *receivedTimestamp = ts;
203}
204
205void HAL_ReadCANPacketLatest(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
206 int32_t* length, uint64_t* receivedTimestamp,
207 int32_t* status) {
208 auto can = canHandles->Get(handle);
209 if (!can) {
210 *status = HAL_HANDLE_ERROR;
211 return;
212 }
213
214 uint32_t messageId = CreateCANId(can.get(), apiId);
215 uint8_t dataSize = 0;
216 uint32_t ts = 0;
217 HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
218
219 std::scoped_lock lock(can->mapMutex);
220 if (*status == 0) {
221 // fresh update
222 auto& msg = can->receives[messageId];
223 msg.length = dataSize;
224 *length = dataSize;
225 msg.lastTimeStamp = ts;
226 *receivedTimestamp = ts;
227 // The NetComm call placed in data, copy into the msg
228 std::memcpy(msg.data, data, dataSize);
229 } else {
230 auto i = can->receives.find(messageId);
231 if (i != can->receives.end()) {
232 // Read the data from the stored message into the output
233 std::memcpy(data, i->second.data, i->second.length);
234 *length = i->second.length;
235 *receivedTimestamp = i->second.lastTimeStamp;
236 *status = 0;
237 }
238 }
239}
240
241void HAL_ReadCANPacketTimeout(HAL_CANHandle handle, int32_t apiId,
242 uint8_t* data, int32_t* length,
243 uint64_t* receivedTimestamp, int32_t timeoutMs,
244 int32_t* status) {
245 auto can = canHandles->Get(handle);
246 if (!can) {
247 *status = HAL_HANDLE_ERROR;
248 return;
249 }
250
251 uint32_t messageId = CreateCANId(can.get(), apiId);
252 uint8_t dataSize = 0;
253 uint32_t ts = 0;
254 HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
255
256 std::scoped_lock lock(can->mapMutex);
257 if (*status == 0) {
258 // fresh update
259 auto& msg = can->receives[messageId];
260 msg.length = dataSize;
261 *length = dataSize;
262 msg.lastTimeStamp = ts;
263 *receivedTimestamp = ts;
264 // The NetComm call placed in data, copy into the msg
265 std::memcpy(msg.data, data, dataSize);
266 } else {
267 auto i = can->receives.find(messageId);
268 if (i != can->receives.end()) {
269 // Found, check if new enough
270 uint32_t now = GetPacketBaseTime();
271 if (now - i->second.lastTimeStamp > static_cast<uint32_t>(timeoutMs)) {
272 // Timeout, return bad status
273 *status = HAL_CAN_TIMEOUT;
274 return;
275 }
276 // Read the data from the stored message into the output
277 std::memcpy(data, i->second.data, i->second.length);
278 *length = i->second.length;
279 *receivedTimestamp = i->second.lastTimeStamp;
280 *status = 0;
281 }
282 }
283}
284} // extern "C"