LCOV - code coverage report
Current view: top level - AP_HAL_Linux - UARTDriver.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 5 188 2.7 %
Date: 2026-09-09 11:02:08 Functions: 1 22 4.5 %

          Line data    Source code
       1             : #include "UARTDriver.h"
       2             : 
       3             : #include <arpa/inet.h>
       4             : #include <assert.h>
       5             : #include <errno.h>
       6             : #include <fcntl.h>
       7             : #include <netinet/in.h>
       8             : #include <netinet/tcp.h>
       9             : #include <poll.h>
      10             : #include <stdio.h>
      11             : #include <stdlib.h>
      12             : #include <string.h>
      13             : #include <sys/ioctl.h>
      14             : #include <sys/socket.h>
      15             : #include <sys/stat.h>
      16             : #include <sys/types.h>
      17             : #include <termios.h>
      18             : #include <unistd.h>
      19             : 
      20             : #include <AP_HAL/AP_HAL.h>
      21             : 
      22             : #include "ConsoleDevice.h"
      23             : #include "TCPServerDevice.h"
      24             : #include "UARTDevice.h"
      25             : #include "UDPDevice.h"
      26             : #include <GCS_MAVLink/GCS.h>
      27             : #if HAL_GCS_ENABLED
      28             : #include <AP_HAL/utility/packetise.h>
      29             : #endif
      30             : 
      31             : extern const AP_HAL::HAL& hal;
      32             : 
      33             : using namespace Linux;
      34             : 
      35         480 : UARTDriver::UARTDriver(bool default_console) :
      36         480 :     _device{new ConsoleDevice()}
      37             : {
      38         480 :     if (default_console) {
      39          48 :         _console = true;
      40             :     }
      41         480 : }
      42             : 
      43             : /*
      44             :   set the tty device to use for this UART
      45             :  */
      46           0 : void UARTDriver::set_device_path(const char *path)
      47             : {
      48           0 :     device_path = path;
      49           0 : }
      50             : 
      51           0 : void UARTDriver::_begin(uint32_t b, uint16_t rxS, uint16_t txS)
      52             : {
      53           0 :     if (!_initialised) {
      54           0 :         if (device_path == nullptr && _console) {
      55           0 :             _device = NEW_NOTHROW ConsoleDevice();
      56             :         } else {
      57           0 :             if (device_path == nullptr) {
      58           0 :                 return;
      59             :             }
      60             : 
      61           0 :             _device = _parseDevicePath(device_path);
      62             : 
      63           0 :             if (!_device.get()) {
      64           0 :                 ::fprintf(stderr, "Argument is not valid. Fallback to console.\n"
      65             :                           "Launch with --help to see an example.\n");
      66           0 :                 _device = NEW_NOTHROW ConsoleDevice();
      67             :             }
      68             :         }
      69             :     }
      70             : 
      71           0 :     if (!_connected) {
      72           0 :         _connected = _device->open();
      73           0 :         if (_connected) {
      74           0 :             _device->set_blocking(false);
      75             :         }
      76             :     }
      77           0 :     _initialised = false;
      78             : 
      79           0 :     while (_in_timer) hal.scheduler->delay(1);
      80             : 
      81           0 :     bool clear_buffers = false;
      82           0 :     if (b != 0 && _baudrate != b) {
      83           0 :         _device->set_speed(b);
      84           0 :         _baudrate = b;
      85           0 :         if (hal.console != this) {
      86           0 :             clear_buffers = true;
      87             :         }
      88             :     }
      89             : 
      90           0 :     _allocate_buffers(rxS, txS);
      91             : 
      92           0 :     if (clear_buffers) {
      93           0 :         _readbuf.clear();
      94           0 :         _writebuf.clear();
      95             :     }
      96             : }
      97             : 
      98           0 : void UARTDriver::_allocate_buffers(uint16_t rxS, uint16_t txS)
      99             : {
     100             :     /* we have enough memory to have a larger transmit buffer for
     101             :      * all ports. This means we don't get delays while waiting to
     102             :      * write GPS config packets
     103             :      */
     104             : 
     105           0 :     if (rxS < 8192) {
     106           0 :         rxS = 8192;
     107             :     }
     108           0 :     if (txS < 32000) {
     109           0 :         txS = 32000;
     110             :     }
     111             : 
     112           0 :     if (_writebuf.set_size(txS) && _readbuf.set_size(rxS)) {
     113           0 :         _initialised = true;
     114             :     }
     115           0 : }
     116             : 
     117           0 : void UARTDriver::_deallocate_buffers()
     118             : {
     119           0 :     _readbuf.set_size(0);
     120           0 :     _writebuf.set_size(0);
     121           0 : }
     122             : 
     123             : /*
     124             :     Device path accepts the following syntaxes:
     125             :         - /dev/ttyO1
     126             :         - tcp:*:1243:wait
     127             :         - udp:192.168.2.15:1243
     128             : */
     129           0 : AP_HAL::OwnPtr<SerialDevice> UARTDriver::_parseDevicePath(const char *arg)
     130             : {
     131             :     struct stat st;
     132             : 
     133           0 :     if (stat(arg, &st) == 0 && S_ISCHR(st.st_mode)) {
     134           0 :         return AP_HAL::OwnPtr<SerialDevice>(NEW_NOTHROW UARTDevice(arg));
     135           0 :     } else if (strncmp(arg, "tcp:", 4) != 0 &&
     136           0 :                strncmp(arg, "udp:", 4) != 0 &&
     137           0 :                strncmp(arg, "udpin:", 6)) {
     138           0 :         return nullptr;
     139             :     }
     140             : 
     141           0 :     char *devstr = strdup(arg);
     142             : 
     143           0 :     if (devstr == nullptr) {
     144           0 :         return nullptr;
     145             :     }
     146             : 
     147           0 :     char *saveptr = nullptr;
     148             :     char *protocol, *ip, *port, *flag;
     149             : 
     150           0 :     protocol = strtok_r(devstr, ":", &saveptr);
     151           0 :     ip = strtok_r(nullptr, ":", &saveptr);
     152           0 :     port = strtok_r(nullptr, ":", &saveptr);
     153           0 :     flag = strtok_r(nullptr, ":", &saveptr);
     154             : 
     155           0 :     if (ip == nullptr || port == nullptr) {
     156           0 :         free(devstr);
     157           0 :         return nullptr;
     158             :     }
     159             : 
     160           0 :     if (_ip) {
     161           0 :         free(_ip);
     162           0 :         _ip = nullptr;
     163             :     }
     164             : 
     165           0 :     if (_flag) {
     166           0 :         free(_flag);
     167           0 :         _flag = nullptr;
     168             :     }
     169             : 
     170           0 :     _base_port = (uint16_t) atoi(port);
     171           0 :     _ip = strdup(ip);
     172             : 
     173             :     /* Optional flag for TCP */
     174           0 :     if (flag != nullptr) {
     175           0 :         _flag = strdup(flag);
     176             :     }
     177             : 
     178           0 :     AP_HAL::OwnPtr<SerialDevice> device = nullptr;
     179             : 
     180           0 :     if (strcmp(protocol, "udp") == 0 || strcmp(protocol, "udpin") == 0) {
     181           0 :         bool bcast = (_flag && strcmp(_flag, "bcast") == 0);
     182             : #if HAL_GCS_ENABLED
     183           0 :         _packetise = true;
     184             : #endif
     185           0 :         if (strcmp(protocol, "udp") == 0) {
     186           0 :             device = NEW_NOTHROW UDPDevice(_ip, _base_port, bcast, false);
     187             :         } else {
     188           0 :             if (bcast) {
     189           0 :                 AP_HAL::panic("Can't combine udpin with bcast");
     190             :             }
     191           0 :             device = NEW_NOTHROW UDPDevice(_ip, _base_port, false, true);
     192             : 
     193             :         }
     194           0 :     } else {
     195           0 :         bool wait = (_flag && strcmp(_flag, "wait") == 0);
     196           0 :         device = NEW_NOTHROW TCPServerDevice(_ip, _base_port, wait);
     197             :     }
     198             : 
     199           0 :     free(devstr);
     200           0 :     return device;
     201           0 : }
     202             : 
     203             : /*
     204             :   shutdown a UART
     205             :  */
     206           0 : void UARTDriver::_end()
     207             : {
     208           0 :     _initialised = false;
     209           0 :     _connected = false;
     210             : 
     211           0 :     while (_in_timer) {
     212           0 :         hal.scheduler->delay(1);
     213             :     }
     214             : 
     215           0 :     _device->close();
     216           0 :     _deallocate_buffers();
     217           0 : }
     218             : 
     219             : 
     220           0 : void UARTDriver::_flush()
     221             : {
     222             :     // we are not doing any buffering, so flush is a no-op
     223           0 : }
     224             : 
     225             : 
     226             : /*
     227             :   return true if the UART is initialised
     228             :  */
     229           0 : bool UARTDriver::is_initialized()
     230             : {
     231           0 :     return _initialised;
     232             : }
     233             : 
     234             : 
     235             : /*
     236             :   do we have any bytes pending transmission?
     237             :  */
     238           0 : bool UARTDriver::tx_pending()
     239             : {
     240           0 :     return (_writebuf.available() > 0);
     241             : }
     242             : 
     243             : /*
     244             :   return the number of bytes available to be read
     245             :  */
     246           0 : uint32_t UARTDriver::_available()
     247             : {
     248           0 :     if (!_initialised) {
     249           0 :         return 0;
     250             :     }
     251           0 :     return _readbuf.available();
     252             : }
     253             : 
     254             : /*
     255             :   how many bytes are available in the output buffer?
     256             :  */
     257           0 : uint32_t UARTDriver::txspace()
     258             : {
     259           0 :     if (!_initialised) {
     260           0 :         return 0;
     261             :     }
     262           0 :     return _writebuf.space();
     263             : }
     264             : 
     265           0 : ssize_t UARTDriver::_read(uint8_t *buffer, uint16_t count)
     266             : {
     267           0 :     if (!_initialised) {
     268           0 :         return 0;
     269             :     }
     270             : 
     271           0 :     return _readbuf.read(buffer, count);
     272             : }
     273             : 
     274           0 : bool UARTDriver::_discard_input()
     275             : {
     276           0 :     if (!_initialised) {
     277           0 :         return false;
     278             :     }
     279           0 :     _readbuf.clear();
     280           0 :     return true;
     281             : }
     282             : 
     283             : /*
     284             :   write size bytes to the write buffer
     285             :  */
     286           0 : size_t UARTDriver::_write(const uint8_t *buffer, size_t size)
     287             : {
     288           0 :     if (!_initialised) {
     289           0 :         return 0;
     290             :     }
     291           0 :     if (!_write_mutex.take_nonblocking()) {
     292           0 :         return 0;
     293             :     }
     294             : 
     295           0 :     size_t ret = _writebuf.write(buffer, size);
     296           0 :     _write_mutex.give();
     297           0 :     return ret;
     298             : }
     299             : 
     300             : /*
     301             :   try writing n bytes, handling an unresponsive port
     302             :  */
     303           0 : int UARTDriver::_write_fd(const uint8_t *buf, uint16_t n)
     304             : {
     305             :     /*
     306             :       allow for delayed connection. This allows ArduPilot to start
     307             :       before a network interface is available.
     308             :      */
     309           0 :     if (!_connected) {
     310           0 :         _connected = _device->open();
     311             :     }
     312           0 :     if (!_connected) {
     313           0 :         return 0;
     314             :     }
     315             : 
     316           0 :     return _device->write(buf, n);
     317             : }
     318             : 
     319             : /*
     320             :   try reading n bytes, handling an unresponsive port
     321             :  */
     322           0 : int UARTDriver::_read_fd(uint8_t *buf, uint16_t n)
     323             : {
     324           0 :     return _device->read(buf, n);
     325             : }
     326             : 
     327             : 
     328             : /*
     329             :   try to push out one lump of pending bytes
     330             :   return true if progress is made
     331             :  */
     332           0 : bool UARTDriver::_write_pending_bytes(void)
     333             : {
     334             :     // write any pending bytes
     335           0 :     uint32_t available_bytes = _writebuf.available();
     336           0 :     uint16_t n = available_bytes;
     337             : 
     338             : #if HAL_GCS_ENABLED
     339           0 :     if (_packetise && n > 0) {
     340             :         // send on MAVLink packet boundaries if possible
     341           0 :         n = mavlink_packetise(_writebuf, n);
     342             :     }
     343             : #endif
     344             : 
     345           0 :     if (n > 0) {
     346             :         int ret;
     347             : 
     348           0 :         if (_packetise) {
     349             :             // keep as a single UDP packet
     350           0 :             uint8_t tmpbuf[n];
     351           0 :             _writebuf.peekbytes(tmpbuf, n);
     352           0 :             ret = _write_fd(tmpbuf, n);
     353           0 :             if (ret > 0)
     354           0 :                 _writebuf.advance(ret);
     355           0 :         } else {
     356             :             ByteBuffer::IoVec vec[2];
     357           0 :             const auto n_vec = _writebuf.peekiovec(vec, n);
     358           0 :             for (int i = 0; i < n_vec; i++) {
     359           0 :                 ret = _write_fd(vec[i].data, (uint16_t)vec[i].len);
     360           0 :                 if (ret < 0) {
     361           0 :                     break;
     362             :                 }
     363           0 :                 _writebuf.advance(ret);
     364             : 
     365             :                 /* We wrote less than we asked for, stop */
     366           0 :                 if ((unsigned)ret != vec[i].len) {
     367           0 :                     break;
     368             :                 }
     369             :             }
     370             :         }
     371             :     }
     372             : 
     373           0 :     return _writebuf.available() != available_bytes;
     374             : }
     375             : 
     376             : /*
     377             :   push any pending bytes to/from the serial port. This is called at
     378             :   1kHz in the timer thread. Doing it this way reduces the system call
     379             :   overhead in the main task enormously.
     380             :  */
     381           0 : void UARTDriver::_timer_tick(void)
     382             : {
     383           0 :     if (!_initialised) return;
     384             : 
     385           0 :     _in_timer = true;
     386             : 
     387           0 :     uint8_t num_send = 10;
     388           0 :     while (num_send != 0 && _write_pending_bytes()) {
     389           0 :         num_send--;
     390             :     }
     391             : 
     392             :     // try to fill the read buffer
     393             :     int ret;
     394             :     ByteBuffer::IoVec vec[2];
     395             : 
     396           0 :     const auto n_vec = _readbuf.reserve(vec, _readbuf.space());
     397           0 :     for (int i = 0; i < n_vec; i++) {
     398           0 :         ret = _read_fd(vec[i].data, vec[i].len);
     399           0 :         if (ret < 0) {
     400           0 :             break;
     401             :         }
     402           0 :         _readbuf.commit((unsigned)ret);
     403             : 
     404             :         // update receive timestamp
     405           0 :         _receive_timestamp[_receive_timestamp_idx^1] = AP_HAL::micros64();
     406           0 :         _receive_timestamp_idx ^= 1;
     407             :         
     408             :         /* stop reading as we read less than we asked for */
     409           0 :         if ((unsigned)ret < vec[i].len) {
     410           0 :             break;
     411             :         }
     412             :     }
     413             : 
     414           0 :     _in_timer = false;
     415             : }
     416             : 
     417           0 : void UARTDriver::configure_parity(uint8_t v) {
     418           0 :     UARTDriver::parity = v;
     419           0 :     _device->set_parity(v);
     420           0 : }
     421             : 
     422             : /*
     423             :   return timestamp estimate in microseconds for when the start of
     424             :   a nbytes packet arrived on the uart. This should be treated as a
     425             :   time constraint, not an exact time. It is guaranteed that the
     426             :   packet did not start being received after this time, but it
     427             :   could have been in a system buffer before the returned time.
     428             :   
     429             :   This takes account of the baudrate of the link. For transports
     430             :   that have no baudrate (such as USB) the time estimate may be
     431             :   less accurate.
     432             :   
     433             :   A return value of zero means the HAL does not support this API
     434             : */
     435           0 : uint64_t UARTDriver::receive_time_constraint_us(uint16_t nbytes)
     436             : {
     437           0 :     uint64_t last_receive_us = _receive_timestamp[_receive_timestamp_idx];
     438           0 :     if (_baudrate > 0) {
     439             :         // assume 10 bits per byte.
     440           0 :         uint32_t transport_time_us = (1000000UL * 10UL / _baudrate) * (nbytes+available());
     441           0 :         last_receive_us -= transport_time_us;
     442             :     }
     443           0 :     return last_receive_us;
     444             : }
     445             : 
     446           0 : uint32_t UARTDriver::bw_in_bytes_per_second() const
     447             : {
     448             :     // if connected, assume at least a 10/100Mbps connection
     449           0 :     const uint32_t bitrate = (_connected && _ip != nullptr) ? 10E6 : _baudrate;
     450           0 :     return bitrate/10; // convert bits to bytes minus overhead
     451             : }

Generated by: LCOV version 1.14