/**************************************************************************** * * Copyright (C) 2023 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name PX4 nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ #include #include // strncpy #include #include #include #include #include #include #define MODULE_NAME "SerialImpl" namespace device { SerialImpl::SerialImpl(const char *port, uint32_t baudrate, ByteSize bytesize, Parity parity, StopBits stopbits, FlowControl flowcontrol) : _baudrate(baudrate), _bytesize(bytesize), _parity(parity), _stopbits(stopbits), _flowcontrol(flowcontrol) { if (validatePort(port)) { setPort(port); } else { _port[0] = 0; } } SerialImpl::~SerialImpl() { if (isOpen()) { close(); } } bool SerialImpl::configure() { /* process baud rate */ int speed; switch (_baudrate) { case 0: // special case, if baudrate is 0 it hangs entire system PX4_ERR("baudrate not specified"); return false; case 9600: speed = B9600; break; case 19200: speed = B19200; break; case 38400: speed = B38400; break; case 57600: speed = B57600; break; case 115200: speed = B115200; break; case 230400: speed = B230400; break; #ifndef B460800 #define B460800 460800 #endif case 460800: speed = B460800; break; #ifndef B500000 #define B500000 500000 #endif case 500000: speed = B500000; break; #ifndef B576000 #define B576000 576000 #endif case 576000: speed = B576000; break; #ifndef B921600 #define B921600 921600 #endif case 921600: speed = B921600; break; #ifndef B1000000 #define B1000000 1000000 #endif case 1000000: speed = B1000000; break; #ifndef B1500000 #define B1500000 1500000 #endif case 1500000: speed = B1500000; break; default: speed = _baudrate; #ifdef CONFIG_ARCH_CHIP_ESP32 PX4_WARN("Using non-standard baudrate: %u", _baudrate); #else PX4_WARN("Using non-standard baudrate: %lu", _baudrate); #endif break; } struct termios uart_config; int termios_state; /* fill the struct for the new configuration */ if ((termios_state = tcgetattr(_serial_fd, &uart_config)) < 0) { PX4_ERR("ERR: %d (tcgetattr)", termios_state); return false; } /* properly configure the terminal (see also https://en.wikibooks.org/wiki/Serial_Programming/termios ) */ // // Input flags - Turn off input processing // // convert break to null byte, no CR to NL translation, // no NL to CR translation, don't mark parity errors or breaks // no input parity check, don't strip high bit off, // no XON/XOFF software flow control // uart_config.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON); // // Output flags - Turn off output processing // // no CR to NL translation, no NL to CR-NL translation, // no NL to CR translation, no column 0 CR suppression, // no Ctrl-D suppression, no fill characters, no case mapping, // no local output processing // // config.c_oflag &= ~(OCRNL | ONLCR | ONLRET | // ONOCR | ONOEOT| OFILL | OLCUC | OPOST); uart_config.c_oflag = 0; // // No line processing // // echo off, echo newline off, canonical mode off, // extended input processing off, signal chars off // uart_config.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG); /* no parity, one stop bit, disable flow control */ uart_config.c_cflag &= ~(CSTOPB | PARENB | CRTSCTS); /* set baud rate */ if ((termios_state = cfsetispeed(&uart_config, speed)) < 0) { PX4_ERR("ERR: %d (cfsetispeed)", termios_state); return false; } if ((termios_state = cfsetospeed(&uart_config, speed)) < 0) { PX4_ERR("ERR: %d (cfsetospeed)", termios_state); return false; } if ((termios_state = tcsetattr(_serial_fd, TCSANOW, &uart_config)) < 0) { PX4_ERR("ERR: %d (tcsetattr)", termios_state); return false; } return true; } bool SerialImpl::open() { if (isOpen()) { return true; } if (!validatePort(_port)) { PX4_ERR("Invalid port %s", _port); return false; } // Open the serial port int serial_fd = ::open(_port, O_RDWR | O_NOCTTY | O_NONBLOCK); if (serial_fd < 0) { PX4_ERR("failed to open %s err: %d", _port, errno); return false; } _serial_fd = serial_fd; // Configure the serial port if (! configure()) { PX4_ERR("failed to configure %s err: %d", _port, errno); return false; } _open = true; // Do pin operations after port has been opened if (_single_wire_mode) { setSingleWireMode(); } if (_swap_rx_tx_mode) { setSwapRxTxMode(); } setInvertedMode(_inverted_mode); return _open; } bool SerialImpl::isOpen() const { return _open; } bool SerialImpl::close() { if (_serial_fd >= 0) { ::close(_serial_fd); } _serial_fd = -1; _open = false; return true; } ssize_t SerialImpl::bytesAvailable() { if (!_open) { PX4_ERR("Device not open!"); return -1; } ssize_t bytes_available = 0; int ret = ioctl(_serial_fd, FIONREAD, &bytes_available); return ret >= 0 ? bytes_available : 0; } ssize_t SerialImpl::read(uint8_t *buffer, size_t buffer_size) { if (!_open) { PX4_ERR("Cannot read from serial device until it has been opened"); return -1; } int ret = ::read(_serial_fd, buffer, buffer_size); if (ret < 0) { PX4_DEBUG("%s read error %d", _port, ret); } return ret; } ssize_t SerialImpl::readAtLeast(uint8_t *buffer, size_t buffer_size, size_t character_count, uint32_t timeout_ms) { if (!_open) { PX4_ERR("Cannot readAtLeast from serial device until it has been opened"); return -1; } if (buffer_size < character_count) { PX4_ERR("%s: Buffer not big enough to hold desired amount of read data", __FUNCTION__); return -1; } const hrt_abstime start_time_us = hrt_absolute_time(); hrt_abstime timeout_us = timeout_ms * 1000; int total_bytes_read = 0; while ((total_bytes_read < (int) character_count) && (hrt_elapsed_time(&start_time_us) < timeout_us)) { // Poll for incoming UART data. pollfd fds[1]; fds[0].fd = _serial_fd; fds[0].events = POLLIN; hrt_abstime elapsed_time_us = hrt_elapsed_time(&start_time_us); if (elapsed_time_us > timeout_us) { break; } int ret = poll(fds, sizeof(fds) / sizeof(fds[0]), (timeout_us - elapsed_time_us) / 1000); if (ret > 0) { if (fds[0].revents & POLLIN) { const unsigned sleeptime = character_count * 1000000 / (_baudrate / 10); int err = 0; int bytes_available = 0; err = ::ioctl(_serial_fd, FIONREAD, (unsigned long)&bytes_available); if (err != 0 || bytes_available < (int)character_count) { px4_usleep(sleeptime); } ret = read(&buffer[total_bytes_read], buffer_size - total_bytes_read); if (ret > 0) { total_bytes_read += ret; } } else { PX4_ERR("Got a poll error"); return -1; } } } return total_bytes_read; } ssize_t SerialImpl::write(const void *buffer, size_t buffer_size) { if (!_open) { PX4_ERR("Cannot write to serial device until it has been opened"); return -1; } int written = ::write(_serial_fd, buffer, buffer_size); tcdrain(_serial_fd); // Wait until all output is transmitted if (written < 0) { PX4_ERR("%s write error %d", _port, written); } return written; } void SerialImpl::flush() { if (_open) { tcflush(_serial_fd, TCIOFLUSH); } } const char *SerialImpl::getPort() const { return _port; } bool SerialImpl::validatePort(const char *port) { return (port && (access(port, R_OK | W_OK) == 0)); } bool SerialImpl::setPort(const char *port) { if (_open) { PX4_ERR("Cannot set port after port has already been opened"); return false; } if (validatePort(port)) { strncpy(_port, port, sizeof(_port) - 1); _port[sizeof(_port) - 1] = '\0'; return true; } return false; } uint32_t SerialImpl::getBaudrate() const { return _baudrate; } bool SerialImpl::setBaudrate(uint32_t baudrate) { // check if already configured if ((baudrate == _baudrate) && _open) { return true; } _baudrate = baudrate; // process baud rate change now if port is already open if (_open) { return configure(); } return true; } ByteSize SerialImpl::getBytesize() const { return _bytesize; } bool SerialImpl::setBytesize(ByteSize bytesize) { return bytesize == ByteSize::EightBits; } Parity SerialImpl::getParity() const { return _parity; } bool SerialImpl::setParity(Parity parity) { return parity == Parity::None; } StopBits SerialImpl::getStopbits() const { return _stopbits; } bool SerialImpl::setStopbits(StopBits stopbits) { return stopbits == StopBits::One; } FlowControl SerialImpl::getFlowcontrol() const { return _flowcontrol; } bool SerialImpl::setFlowcontrol(FlowControl flowcontrol) { return flowcontrol == FlowControl::Disabled; } bool SerialImpl::getSingleWireMode() const { return _single_wire_mode; } bool SerialImpl::setSingleWireMode() { #if defined(TIOCSSINGLEWIRE) if (_open) { ioctl(_serial_fd, TIOCSSINGLEWIRE, SER_SINGLEWIRE_ENABLED); } _single_wire_mode = true; return true; #else return false; #endif // TIOCSSINGLEWIRE } bool SerialImpl::getSwapRxTxMode() const { return _swap_rx_tx_mode; } bool SerialImpl::setSwapRxTxMode() { #if defined(TIOCSSWAP) if (_open) { ioctl(_serial_fd, TIOCSSWAP, SER_SWAP_ENABLED); } _swap_rx_tx_mode = true; return true; #else return false; #endif // TIOCSSWAP } bool SerialImpl::getInvertedMode() const { return _inverted_mode; } bool SerialImpl::setInvertedMode(bool enable) { #if defined(TIOCSINVERT) if (_open) { if (enable) { ioctl(_serial_fd, TIOCSINVERT, SER_INVERT_ENABLED_RX | SER_INVERT_ENABLED_TX); } else { ioctl(_serial_fd, TIOCSINVERT, 0); } } _inverted_mode = enable; return true; #else return _inverted_mode == enable; #endif // TIOCSINVERT } } // namespace device