Squashed 'third_party/allwpilib_2019/' content from commit bd05dfa1c
Change-Id: I2b1c2250cdb9b055133780c33593292098c375b7
git-subtree-dir: third_party/allwpilib_2019
git-subtree-split: bd05dfa1c7cca74c4fac451e7b9d6a37e7b53447
diff --git a/hal/src/main/native/athena/SPI.cpp b/hal/src/main/native/athena/SPI.cpp
new file mode 100644
index 0000000..ddced41
--- /dev/null
+++ b/hal/src/main/native/athena/SPI.cpp
@@ -0,0 +1,634 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
+/* Open Source Software - may be modified and shared by FRC teams. The code */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project. */
+/*----------------------------------------------------------------------------*/
+
+#include "hal/SPI.h"
+
+#include <fcntl.h>
+#include <linux/spi/spidev.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include <array>
+#include <atomic>
+#include <cstring>
+
+#include <wpi/mutex.h>
+#include <wpi/raw_ostream.h>
+
+#include "DigitalInternal.h"
+#include "HALInitializer.h"
+#include "hal/DIO.h"
+#include "hal/HAL.h"
+#include "hal/handles/HandlesInternal.h"
+
+using namespace hal;
+
+static int32_t m_spiCS0Handle{0};
+static int32_t m_spiCS1Handle{0};
+static int32_t m_spiCS2Handle{0};
+static int32_t m_spiCS3Handle{0};
+static int32_t m_spiMXPHandle{0};
+
+static constexpr int32_t kSpiMaxHandles = 5;
+
+// Indices 0-3 are for onboard CS0-CS2. Index 4 is for MXP.
+static std::array<wpi::mutex, kSpiMaxHandles> spiHandleMutexes;
+static std::array<wpi::mutex, kSpiMaxHandles> spiApiMutexes;
+static std::array<wpi::mutex, kSpiMaxHandles> spiAccumulatorMutexes;
+
+// MXP SPI does not count towards this
+static std::atomic<int32_t> spiPortCount{0};
+
+static HAL_DigitalHandle digitalHandles[9]{HAL_kInvalidHandle};
+
+static wpi::mutex spiAutoMutex;
+static int32_t spiAutoPort = kSpiMaxHandles;
+static std::atomic_bool spiAutoRunning{false};
+static std::unique_ptr<tDMAManager> spiAutoDMA;
+
+static bool SPIInUseByAuto(HAL_SPIPort port) {
+ // SPI engine conflicts with any other chip selects on the same SPI device.
+ // There are two SPI devices: one for ports 0-3 (onboard), the other for port
+ // 4 (MXP).
+ if (!spiAutoRunning) return false;
+ std::lock_guard<wpi::mutex> lock(spiAutoMutex);
+ return (spiAutoPort >= 0 && spiAutoPort <= 3 && port >= 0 && port <= 3) ||
+ (spiAutoPort == 4 && port == 4);
+}
+
+namespace hal {
+namespace init {
+void InitializeSPI() {}
+} // namespace init
+} // namespace hal
+
+extern "C" {
+
+static void CommonSPIPortInit(int32_t* status) {
+ // All false cases will set
+ if (spiPortCount.fetch_add(1) == 0) {
+ // Have not been initialized yet
+ initializeDigital(status);
+ if (*status != 0) return;
+ // MISO
+ if ((digitalHandles[3] = HAL_InitializeDIOPort(createPortHandleForSPI(29),
+ false, status)) ==
+ HAL_kInvalidHandle) {
+ std::printf("Failed to allocate DIO 29 (MISO)\n");
+ return;
+ }
+ // MOSI
+ if ((digitalHandles[4] = HAL_InitializeDIOPort(createPortHandleForSPI(30),
+ false, status)) ==
+ HAL_kInvalidHandle) {
+ std::printf("Failed to allocate DIO 30 (MOSI)\n");
+ HAL_FreeDIOPort(digitalHandles[3]); // free the first port allocated
+ return;
+ }
+ }
+}
+
+static void CommonSPIPortFree(void) {
+ if (spiPortCount.fetch_sub(1) == 1) {
+ // Clean up SPI Handles
+ HAL_FreeDIOPort(digitalHandles[3]);
+ HAL_FreeDIOPort(digitalHandles[4]);
+ }
+}
+
+void HAL_InitializeSPI(HAL_SPIPort port, int32_t* status) {
+ hal::init::CheckInit();
+ if (port < 0 || port >= kSpiMaxHandles) {
+ *status = PARAMETER_OUT_OF_RANGE;
+ return;
+ }
+
+ int handle;
+ if (HAL_GetSPIHandle(port) != 0) return;
+ switch (port) {
+ case HAL_SPI_kOnboardCS0:
+ CommonSPIPortInit(status);
+ if (*status != 0) return;
+ // CS0 is not a DIO port, so nothing to allocate
+ handle = open("/dev/spidev0.0", O_RDWR);
+ if (handle < 0) {
+ std::printf("Failed to open SPI port %d: %s\n", port,
+ std::strerror(errno));
+ CommonSPIPortFree();
+ return;
+ }
+ HAL_SetSPIHandle(HAL_SPI_kOnboardCS0, handle);
+ break;
+ case HAL_SPI_kOnboardCS1:
+ CommonSPIPortInit(status);
+ if (*status != 0) return;
+ // CS1, Allocate
+ if ((digitalHandles[0] = HAL_InitializeDIOPort(createPortHandleForSPI(26),
+ false, status)) ==
+ HAL_kInvalidHandle) {
+ std::printf("Failed to allocate DIO 26 (CS1)\n");
+ CommonSPIPortFree();
+ return;
+ }
+ handle = open("/dev/spidev0.1", O_RDWR);
+ if (handle < 0) {
+ std::printf("Failed to open SPI port %d: %s\n", port,
+ std::strerror(errno));
+ CommonSPIPortFree();
+ HAL_FreeDIOPort(digitalHandles[0]);
+ return;
+ }
+ HAL_SetSPIHandle(HAL_SPI_kOnboardCS1, handle);
+ break;
+ case HAL_SPI_kOnboardCS2:
+ CommonSPIPortInit(status);
+ if (*status != 0) return;
+ // CS2, Allocate
+ if ((digitalHandles[1] = HAL_InitializeDIOPort(createPortHandleForSPI(27),
+ false, status)) ==
+ HAL_kInvalidHandle) {
+ std::printf("Failed to allocate DIO 27 (CS2)\n");
+ CommonSPIPortFree();
+ return;
+ }
+ handle = open("/dev/spidev0.2", O_RDWR);
+ if (handle < 0) {
+ std::printf("Failed to open SPI port %d: %s\n", port,
+ std::strerror(errno));
+ CommonSPIPortFree();
+ HAL_FreeDIOPort(digitalHandles[1]);
+ return;
+ }
+ HAL_SetSPIHandle(HAL_SPI_kOnboardCS2, handle);
+ break;
+ case HAL_SPI_kOnboardCS3:
+ CommonSPIPortInit(status);
+ if (*status != 0) return;
+ // CS3, Allocate
+ if ((digitalHandles[2] = HAL_InitializeDIOPort(createPortHandleForSPI(28),
+ false, status)) ==
+ HAL_kInvalidHandle) {
+ std::printf("Failed to allocate DIO 28 (CS3)\n");
+ CommonSPIPortFree();
+ return;
+ }
+ handle = open("/dev/spidev0.3", O_RDWR);
+ if (handle < 0) {
+ std::printf("Failed to open SPI port %d: %s\n", port,
+ std::strerror(errno));
+ CommonSPIPortFree();
+ HAL_FreeDIOPort(digitalHandles[2]);
+ return;
+ }
+ HAL_SetSPIHandle(HAL_SPI_kOnboardCS3, handle);
+ break;
+ case HAL_SPI_kMXP:
+ initializeDigital(status);
+ if (*status != 0) return;
+ if ((digitalHandles[5] = HAL_InitializeDIOPort(createPortHandleForSPI(14),
+ false, status)) ==
+ HAL_kInvalidHandle) {
+ wpi::outs() << "Failed to allocate DIO 14\n";
+ return;
+ }
+ if ((digitalHandles[6] = HAL_InitializeDIOPort(createPortHandleForSPI(15),
+ false, status)) ==
+ HAL_kInvalidHandle) {
+ wpi::outs() << "Failed to allocate DIO 15\n";
+ HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
+ return;
+ }
+ if ((digitalHandles[7] = HAL_InitializeDIOPort(createPortHandleForSPI(16),
+ false, status)) ==
+ HAL_kInvalidHandle) {
+ wpi::outs() << "Failed to allocate DIO 16\n";
+ HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
+ HAL_FreeDIOPort(digitalHandles[6]); // free the second port allocated
+ return;
+ }
+ if ((digitalHandles[8] = HAL_InitializeDIOPort(createPortHandleForSPI(17),
+ false, status)) ==
+ HAL_kInvalidHandle) {
+ wpi::outs() << "Failed to allocate DIO 17\n";
+ HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
+ HAL_FreeDIOPort(digitalHandles[6]); // free the second port allocated
+ HAL_FreeDIOPort(digitalHandles[7]); // free the third port allocated
+ return;
+ }
+ digitalSystem->writeEnableMXPSpecialFunction(
+ digitalSystem->readEnableMXPSpecialFunction(status) | 0x00F0, status);
+ handle = open("/dev/spidev1.0", O_RDWR);
+ if (handle < 0) {
+ std::printf("Failed to open SPI port %d: %s\n", port,
+ std::strerror(errno));
+ HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
+ HAL_FreeDIOPort(digitalHandles[6]); // free the second port allocated
+ HAL_FreeDIOPort(digitalHandles[7]); // free the third port allocated
+ HAL_FreeDIOPort(digitalHandles[8]); // free the fourth port allocated
+ return;
+ }
+ HAL_SetSPIHandle(HAL_SPI_kMXP, handle);
+ break;
+ default:
+ *status = PARAMETER_OUT_OF_RANGE;
+ break;
+ }
+}
+
+int32_t HAL_TransactionSPI(HAL_SPIPort port, const uint8_t* dataToSend,
+ uint8_t* dataReceived, int32_t size) {
+ if (port < 0 || port >= kSpiMaxHandles) {
+ return -1;
+ }
+
+ if (SPIInUseByAuto(port)) return -1;
+
+ struct spi_ioc_transfer xfer;
+ std::memset(&xfer, 0, sizeof(xfer));
+ xfer.tx_buf = (__u64)dataToSend;
+ xfer.rx_buf = (__u64)dataReceived;
+ xfer.len = size;
+
+ std::lock_guard<wpi::mutex> lock(spiApiMutexes[port]);
+ return ioctl(HAL_GetSPIHandle(port), SPI_IOC_MESSAGE(1), &xfer);
+}
+
+int32_t HAL_WriteSPI(HAL_SPIPort port, const uint8_t* dataToSend,
+ int32_t sendSize) {
+ if (port < 0 || port >= kSpiMaxHandles) {
+ return -1;
+ }
+
+ if (SPIInUseByAuto(port)) return -1;
+
+ struct spi_ioc_transfer xfer;
+ std::memset(&xfer, 0, sizeof(xfer));
+ xfer.tx_buf = (__u64)dataToSend;
+ xfer.len = sendSize;
+
+ std::lock_guard<wpi::mutex> lock(spiApiMutexes[port]);
+ return ioctl(HAL_GetSPIHandle(port), SPI_IOC_MESSAGE(1), &xfer);
+}
+
+int32_t HAL_ReadSPI(HAL_SPIPort port, uint8_t* buffer, int32_t count) {
+ if (port < 0 || port >= kSpiMaxHandles) {
+ return -1;
+ }
+
+ if (SPIInUseByAuto(port)) return -1;
+
+ struct spi_ioc_transfer xfer;
+ std::memset(&xfer, 0, sizeof(xfer));
+ xfer.rx_buf = (__u64)buffer;
+ xfer.len = count;
+
+ std::lock_guard<wpi::mutex> lock(spiApiMutexes[port]);
+ return ioctl(HAL_GetSPIHandle(port), SPI_IOC_MESSAGE(1), &xfer);
+}
+
+void HAL_CloseSPI(HAL_SPIPort port) {
+ if (port < 0 || port >= kSpiMaxHandles) {
+ return;
+ }
+
+ int32_t status = 0;
+ HAL_FreeSPIAuto(port, &status);
+
+ {
+ std::lock_guard<wpi::mutex> lock(spiApiMutexes[port]);
+ close(HAL_GetSPIHandle(port));
+ }
+
+ HAL_SetSPIHandle(port, 0);
+ if (port < 4) {
+ CommonSPIPortFree();
+ }
+
+ switch (port) {
+ // Case 0 does not need to do anything
+ case 1:
+ HAL_FreeDIOPort(digitalHandles[0]);
+ break;
+ case 2:
+ HAL_FreeDIOPort(digitalHandles[1]);
+ break;
+ case 3:
+ HAL_FreeDIOPort(digitalHandles[2]);
+ break;
+ case 4:
+ HAL_FreeDIOPort(digitalHandles[5]);
+ HAL_FreeDIOPort(digitalHandles[6]);
+ HAL_FreeDIOPort(digitalHandles[7]);
+ HAL_FreeDIOPort(digitalHandles[8]);
+ break;
+ default:
+ break;
+ }
+}
+
+void HAL_SetSPISpeed(HAL_SPIPort port, int32_t speed) {
+ if (port < 0 || port >= kSpiMaxHandles) {
+ return;
+ }
+
+ std::lock_guard<wpi::mutex> lock(spiApiMutexes[port]);
+ ioctl(HAL_GetSPIHandle(port), SPI_IOC_WR_MAX_SPEED_HZ, &speed);
+}
+
+void HAL_SetSPIOpts(HAL_SPIPort port, HAL_Bool msbFirst,
+ HAL_Bool sampleOnTrailing, HAL_Bool clkIdleHigh) {
+ if (port < 0 || port >= kSpiMaxHandles) {
+ return;
+ }
+
+ uint8_t mode = 0;
+ mode |= (!msbFirst ? 8 : 0);
+ mode |= (clkIdleHigh ? 2 : 0);
+ mode |= (sampleOnTrailing ? 1 : 0);
+
+ std::lock_guard<wpi::mutex> lock(spiApiMutexes[port]);
+ ioctl(HAL_GetSPIHandle(port), SPI_IOC_WR_MODE, &mode);
+}
+
+void HAL_SetSPIChipSelectActiveHigh(HAL_SPIPort port, int32_t* status) {
+ if (port < 0 || port >= kSpiMaxHandles) {
+ *status = PARAMETER_OUT_OF_RANGE;
+ return;
+ }
+
+ std::lock_guard<wpi::mutex> lock(spiApiMutexes[port]);
+ if (port < 4) {
+ spiSystem->writeChipSelectActiveHigh_Hdr(
+ spiSystem->readChipSelectActiveHigh_Hdr(status) | (1 << port), status);
+ } else {
+ spiSystem->writeChipSelectActiveHigh_MXP(1, status);
+ }
+}
+
+void HAL_SetSPIChipSelectActiveLow(HAL_SPIPort port, int32_t* status) {
+ if (port < 0 || port >= kSpiMaxHandles) {
+ *status = PARAMETER_OUT_OF_RANGE;
+ return;
+ }
+
+ std::lock_guard<wpi::mutex> lock(spiApiMutexes[port]);
+ if (port < 4) {
+ spiSystem->writeChipSelectActiveHigh_Hdr(
+ spiSystem->readChipSelectActiveHigh_Hdr(status) & ~(1 << port), status);
+ } else {
+ spiSystem->writeChipSelectActiveHigh_MXP(0, status);
+ }
+}
+
+int32_t HAL_GetSPIHandle(HAL_SPIPort port) {
+ if (port < 0 || port >= kSpiMaxHandles) {
+ return 0;
+ }
+
+ std::lock_guard<wpi::mutex> lock(spiHandleMutexes[port]);
+ switch (port) {
+ case 0:
+ return m_spiCS0Handle;
+ case 1:
+ return m_spiCS1Handle;
+ case 2:
+ return m_spiCS2Handle;
+ case 3:
+ return m_spiCS3Handle;
+ case 4:
+ return m_spiMXPHandle;
+ default:
+ return 0;
+ }
+}
+
+void HAL_SetSPIHandle(HAL_SPIPort port, int32_t handle) {
+ if (port < 0 || port >= kSpiMaxHandles) {
+ return;
+ }
+
+ std::lock_guard<wpi::mutex> lock(spiHandleMutexes[port]);
+ switch (port) {
+ case 0:
+ m_spiCS0Handle = handle;
+ break;
+ case 1:
+ m_spiCS1Handle = handle;
+ break;
+ case 2:
+ m_spiCS2Handle = handle;
+ break;
+ case 3:
+ m_spiCS3Handle = handle;
+ break;
+ case 4:
+ m_spiMXPHandle = handle;
+ break;
+ default:
+ break;
+ }
+}
+
+void HAL_InitSPIAuto(HAL_SPIPort port, int32_t bufferSize, int32_t* status) {
+ if (port < 0 || port >= kSpiMaxHandles) {
+ *status = PARAMETER_OUT_OF_RANGE;
+ return;
+ }
+
+ std::lock_guard<wpi::mutex> lock(spiAutoMutex);
+ // FPGA only has one auto SPI engine
+ if (spiAutoPort != kSpiMaxHandles) {
+ *status = RESOURCE_IS_ALLOCATED;
+ return;
+ }
+
+ // remember the initialized port for other entry points
+ spiAutoPort = port;
+
+ // configure the correct chip select
+ if (port < 4) {
+ spiSystem->writeAutoSPI1Select(false, status);
+ spiSystem->writeAutoChipSelect(port, status);
+ } else {
+ spiSystem->writeAutoSPI1Select(true, status);
+ spiSystem->writeAutoChipSelect(0, status);
+ }
+
+ // configure DMA
+ tDMAChannelDescriptor desc;
+ spiSystem->getSystemInterface()->getDmaDescriptor(g_SpiAutoData_index, &desc);
+ spiAutoDMA = std::make_unique<tDMAManager>(desc.channel, bufferSize, status);
+}
+
+void HAL_FreeSPIAuto(HAL_SPIPort port, int32_t* status) {
+ if (port < 0 || port >= kSpiMaxHandles) {
+ *status = PARAMETER_OUT_OF_RANGE;
+ return;
+ }
+
+ std::lock_guard<wpi::mutex> lock(spiAutoMutex);
+ if (spiAutoPort != port) return;
+ spiAutoPort = kSpiMaxHandles;
+
+ // disable by setting to internal clock and setting rate=0
+ spiSystem->writeAutoRate(0, status);
+ spiSystem->writeAutoTriggerConfig_ExternalClock(false, status);
+
+ // stop the DMA
+ spiAutoDMA->stop(status);
+
+ spiAutoDMA.reset(nullptr);
+
+ spiAutoRunning = false;
+}
+
+void HAL_StartSPIAutoRate(HAL_SPIPort port, double period, int32_t* status) {
+ std::lock_guard<wpi::mutex> lock(spiAutoMutex);
+ // FPGA only has one auto SPI engine
+ if (port != spiAutoPort) {
+ *status = INCOMPATIBLE_STATE;
+ return;
+ }
+
+ spiAutoRunning = true;
+
+ // start the DMA
+ spiAutoDMA->start(status);
+
+ // auto rate is in microseconds
+ spiSystem->writeAutoRate(period * 1000000, status);
+
+ // disable the external clock
+ spiSystem->writeAutoTriggerConfig_ExternalClock(false, status);
+}
+
+void HAL_StartSPIAutoTrigger(HAL_SPIPort port, HAL_Handle digitalSourceHandle,
+ HAL_AnalogTriggerType analogTriggerType,
+ HAL_Bool triggerRising, HAL_Bool triggerFalling,
+ int32_t* status) {
+ std::lock_guard<wpi::mutex> lock(spiAutoMutex);
+ // FPGA only has one auto SPI engine
+ if (port != spiAutoPort) {
+ *status = INCOMPATIBLE_STATE;
+ return;
+ }
+
+ spiAutoRunning = true;
+
+ // start the DMA
+ spiAutoDMA->start(status);
+
+ // get channel routing
+ bool routingAnalogTrigger = false;
+ uint8_t routingChannel = 0;
+ uint8_t routingModule = 0;
+ if (!remapDigitalSource(digitalSourceHandle, analogTriggerType,
+ routingChannel, routingModule,
+ routingAnalogTrigger)) {
+ *status = HAL_HANDLE_ERROR;
+ return;
+ }
+
+ // configure external trigger and enable it
+ tSPI::tAutoTriggerConfig config;
+ config.ExternalClock = 1;
+ config.FallingEdge = triggerFalling ? 1 : 0;
+ config.RisingEdge = triggerRising ? 1 : 0;
+ config.ExternalClockSource_AnalogTrigger = routingAnalogTrigger ? 1 : 0;
+ config.ExternalClockSource_Module = routingModule;
+ config.ExternalClockSource_Channel = routingChannel;
+ spiSystem->writeAutoTriggerConfig(config, status);
+}
+
+void HAL_StopSPIAuto(HAL_SPIPort port, int32_t* status) {
+ std::lock_guard<wpi::mutex> lock(spiAutoMutex);
+ // FPGA only has one auto SPI engine
+ if (port != spiAutoPort) {
+ *status = INCOMPATIBLE_STATE;
+ return;
+ }
+
+ // disable by setting to internal clock and setting rate=0
+ spiSystem->writeAutoRate(0, status);
+ spiSystem->writeAutoTriggerConfig_ExternalClock(false, status);
+
+ // stop the DMA
+ spiAutoDMA->stop(status);
+
+ spiAutoRunning = false;
+}
+
+void HAL_SetSPIAutoTransmitData(HAL_SPIPort port, const uint8_t* dataToSend,
+ int32_t dataSize, int32_t zeroSize,
+ int32_t* status) {
+ if (dataSize < 0 || dataSize > 16) {
+ *status = PARAMETER_OUT_OF_RANGE;
+ return;
+ }
+
+ if (zeroSize < 0 || zeroSize > 127) {
+ *status = PARAMETER_OUT_OF_RANGE;
+ return;
+ }
+
+ std::lock_guard<wpi::mutex> lock(spiAutoMutex);
+ // FPGA only has one auto SPI engine
+ if (port != spiAutoPort) {
+ *status = INCOMPATIBLE_STATE;
+ return;
+ }
+
+ // set tx data registers
+ for (int32_t i = 0; i < dataSize; ++i)
+ spiSystem->writeAutoTx(i >> 2, i & 3, dataToSend[i], status);
+
+ // set byte counts
+ tSPI::tAutoByteCount config;
+ config.ZeroByteCount = static_cast<unsigned>(zeroSize) & 0x7f;
+ config.TxByteCount = static_cast<unsigned>(dataSize) & 0xf;
+ spiSystem->writeAutoByteCount(config, status);
+}
+
+void HAL_ForceSPIAutoRead(HAL_SPIPort port, int32_t* status) {
+ std::lock_guard<wpi::mutex> lock(spiAutoMutex);
+ // FPGA only has one auto SPI engine
+ if (port != spiAutoPort) {
+ *status = INCOMPATIBLE_STATE;
+ return;
+ }
+
+ spiSystem->strobeAutoForceOne(status);
+}
+
+int32_t HAL_ReadSPIAutoReceivedData(HAL_SPIPort port, uint32_t* buffer,
+ int32_t numToRead, double timeout,
+ int32_t* status) {
+ std::lock_guard<wpi::mutex> lock(spiAutoMutex);
+ // FPGA only has one auto SPI engine
+ if (port != spiAutoPort) {
+ *status = INCOMPATIBLE_STATE;
+ return 0;
+ }
+
+ size_t numRemaining = 0;
+ // timeout is in ms
+ spiAutoDMA->read(buffer, numToRead, timeout * 1000, &numRemaining, status);
+ return numRemaining;
+}
+
+int32_t HAL_GetSPIAutoDroppedCount(HAL_SPIPort port, int32_t* status) {
+ std::lock_guard<wpi::mutex> lock(spiAutoMutex);
+ // FPGA only has one auto SPI engine
+ if (port != spiAutoPort) {
+ *status = INCOMPATIBLE_STATE;
+ return 0;
+ }
+
+ return spiSystem->readTransferSkippedFullCount(status);
+}
+
+} // extern "C"