304 lines
11 KiB
C++
304 lines
11 KiB
C++
|
#include "SpiComIF.h"
|
||
|
#include "spiDefinitions.h"
|
||
|
|
||
|
#include <linux/spi/SpiCookie.h>
|
||
|
#include <linux/utility/errorhandling.h>
|
||
|
|
||
|
#include <fsfw/ipc/MutexFactory.h>
|
||
|
|
||
|
#include <fcntl.h>
|
||
|
#include <unistd.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <linux/spi/spidev.h>
|
||
|
#include <errno.h>
|
||
|
#include <fsfw/ipc/MutexHelper.h>
|
||
|
#include <cstring>
|
||
|
|
||
|
SpiComIF::SpiComIF(object_id_t objectId, GpioIF* gpioComIF): SystemObject(objectId),
|
||
|
gpioComIF(gpioComIF) {
|
||
|
if(gpioComIF == nullptr) {
|
||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||
|
sif::error << "SpiComIF::SpiComIF: GPIO communication interface invalid!" << std::endl;
|
||
|
#else
|
||
|
sif::printError("SpiComIF::SpiComIF: GPIO communication interface invalid!\n");
|
||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||
|
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||
|
}
|
||
|
|
||
|
spiMutex = MutexFactory::instance()->createMutex();
|
||
|
}
|
||
|
|
||
|
ReturnValue_t SpiComIF::initializeInterface(CookieIF *cookie) {
|
||
|
SpiCookie* spiCookie = dynamic_cast<SpiCookie*>(cookie);
|
||
|
if(spiCookie == nullptr) {
|
||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||
|
}
|
||
|
|
||
|
address_t spiAddress = spiCookie->getSpiAddress();
|
||
|
|
||
|
auto iter = spiDeviceMap.find(spiAddress);
|
||
|
if(iter == spiDeviceMap.end()) {
|
||
|
size_t bufferSize = spiCookie->getMaxBufferSize();
|
||
|
SpiInstance spiInstance = {std::vector<uint8_t>(bufferSize)};
|
||
|
auto statusPair = spiDeviceMap.emplace(spiAddress, spiInstance);
|
||
|
if (not statusPair.second) {
|
||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||
|
sif::error << "SpiComIF::initializeInterface: Failed to insert device with address " <<
|
||
|
spiAddress << "to SPI device map" << std::endl;
|
||
|
#else
|
||
|
sif::printError("SpiComIF::initializeInterface: Failed to insert device with address "
|
||
|
"%lu to SPI device map\n", static_cast<unsigned long>(spiAddress));
|
||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||
|
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||
|
}
|
||
|
/* Now we emplaced the read buffer in the map, we still need to assign that location
|
||
|
to the SPI driver transfer struct */
|
||
|
spiCookie->assignReadBuffer(statusPair.first->second.replyBuffer.data());
|
||
|
}
|
||
|
else {
|
||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||
|
sif::error << "SpiComIF::initializeInterface: SPI address already exists!" << std::endl;
|
||
|
#else
|
||
|
sif::printError("SpiComIF::initializeInterface: SPI address already exists!\n");
|
||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||
|
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||
|
}
|
||
|
|
||
|
gpioId_t gpioId = spiCookie->getChipSelectPin();
|
||
|
if(gpioId != gpio::NO_GPIO) {
|
||
|
gpioComIF->pullHigh(gpioId);
|
||
|
}
|
||
|
|
||
|
size_t spiSpeed = 0;
|
||
|
spi::SpiMode spiMode = spi::SpiMode::MODE_0;
|
||
|
|
||
|
SpiCookie::UncommonParameters params;
|
||
|
spiCookie->getSpiParameters(spiMode, spiSpeed, ¶ms);
|
||
|
|
||
|
int fileDescriptor = 0;
|
||
|
ReturnValue_t result = openDevice(spiCookie->getSpiDevice(), &fileDescriptor);
|
||
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
int retval = ioctl(fileDescriptor, SPI_IOC_WR_MODE, spiSpeed);
|
||
|
if(retval != 0) {
|
||
|
utility::handleIoctlError("SpiComIF::initializeInterface: Setting SPI mode failed!");
|
||
|
}
|
||
|
|
||
|
retval = ioctl(fileDescriptor, SPI_IOC_WR_MAX_SPEED_HZ, spiSpeed);
|
||
|
if(retval != 0) {
|
||
|
utility::handleIoctlError("SpiComIF::initializeInterface: Setting SPI speed failed!");
|
||
|
}
|
||
|
|
||
|
/* These flags are rather uncommon */
|
||
|
if(params.threeWireSpi or params.noCs or params.csHigh) {
|
||
|
uint32_t currentMode = 0;
|
||
|
retval = ioctl(fileDescriptor, SPI_IOC_RD_MODE32, ¤tMode);
|
||
|
if(retval != 0) {
|
||
|
utility::handleIoctlError("SpiComIF::initialiezInterface: Could not read full mode!");
|
||
|
}
|
||
|
|
||
|
if(params.threeWireSpi) {
|
||
|
currentMode |= SPI_3WIRE;
|
||
|
}
|
||
|
if(params.noCs) {
|
||
|
/* Some drivers like the Raspberry Pi ignore this flag in any case */
|
||
|
currentMode |= SPI_NO_CS;
|
||
|
}
|
||
|
if(params.csHigh) {
|
||
|
currentMode |= SPI_CS_HIGH;
|
||
|
}
|
||
|
/* Write adapted mode */
|
||
|
retval = ioctl(fileDescriptor, SPI_IOC_WR_MODE32, currentMode);
|
||
|
if(retval != 0) {
|
||
|
utility::handleIoctlError("SpiComIF::initialiezInterface: Could not write full mode!");
|
||
|
}
|
||
|
}
|
||
|
if(params.lsbFirst) {
|
||
|
retval = ioctl(fileDescriptor, SPI_IOC_WR_LSB_FIRST, true);
|
||
|
if(retval != 0) {
|
||
|
utility::handleIoctlError("SpiComIF::initializeInterface: Setting LSB first failed");
|
||
|
}
|
||
|
}
|
||
|
if(params.bitsPerWord != 8) {
|
||
|
retval = ioctl(fileDescriptor, SPI_IOC_WR_BITS_PER_WORD, params.bitsPerWord);
|
||
|
if(retval != 0) {
|
||
|
utility::handleIoctlError("SpiComIF::initializeInterface: "
|
||
|
"Could not write bits per word!");
|
||
|
}
|
||
|
}
|
||
|
return HasReturnvaluesIF::RETURN_OK;
|
||
|
}
|
||
|
|
||
|
ReturnValue_t SpiComIF::sendMessage(CookieIF *cookie, const uint8_t *sendData, size_t sendLen) {
|
||
|
SpiCookie* spiCookie = dynamic_cast<SpiCookie*>(cookie);
|
||
|
if(spiCookie == nullptr) {
|
||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||
|
}
|
||
|
|
||
|
if(sendLen > spiCookie->getMaxBufferSize()) {
|
||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||
|
sif::warning << "SpiComIF::sendMessage: Too much data sent, send length" << sendLen <<
|
||
|
"larger than maximum buffer length" << spiCookie->getMaxBufferSize() << std::endl;
|
||
|
#else
|
||
|
sif::printWarning("SpiComIF::sendMessage: Too much data sent, send length %lu larger "
|
||
|
"than maximum buffer length %lu!\n", static_cast<unsigned long>(sendLen),
|
||
|
static_cast<unsigned long>(spiCookie->getMaxBufferSize()));
|
||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||
|
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||
|
return DeviceCommunicationIF::TOO_MUCH_DATA;
|
||
|
}
|
||
|
|
||
|
spiCookie->assignWriteBuffer(sendData);
|
||
|
spiCookie->assignTransferSize(sendLen);
|
||
|
int fileDescriptor = 0;
|
||
|
std::string device = spiCookie->getSpiDevice();
|
||
|
ReturnValue_t result = openDevice(device, &fileDescriptor);
|
||
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
bool fullDuplex = spiCookie->isFullDuplex();
|
||
|
int retval = 0;
|
||
|
|
||
|
gpioId_t gpioId = spiCookie->getChipSelectPin();
|
||
|
MutexHelper(spiMutex, timeoutType, timeoutMs);
|
||
|
if(gpioId != gpio::NO_GPIO) {
|
||
|
/* For now, no support for active high given */
|
||
|
gpioComIF->pullLow(gpioId);
|
||
|
}
|
||
|
|
||
|
if(fullDuplex) {
|
||
|
/* Initiate a full duplex SPI transfer. */
|
||
|
retval = ioctl(fileDescriptor, SPI_IOC_MESSAGE(1), spiCookie->getTransferStructHandle());
|
||
|
if(retval != 0) {
|
||
|
utility::handleIoctlError("SpiComIF::sendMessage: ioctl error.");
|
||
|
/* TODO: Better returnvalue */
|
||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
/* We write with a blocking transfer here */
|
||
|
if (write(fileDescriptor, sendData, sendLen) != static_cast<ssize_t>(sendLen)) {
|
||
|
sif::warning << "SpiComIF::sendMessage: Half-Duplex write operation failed!" <<
|
||
|
std::endl;
|
||
|
/* TODO: Better returnvalue */
|
||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(gpioId != gpio::NO_GPIO) {
|
||
|
gpioComIF->pullHigh(gpioId);
|
||
|
}
|
||
|
return HasReturnvaluesIF::RETURN_OK;
|
||
|
}
|
||
|
|
||
|
ReturnValue_t SpiComIF::getSendSuccess(CookieIF *cookie) {
|
||
|
return HasReturnvaluesIF::RETURN_OK;
|
||
|
}
|
||
|
|
||
|
ReturnValue_t SpiComIF::requestReceiveMessage(CookieIF *cookie, size_t requestLen) {
|
||
|
SpiCookie* spiCookie = dynamic_cast<SpiCookie*>(cookie);
|
||
|
if(spiCookie == nullptr) {
|
||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||
|
}
|
||
|
|
||
|
bool fullDuplex = spiCookie->isFullDuplex();
|
||
|
if(fullDuplex) {
|
||
|
return HasReturnvaluesIF::RETURN_OK;
|
||
|
}
|
||
|
|
||
|
std::string device = spiCookie->getSpiDevice();
|
||
|
int fileDescriptor = 0;
|
||
|
ReturnValue_t result = openDevice(device, &fileDescriptor);
|
||
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
uint8_t* rxBuf = nullptr;
|
||
|
size_t readSize = spiCookie->getCurrentTransferSize();
|
||
|
result = getReadBuffer(spiCookie->getSpiAddress(), &rxBuf);
|
||
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
gpioId_t gpioId = spiCookie->getChipSelectPin();
|
||
|
MutexHelper(spiMutex, timeoutType, timeoutMs);
|
||
|
if(gpioId != gpio::NO_GPIO) {
|
||
|
gpioComIF->pullLow(gpioId);
|
||
|
}
|
||
|
|
||
|
if(read(fileDescriptor, rxBuf, readSize) != static_cast<ssize_t>(readSize)) {
|
||
|
sif::warning << "SpiComIF::sendMessage: Half-Duplex read operation failed!" << std::endl;
|
||
|
if(gpioId != gpio::NO_GPIO) {
|
||
|
gpioComIF->pullHigh(gpioId);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return HasReturnvaluesIF::RETURN_OK;
|
||
|
}
|
||
|
|
||
|
ReturnValue_t SpiComIF::readReceivedMessage(CookieIF *cookie, uint8_t **buffer, size_t *size) {
|
||
|
SpiCookie* spiCookie = dynamic_cast<SpiCookie*>(cookie);
|
||
|
if(spiCookie == nullptr) {
|
||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||
|
}
|
||
|
uint8_t* rxBuf = nullptr;
|
||
|
ReturnValue_t result = getReadBuffer(spiCookie->getSpiAddress(), &rxBuf);
|
||
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
*buffer = rxBuf;
|
||
|
*size = spiCookie->getCurrentTransferSize();
|
||
|
return HasReturnvaluesIF::RETURN_OK;
|
||
|
}
|
||
|
|
||
|
ReturnValue_t SpiComIF::openDevice(std::string deviceFile, int *fileDescriptor, bool nonBlocking) {
|
||
|
if(fileDescriptor == nullptr) {
|
||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||
|
}
|
||
|
|
||
|
int flags = O_RDWR;
|
||
|
if(nonBlocking) {
|
||
|
flags |= O_NONBLOCK;
|
||
|
}
|
||
|
*fileDescriptor = open(deviceFile.c_str(), flags);
|
||
|
if (*fileDescriptor < 0) {
|
||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||
|
sif::warning << "SpiComIF: Opening SPI device failed with error code " << errno << "." <<
|
||
|
std::endl;
|
||
|
sif::warning << "Error description: " << strerror(errno) << std::endl;
|
||
|
#else
|
||
|
sif::printError("SpiComIF: Opening SPI device failed with error code %d.\n");
|
||
|
sif::printWarning("Error description: %s\n", strerror(errno));
|
||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||
|
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||
|
}
|
||
|
|
||
|
return HasReturnvaluesIF::RETURN_OK;
|
||
|
}
|
||
|
|
||
|
ReturnValue_t SpiComIF::getReadBuffer(address_t spiAddress, uint8_t** buffer) {
|
||
|
if(buffer == nullptr) {
|
||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||
|
}
|
||
|
|
||
|
auto iter = spiDeviceMap.find(spiAddress);
|
||
|
if(iter == spiDeviceMap.end()) {
|
||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||
|
}
|
||
|
|
||
|
*buffer = iter->second.replyBuffer.data();
|
||
|
return HasReturnvaluesIF::RETURN_OK;
|
||
|
}
|