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