#include "UartComIF.h" #include #include #include #include #include #include "fsfw/FSFW.h" #include "fsfw/serviceinterface.h" #include "fsfw_hal/linux/utility.h" UartComIF::UartComIF(object_id_t objectId) : SystemObject(objectId) {} UartComIF::~UartComIF() {} ReturnValue_t UartComIF::initializeInterface(CookieIF* cookie) { std::string deviceFile; UartDeviceMapIter uartDeviceMapIter; if (cookie == nullptr) { return NULLPOINTER; } auto* uartCookie = dynamic_cast(cookie); if (uartCookie == nullptr) { FSFW_LOGE("{}", "initializeInterface: Invalid UART Cookie\n"); return NULLPOINTER; } deviceFile = uartCookie->getDeviceFile(); uartDeviceMapIter = uartDeviceMap.find(deviceFile); if (uartDeviceMapIter == uartDeviceMap.end()) { int fileDescriptor = configureUartPort(uartCookie); if (fileDescriptor < 0) { return RETURN_FAILED; } size_t maxReplyLen = uartCookie->getMaxReplyLen(); UartElements uartElements = {fileDescriptor, std::vector(maxReplyLen), 0}; auto status = uartDeviceMap.emplace(deviceFile, uartElements); if (!status.second) { FSFW_LOGW("initializeInterface: Failed to insert device {} to UART device map\n", deviceFile); return RETURN_FAILED; } } else { FSFW_LOGW("initializeInterface: UART device {} already in use\n", deviceFile); return RETURN_FAILED; } return RETURN_OK; } int UartComIF::configureUartPort(UartCookie* uartCookie) { struct termios options = {}; std::string deviceFile = uartCookie->getDeviceFile(); int flags = O_RDWR; if (uartCookie->getUartMode() == UartModes::CANONICAL) { // In non-canonical mode, don't specify O_NONBLOCK because these properties will be // controlled by the VTIME and VMIN parameters and O_NONBLOCK would override this flags |= O_NONBLOCK; } int fd = open(deviceFile.c_str(), flags); if (fd < 0) { FSFW_LOGW("configureUartPort: Failed to open UART {} with error code {} | {}\n", deviceFile, errno, strerror(errno)); return fd; } /* Read in existing settings */ if (tcgetattr(fd, &options) != 0) { FSFW_LOGW("configureUartPort: Error {} from tcgetattr: {}\n", errno, strerror(errno)); return fd; } setParityOptions(&options, uartCookie); setStopBitOptions(&options, uartCookie); setDatasizeOptions(&options, uartCookie); setFixedOptions(&options); setUartMode(&options, *uartCookie); if (uartCookie->getInputShouldBeFlushed()) { tcflush(fd, TCIFLUSH); } /* Sets uart to non-blocking mode. Read returns immediately when there are no data available */ options.c_cc[VTIME] = 0; options.c_cc[VMIN] = 0; configureBaudrate(&options, uartCookie); /* Save option settings */ if (tcsetattr(fd, TCSANOW, &options) != 0) { FSFW_LOGW("configureUartPort: Failed to set options with error {} | {}\n", errno, strerror(errno)); return fd; } return fd; } void UartComIF::setParityOptions(struct termios* options, UartCookie* uartCookie) { /* Clear parity bit */ options->c_cflag &= ~PARENB; switch (uartCookie->getParity()) { case Parity::EVEN: options->c_cflag |= PARENB; options->c_cflag &= ~PARODD; break; case Parity::ODD: options->c_cflag |= PARENB; options->c_cflag |= PARODD; break; default: break; } } void UartComIF::setStopBitOptions(struct termios* options, UartCookie* uartCookie) { /* Clear stop field. Sets stop bit to one bit */ options->c_cflag &= ~CSTOPB; switch (uartCookie->getStopBits()) { case StopBits::TWO_STOP_BITS: options->c_cflag |= CSTOPB; break; default: break; } } void UartComIF::setDatasizeOptions(struct termios* options, UartCookie* uartCookie) { /* Clear size bits */ options->c_cflag &= ~CSIZE; switch (uartCookie->getBitsPerWord()) { case BitsPerWord::BITS_5: options->c_cflag |= CS5; break; case BitsPerWord::BITS_6: options->c_cflag |= CS6; break; case BitsPerWord::BITS_7: options->c_cflag |= CS7; break; case BitsPerWord::BITS_8: options->c_cflag |= CS8; break; default: FSFW_LOGW("setDatasizeOptions: Invalid size {} specified\n", static_cast(uartCookie->getBitsPerWord())); break; } } void UartComIF::setFixedOptions(struct termios* options) { /* Disable RTS/CTS hardware flow control */ options->c_cflag &= ~CRTSCTS; /* Turn on READ & ignore ctrl lines (CLOCAL = 1) */ options->c_cflag |= CREAD | CLOCAL; /* Disable echo */ options->c_lflag &= ~ECHO; /* Disable erasure */ options->c_lflag &= ~ECHOE; /* Disable new-line echo */ options->c_lflag &= ~ECHONL; /* Disable interpretation of INTR, QUIT and SUSP */ options->c_lflag &= ~ISIG; /* Turn off s/w flow ctrl */ options->c_iflag &= ~(IXON | IXOFF | IXANY); /* Disable any special handling of received bytes */ options->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); /* Prevent special interpretation of output bytes (e.g. newline chars) */ options->c_oflag &= ~OPOST; /* Prevent conversion of newline to carriage return/line feed */ options->c_oflag &= ~ONLCR; } void UartComIF::configureBaudrate(struct termios* options, UartCookie* uartCookie) { switch (uartCookie->getBaudrate()) { case UartBaudRate::RATE_50: cfsetispeed(options, B50); cfsetospeed(options, B50); break; case UartBaudRate::RATE_75: cfsetispeed(options, B75); cfsetospeed(options, B75); break; case UartBaudRate::RATE_110: cfsetispeed(options, B110); cfsetospeed(options, B110); break; case UartBaudRate::RATE_134: cfsetispeed(options, B134); cfsetospeed(options, B134); break; case UartBaudRate::RATE_150: cfsetispeed(options, B150); cfsetospeed(options, B150); break; case UartBaudRate::RATE_200: cfsetispeed(options, B200); cfsetospeed(options, B200); break; case UartBaudRate::RATE_300: cfsetispeed(options, B300); cfsetospeed(options, B300); break; case UartBaudRate::RATE_600: cfsetispeed(options, B600); cfsetospeed(options, B600); break; case UartBaudRate::RATE_1200: cfsetispeed(options, B1200); cfsetospeed(options, B1200); break; case UartBaudRate::RATE_1800: cfsetispeed(options, B1800); cfsetospeed(options, B1800); break; case UartBaudRate::RATE_2400: cfsetispeed(options, B2400); cfsetospeed(options, B2400); break; case UartBaudRate::RATE_4800: cfsetispeed(options, B4800); cfsetospeed(options, B4800); break; case UartBaudRate::RATE_9600: cfsetispeed(options, B9600); cfsetospeed(options, B9600); break; case UartBaudRate::RATE_19200: cfsetispeed(options, B19200); cfsetospeed(options, B19200); break; case UartBaudRate::RATE_38400: cfsetispeed(options, B38400); cfsetospeed(options, B38400); break; case UartBaudRate::RATE_57600: cfsetispeed(options, B57600); cfsetospeed(options, B57600); break; case UartBaudRate::RATE_115200: cfsetispeed(options, B115200); cfsetospeed(options, B115200); break; case UartBaudRate::RATE_230400: cfsetispeed(options, B230400); cfsetospeed(options, B230400); break; #ifndef __APPLE__ case UartBaudRate::RATE_460800: cfsetispeed(options, B460800); cfsetospeed(options, B460800); break; case UartBaudRate::RATE_500000: cfsetispeed(options, B500000); cfsetospeed(options, B500000); break; case UartBaudRate::RATE_576000: cfsetispeed(options, B576000); cfsetospeed(options, B576000); break; case UartBaudRate::RATE_921600: cfsetispeed(options, B921600); cfsetospeed(options, B921600); break; case UartBaudRate::RATE_1000000: cfsetispeed(options, B1000000); cfsetospeed(options, B1000000); break; case UartBaudRate::RATE_1152000: cfsetispeed(options, B1152000); cfsetospeed(options, B1152000); break; case UartBaudRate::RATE_1500000: cfsetispeed(options, B1500000); cfsetospeed(options, B1500000); break; case UartBaudRate::RATE_2000000: cfsetispeed(options, B2000000); cfsetospeed(options, B2000000); break; case UartBaudRate::RATE_2500000: cfsetispeed(options, B2500000); cfsetospeed(options, B2500000); break; case UartBaudRate::RATE_3000000: cfsetispeed(options, B3000000); cfsetospeed(options, B3000000); break; case UartBaudRate::RATE_3500000: cfsetispeed(options, B3500000); cfsetospeed(options, B3500000); break; case UartBaudRate::RATE_4000000: cfsetispeed(options, B4000000); cfsetospeed(options, B4000000); break; #endif // ! __APPLE__ default: FSFW_LOGW("{}", "UartComIF::configureBaudrate: Baudrate not supported\n"); break; } } ReturnValue_t UartComIF::sendMessage(CookieIF* cookie, const uint8_t* sendData, size_t sendLen) { int fd = 0; std::string deviceFile; UartDeviceMapIter uartDeviceMapIter; if (sendLen == 0) { return RETURN_OK; } if (sendData == nullptr) { FSFW_LOGWT("{}", "sendMessage: Send data is nullptr"); return RETURN_FAILED; } auto* uartCookie = dynamic_cast(cookie); if (uartCookie == nullptr) { FSFW_LOGWT("{}", "sendMessage: Invalid UART Cookie\n"); return NULLPOINTER; } deviceFile = uartCookie->getDeviceFile(); uartDeviceMapIter = uartDeviceMap.find(deviceFile); if (uartDeviceMapIter == uartDeviceMap.end()) { FSFW_LOGWT("{}", "sendMessage: Device file {} not in UART map\n", deviceFile); return RETURN_FAILED; } fd = uartDeviceMapIter->second.fileDescriptor; if (write(fd, sendData, sendLen) != static_cast(sendLen)) { FSFW_LOGE("sendMessage: Failed to send data with error code {} | {}", errno, strerror(errno)); return RETURN_FAILED; } return RETURN_OK; } ReturnValue_t UartComIF::getSendSuccess(CookieIF* cookie) { return RETURN_OK; } ReturnValue_t UartComIF::requestReceiveMessage(CookieIF* cookie, size_t requestLen) { std::string deviceFile; UartDeviceMapIter uartDeviceMapIter; auto* uartCookie = dynamic_cast(cookie); if (uartCookie == nullptr) { FSFW_LOGWT("{}", "requestReceiveMessage: Invalid UART Cookie\n"); return NULLPOINTER; } UartModes uartMode = uartCookie->getUartMode(); deviceFile = uartCookie->getDeviceFile(); uartDeviceMapIter = uartDeviceMap.find(deviceFile); if (uartMode == UartModes::NON_CANONICAL and requestLen == 0) { return RETURN_OK; } if (uartDeviceMapIter == uartDeviceMap.end()) { FSFW_LOGW("requestReceiveMessage: Device file {} not in UART map\n", deviceFile); return RETURN_FAILED; } if (uartMode == UartModes::CANONICAL) { return handleCanonicalRead(*uartCookie, uartDeviceMapIter, requestLen); } else if (uartMode == UartModes::NON_CANONICAL) { return handleNoncanonicalRead(*uartCookie, uartDeviceMapIter, requestLen); } else { return HasReturnvaluesIF::RETURN_FAILED; } } ReturnValue_t UartComIF::handleCanonicalRead(UartCookie& uartCookie, UartDeviceMapIter& iter, size_t requestLen) { ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; uint8_t maxReadCycles = uartCookie.getReadCycles(); uint8_t currentReadCycles = 0; ssize_t bytesRead = 0; size_t currentBytesRead = 0; size_t maxReplySize = uartCookie.getMaxReplyLen(); int fd = iter->second.fileDescriptor; auto bufferPtr = iter->second.replyBuffer.data(); iter->second.replyLen = 0; do { size_t allowedReadSize = 0; if (currentBytesRead >= maxReplySize) { // Overflow risk. Emit warning, trigger event and break. If this happens, // the reception buffer is not large enough or data is not polled often enough. FSFW_LOGWT("{}", "requestReceiveMessage: Next read would cause overflow\n"); result = UART_RX_BUFFER_TOO_SMALL; break; } else { allowedReadSize = maxReplySize - currentBytesRead; } bytesRead = read(fd, bufferPtr, allowedReadSize); if (bytesRead < 0) { // EAGAIN: No data available in non-blocking mode if (errno != EAGAIN) { FSFW_LOGWT("handleCanonicalRead: read failed with code {} | {}\n", errno, strerror(errno)); return RETURN_FAILED; } } else if (bytesRead > 0) { iter->second.replyLen += bytesRead; bufferPtr += bytesRead; currentBytesRead += bytesRead; } currentReadCycles++; } while (bytesRead > 0 and currentReadCycles < maxReadCycles); return result; } ReturnValue_t UartComIF::handleNoncanonicalRead(UartCookie& uartCookie, UartDeviceMapIter& iter, size_t requestLen) { int fd = iter->second.fileDescriptor; auto bufferPtr = iter->second.replyBuffer.data(); // Size check to prevent buffer overflow if (requestLen > uartCookie.getMaxReplyLen()) { FSFW_LOGW("{}", "requestReceiveMessage: Next read would cause overflow\n"); return UART_RX_BUFFER_TOO_SMALL; } ssize_t bytesRead = read(fd, bufferPtr, requestLen); if (bytesRead < 0) { return RETURN_FAILED; } else if (bytesRead != static_cast(requestLen)) { if (uartCookie.isReplySizeFixed()) { FSFW_LOGWT("UartComIF::requestReceiveMessage: Only read {} of {} bytes\n", bytesRead, requestLen); return RETURN_FAILED; } } iter->second.replyLen = bytesRead; return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t UartComIF::readReceivedMessage(CookieIF* cookie, uint8_t** buffer, size_t* size) { std::string deviceFile; UartDeviceMapIter uartDeviceMapIter; auto* uartCookie = dynamic_cast(cookie); if (uartCookie == nullptr) { FSFW_LOGWT("{}", "readReceivedMessage: Invalid uart cookie"); return NULLPOINTER; } deviceFile = uartCookie->getDeviceFile(); uartDeviceMapIter = uartDeviceMap.find(deviceFile); if (uartDeviceMapIter == uartDeviceMap.end()) { FSFW_LOGW("UartComIF::readReceivedMessage: Device file {} not in UART map\n", deviceFile); return RETURN_FAILED; } *buffer = uartDeviceMapIter->second.replyBuffer.data(); *size = uartDeviceMapIter->second.replyLen; /* Length is reset to 0 to prevent reading the same data twice */ uartDeviceMapIter->second.replyLen = 0; return RETURN_OK; } ReturnValue_t UartComIF::flushUartRxBuffer(CookieIF* cookie) { std::string deviceFile; UartDeviceMapIter uartDeviceMapIter; auto* uartCookie = dynamic_cast(cookie); if (uartCookie == nullptr) { FSFW_LOGWT("{}", "flushUartRxBuffer: Invalid UART cookie\n"); return NULLPOINTER; } deviceFile = uartCookie->getDeviceFile(); uartDeviceMapIter = uartDeviceMap.find(deviceFile); if (uartDeviceMapIter != uartDeviceMap.end()) { int fd = uartDeviceMapIter->second.fileDescriptor; tcflush(fd, TCIFLUSH); return RETURN_OK; } return RETURN_FAILED; } ReturnValue_t UartComIF::flushUartTxBuffer(CookieIF* cookie) { std::string deviceFile; UartDeviceMapIter uartDeviceMapIter; auto* uartCookie = dynamic_cast(cookie); if (uartCookie == nullptr) { FSFW_LOGWT("{}", "flushUartTxBuffer: Invalid uart cookie\n"); return NULLPOINTER; } deviceFile = uartCookie->getDeviceFile(); uartDeviceMapIter = uartDeviceMap.find(deviceFile); if (uartDeviceMapIter != uartDeviceMap.end()) { int fd = uartDeviceMapIter->second.fileDescriptor; tcflush(fd, TCOFLUSH); return RETURN_OK; } return RETURN_FAILED; } ReturnValue_t UartComIF::flushUartTxAndRxBuf(CookieIF* cookie) { std::string deviceFile; UartDeviceMapIter uartDeviceMapIter; auto* uartCookie = dynamic_cast(cookie); if (uartCookie == nullptr) { FSFW_LOGWT("{}", "flushUartTxAndRxBuf: Invalid UART cookie\n"); return NULLPOINTER; } deviceFile = uartCookie->getDeviceFile(); uartDeviceMapIter = uartDeviceMap.find(deviceFile); if (uartDeviceMapIter != uartDeviceMap.end()) { int fd = uartDeviceMapIter->second.fileDescriptor; tcflush(fd, TCIOFLUSH); return RETURN_OK; } return RETURN_FAILED; } void UartComIF::setUartMode(struct termios* options, UartCookie& uartCookie) { UartModes uartMode = uartCookie.getUartMode(); if (uartMode == UartModes::NON_CANONICAL) { /* Disable canonical mode */ options->c_lflag &= ~ICANON; } else if (uartMode == UartModes::CANONICAL) { options->c_lflag |= ICANON; } }