blob: a7c5b37f6ec8504cd5888823d33a5ab2c9738f18 [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);
88
89 std::scoped_lock lock(data->mapMutex);
90
91 for (auto&& i : data->periodicSends) {
92 int32_t s = 0;
93 auto id = CreateCANId(data.get(), i.first);
94 HAL_CAN_SendMessage(id, nullptr, 0, HAL_CAN_SEND_PERIOD_STOP_REPEATING, &s);
95 i.second = -1;
96 }
97}
98
99void HAL_WriteCANPacket(HAL_CANHandle handle, const uint8_t* data,
100 int32_t length, int32_t apiId, int32_t* status) {
101 auto can = canHandles->Get(handle);
102 if (!can) {
103 *status = HAL_HANDLE_ERROR;
104 return;
105 }
106 auto id = CreateCANId(can.get(), apiId);
107
108 HAL_CAN_SendMessage(id, data, length, HAL_CAN_SEND_PERIOD_NO_REPEAT, status);
109
110 if (*status != 0) {
111 return;
112 }
113 std::scoped_lock lock(can->mapMutex);
114 can->periodicSends[apiId] = -1;
115}
116
117void HAL_WriteCANPacketRepeating(HAL_CANHandle handle, const uint8_t* data,
118 int32_t length, int32_t apiId,
119 int32_t repeatMs, int32_t* status) {
120 auto can = canHandles->Get(handle);
121 if (!can) {
122 *status = HAL_HANDLE_ERROR;
123 return;
124 }
125 auto id = CreateCANId(can.get(), apiId);
126
127 HAL_CAN_SendMessage(id, data, length, repeatMs, status);
128
129 if (*status != 0) {
130 return;
131 }
132 std::scoped_lock lock(can->mapMutex);
133 can->periodicSends[apiId] = repeatMs;
134}
135
136void HAL_WriteCANRTRFrame(HAL_CANHandle handle, int32_t length, int32_t apiId,
137 int32_t* status) {
138 auto can = canHandles->Get(handle);
139 if (!can) {
140 *status = HAL_HANDLE_ERROR;
141 return;
142 }
143 auto id = CreateCANId(can.get(), apiId);
144 id |= HAL_CAN_IS_FRAME_REMOTE;
145 uint8_t data[8];
146 std::memset(data, 0, sizeof(data));
147
148 HAL_CAN_SendMessage(id, data, length, HAL_CAN_SEND_PERIOD_NO_REPEAT, status);
149
150 if (*status != 0) {
151 return;
152 }
153 std::scoped_lock lock(can->mapMutex);
154 can->periodicSends[apiId] = -1;
155}
156
157void HAL_StopCANPacketRepeating(HAL_CANHandle handle, int32_t apiId,
158 int32_t* status) {
159 auto can = canHandles->Get(handle);
160 if (!can) {
161 *status = HAL_HANDLE_ERROR;
162 return;
163 }
164 auto id = CreateCANId(can.get(), apiId);
165
166 HAL_CAN_SendMessage(id, nullptr, 0, HAL_CAN_SEND_PERIOD_STOP_REPEATING,
167 status);
168
169 if (*status != 0) {
170 return;
171 }
172 std::scoped_lock lock(can->mapMutex);
173 can->periodicSends[apiId] = -1;
174}
175
176void HAL_ReadCANPacketNew(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
177 int32_t* length, uint64_t* receivedTimestamp,
178 int32_t* status) {
179 auto can = canHandles->Get(handle);
180 if (!can) {
181 *status = HAL_HANDLE_ERROR;
182 return;
183 }
184
185 uint32_t messageId = CreateCANId(can.get(), apiId);
186 uint8_t dataSize = 0;
187 uint32_t ts = 0;
188 HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
189
190 if (*status == 0) {
191 std::scoped_lock lock(can->mapMutex);
192 auto& msg = can->receives[messageId];
193 msg.length = dataSize;
194 msg.lastTimeStamp = ts;
195 // The NetComm call placed in data, copy into the msg
196 std::memcpy(msg.data, data, dataSize);
197 }
198 *length = dataSize;
199 *receivedTimestamp = ts;
200}
201
202void HAL_ReadCANPacketLatest(HAL_CANHandle handle, int32_t apiId, uint8_t* data,
203 int32_t* length, uint64_t* receivedTimestamp,
204 int32_t* status) {
205 auto can = canHandles->Get(handle);
206 if (!can) {
207 *status = HAL_HANDLE_ERROR;
208 return;
209 }
210
211 uint32_t messageId = CreateCANId(can.get(), apiId);
212 uint8_t dataSize = 0;
213 uint32_t ts = 0;
214 HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
215
216 std::scoped_lock lock(can->mapMutex);
217 if (*status == 0) {
218 // fresh update
219 auto& msg = can->receives[messageId];
220 msg.length = dataSize;
221 *length = dataSize;
222 msg.lastTimeStamp = ts;
223 *receivedTimestamp = ts;
224 // The NetComm call placed in data, copy into the msg
225 std::memcpy(msg.data, data, dataSize);
226 } else {
227 auto i = can->receives.find(messageId);
228 if (i != can->receives.end()) {
229 // Read the data from the stored message into the output
230 std::memcpy(data, i->second.data, i->second.length);
231 *length = i->second.length;
232 *receivedTimestamp = i->second.lastTimeStamp;
233 *status = 0;
234 }
235 }
236}
237
238void HAL_ReadCANPacketTimeout(HAL_CANHandle handle, int32_t apiId,
239 uint8_t* data, int32_t* length,
240 uint64_t* receivedTimestamp, int32_t timeoutMs,
241 int32_t* status) {
242 auto can = canHandles->Get(handle);
243 if (!can) {
244 *status = HAL_HANDLE_ERROR;
245 return;
246 }
247
248 uint32_t messageId = CreateCANId(can.get(), apiId);
249 uint8_t dataSize = 0;
250 uint32_t ts = 0;
251 HAL_CAN_ReceiveMessage(&messageId, 0x1FFFFFFF, data, &dataSize, &ts, status);
252
253 std::scoped_lock lock(can->mapMutex);
254 if (*status == 0) {
255 // fresh update
256 auto& msg = can->receives[messageId];
257 msg.length = dataSize;
258 *length = dataSize;
259 msg.lastTimeStamp = ts;
260 *receivedTimestamp = ts;
261 // The NetComm call placed in data, copy into the msg
262 std::memcpy(msg.data, data, dataSize);
263 } else {
264 auto i = can->receives.find(messageId);
265 if (i != can->receives.end()) {
266 // Found, check if new enough
267 uint32_t now = GetPacketBaseTime();
268 if (now - i->second.lastTimeStamp > static_cast<uint32_t>(timeoutMs)) {
269 // Timeout, return bad status
270 *status = HAL_CAN_TIMEOUT;
271 return;
272 }
273 // Read the data from the stored message into the output
274 std::memcpy(data, i->second.data, i->second.length);
275 *length = i->second.length;
276 *receivedTimestamp = i->second.lastTimeStamp;
277 *status = 0;
278 }
279 }
280}
281} // extern "C"