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