diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 3362402..0d9082f 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -9,3 +9,4 @@ target_sources(${LIB_FSFW_HAL_NAME} PRIVATE add_subdirectory(gpio) add_subdirectory(spi) +add_subdirectory(i2c) \ No newline at end of file diff --git a/linux/i2c/CMakeLists.txt b/linux/i2c/CMakeLists.txt new file mode 100644 index 0000000..3f4bad3 --- /dev/null +++ b/linux/i2c/CMakeLists.txt @@ -0,0 +1,8 @@ +target_sources(${TARGET_NAME} PUBLIC + I2cComIF.cpp + I2cCookie.cpp +) + + + + diff --git a/linux/i2c/I2cComIF.cpp b/linux/i2c/I2cComIF.cpp new file mode 100644 index 0000000..06adf30 --- /dev/null +++ b/linux/i2c/I2cComIF.cpp @@ -0,0 +1,199 @@ +#include "I2cComIF.h" +#include "../utility.h" +#include "../UnixFileGuard.h" + +#include + +#include +#include +#include +#include +#include + +#include + + +I2cComIF::I2cComIF(object_id_t objectId): SystemObject(objectId){ +} + +I2cComIF::~I2cComIF() {} + +ReturnValue_t I2cComIF::initializeInterface(CookieIF* cookie) { + + address_t i2cAddress; + std::string deviceFile; + + if(cookie == nullptr) { + sif::error << "I2cComIF::initializeInterface: Invalid cookie!" << std::endl; + return NULLPOINTER; + } + I2cCookie* i2cCookie = dynamic_cast(cookie); + if(i2cCookie == nullptr) { + sif::error << "I2cComIF::initializeInterface: Invalid I2C cookie!" << std::endl; + return NULLPOINTER; + } + + i2cAddress = i2cCookie->getAddress(); + + i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress); + if(i2cDeviceMapIter == i2cDeviceMap.end()) { + size_t maxReplyLen = i2cCookie->getMaxReplyLen(); + I2cInstance_t i2cInstance = {std::vector(maxReplyLen), 0}; + auto statusPair = i2cDeviceMap.emplace(i2cAddress, i2cInstance); + if (not statusPair.second) { + sif::error << "I2cComIF::initializeInterface: Failed to insert device with address " << + i2cAddress << "to I2C device " << "map" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + return HasReturnvaluesIF::RETURN_OK; + } + + sif::error << "I2cComIF::initializeInterface: Device with address " << i2cAddress << + "already in use" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; +} + +ReturnValue_t I2cComIF::sendMessage(CookieIF *cookie, + const uint8_t *sendData, size_t sendLen) { + + ReturnValue_t result; + int fd; + std::string deviceFile; + + if(sendData == nullptr) { + sif::error << "I2cComIF::sendMessage: Send Data is nullptr" + << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + + if(sendLen == 0) { + return HasReturnvaluesIF::RETURN_OK; + } + + I2cCookie* i2cCookie = dynamic_cast(cookie); + if(i2cCookie == nullptr) { + sif::error << "I2cComIF::sendMessage: Invalid I2C Cookie!" << std::endl; + return NULLPOINTER; + } + + address_t i2cAddress = i2cCookie->getAddress(); + i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress); + if (i2cDeviceMapIter == i2cDeviceMap.end()) { + sif::error << "I2cComIF::sendMessage: i2cAddress of Cookie not " + << "registered in i2cDeviceMap" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + + deviceFile = i2cCookie->getDeviceFile(); + UnixFileGuard fileHelper(deviceFile, &fd, O_RDWR, "I2cComIF::sendMessage"); + if(fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) { + return fileHelper.getOpenResult(); + } + result = openDevice(deviceFile, i2cAddress, &fd); + if (result != HasReturnvaluesIF::RETURN_OK){ + return result; + } + + if (write(fd, sendData, sendLen) != (int)sendLen) { + sif::error << "I2cComIF::sendMessage: Failed to send data to I2C " + "device with error code " << errno << ". Error description: " + << strerror(errno) << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t I2cComIF::getSendSuccess(CookieIF *cookie) { + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t I2cComIF::requestReceiveMessage(CookieIF *cookie, + size_t requestLen) { + ReturnValue_t result; + int fd; + std::string deviceFile; + + if (requestLen == 0) { + return HasReturnvaluesIF::RETURN_OK; + } + + I2cCookie* i2cCookie = dynamic_cast(cookie); + if(i2cCookie == nullptr) { + sif::error << "I2cComIF::requestReceiveMessage: Invalid I2C Cookie!" << std::endl; + i2cDeviceMapIter->second.replyLen = 0; + return NULLPOINTER; + } + + address_t i2cAddress = i2cCookie->getAddress(); + i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress); + if (i2cDeviceMapIter == i2cDeviceMap.end()) { + sif::error << "I2cComIF::requestReceiveMessage: i2cAddress of Cookie not " + << "registered in i2cDeviceMap" << std::endl; + i2cDeviceMapIter->second.replyLen = 0; + return HasReturnvaluesIF::RETURN_FAILED; + } + + deviceFile = i2cCookie->getDeviceFile(); + UnixFileGuard fileHelper(deviceFile, &fd, O_RDWR, "I2cComIF::requestReceiveMessage"); + if(fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) { + return fileHelper.getOpenResult(); + } + result = openDevice(deviceFile, i2cAddress, &fd); + if (result != HasReturnvaluesIF::RETURN_OK){ + i2cDeviceMapIter->second.replyLen = 0; + return result; + } + + uint8_t* replyBuffer = i2cDeviceMapIter->second.replyBuffer.data(); + + if (read(fd, replyBuffer, requestLen) != static_cast(requestLen)) { + sif::error << "I2cComIF::requestReceiveMessage: Reading from I2C " + << "device failed with error code " << errno <<". Description" + << " of error: " << strerror(errno) << std::endl; + i2cDeviceMapIter->second.replyLen = 0; + return HasReturnvaluesIF::RETURN_FAILED; + } + + i2cDeviceMapIter->second.replyLen = requestLen; + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t I2cComIF::readReceivedMessage(CookieIF *cookie, + uint8_t **buffer, size_t* size) { + I2cCookie* i2cCookie = dynamic_cast(cookie); + if(i2cCookie == nullptr) { + sif::error << "I2cComIF::readReceivedMessage: Invalid I2C Cookie!" << std::endl; + return NULLPOINTER; + } + + address_t i2cAddress = i2cCookie->getAddress(); + i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress); + if (i2cDeviceMapIter == i2cDeviceMap.end()) { + sif::error << "I2cComIF::readReceivedMessage: i2cAddress of Cookie not " + << "found in i2cDeviceMap" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + *buffer = i2cDeviceMapIter->second.replyBuffer.data(); + *size = i2cDeviceMapIter->second.replyLen; + + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t I2cComIF::openDevice(std::string deviceFile, + address_t i2cAddress, int* fileDescriptor) { + + if (ioctl(*fileDescriptor, I2C_SLAVE, i2cAddress) < 0) { +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "I2cComIF: Specifying target device failed with error code " << errno << "." + << std::endl; + sif::warning << "Error description " << strerror(errno) << std::endl; +#else + sif::printWarning("I2cComIF: Specifying target 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; +} diff --git a/linux/i2c/I2cComIF.h b/linux/i2c/I2cComIF.h new file mode 100644 index 0000000..3529bde --- /dev/null +++ b/linux/i2c/I2cComIF.h @@ -0,0 +1,59 @@ +#ifndef LINUX_I2C_I2COMIF_H_ +#define LINUX_I2C_I2COMIF_H_ + +#include "I2cCookie.h" +#include +#include + +#include +#include + +/** + * @brief This is the communication interface for i2c devices connected + * to a system running a linux OS. + * + * @author J. Meier + */ +class I2cComIF: public DeviceCommunicationIF, public SystemObject { +public: + I2cComIF(object_id_t objectId); + + virtual ~I2cComIF(); + + ReturnValue_t initializeInterface(CookieIF * cookie) override; + ReturnValue_t sendMessage(CookieIF *cookie,const uint8_t *sendData, + size_t sendLen) override; + ReturnValue_t getSendSuccess(CookieIF *cookie) override; + ReturnValue_t requestReceiveMessage(CookieIF *cookie, + size_t requestLen) override; + ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer, + size_t *size) override; + +private: + + typedef struct I2cInstance { + std::vector replyBuffer; + size_t replyLen; + } I2cInstance_t; + + using I2cDeviceMap = std::unordered_map; + using I2cDeviceMapIter = I2cDeviceMap::iterator; + + /* In this map all i2c devices will be registered with their address and + * the appropriate file descriptor will be stored */ + I2cDeviceMap i2cDeviceMap; + I2cDeviceMapIter i2cDeviceMapIter; + + /** + * @brief This function opens an I2C device and binds the opened file + * to a specific I2C address. + * @param deviceFile The name of the device file. E.g. i2c-0 + * @param i2cAddress The address of the i2c slave device. + * @param fileDescriptor Pointer to device descriptor. + * @return RETURN_OK if successful, otherwise RETURN_FAILED. + */ + ReturnValue_t openDevice(std::string deviceFile, + address_t i2cAddress, int* fileDescriptor); +}; + +#endif /* LINUX_I2C_I2COMIF_H_ */ diff --git a/linux/i2c/I2cCookie.cpp b/linux/i2c/I2cCookie.cpp new file mode 100644 index 0000000..fe0f3f9 --- /dev/null +++ b/linux/i2c/I2cCookie.cpp @@ -0,0 +1,20 @@ +#include "I2cCookie.h" + +I2cCookie::I2cCookie(address_t i2cAddress_, size_t maxReplyLen_, + std::string deviceFile_) : + i2cAddress(i2cAddress_), maxReplyLen(maxReplyLen_), deviceFile(deviceFile_) { +} + +address_t I2cCookie::getAddress() const { + return i2cAddress; +} + +size_t I2cCookie::getMaxReplyLen() const { + return maxReplyLen; +} + +std::string I2cCookie::getDeviceFile() const { + return deviceFile; +} + +I2cCookie::~I2cCookie() {} diff --git a/linux/i2c/I2cCookie.h b/linux/i2c/I2cCookie.h new file mode 100644 index 0000000..c924eb4 --- /dev/null +++ b/linux/i2c/I2cCookie.h @@ -0,0 +1,37 @@ +#ifndef LINUX_I2C_I2CCOOKIE_H_ +#define LINUX_I2C_I2CCOOKIE_H_ + +#include +#include + +/** + * @brief Cookie for the i2cDeviceComIF. + * + * @author J. Meier + */ +class I2cCookie: public CookieIF { +public: + + /** + * @brief Constructor for the I2C cookie. + * @param i2cAddress_ The i2c address of the target device. + * @param maxReplyLen The maximum expected length of a reply from the + * target device. + */ + I2cCookie(address_t i2cAddress_, size_t maxReplyLen_, + std::string deviceFile_); + + virtual ~I2cCookie(); + + address_t getAddress() const; + size_t getMaxReplyLen() const; + std::string getDeviceFile() const; + +private: + + address_t i2cAddress = 0; + size_t maxReplyLen = 0; + std::string deviceFile; +}; + +#endif /* LINUX_I2C_I2CCOOKIE_H_ */