blob: 1121fd88ddff956334f1ddf3c763baab1ffeeb5b [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/SPI.h"
6
7#include <fcntl.h>
8#include <linux/spi/spidev.h>
9#include <sys/ioctl.h>
10#include <unistd.h>
11
12#include <array>
13#include <atomic>
Austin Schuh812d0d12021-11-04 20:16:48 -070014#include <cstdio>
Brian Silverman8fce7482020-01-05 13:18:21 -080015#include <cstring>
16
Austin Schuh812d0d12021-11-04 20:16:48 -070017#include <fmt/format.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080018#include <wpi/mutex.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080019
20#include "DigitalInternal.h"
21#include "HALInitializer.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070022#include "HALInternal.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080023#include "hal/DIO.h"
24#include "hal/HAL.h"
25#include "hal/handles/HandlesInternal.h"
26
27using namespace hal;
28
29static int32_t m_spiCS0Handle{0};
30static int32_t m_spiCS1Handle{0};
31static int32_t m_spiCS2Handle{0};
32static int32_t m_spiCS3Handle{0};
33static int32_t m_spiMXPHandle{0};
34
35static constexpr int32_t kSpiMaxHandles = 5;
36
37// Indices 0-3 are for onboard CS0-CS2. Index 4 is for MXP.
38static std::array<wpi::mutex, kSpiMaxHandles> spiHandleMutexes;
39static std::array<wpi::mutex, kSpiMaxHandles> spiApiMutexes;
40static std::array<wpi::mutex, kSpiMaxHandles> spiAccumulatorMutexes;
41
42// MXP SPI does not count towards this
43static std::atomic<int32_t> spiPortCount{0};
44
45static HAL_DigitalHandle digitalHandles[9]{HAL_kInvalidHandle};
46
47static wpi::mutex spiAutoMutex;
48static int32_t spiAutoPort = kSpiMaxHandles;
49static std::atomic_bool spiAutoRunning{false};
50static std::unique_ptr<tDMAManager> spiAutoDMA;
51
52static bool SPIInUseByAuto(HAL_SPIPort port) {
53 // SPI engine conflicts with any other chip selects on the same SPI device.
54 // There are two SPI devices: one for ports 0-3 (onboard), the other for port
55 // 4 (MXP).
Austin Schuh812d0d12021-11-04 20:16:48 -070056 if (!spiAutoRunning) {
57 return false;
58 }
Brian Silverman8fce7482020-01-05 13:18:21 -080059 std::scoped_lock lock(spiAutoMutex);
60 return (spiAutoPort >= 0 && spiAutoPort <= 3 && port >= 0 && port <= 3) ||
61 (spiAutoPort == 4 && port == 4);
62}
63
Austin Schuh812d0d12021-11-04 20:16:48 -070064namespace hal::init {
Brian Silverman8fce7482020-01-05 13:18:21 -080065void InitializeSPI() {}
Austin Schuh812d0d12021-11-04 20:16:48 -070066} // namespace hal::init
Brian Silverman8fce7482020-01-05 13:18:21 -080067
68extern "C" {
69
70static void CommonSPIPortInit(int32_t* status) {
71 // All false cases will set
72 if (spiPortCount.fetch_add(1) == 0) {
73 // Have not been initialized yet
74 initializeDigital(status);
Austin Schuh812d0d12021-11-04 20:16:48 -070075 if (*status != 0) {
76 return;
77 }
Brian Silverman8fce7482020-01-05 13:18:21 -080078 // MISO
79 if ((digitalHandles[3] = HAL_InitializeDIOPort(createPortHandleForSPI(29),
Austin Schuh812d0d12021-11-04 20:16:48 -070080 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -080081 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -070082 std::puts("Failed to allocate DIO 29 (MISO)");
Brian Silverman8fce7482020-01-05 13:18:21 -080083 return;
84 }
85 // MOSI
86 if ((digitalHandles[4] = HAL_InitializeDIOPort(createPortHandleForSPI(30),
Austin Schuh812d0d12021-11-04 20:16:48 -070087 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -080088 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -070089 std::puts("Failed to allocate DIO 30 (MOSI)");
Brian Silverman8fce7482020-01-05 13:18:21 -080090 HAL_FreeDIOPort(digitalHandles[3]); // free the first port allocated
91 return;
92 }
93 }
94}
95
96static void CommonSPIPortFree(void) {
97 if (spiPortCount.fetch_sub(1) == 1) {
98 // Clean up SPI Handles
99 HAL_FreeDIOPort(digitalHandles[3]);
100 HAL_FreeDIOPort(digitalHandles[4]);
101 }
102}
103
104void HAL_InitializeSPI(HAL_SPIPort port, int32_t* status) {
105 hal::init::CheckInit();
106 if (port < 0 || port >= kSpiMaxHandles) {
107 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700108 hal::SetLastError(
109 status,
110 fmt::format("Serial port must be between 0 and {}. Requested {}",
111 kSpiMaxHandles, static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800112 return;
113 }
114
115 int handle;
Austin Schuh812d0d12021-11-04 20:16:48 -0700116 if (HAL_GetSPIHandle(port) != 0) {
117 return;
118 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800119 switch (port) {
120 case HAL_SPI_kOnboardCS0:
121 CommonSPIPortInit(status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700122 if (*status != 0) {
123 return;
124 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800125 // CS0 is not a DIO port, so nothing to allocate
126 handle = open("/dev/spidev0.0", O_RDWR);
127 if (handle < 0) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700128 fmt::print("Failed to open SPI port {}: {}\n", port,
129 std::strerror(errno));
Brian Silverman8fce7482020-01-05 13:18:21 -0800130 CommonSPIPortFree();
131 return;
132 }
133 HAL_SetSPIHandle(HAL_SPI_kOnboardCS0, handle);
134 break;
135 case HAL_SPI_kOnboardCS1:
136 CommonSPIPortInit(status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700137 if (*status != 0) {
138 return;
139 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800140 // CS1, Allocate
141 if ((digitalHandles[0] = HAL_InitializeDIOPort(createPortHandleForSPI(26),
Austin Schuh812d0d12021-11-04 20:16:48 -0700142 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -0800143 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700144 std::puts("Failed to allocate DIO 26 (CS1)");
Brian Silverman8fce7482020-01-05 13:18:21 -0800145 CommonSPIPortFree();
146 return;
147 }
148 handle = open("/dev/spidev0.1", O_RDWR);
149 if (handle < 0) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700150 fmt::print("Failed to open SPI port {}: {}\n", port,
151 std::strerror(errno));
Brian Silverman8fce7482020-01-05 13:18:21 -0800152 CommonSPIPortFree();
153 HAL_FreeDIOPort(digitalHandles[0]);
154 return;
155 }
156 HAL_SetSPIHandle(HAL_SPI_kOnboardCS1, handle);
157 break;
158 case HAL_SPI_kOnboardCS2:
159 CommonSPIPortInit(status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700160 if (*status != 0) {
161 return;
162 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800163 // CS2, Allocate
164 if ((digitalHandles[1] = HAL_InitializeDIOPort(createPortHandleForSPI(27),
Austin Schuh812d0d12021-11-04 20:16:48 -0700165 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -0800166 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700167 std::puts("Failed to allocate DIO 27 (CS2)");
Brian Silverman8fce7482020-01-05 13:18:21 -0800168 CommonSPIPortFree();
169 return;
170 }
171 handle = open("/dev/spidev0.2", O_RDWR);
172 if (handle < 0) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700173 fmt::print("Failed to open SPI port {}: {}\n", port,
174 std::strerror(errno));
Brian Silverman8fce7482020-01-05 13:18:21 -0800175 CommonSPIPortFree();
176 HAL_FreeDIOPort(digitalHandles[1]);
177 return;
178 }
179 HAL_SetSPIHandle(HAL_SPI_kOnboardCS2, handle);
180 break;
181 case HAL_SPI_kOnboardCS3:
182 CommonSPIPortInit(status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700183 if (*status != 0) {
184 return;
185 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800186 // CS3, Allocate
187 if ((digitalHandles[2] = HAL_InitializeDIOPort(createPortHandleForSPI(28),
Austin Schuh812d0d12021-11-04 20:16:48 -0700188 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -0800189 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700190 std::puts("Failed to allocate DIO 28 (CS3)");
Brian Silverman8fce7482020-01-05 13:18:21 -0800191 CommonSPIPortFree();
192 return;
193 }
194 handle = open("/dev/spidev0.3", O_RDWR);
195 if (handle < 0) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700196 fmt::print("Failed to open SPI port {}: {}\n", port,
197 std::strerror(errno));
Brian Silverman8fce7482020-01-05 13:18:21 -0800198 CommonSPIPortFree();
199 HAL_FreeDIOPort(digitalHandles[2]);
200 return;
201 }
202 HAL_SetSPIHandle(HAL_SPI_kOnboardCS3, handle);
203 break;
204 case HAL_SPI_kMXP:
205 initializeDigital(status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700206 if (*status != 0) {
207 return;
208 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800209 if ((digitalHandles[5] = HAL_InitializeDIOPort(createPortHandleForSPI(14),
Austin Schuh812d0d12021-11-04 20:16:48 -0700210 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -0800211 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700212 std::puts("Failed to allocate DIO 14");
Brian Silverman8fce7482020-01-05 13:18:21 -0800213 return;
214 }
215 if ((digitalHandles[6] = HAL_InitializeDIOPort(createPortHandleForSPI(15),
Austin Schuh812d0d12021-11-04 20:16:48 -0700216 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -0800217 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700218 std::puts("Failed to allocate DIO 15");
Brian Silverman8fce7482020-01-05 13:18:21 -0800219 HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
220 return;
221 }
222 if ((digitalHandles[7] = HAL_InitializeDIOPort(createPortHandleForSPI(16),
Austin Schuh812d0d12021-11-04 20:16:48 -0700223 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -0800224 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700225 std::puts("Failed to allocate DIO 16");
Brian Silverman8fce7482020-01-05 13:18:21 -0800226 HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
227 HAL_FreeDIOPort(digitalHandles[6]); // free the second port allocated
228 return;
229 }
230 if ((digitalHandles[8] = HAL_InitializeDIOPort(createPortHandleForSPI(17),
Austin Schuh812d0d12021-11-04 20:16:48 -0700231 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -0800232 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700233 std::puts("Failed to allocate DIO 17");
Brian Silverman8fce7482020-01-05 13:18:21 -0800234 HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
235 HAL_FreeDIOPort(digitalHandles[6]); // free the second port allocated
236 HAL_FreeDIOPort(digitalHandles[7]); // free the third port allocated
237 return;
238 }
239 digitalSystem->writeEnableMXPSpecialFunction(
240 digitalSystem->readEnableMXPSpecialFunction(status) | 0x00F0, status);
241 handle = open("/dev/spidev1.0", O_RDWR);
242 if (handle < 0) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700243 fmt::print("Failed to open SPI port {}: {}\n", port,
244 std::strerror(errno));
Brian Silverman8fce7482020-01-05 13:18:21 -0800245 HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
246 HAL_FreeDIOPort(digitalHandles[6]); // free the second port allocated
247 HAL_FreeDIOPort(digitalHandles[7]); // free the third port allocated
248 HAL_FreeDIOPort(digitalHandles[8]); // free the fourth port allocated
249 return;
250 }
251 HAL_SetSPIHandle(HAL_SPI_kMXP, handle);
252 break;
253 default:
254 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700255 hal::SetLastError(
256 status, fmt::format("Invalid SPI port {}", static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800257 break;
258 }
259}
260
261int32_t HAL_TransactionSPI(HAL_SPIPort port, const uint8_t* dataToSend,
262 uint8_t* dataReceived, int32_t size) {
263 if (port < 0 || port >= kSpiMaxHandles) {
264 return -1;
265 }
266
Austin Schuh812d0d12021-11-04 20:16:48 -0700267 if (SPIInUseByAuto(port)) {
268 return -1;
269 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800270
271 struct spi_ioc_transfer xfer;
272 std::memset(&xfer, 0, sizeof(xfer));
273 xfer.tx_buf = (__u64)dataToSend;
274 xfer.rx_buf = (__u64)dataReceived;
275 xfer.len = size;
276
277 std::scoped_lock lock(spiApiMutexes[port]);
278 return ioctl(HAL_GetSPIHandle(port), SPI_IOC_MESSAGE(1), &xfer);
279}
280
281int32_t HAL_WriteSPI(HAL_SPIPort port, const uint8_t* dataToSend,
282 int32_t sendSize) {
283 if (port < 0 || port >= kSpiMaxHandles) {
284 return -1;
285 }
286
Austin Schuh812d0d12021-11-04 20:16:48 -0700287 if (SPIInUseByAuto(port)) {
288 return -1;
289 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800290
291 struct spi_ioc_transfer xfer;
292 std::memset(&xfer, 0, sizeof(xfer));
293 xfer.tx_buf = (__u64)dataToSend;
294 xfer.len = sendSize;
295
296 std::scoped_lock lock(spiApiMutexes[port]);
297 return ioctl(HAL_GetSPIHandle(port), SPI_IOC_MESSAGE(1), &xfer);
298}
299
300int32_t HAL_ReadSPI(HAL_SPIPort port, uint8_t* buffer, int32_t count) {
301 if (port < 0 || port >= kSpiMaxHandles) {
302 return -1;
303 }
304
Austin Schuh812d0d12021-11-04 20:16:48 -0700305 if (SPIInUseByAuto(port)) {
306 return -1;
307 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800308
309 struct spi_ioc_transfer xfer;
310 std::memset(&xfer, 0, sizeof(xfer));
311 xfer.rx_buf = (__u64)buffer;
312 xfer.len = count;
313
314 std::scoped_lock lock(spiApiMutexes[port]);
315 return ioctl(HAL_GetSPIHandle(port), SPI_IOC_MESSAGE(1), &xfer);
316}
317
318void HAL_CloseSPI(HAL_SPIPort port) {
319 if (port < 0 || port >= kSpiMaxHandles) {
320 return;
321 }
322
323 int32_t status = 0;
324 HAL_FreeSPIAuto(port, &status);
325
326 {
327 std::scoped_lock lock(spiApiMutexes[port]);
328 close(HAL_GetSPIHandle(port));
329 }
330
331 HAL_SetSPIHandle(port, 0);
332 if (port < 4) {
333 CommonSPIPortFree();
334 }
335
336 switch (port) {
337 // Case 0 does not need to do anything
338 case 1:
339 HAL_FreeDIOPort(digitalHandles[0]);
340 break;
341 case 2:
342 HAL_FreeDIOPort(digitalHandles[1]);
343 break;
344 case 3:
345 HAL_FreeDIOPort(digitalHandles[2]);
346 break;
347 case 4:
348 HAL_FreeDIOPort(digitalHandles[5]);
349 HAL_FreeDIOPort(digitalHandles[6]);
350 HAL_FreeDIOPort(digitalHandles[7]);
351 HAL_FreeDIOPort(digitalHandles[8]);
352 break;
353 default:
354 break;
355 }
356}
357
358void HAL_SetSPISpeed(HAL_SPIPort port, int32_t speed) {
359 if (port < 0 || port >= kSpiMaxHandles) {
360 return;
361 }
362
363 std::scoped_lock lock(spiApiMutexes[port]);
364 ioctl(HAL_GetSPIHandle(port), SPI_IOC_WR_MAX_SPEED_HZ, &speed);
365}
366
367void HAL_SetSPIOpts(HAL_SPIPort port, HAL_Bool msbFirst,
368 HAL_Bool sampleOnTrailing, HAL_Bool clkIdleHigh) {
369 if (port < 0 || port >= kSpiMaxHandles) {
370 return;
371 }
372
373 uint8_t mode = 0;
374 mode |= (!msbFirst ? 8 : 0);
375 mode |= (clkIdleHigh ? 2 : 0);
376 mode |= (sampleOnTrailing ? 1 : 0);
377
378 std::scoped_lock lock(spiApiMutexes[port]);
379 ioctl(HAL_GetSPIHandle(port), SPI_IOC_WR_MODE, &mode);
380}
381
382void HAL_SetSPIChipSelectActiveHigh(HAL_SPIPort port, int32_t* status) {
383 if (port < 0 || port >= kSpiMaxHandles) {
384 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700385 hal::SetLastError(
386 status,
387 fmt::format("Serial port must be between 0 and {}. Requested {}",
388 kSpiMaxHandles, static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800389 return;
390 }
391
392 std::scoped_lock lock(spiApiMutexes[port]);
393 if (port < 4) {
394 spiSystem->writeChipSelectActiveHigh_Hdr(
395 spiSystem->readChipSelectActiveHigh_Hdr(status) | (1 << port), status);
396 } else {
397 spiSystem->writeChipSelectActiveHigh_MXP(1, status);
398 }
399}
400
401void HAL_SetSPIChipSelectActiveLow(HAL_SPIPort port, int32_t* status) {
402 if (port < 0 || port >= kSpiMaxHandles) {
403 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700404 hal::SetLastError(
405 status,
406 fmt::format("Serial port must be between 0 and {}. Requested {}",
407 kSpiMaxHandles, static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800408 return;
409 }
410
411 std::scoped_lock lock(spiApiMutexes[port]);
412 if (port < 4) {
413 spiSystem->writeChipSelectActiveHigh_Hdr(
414 spiSystem->readChipSelectActiveHigh_Hdr(status) & ~(1 << port), status);
415 } else {
416 spiSystem->writeChipSelectActiveHigh_MXP(0, status);
417 }
418}
419
420int32_t HAL_GetSPIHandle(HAL_SPIPort port) {
421 if (port < 0 || port >= kSpiMaxHandles) {
422 return 0;
423 }
424
425 std::scoped_lock lock(spiHandleMutexes[port]);
426 switch (port) {
427 case 0:
428 return m_spiCS0Handle;
429 case 1:
430 return m_spiCS1Handle;
431 case 2:
432 return m_spiCS2Handle;
433 case 3:
434 return m_spiCS3Handle;
435 case 4:
436 return m_spiMXPHandle;
437 default:
438 return 0;
439 }
440}
441
442void HAL_SetSPIHandle(HAL_SPIPort port, int32_t handle) {
443 if (port < 0 || port >= kSpiMaxHandles) {
444 return;
445 }
446
447 std::scoped_lock lock(spiHandleMutexes[port]);
448 switch (port) {
449 case 0:
450 m_spiCS0Handle = handle;
451 break;
452 case 1:
453 m_spiCS1Handle = handle;
454 break;
455 case 2:
456 m_spiCS2Handle = handle;
457 break;
458 case 3:
459 m_spiCS3Handle = handle;
460 break;
461 case 4:
462 m_spiMXPHandle = handle;
463 break;
464 default:
465 break;
466 }
467}
468
469void HAL_InitSPIAuto(HAL_SPIPort port, int32_t bufferSize, int32_t* status) {
470 if (port < 0 || port >= kSpiMaxHandles) {
471 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700472 hal::SetLastError(
473 status,
474 fmt::format("Serial port must be between 0 and {}. Requested {}",
475 kSpiMaxHandles, static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800476 return;
477 }
478
479 std::scoped_lock lock(spiAutoMutex);
480 // FPGA only has one auto SPI engine
481 if (spiAutoPort != kSpiMaxHandles) {
482 *status = RESOURCE_IS_ALLOCATED;
483 return;
484 }
485
486 // remember the initialized port for other entry points
487 spiAutoPort = port;
488
489 // configure the correct chip select
490 if (port < 4) {
491 spiSystem->writeAutoSPI1Select(false, status);
492 spiSystem->writeAutoChipSelect(port, status);
493 } else {
494 spiSystem->writeAutoSPI1Select(true, status);
495 spiSystem->writeAutoChipSelect(0, status);
496 }
497
498 // configure DMA
499 tDMAChannelDescriptor desc;
500 spiSystem->getSystemInterface()->getDmaDescriptor(g_SpiAutoData_index, &desc);
501 spiAutoDMA = std::make_unique<tDMAManager>(desc.channel, bufferSize, status);
502}
503
504void HAL_FreeSPIAuto(HAL_SPIPort port, int32_t* status) {
505 if (port < 0 || port >= kSpiMaxHandles) {
506 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700507 hal::SetLastError(
508 status,
509 fmt::format("Serial port must be between 0 and {}. Requested {}",
510 kSpiMaxHandles, static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800511 return;
512 }
513
514 std::scoped_lock lock(spiAutoMutex);
Austin Schuh812d0d12021-11-04 20:16:48 -0700515 if (spiAutoPort != port) {
516 return;
517 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800518 spiAutoPort = kSpiMaxHandles;
519
520 // disable by setting to internal clock and setting rate=0
521 spiSystem->writeAutoRate(0, status);
522 spiSystem->writeAutoTriggerConfig_ExternalClock(false, status);
523
524 // stop the DMA
525 spiAutoDMA->stop(status);
526
527 spiAutoDMA.reset(nullptr);
528
529 spiAutoRunning = false;
530}
531
532void HAL_StartSPIAutoRate(HAL_SPIPort port, double period, int32_t* status) {
533 std::scoped_lock lock(spiAutoMutex);
534 // FPGA only has one auto SPI engine
535 if (port != spiAutoPort) {
536 *status = INCOMPATIBLE_STATE;
537 return;
538 }
539
540 spiAutoRunning = true;
541
542 // start the DMA
543 spiAutoDMA->start(status);
544
545 // auto rate is in microseconds
546 spiSystem->writeAutoRate(period * 1000000, status);
547
548 // disable the external clock
549 spiSystem->writeAutoTriggerConfig_ExternalClock(false, status);
550}
551
552void HAL_StartSPIAutoTrigger(HAL_SPIPort port, HAL_Handle digitalSourceHandle,
553 HAL_AnalogTriggerType analogTriggerType,
554 HAL_Bool triggerRising, HAL_Bool triggerFalling,
555 int32_t* status) {
556 std::scoped_lock lock(spiAutoMutex);
557 // FPGA only has one auto SPI engine
558 if (port != spiAutoPort) {
559 *status = INCOMPATIBLE_STATE;
560 return;
561 }
562
563 spiAutoRunning = true;
564
565 // start the DMA
566 spiAutoDMA->start(status);
567
568 // get channel routing
569 bool routingAnalogTrigger = false;
570 uint8_t routingChannel = 0;
571 uint8_t routingModule = 0;
572 if (!remapDigitalSource(digitalSourceHandle, analogTriggerType,
573 routingChannel, routingModule,
574 routingAnalogTrigger)) {
575 *status = HAL_HANDLE_ERROR;
576 return;
577 }
578
579 // configure external trigger and enable it
580 tSPI::tAutoTriggerConfig config;
581 config.ExternalClock = 1;
582 config.FallingEdge = triggerFalling ? 1 : 0;
583 config.RisingEdge = triggerRising ? 1 : 0;
584 config.ExternalClockSource_AnalogTrigger = routingAnalogTrigger ? 1 : 0;
585 config.ExternalClockSource_Module = routingModule;
586 config.ExternalClockSource_Channel = routingChannel;
587 spiSystem->writeAutoTriggerConfig(config, status);
588}
589
590void HAL_StopSPIAuto(HAL_SPIPort port, int32_t* status) {
591 std::scoped_lock lock(spiAutoMutex);
592 // FPGA only has one auto SPI engine
593 if (port != spiAutoPort) {
594 *status = INCOMPATIBLE_STATE;
595 return;
596 }
597
598 // disable by setting to internal clock and setting rate=0
599 spiSystem->writeAutoRate(0, status);
600 spiSystem->writeAutoTriggerConfig_ExternalClock(false, status);
601
602 // stop the DMA
603 spiAutoDMA->stop(status);
604
605 spiAutoRunning = false;
606}
607
608void HAL_SetSPIAutoTransmitData(HAL_SPIPort port, const uint8_t* dataToSend,
609 int32_t dataSize, int32_t zeroSize,
610 int32_t* status) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800611 if (dataSize < 0 || dataSize > 32) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800612 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700613 hal::SetLastError(
614 status,
615 fmt::format(
616 "Data size must be between 0 and 32 inclusive. Requested {}",
617 dataSize));
Brian Silverman8fce7482020-01-05 13:18:21 -0800618 return;
619 }
620
Austin Schuh1e69f942020-11-14 15:06:14 -0800621 if (zeroSize < 0 || zeroSize > 127) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800622 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700623 hal::SetLastError(
624 status,
625 fmt::format(
626 "Zero size must be between 0 and 127 inclusive. Requested {}",
627 zeroSize));
Brian Silverman8fce7482020-01-05 13:18:21 -0800628 return;
629 }
630
631 std::scoped_lock lock(spiAutoMutex);
632 // FPGA only has one auto SPI engine
633 if (port != spiAutoPort) {
634 *status = INCOMPATIBLE_STATE;
635 return;
636 }
637
638 // set tx data registers
639 for (int32_t i = 0; i < dataSize; ++i)
640 spiSystem->writeAutoTx(i >> 2, i & 3, dataToSend[i], status);
641
642 // set byte counts
643 tSPI::tAutoByteCount config;
644 config.ZeroByteCount = static_cast<unsigned>(zeroSize) & 0x7f;
645 config.TxByteCount = static_cast<unsigned>(dataSize) & 0x1f;
646 spiSystem->writeAutoByteCount(config, status);
647}
648
649void HAL_ForceSPIAutoRead(HAL_SPIPort port, int32_t* status) {
650 std::scoped_lock lock(spiAutoMutex);
651 // FPGA only has one auto SPI engine
652 if (port != spiAutoPort) {
653 *status = INCOMPATIBLE_STATE;
654 return;
655 }
656
657 spiSystem->strobeAutoForceOne(status);
658}
659
660int32_t HAL_ReadSPIAutoReceivedData(HAL_SPIPort port, uint32_t* buffer,
661 int32_t numToRead, double timeout,
662 int32_t* status) {
663 std::scoped_lock lock(spiAutoMutex);
664 // FPGA only has one auto SPI engine
665 if (port != spiAutoPort) {
666 *status = INCOMPATIBLE_STATE;
667 return 0;
668 }
669
670 size_t numRemaining = 0;
671 // timeout is in ms
672 spiAutoDMA->read(buffer, numToRead, timeout * 1000, &numRemaining, status);
673 return numRemaining;
674}
675
676int32_t HAL_GetSPIAutoDroppedCount(HAL_SPIPort port, int32_t* status) {
677 std::scoped_lock lock(spiAutoMutex);
678 // FPGA only has one auto SPI engine
679 if (port != spiAutoPort) {
680 *status = INCOMPATIBLE_STATE;
681 return 0;
682 }
683
684 return spiSystem->readTransferSkippedFullCount(status);
685}
686
687void HAL_ConfigureSPIAutoStall(HAL_SPIPort port, int32_t csToSclkTicks,
688 int32_t stallTicks, int32_t pow2BytesPerRead,
689 int32_t* status) {
690 std::scoped_lock lock(spiAutoMutex);
691 // FPGA only has one auto SPI engine
692 if (port != spiAutoPort) {
693 *status = INCOMPATIBLE_STATE;
694 return;
695 }
696
697 tSPI::tStallConfig stallConfig;
698 stallConfig.CsToSclkTicks = static_cast<uint8_t>(csToSclkTicks);
699 stallConfig.StallTicks = static_cast<uint16_t>(stallTicks);
700 stallConfig.Pow2BytesPerRead = static_cast<uint8_t>(pow2BytesPerRead);
701 spiSystem->writeStallConfig(stallConfig, status);
702}
703
704} // extern "C"