LCOV - code coverage report
Current view: top level - AP_HAL_SITL - UARTDriver.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 338 661 51.1 %
Date: 2026-09-09 11:02:08 Functions: 25 29 86.2 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : 
      16             : //
      17             : //      Copyright (c) 2010 Michael Smith. All rights reserved.
      18             : //
      19             : #include <AP_HAL/AP_HAL.h>
      20             : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
      21             : 
      22             : #include <limits.h>
      23             : #include <stdlib.h>
      24             : #include <stdio.h>
      25             : #include <unistd.h>
      26             : #include <fcntl.h>
      27             : #include <stdarg.h>
      28             : #include <AP_Math/AP_Math.h>
      29             : 
      30             : #include <errno.h>
      31             : #include <sys/ioctl.h>
      32             : #include <sys/stat.h>
      33             : #include <sys/types.h>
      34             : #include <sys/socket.h>
      35             : #include <sys/un.h>
      36             : #include <netinet/in.h>
      37             : #include <netinet/tcp.h>
      38             : #include <sys/select.h>
      39             : #include <termios.h>
      40             : #include <sys/time.h>
      41             : #include <arpa/inet.h>
      42             : 
      43             : #include "UARTDriver.h"
      44             : #include "SITL_State.h"
      45             : #if HAL_GCS_ENABLED
      46             : #include <AP_HAL/utility/packetise.h>
      47             : #endif
      48             : 
      49             : #include <AP_Vehicle/AP_Vehicle_Type.h>
      50             : #include <AP_Filesystem/AP_Filesystem.h>
      51             : #include <AP_Common/ExpandingString.h>
      52             : #include <AP_HAL/utility/Socket_native.h>
      53             : 
      54             : extern const AP_HAL::HAL& hal;
      55             : 
      56             : using namespace HALSITL;
      57             : 
      58             : bool UARTDriver::_console;
      59             : 
      60             : /* UARTDriver method implementations */
      61             : 
      62      542778 : void UARTDriver::_begin(uint32_t baud, uint16_t rxSpace, uint16_t txSpace)
      63             : {
      64      542778 :     if (baud == 0 && rxSpace == 0 && txSpace == 0) {
      65             :         // this is a claim of the uart for the current thread, which
      66             :         // is currently not implemented in SITL
      67      506810 :         return;
      68             :     }
      69             : 
      70       35968 :     if (_portNumber >= ARRAY_SIZE(_sitlState->_serial_path)) {
      71           0 :         AP_HAL::panic("port number out of range; you may need to extend _sitlState->_serial_path");
      72             :     }
      73             : 
      74       35968 :     const char *path = _sitlState->_serial_path[_portNumber];
      75             : 
      76       35968 :     if (baud != 0) {
      77       35963 :         _uart_baudrate = baud;
      78             :     }
      79             :     
      80       35968 :     if (strcmp(path, "GPS1") == 0) {
      81             :         /* gps */
      82       12464 :         _connected = true;
      83       12464 :         _sim_serial_device = _sitlState->create_serial_sim("gps", "1", _portNumber);
      84       23504 :     } else if (strcmp(path, "GPS2") == 0) {
      85             :         /* 2nd gps */
      86        2066 :         _connected = true;
      87        2066 :         _sim_serial_device = _sitlState->create_serial_sim("gps", "2", _portNumber);
      88             :     } else {
      89             :         /* parse type:args:flags string for path. 
      90             :            For example:
      91             :              tcp:5760:wait    // tcp listen on port 5760
      92             :              tcp:0:wait       // tcp listen on use base_port + 0
      93             :              uds:APM-UDS-serial0:wait
      94             :              tcpclient:192.168.2.15:5762
      95             :              udpclient:127.0.0.1
      96             :              udpclient:127.0.0.1:14550
      97             :              mcast:
      98             :              mcast:239.255.145.50:14550
      99             :              uart:/dev/ttyUSB0:57600
     100             :              sim:ParticleSensor_SDS021:
     101             :              file:/tmp/my-device-capture.BIN
     102             :              logic_async_csv:/tmp/logic_async.csv:
     103             :          */
     104       21438 :         char *saveptr = nullptr;
     105       21438 :         char *s = strdup(path);
     106       21438 :         char *devtype = strtok_r(s, ":", &saveptr);
     107       21438 :         char *args1 = strtok_r(nullptr, ":", &saveptr);
     108       21438 :         char *args2 = strtok_r(nullptr, ":", &saveptr);
     109             : #if APM_BUILD_COPTER_OR_HELI || APM_BUILD_TYPE(APM_BUILD_ArduPlane)
     110       17388 :         if (_portNumber == 1 && AP::sitl()->adsb_plane_count >= 0) {
     111             :             // this is ordinarily port 5762.  The ADSB simulation assumed
     112             :             // this port, so if enabled we assume we'll be doing ADSB...
     113             :             // add sanity check here that we're doing mavlink on this port?
     114           6 :             ::printf("SIM-ADSB connection on SERIAL%u\n", _portNumber);
     115           6 :             _connected = true;
     116           6 :             _sim_serial_device = _sitlState->create_serial_sim("adsb", nullptr, _portNumber);
     117             :         } else
     118             : #endif
     119       21432 :         if (strcmp(devtype, "tcp") == 0) {
     120       21028 :             uint16_t port = atoi(args1);
     121       21028 :             bool wait = (args2 && strcmp(args2, "wait") == 0);
     122       21028 :             _tcp_start_connection(port, wait);
     123         404 :         } else if (strcmp(devtype, "uds") == 0) {
     124           0 :             if (args1 == nullptr || args1[0] == '\0') {
     125           0 :                 AP_HAL::panic("Invalid Unix domain socket path: %s", path);
     126             :             }
     127           0 :             bool wait = (args2 && strcmp(args2, "wait") == 0);
     128           0 :             _unix_start_connection(args1, wait);
     129         404 :         } else if (strcmp(devtype, "tcpclient") == 0) {
     130           1 :             if (args2 == nullptr) {
     131           0 :                 AP_HAL::panic("Invalid tcp client path: %s", path);
     132             :             }
     133           1 :             uint16_t port = atoi(args2);
     134           1 :             _tcp_start_client(args1, port);
     135         403 :         } else if (strcmp(devtype, "uart") == 0) {
     136           0 :             uint32_t baudrate = args2? atoi(args2) : baud;
     137           0 :             if (_uart_path == nullptr) {
     138           0 :                 ::printf("UART connection %s:%u\n", args1, baudrate);
     139           0 :                 _uart_path = strdup(args1);
     140             :             }
     141           0 :             _uart_baudrate = baudrate;
     142           0 :             _uart_start_connection();
     143         403 :         } else if (strcmp(devtype, "sim") == 0) {
     144         397 :             if (!_connected) {
     145         232 :                 ::printf("SIM connection %s:%s on SERIAL%u\n", args1, args2, _portNumber);
     146         232 :                 _connected = true;
     147         232 :                 _sim_serial_device = _sitlState->create_serial_sim(args1, args2, _portNumber);
     148             :             }
     149           6 :         } else if (strcmp(devtype, "udpclient") == 0) {
     150             :             // udp client connection
     151           0 :             const char *ip = args1;
     152           0 :             uint16_t port = args2?atoi(args2):14550;
     153           0 :             if (!_connected) {
     154           0 :                 ::printf("UDP connection %s:%u\n", ip, port);
     155           0 :                 _udp_start_client(ip, port);
     156             :             }
     157           6 :         } else if (strcmp(devtype, "mcast") == 0) {
     158             :             // udp multicast connection
     159           6 :             const char *ip = args1 && *args1?args1:mcast_ip_default;
     160           6 :             uint16_t port = args2?atoi(args2):mcast_port_default;
     161           6 :             if (!_connected) {
     162           2 :                 ::printf("UDP multicast connection %s:%u\n", ip, port);
     163           2 :                 _udp_start_multicast(ip, port);
     164             :             }
     165           0 :         } else if (strcmp(devtype,"none") == 0) {
     166             :             // skipping port
     167           0 :             ::printf("Skipping port %s\n", args1);
     168           0 :         } else if (strcmp(devtype, "file") == 0) {
     169           0 :             if (_connected) {
     170           0 :                 AP::FS().close(_fd);
     171             :             }
     172           0 :             ::printf("FILE connection %s\n", args1);
     173           0 :             _fd = AP::FS().open(args1, O_RDONLY);
     174           0 :             if (_fd == -1) {
     175           0 :                 AP_HAL::panic("Failed to open (%s): %m", args1);
     176             :             }
     177           0 :             _connected = true;
     178           0 :         } else if (strcmp(devtype, "outfile") == 0) {
     179           0 :             if (_connected) {
     180           0 :                 AP::FS().close(_fd);
     181             :             }
     182           0 :             ::printf("FILE output connection %s\n", args1);
     183           0 :             _fd = AP::FS().open(args1, O_WRONLY|O_CREAT|O_TRUNC, 0644);
     184           0 :             if (_fd == -1) {
     185           0 :                 AP_HAL::panic("Failed to open (%s): %m", args1);
     186             :             }
     187           0 :             _connected = true;
     188           0 :         } else if (strcmp(devtype, "logic_async_csv") == 0) {
     189           0 :             if (_connected) {
     190           0 :                 AP::FS().close(_fd);
     191             :             }
     192           0 :             ::printf("logic_async_csv connection %s\n", args1);
     193           0 :             _fd = AP::FS().open(args1, O_RDONLY);
     194           0 :             if (_fd == -1) {
     195           0 :                 AP_HAL::panic("Failed to open (%s): %m", args1);
     196             :             }
     197           0 :             _connected = true;
     198           0 :             logic_async_csv.active = true;
     199             :         } else {
     200           0 :             AP_HAL::panic("Invalid device path: %s", path);
     201             :         }
     202       21438 :         free(s);
     203             :     }
     204             : 
     205       35968 :     if (_sim_serial_device != nullptr) {
     206       14933 :         _sim_serial_device->set_autopilot_baud(baud);
     207             :     }
     208             : 
     209       35968 :     if (hal.console != this) { // don't clear USB buffers (allows early startup messages to escape)
     210       27592 :         _readbuffer.clear();
     211       27592 :         _writebuffer.clear();
     212             :     }
     213             : }
     214             : 
     215           0 : void UARTDriver::_end()
     216             : {
     217           0 : }
     218             : 
     219   136181823 : uint32_t UARTDriver::_available(void)
     220             : {
     221   136181823 :     _check_connection();
     222             : 
     223   136181828 :     if (!_connected) {
     224    82343345 :         return 0;
     225             :     }
     226             : 
     227    53838483 :     return _readbuffer.available();
     228             : }
     229             : 
     230   430642998 : uint32_t UARTDriver::txspace(void)
     231             : {
     232   430642998 :     _check_connection();
     233   430642989 :     if (!_connected) {
     234   168060453 :         return 0;
     235             :     }
     236   262582536 :     return _writebuffer.space();
     237             : }
     238             : 
     239       39949 : void UARTDriver::set_flow_control(enum flow_control flow_control_setting)
     240             : {
     241       39949 :     _flow_control = flow_control_setting;
     242       39949 :     if (_flow_control == FLOW_CONTROL_AUTO) {
     243             :         // reset auto-detection state
     244           0 :         _auto_flow_start_ms = 0;
     245           0 :         _auto_flow_detected = false;
     246             :     }
     247             :     // If already connected on a real UART path, apply immediately
     248       39949 :     if (_uart_path && _connected && _fd != -1) {
     249           0 :         struct termios t {};
     250           0 :         if (tcgetattr(_fd, &t) == 0) {
     251           0 :             if (_flow_control != FLOW_CONTROL_DISABLE) {
     252           0 :                 t.c_cflag |= CRTSCTS;
     253             :             } else {
     254           0 :                 t.c_cflag &= ~CRTSCTS;
     255             :             }
     256           0 :             tcsetattr(_fd, TCSANOW, &t);
     257             :         }
     258             :     }
     259       39949 : }
     260             : 
     261       13351 : enum AP_HAL::UARTDriver::flow_control UARTDriver::get_flow_control(void)
     262             : {
     263       13351 :     if (_uart_path) {
     264           0 :         return _flow_control;
     265             :     }
     266       13351 :     return FLOW_CONTROL_ENABLE;
     267             : }
     268             : 
     269   444364572 : ssize_t UARTDriver::_read(uint8_t *buffer, uint16_t count)
     270             : {
     271   444364572 :     const ssize_t ret = _readbuffer.read(buffer, count);
     272   444363251 :     _rx_stats_bytes += ret;
     273   444363251 :     return ret;
     274             : }
     275             : 
     276      191581 : bool UARTDriver::_discard_input(void)
     277             : {
     278      191581 :     _readbuffer.clear();
     279      191581 :     return true;
     280             : }
     281             : 
     282        1639 : void UARTDriver::_flush(void)
     283             : {
     284             :     // flush the write buffer - but don't fail and don't
     285             :     // infinitely-loop.  This is not a good definition of "flush", but
     286             :     // it was judged that we had to return from this function even if
     287             :     // we hadn't actually done our job.
     288        1639 :     uint32_t start_ms = AP_HAL::millis();
     289        3278 :     while (AP_HAL::millis() - start_ms < 1000) {
     290        3278 :         if (_writebuffer.available() == 0) {
     291        1639 :             break;
     292             :         }
     293        1639 :         _timer_tick();
     294             :     }
     295             : 
     296             :     // ensure that the outbound TCP queue is also empty...
     297        1639 :     start_ms = AP_HAL::millis();
     298        3270 :     while (AP_HAL::millis() - start_ms < 1000) {
     299        3270 :         if (((HALSITL::UARTDriver*)hal.serial(0))->get_system_outqueue_length() == 0) {
     300        1639 :             break;
     301             :         }
     302        1631 :         usleep(1000);
     303             :     }
     304        1639 : }
     305             : 
     306   117372606 : size_t UARTDriver::_write(const uint8_t *buffer, size_t size)
     307             : {
     308   117372606 :     const auto _txspace = txspace();
     309   117372603 :     if (_txspace < size) {
     310       29170 :         size = _txspace;
     311             :     }
     312   117372603 :     if (size <= 0) {
     313     1345947 :         return 0;
     314             :     }
     315             : 
     316             :         /*
     317             :           simulate byte loss at the link layer
     318             :          */
     319   116026656 :         uint8_t lost_byte = 0;
     320             : #if !defined(HAL_BUILD_AP_PERIPH)
     321   116026656 :         SITL::SIM *_sitl = AP::sitl();
     322             : 
     323   116026655 :         if (_sitl && _sitl->uart_byte_loss_pct > 0) {
     324           0 :             if (fabsf(rand_float()) < _sitl->uart_byte_loss_pct.get() * 0.01 * size) {
     325           0 :                 lost_byte = 1;
     326             :             }
     327             :         }
     328             : #endif // HAL_BUILD_AP_PERIPH
     329             : 
     330             :     // Include lost byte in tx count, we think we sent it even though it was never added to the write buffer
     331   116026654 :     _tx_stats_bytes += lost_byte;
     332             : 
     333   116026654 :     const size_t ret = _writebuffer.write(buffer, size - lost_byte) + lost_byte;
     334   116026654 :     if (_unbuffered_writes) {
     335           0 :         handle_writing_from_writebuffer_to_device();
     336             :     }
     337   116026654 :     return ret;
     338             : }
     339             : 
     340             :     
     341             : /*
     342             :   start a TCP connection for the serial port. If wait_for_connection
     343             :   is true then block until a client connects
     344             :  */
     345       21028 : void UARTDriver::_tcp_start_connection(uint16_t port, bool wait_for_connection)
     346             : {
     347       21028 :     int one=1;
     348             :     int ret;
     349       21028 :     struct sockaddr_in _listen_sockaddr {};
     350             : 
     351       21028 :     if (_connected) {
     352        6183 :         return;
     353             :     }
     354             : 
     355       14845 :     _use_send_recv = true;
     356             : 
     357       14845 :     if (_console) {
     358             :         // hack for console access
     359           0 :         _connected = true;
     360           0 :         _use_send_recv = false;
     361           0 :         _listen_fd = -1;
     362           0 :         _fd = 1;
     363           0 :         return;
     364             :     }
     365             : 
     366       14845 :     if (_fd != -1) {
     367           0 :         close(_fd);
     368             :     }
     369             : 
     370       14845 :     if (_listen_fd == -1) {
     371        6320 :         memset(&_listen_sockaddr,0,sizeof(_listen_sockaddr));
     372             : 
     373             : #ifdef HAVE_SOCK_SIN_LEN
     374             :         _listen_sockaddr.sin_len = sizeof(_listen_sockaddr);
     375             : #endif
     376        6320 :         if (port > 1000) {
     377          15 :             _listen_sockaddr.sin_port = htons(port);
     378             :         } else {
     379        6305 :             _listen_sockaddr.sin_port = htons(_sitlState->base_port() + port);
     380             :         }
     381        6320 :         _listen_sockaddr.sin_family = AF_INET;
     382             : 
     383        6320 :         _listen_fd = socket(AF_INET, SOCK_STREAM, 0);
     384        6320 :         if (_listen_fd == -1) {
     385           0 :             fprintf(stderr, "socket failed - %s\n", strerror(errno));
     386           0 :             exit(1);
     387             :         }
     388        6320 :         ret = fcntl(_listen_fd, F_SETFD, FD_CLOEXEC);
     389        6320 :         if (ret == -1) {
     390           0 :             fprintf(stderr, "fcntl failed on setting FD_CLOEXEC - %s\n", strerror(errno));
     391           0 :             exit(1);
     392             :         }
     393             : 
     394             :         /* we want to be able to re-use ports quickly */
     395        6320 :         if (setsockopt(_listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) {
     396           0 :             fprintf(stderr, "setsockopt failed: %s\n", strerror(errno));
     397           0 :             exit(1);
     398             :         }
     399             : 
     400        6320 :         fprintf(stderr, "bind port %u for SERIAL%u\n",
     401        6320 :                 (unsigned)ntohs(_listen_sockaddr.sin_port),
     402        6320 :                 (unsigned)_portNumber);
     403             : 
     404        6320 :         ret = bind(_listen_fd, (struct sockaddr *)&_listen_sockaddr, sizeof(_listen_sockaddr));
     405        6320 :         if (ret == -1) {
     406           0 :             fprintf(stderr, "bind failed on port %u - %s\n",
     407           0 :                     (unsigned)ntohs(_listen_sockaddr.sin_port),
     408           0 :                     strerror(errno));
     409           0 :             exit(1);
     410             :         }
     411             : 
     412        6320 :         ret = listen(_listen_fd, 5);
     413        6320 :         if (ret == -1) {
     414           0 :             fprintf(stderr, "listen failed - %s\n", strerror(errno));
     415           0 :             exit(1);
     416             :         }
     417             : 
     418        6320 :         fprintf(stderr, "SERIAL%u on TCP port %u\n", _portNumber,
     419        6320 :                 (unsigned)ntohs(_listen_sockaddr.sin_port));
     420        6320 :         fflush(stdout);
     421             :     }
     422             : 
     423       14845 :     if (wait_for_connection) {
     424        2199 :         fprintf(stdout, "Waiting for connection ....\n");
     425        2199 :         fflush(stdout);
     426        2199 :         _fd = accept(_listen_fd, nullptr, nullptr);
     427        2199 :         if (_fd == -1) {
     428           0 :             fprintf(stderr, "accept() error - %s", strerror(errno));
     429           0 :             exit(1);
     430             :         }
     431        2199 :         setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
     432        2199 :         setsockopt(_fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
     433        2199 :         fcntl(_fd, F_SETFD, FD_CLOEXEC);
     434        2199 :         _connected = true;
     435        2199 :         fprintf(stdout, "Connection on serial port %u\n", (unsigned)ntohs(_listen_sockaddr.sin_port));
     436             :     }
     437             : }
     438             : 
     439             : 
     440             : /*
     441             :   start a Unix domain socket connection for the serial port. If
     442             :   wait_for_connection is true then block until a client connects
     443             :  */
     444           0 : void UARTDriver::_unix_start_connection(const char *path, bool wait_for_connection)
     445             : {
     446           0 :     struct sockaddr_un listen_sockaddr {};
     447             : 
     448           0 :     if (_connected) {
     449           0 :         return;
     450             :     }
     451             : 
     452           0 :     _use_send_recv = true;
     453           0 :     _is_unix_socket = true;
     454             : 
     455           0 :     if (_console) {
     456           0 :         _connected = true;
     457           0 :         _use_send_recv = false;
     458           0 :         _listen_fd = -1;
     459           0 :         _fd = 1;
     460           0 :         return;
     461             :     }
     462             : 
     463           0 :     if (_fd != -1) {
     464           0 :         close(_fd);
     465             :     }
     466             : 
     467           0 :     if (_listen_fd == -1) {
     468           0 :         if (strlen(path) >= sizeof(listen_sockaddr.sun_path)) {
     469           0 :             fprintf(stderr, "Unix domain socket path is too long: %s\n", path);
     470           0 :             exit(1);
     471             :         }
     472             : 
     473           0 :         listen_sockaddr.sun_family = AF_UNIX;
     474           0 :         strncpy(listen_sockaddr.sun_path, path, sizeof(listen_sockaddr.sun_path) - 1);
     475             : 
     476           0 :         _listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
     477           0 :         if (_listen_fd == -1) {
     478           0 :             fprintf(stderr, "socket failed - %s\n", strerror(errno));
     479           0 :             exit(1);
     480             :         }
     481           0 :         if (fcntl(_listen_fd, F_SETFD, FD_CLOEXEC) == -1) {
     482           0 :             fprintf(stderr, "fcntl failed on setting FD_CLOEXEC - %s\n", strerror(errno));
     483           0 :             exit(1);
     484             :         }
     485             : 
     486           0 :         int ret = bind(_listen_fd, (struct sockaddr *)&listen_sockaddr, sizeof(listen_sockaddr));
     487           0 :         if (ret == -1 && errno == EADDRINUSE) {
     488             :             struct stat path_stat;
     489           0 :             if (lstat(path, &path_stat) == 0 && S_ISSOCK(path_stat.st_mode)) {
     490           0 :                 int probe_fd = socket(AF_UNIX, SOCK_STREAM, 0);
     491           0 :                 int connect_ret = probe_fd == -1 ? -1 : connect(
     492             :                     probe_fd,
     493             :                     (struct sockaddr *)&listen_sockaddr,
     494           0 :                     sizeof(listen_sockaddr));
     495           0 :                 int connect_errno = errno;
     496           0 :                 if (probe_fd != -1) {
     497           0 :                     close(probe_fd);
     498             :                 }
     499           0 :                 if (connect_ret == -1 && connect_errno == ECONNREFUSED) {
     500             :                     // A newly bound stream socket also refuses connections in
     501             :                     // the brief interval before listen(). Give it time to
     502             :                     // become live before treating the path as stale.
     503           0 :                     usleep(100000);
     504           0 :                     probe_fd = socket(AF_UNIX, SOCK_STREAM, 0);
     505           0 :                     connect_ret = probe_fd == -1 ? -1 : connect(
     506             :                         probe_fd,
     507             :                         (struct sockaddr *)&listen_sockaddr,
     508             :                         sizeof(listen_sockaddr));
     509           0 :                     connect_errno = errno;
     510           0 :                     if (probe_fd != -1) {
     511           0 :                         close(probe_fd);
     512             :                     }
     513             : 
     514             :                     struct stat current_path_stat;
     515           0 :                     if (connect_ret == -1 && connect_errno == ECONNREFUSED &&
     516           0 :                         lstat(path, &current_path_stat) == 0 &&
     517           0 :                         S_ISSOCK(current_path_stat.st_mode) &&
     518           0 :                         current_path_stat.st_dev == path_stat.st_dev &&
     519           0 :                         current_path_stat.st_ino == path_stat.st_ino &&
     520           0 :                         unlink(path) == 0) {
     521           0 :                         ret = bind(_listen_fd, (struct sockaddr *)&listen_sockaddr, sizeof(listen_sockaddr));
     522             :                     }
     523             :                 }
     524             :             }
     525             :         }
     526           0 :         if (ret == -1) {
     527           0 :             fprintf(stderr, "bind failed on Unix domain socket %s - %s\n", path, strerror(errno));
     528           0 :             exit(1);
     529             :         }
     530           0 :         SocketAPM_native::register_unix_path(_listen_fd, path);
     531             : 
     532           0 :         if (listen(_listen_fd, 5) == -1) {
     533           0 :             fprintf(stderr, "listen failed - %s\n", strerror(errno));
     534           0 :             exit(1);
     535             :         }
     536             : 
     537           0 :         fprintf(stderr, "SERIAL%u on Unix domain socket %s\n", _portNumber, path);
     538           0 :         fflush(stdout);
     539             :     }
     540             : 
     541           0 :     if (wait_for_connection) {
     542           0 :         fprintf(stdout, "Waiting for connection ....\n");
     543           0 :         fflush(stdout);
     544           0 :         _fd = accept(_listen_fd, nullptr, nullptr);
     545           0 :         if (_fd == -1) {
     546           0 :             fprintf(stderr, "accept() error - %s", strerror(errno));
     547           0 :             exit(1);
     548             :         }
     549           0 :         if (fcntl(_fd, F_SETFD, FD_CLOEXEC) == -1) {
     550           0 :             fprintf(stderr, "fcntl failed on setting FD_CLOEXEC - %s\n", strerror(errno));
     551           0 :             exit(1);
     552             :         }
     553           0 :         _connected = true;
     554           0 :         fprintf(stdout, "Connection on Unix domain socket for SERIAL%u\n", _portNumber);
     555             :     }
     556             : }
     557             : 
     558             : 
     559             : /*
     560             :   start a TCP client connection for the serial port. 
     561             :  */
     562           1 : void UARTDriver::_tcp_start_client(const char *address, uint16_t port)
     563             : {
     564             : 
     565           1 :     if (_connected) {
     566           0 :         return;
     567             :     }
     568             : 
     569           1 :     _use_send_recv = true;
     570             : 
     571           1 :     if (_fd != -1) {
     572           0 :         close(_fd);
     573             :     }
     574             : 
     575             :     struct sockaddr_in sockaddr;
     576           1 :     memset(&sockaddr,0,sizeof(sockaddr));
     577             : 
     578             : #ifdef HAVE_SOCK_SIN_LEN
     579             :     sockaddr.sin_len = sizeof(sockaddr);
     580             : #endif
     581           1 :     sockaddr.sin_port = htons(port);
     582           1 :     sockaddr.sin_family = AF_INET;
     583           1 :     sockaddr.sin_addr.s_addr = inet_addr(address);
     584             : 
     585           1 :     constexpr auto one=1;
     586             :     int ret;
     587             :     // Retry for up to 30 seconds.  Companion processes (e.g. AP_Periph
     588             :     // connecting to an ArduPlane SITL TCP listener for a PPP link) can be
     589             :     // launched ahead of the peer's socket, so a few seconds of grace is
     590             :     // not enough when the peer is also rebuilding.
     591           1 :     constexpr uint8_t max_attempts = 30;
     592           1 :     for (uint8_t attempt = 0; attempt < max_attempts; ++attempt) {
     593           1 :         _fd = socket(AF_INET, SOCK_STREAM, 0);
     594           1 :         if (_fd == -1) {
     595           0 :             fprintf(stderr, "socket failed - %s\n", strerror(errno));
     596           0 :             exit(1);
     597             :         }
     598           1 :         ret = fcntl(_fd, F_SETFD, FD_CLOEXEC);
     599           1 :         if (ret == -1) {
     600           0 :             fprintf(stderr, "fcntl failed on setting FD_CLOEXEC - %s\n", strerror(errno));
     601           0 :             exit(1);
     602             :         }
     603             : 
     604             :         /* we want to be able to re-use ports quickly */
     605           1 :         setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
     606             : 
     607           1 :         ret = connect(_fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
     608           1 :         if (ret == 0) {
     609           1 :             break;
     610             :         }
     611           0 :         fprintf(stderr, "connect failed on port %u - %s at %d retrying\n",
     612           0 :                 (unsigned) ntohs(sockaddr.sin_port), strerror(errno), AP_HAL::millis());
     613           0 :         close(_fd);
     614             :         // If connection failed, wait for a bit before retrying
     615           0 :         sleep(1);
     616             :     }
     617             : 
     618           1 :     if (ret == -1) {
     619           0 :         close(_fd);
     620           0 :         fprintf(stderr, "connect failed on port %u - %s\n",
     621           0 :                 (unsigned)ntohs(sockaddr.sin_port),
     622           0 :                 strerror(errno));
     623           0 :         exit(1);
     624             :     }
     625             : 
     626           1 :     setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
     627           1 :     setsockopt(_fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
     628           1 :     fcntl(_fd, F_SETFD, FD_CLOEXEC);
     629           1 :     _connected = true;
     630           1 :     fprintf(stdout, "New remote connection on serial port %u, p %u at %d\n", _portNumber, (unsigned) ntohs(sockaddr.sin_port), AP_HAL::millis());
     631             : }
     632             : 
     633             : 
     634             : /*
     635             :   start a UDP client connection for the serial port.
     636             :  */
     637           2 : void UARTDriver::_udp_start_client(const char *address, uint16_t port)
     638             : {
     639             :     struct sockaddr_in sockaddr;
     640             :     int ret;
     641             : 
     642           2 :     if (_connected) {
     643           0 :         return;
     644             :     }
     645             : 
     646           2 :     _use_send_recv = true;
     647             :     
     648           2 :     if (_fd != -1) {
     649           0 :         close(_fd);
     650             :     }
     651             : 
     652           2 :     memset(&sockaddr,0,sizeof(sockaddr));
     653             : 
     654             : #ifdef HAVE_SOCK_SIN_LEN
     655             :     sockaddr.sin_len = sizeof(sockaddr);
     656             : #endif
     657           2 :     sockaddr.sin_port = htons(port);
     658           2 :     sockaddr.sin_family = AF_INET;
     659           2 :     sockaddr.sin_addr.s_addr = inet_addr(address);
     660             : 
     661           2 :     _fd = socket(AF_INET, SOCK_DGRAM, 0);
     662           2 :     if (_fd == -1) {
     663           0 :         fprintf(stderr, "socket failed - %s\n", strerror(errno));
     664           0 :         exit(1);
     665             :     }
     666           2 :     ret = fcntl(_fd, F_SETFD, FD_CLOEXEC);
     667           2 :     if (ret == -1) {
     668           0 :         fprintf(stderr, "fcntl failed on setting FD_CLOEXEC - %s\n", strerror(errno));
     669           0 :         exit(1);
     670             :     }
     671             : 
     672             :     // try to setup for broadcast, this may fail if insufficient privileges
     673           2 :     int one = 1;
     674           2 :     setsockopt(_fd,SOL_SOCKET,SO_BROADCAST,(char *)&one,sizeof(one));
     675             : 
     676           2 :     ret = connect(_fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
     677           2 :     if (ret == -1) {
     678           0 :         fprintf(stderr, "udp connect failed on port %u - %s\n",
     679           0 :                 (unsigned)ntohs(sockaddr.sin_port),
     680           0 :                 strerror(errno));
     681           0 :         exit(1);
     682             :     }
     683             : 
     684           2 :     _is_udp = true;
     685             : #if HAL_GCS_ENABLED
     686           2 :     _packetise = true;
     687             : #endif
     688           2 :     _connected = true;
     689             : }
     690             : 
     691             : /*
     692             :   start a UDP multicast connection
     693             :  */
     694           2 : void UARTDriver::_udp_start_multicast(const char *address, uint16_t port)
     695             : {
     696           2 :     if (_connected) {
     697           0 :         return;
     698             :     }
     699             : 
     700             :     // establish the listening port
     701             :     struct sockaddr_in sockaddr;
     702             :     int ret;
     703             : 
     704           2 :     memset(&sockaddr,0,sizeof(sockaddr));
     705             : 
     706             : #ifdef HAVE_SOCK_SIN_LEN
     707             :     sockaddr.sin_len = sizeof(sockaddr);
     708             : #endif
     709           2 :     sockaddr.sin_port = htons(port);
     710           2 :     sockaddr.sin_family = AF_INET;
     711           2 :     sockaddr.sin_addr.s_addr = inet_addr(address);
     712             : 
     713           2 :     _mc_fd = socket(AF_INET, SOCK_DGRAM, 0);
     714           2 :     if (_mc_fd == -1) {
     715           0 :         fprintf(stderr, "socket failed - %s\n", strerror(errno));
     716           0 :         exit(1);
     717             :     }
     718           2 :     ret = fcntl(_mc_fd, F_SETFD, FD_CLOEXEC);
     719           2 :     if (ret == -1) {
     720           0 :         fprintf(stderr, "fcntl failed on setting FD_CLOEXEC - %s\n", strerror(errno));
     721           0 :         exit(1);
     722             :     }
     723           2 :     int one = 1;
     724           2 :     if (setsockopt(_mc_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) {
     725           0 :         fprintf(stderr, "setsockopt failed: %s\n", strerror(errno));
     726           0 :         exit(1);
     727             :     }
     728             : 
     729             :     // close on exec, to allow reboot
     730           2 :     fcntl(_mc_fd, F_SETFD, FD_CLOEXEC);
     731             : 
     732             : #if defined(__CYGWIN__) || defined(__CYGWIN64__) || defined(CYGWIN_BUILD)
     733             :     /*
     734             :       on cygwin you need to bind to INADDR_ANY then use the multicast
     735             :       IP_ADD_MEMBERSHIP to get on the right address
     736             :      */
     737             :     sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
     738             : #endif
     739             :     
     740           2 :     ret = bind(_mc_fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
     741           2 :     if (ret == -1) {
     742           0 :         fprintf(stderr, "multicast bind failed on port %u - %s\n",
     743           0 :                 (unsigned)ntohs(sockaddr.sin_port),
     744           0 :                 strerror(errno));
     745           0 :         exit(1);
     746             :     }
     747             : 
     748           2 :     struct ip_mreq mreq {};
     749           2 :     mreq.imr_multiaddr.s_addr = inet_addr(address);
     750           2 :     mreq.imr_interface.s_addr = htonl(INADDR_ANY);
     751             : 
     752           2 :     ret = setsockopt(_mc_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
     753           2 :     if (ret == -1) {
     754           0 :         fprintf(stderr, "multicast membership add failed on port %u - %s\n",
     755           0 :                 (unsigned)ntohs(sockaddr.sin_port),
     756           0 :                 strerror(errno));
     757           0 :         exit(1);
     758             :     }
     759             : 
     760             :     // now start the outgoing connection as an ordinary UDP connection
     761           2 :     _udp_start_client(address, port);
     762             : }
     763             : 
     764             : 
     765             : /*
     766             :   start a UART connection for the serial port
     767             :  */
     768           0 : void UARTDriver::_uart_start_connection(void)
     769             : {
     770           0 :     struct termios t {};
     771           0 :     if (!_connected) {
     772           0 :         _fd = ::open(_uart_path, O_RDWR | O_CLOEXEC);
     773           0 :         if (_fd == -1) {
     774             :             static uint32_t last_error_print_ms;
     775           0 :             if (AP_HAL::millis() - last_error_print_ms > 5000) {
     776           0 :                 ::printf("Failed to open (%s): %s\n", _uart_path, strerror(errno));
     777           0 :                 last_error_print_ms = AP_HAL::millis();
     778             :             }
     779           0 :             return;
     780             :         }
     781             :         // use much smaller buffer sizes on real UARTs
     782           0 :         _writebuffer.set_size(1024);
     783           0 :         _readbuffer.set_size(512);
     784           0 :         ::printf("Opened %s\n", _uart_path);
     785             :     }
     786             : 
     787           0 :     if (_fd == -1) {
     788           0 :         AP_HAL::panic("Unable to open UART %s", _uart_path);
     789             :     }
     790             : 
     791             :     // set non-blocking
     792           0 :     int flags = fcntl(_fd, F_GETFL, 0);
     793           0 :     flags = flags | O_NONBLOCK;
     794           0 :     fcntl(_fd, F_SETFL, flags);
     795             : 
     796             :     // disable LF -> CR/LF
     797           0 :     tcgetattr(_fd, &t);
     798           0 :     t.c_iflag &= ~(BRKINT | ICRNL | IMAXBEL | IXON | IXOFF);
     799           0 :     t.c_oflag &= ~(OPOST | ONLCR);
     800           0 :     t.c_lflag &= ~(ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE);
     801           0 :     t.c_cc[VMIN] = 0;
     802           0 :     if (_flow_control != FLOW_CONTROL_DISABLE) {
     803           0 :         t.c_cflag |= CRTSCTS;
     804             :     }
     805           0 :     tcsetattr(_fd, TCSANOW, &t);
     806             : 
     807             :     // set baudrate
     808           0 :     if (_uart_baudrate != 0) {
     809           0 :         set_speed(_uart_baudrate);
     810             :     }
     811             : 
     812           0 :     _connected = true;
     813           0 :     _use_send_recv = false;
     814             : }
     815             : 
     816             : /*
     817             :   see if a new connection is coming in
     818             :  */
     819   566824738 : void UARTDriver::_check_connection(void)
     820             : {
     821   566824738 :     if (_connected) {
     822             :         // we only want 1 connection at a time
     823   316420754 :         return;
     824             :     }
     825   250403984 :     if (_select_check(_listen_fd)) {
     826         242 :         _fd = accept(_listen_fd, nullptr, nullptr);
     827         242 :         if (_fd != -1) {
     828         242 :             int one = 1;
     829         242 :             if (!_is_unix_socket) {
     830         242 :                 setsockopt(_fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
     831             :             }
     832         242 :             setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
     833         242 :             if (fcntl(_fd, F_SETFD, FD_CLOEXEC) == -1) {
     834           0 :                 fprintf(stderr, "fcntl failed on setting FD_CLOEXEC - %s\n", strerror(errno));
     835           0 :                 close(_fd);
     836           0 :                 _fd = -1;
     837           0 :                 return;
     838             :             }
     839         242 :             _connected = true;
     840         242 :             fprintf(stdout, "New connection on SERIAL%u\n", _portNumber);
     841             :         }
     842             :     }
     843             : }
     844             : 
     845             : /*
     846             :   use select() to see if something is pending
     847             :  */
     848   504612816 : bool UARTDriver::_select_check(int fd)
     849             : {
     850   504612816 :     if (fd == -1) {
     851           6 :         return false;
     852             :     }
     853             : #if !APM_BUILD_TYPE(APM_BUILD_Replay)
     854             :     fd_set fds;
     855             :     struct timeval tv;
     856             : 
     857  8578415656 :     FD_ZERO(&fds);
     858   504612810 :     FD_SET(fd, &fds);
     859             : 
     860             :     // zero time means immediate return from select()
     861   504612810 :     tv.tv_sec = 0;
     862   504612810 :     tv.tv_usec = 0;
     863             : 
     864   504612810 :     if (select(fd+1, &fds, nullptr, nullptr, &tv) == 1) {
     865     2065918 :         return true;
     866             :     }
     867             : #endif
     868   502546912 :     return false;
     869             : }
     870             : 
     871             : 
     872          20 : bool UARTDriver::set_unbuffered_writes(bool on) {
     873          20 :     if (_fd == -1) {
     874          20 :         return false;
     875             :     }
     876           0 :     _unbuffered_writes = on;
     877             : 
     878             :     // this has no effect
     879           0 :     unsigned v = fcntl(_fd, F_GETFL, 0);
     880           0 :     v &= ~O_NONBLOCK;
     881             : #if defined(__APPLE__) && defined(__MACH__)
     882             :     fcntl(_fd, F_SETFL | F_NOCACHE, v | O_SYNC);
     883             : #elif defined(__OpenBSD__)
     884             :     fcntl(_fd, F_SETFL, v | O_SYNC);
     885             : #else
     886           0 :     fcntl(_fd, F_SETFL, v | O_DIRECT | O_SYNC);
     887             : #endif
     888           0 :     return _unbuffered_writes;
     889             : }
     890             : 
     891  3370231908 : void UARTDriver::_check_reconnect(void)
     892             : {
     893  3370231908 :     if (!_uart_path) {
     894  3370231908 :         return;
     895             :     }
     896           0 :     _uart_start_connection();
     897             : }
     898             : 
     899           0 : uint16_t UARTDriver::read_from_async_csv(uint8_t *buffer, uint16_t space)
     900             : {
     901           0 :     if (_fd == -1) {
     902           0 :         return 0;
     903             :     }
     904           0 :     const uint32_t micros = AP_HAL::micros();
     905           0 :     if (micros < 5000000) {
     906             :         // don't inject for the first several seconds
     907           0 :         return 0;
     908             :     }
     909             : 
     910             :     uint8_t i;
     911           0 :     for (i=0; i<space; i++) {
     912           0 :         if (logic_async_csv.loaded) {
     913           0 :             const uint32_t emit_timestamp_us = micros - logic_async_csv.first_emit_micros_us;
     914           0 :             const uint32_t data_timestamp_us = logic_async_csv.loaded_data.timestamp_us - logic_async_csv.first_timestamp_us;
     915           0 :             if (data_timestamp_us > emit_timestamp_us) {
     916           0 :                 return i;
     917             :             }
     918           0 :             buffer[i] = logic_async_csv.loaded_data.b;
     919           0 :             logic_async_csv.loaded = false;
     920             :         }
     921             : 
     922           0 :         while (!logic_async_csv.loaded) {
     923             :             uint8_t c;
     924           0 :             const ssize_t nread = ::read(_fd, &c, 1);
     925           0 :             if (nread == 0) {
     926             :                 // EOF
     927           0 :                 close(_fd);
     928           0 :                 _fd = -1;
     929           0 :                 return i;
     930             :             }
     931             : 
     932             :             // feed data into CSV Reader, handle new state:
     933           0 :             auto retcode = logic_async_csv.csvreader.feed(c);
     934           0 :             switch (retcode) {
     935           0 :             case AP_CSVReader::RetCode::OK:
     936           0 :                 continue;
     937           0 :             case AP_CSVReader::RetCode::ERROR:
     938           0 :                 AP_HAL::panic("Malformed CSV?");
     939           0 :             case AP_CSVReader::RetCode::TERM_DONE:
     940             :             case AP_CSVReader::RetCode::VECTOR_DONE:
     941           0 :                 switch (logic_async_csv.terms_seen) {
     942           0 :                 case 0:  // start_time
     943           0 :                     if (!logic_async_csv.done_first_line) {
     944           0 :                         break;
     945             :                     }
     946           0 :                     logic_async_csv.loaded_data.timestamp_us = atof((char*)logic_async_csv.term) * 1000000;  // seconds to microseconds
     947           0 :                     break;
     948           0 :                 case 1:  // data
     949           0 :                     if (!logic_async_csv.done_first_line) {
     950           0 :                         break;
     951             :                     }
     952           0 :                     if (!hex_twochars_to_uint8((const char*)&logic_async_csv.term[2], logic_async_csv.loaded_data.b)) {
     953             :                         // invalid character; panic as we do for other
     954             :                         // malformed-CSV cases rather than silently stopping
     955           0 :                         AP_HAL::panic("Malformed CSV?");
     956             :                     }
     957           0 :                     break;
     958           0 :                 case 2:  // error
     959             :                 case 3:  // framing error
     960           0 :                     break;
     961           0 :                 case 4:
     962           0 :                     AP_HAL::panic("Too many terms in CSV, want (name,type,start_time,duration,data");
     963             :                 }
     964           0 :                 logic_async_csv.terms_seen++;
     965           0 :                 if (retcode != AP_CSVReader::RetCode::VECTOR_DONE) {
     966           0 :                     break;
     967             :                 }
     968             : 
     969             :                 // we've handled the last term, now handle the vector:
     970           0 :                 if (logic_async_csv.terms_seen != 4) {
     971           0 :                     AP_HAL::panic("Incorrect number off terms in CSV, want (Time [s],Value,Parity Error,Framing Error)");
     972             :                 }
     973           0 :                 logic_async_csv.terms_seen = 0;
     974           0 :                 if (!logic_async_csv.done_first_line) {
     975             :                     // skip the headers
     976           0 :                     logic_async_csv.done_first_line = true;
     977           0 :                     break;
     978             :                 }
     979           0 :                 if (logic_async_csv.first_timestamp_us == 0) {
     980           0 :                     logic_async_csv.first_timestamp_us = logic_async_csv.loaded_data.timestamp_us;
     981           0 :                     logic_async_csv.first_emit_micros_us = micros;
     982             :                 }
     983           0 :                 logic_async_csv.loaded = true;
     984             :             }
     985             :         }
     986             :     }
     987           0 :     return i;
     988             : }
     989             : 
     990  2433580250 : void UARTDriver::handle_writing_from_writebuffer_to_device()
     991             : {
     992  2433580250 :     WITH_SEMAPHORE(write_mtx);
     993  2433580250 :     if (!_connected) {
     994  1685115954 :         _check_reconnect();
     995  1685115954 :         return;
     996             :     }
     997   748464296 :     ssize_t nwritten = 0;
     998   748464296 :     uint32_t max_bytes = 10000;
     999             : #if !defined(HAL_BUILD_AP_PERIPH)
    1000   748464296 :     SITL::SIM *_sitl = AP::sitl();
    1001   748464296 :     if (_sitl && _sitl->telem_baudlimit_enable) {
    1002             :         // limit byte rate to configured baudrate
    1003             :         // Byte rate is bit rate divided by 10. 8 bits of data + start/stop bits
    1004           0 :         max_bytes = baud_limits.write.max_bytes(float(_uart_baudrate) * 0.1);
    1005           0 :         if (max_bytes == 0) {
    1006           0 :             return;
    1007             :         }
    1008             :     }
    1009             : #endif
    1010   748464296 :     if (_packetise) {
    1011       78802 :         uint16_t n = _writebuffer.available();
    1012       78802 :         n = MIN(n, max_bytes);
    1013             : #if HAL_GCS_ENABLED
    1014       78802 :         if (n > 0) {
    1015         302 :             n = mavlink_packetise(_writebuffer, n);
    1016             :         }
    1017             : #endif
    1018       78802 :         if (n > 0) {
    1019             :             // keep as a single UDP packet
    1020         302 :             uint8_t tmpbuf[n];
    1021         302 :             _writebuffer.peekbytes(tmpbuf, n);
    1022         302 :             ssize_t ret = send(_fd, tmpbuf, n, MSG_DONTWAIT);
    1023         302 :             if (ret > 0) {
    1024         302 :                 _writebuffer.advance(ret);
    1025         302 :                 _tx_stats_bytes += ret;
    1026             :             }
    1027         302 :         }
    1028             :     } else {
    1029             :         uint32_t navail;
    1030   748385494 :         const uint8_t *readptr = _writebuffer.readptr(navail);
    1031   748385494 :         if (readptr && navail > 0) {
    1032     6097160 :             navail = MIN(navail, max_bytes);
    1033     6097160 :             if (_sim_serial_device != nullptr) {
    1034     3961457 :                 nwritten = _sim_serial_device->write_to_device((const char*)readptr, navail);
    1035     2135703 :             } else if (!_use_send_recv) {
    1036             :                 // For ENABLE/AUTO modes on a real UART, check CTS via ioctl
    1037             :                 // before writing. This provides reliable flow control on
    1038             :                 // platforms where kernel CRTSCTS may not work (e.g. macOS
    1039             :                 // with USB-serial adapters).
    1040           0 :                 bool cts_blocked = false;
    1041           0 :                 if (_flow_control != FLOW_CONTROL_DISABLE) {
    1042           0 :                     int modem_bits = 0;
    1043           0 :                     if (ioctl(_fd, TIOCMGET, &modem_bits) == 0 &&
    1044           0 :                         !(modem_bits & TIOCM_CTS)) {
    1045             :                         // CTS deasserted - remote end asked us to stop sending
    1046           0 :                         cts_blocked = true;
    1047             :                     }
    1048             :                 }
    1049           0 :                 if (!cts_blocked) {
    1050           0 :                     nwritten = ::write(_fd, readptr, navail);
    1051           0 :                     if (nwritten == -1 && errno != EAGAIN && _uart_path) {
    1052           0 :                         close(_fd);
    1053           0 :                         _fd = -1;
    1054           0 :                         _connected = false;
    1055             :                     }
    1056             :                 }
    1057             :                 // AUTO flow control detection: mirror ChibiOS 500ms timeout logic
    1058           0 :                 if (_flow_control == FLOW_CONTROL_AUTO && _uart_path) {
    1059           0 :                     if (_auto_flow_start_ms == 0) {
    1060           0 :                         _auto_flow_start_ms = AP_HAL::millis();
    1061             :                     }
    1062           0 :                     if (nwritten > 0 && !_auto_flow_detected) {
    1063             :                         // first successful write - CTS active
    1064           0 :                         ::printf("SERIAL%u: hardware flow control enabled\n", _portNumber);
    1065           0 :                         _auto_flow_detected = true;
    1066           0 :                     } else if (!_auto_flow_detected && AP_HAL::millis() - _auto_flow_start_ms > 500U) {
    1067             :                         // CTS never seen since AUTO was set - actually disable
    1068           0 :                         ::printf("SERIAL%u: hardware flow control not detected, disabled\n", _portNumber);
    1069           0 :                         set_flow_control(FLOW_CONTROL_DISABLE);
    1070             :                     }
    1071             :                 }
    1072             :             } else {
    1073     2135703 :                 nwritten = send(_fd, readptr, navail, MSG_DONTWAIT);
    1074             :             }
    1075     6097160 :             if (nwritten > 0) {
    1076     4258209 :                 _writebuffer.advance(nwritten);
    1077     4258209 :                 _tx_stats_bytes += nwritten;
    1078             :             }
    1079             :         }
    1080             :     }
    1081  2433580250 : }
    1082             : 
    1083  2434445787 : void UARTDriver::handle_reading_from_device_to_readbuffer()
    1084             : {
    1085  2434445787 :     if (!_connected) {
    1086  1685115954 :         _check_reconnect();
    1087  1688536700 :         return;
    1088             :     }
    1089             : 
    1090   749329833 :     uint32_t space = _readbuffer.space();
    1091   749329833 :     if (space == 0) {
    1092     3420188 :         return;
    1093             :     }
    1094             : 
    1095   745909645 :     uint32_t max_bytes = 10000;
    1096             : #if !defined(HAL_BUILD_AP_PERIPH)
    1097   745909645 :     SITL::SIM *_sitl = AP::sitl();
    1098   745909645 :     if (_sitl && _sitl->telem_baudlimit_enable) {
    1099             :         // limit byte rate to configured baudrate
    1100             :         // Byte rate is bit rate divided by 10. 8 bits of data + start/stop bits
    1101           0 :         max_bytes = baud_limits.read.max_bytes(float(_uart_baudrate) * 0.1);
    1102           0 :         if (max_bytes == 0) {
    1103           0 :             return;
    1104             :         }
    1105             :     }
    1106             : #endif
    1107             : 
    1108   745909645 :     space = MIN(space, max_bytes);
    1109             : 
    1110   745909645 :     char buf[space];
    1111   745909645 :     ssize_t nread = 0;
    1112   745909645 :     if (_mc_fd >= 0) {
    1113       78802 :         if (_select_check(_mc_fd)) {
    1114             :             struct sockaddr_in from;
    1115         303 :             socklen_t fromlen = sizeof(from);
    1116         303 :             nread = recvfrom(_mc_fd, buf, space, MSG_DONTWAIT, (struct sockaddr *)&from, &fromlen);
    1117         303 :             uint16_t port = ntohs(from.sin_port);
    1118         303 :             if (_mc_myport == 0) {
    1119             :                 // get our own address, so we can recognise packets from ourself
    1120             :                 struct sockaddr_in myaddr;
    1121             :                 socklen_t myaddrlen;
    1122         293 :                 if (getsockname(_fd, (struct sockaddr *)&myaddr, &myaddrlen) == 0) {
    1123           1 :                     _mc_myport = ntohs(myaddr.sin_port);
    1124             :                 }
    1125             :             }
    1126         303 :             if (_mc_myport == port) {
    1127             :                 // assume this is a packet from ourselves. This is not
    1128             :                 // entirely accurate, as it could be a packet from
    1129             :                 // another machine that has assigned the same port,
    1130             :                 // unfortunately we don't have a better way to detect
    1131             :                 // packets from ourselves
    1132          10 :                 nread = 0;
    1133             :             }
    1134             :         }
    1135   745830843 :     } else if (_sim_serial_device != nullptr) {
    1136   491700826 :         nread = _sim_serial_device->read_from_device(buf, space);
    1137   254130017 :     } else if (logic_async_csv.active) {
    1138           0 :         nread = read_from_async_csv((uint8_t*)buf, space);
    1139   254130017 :     } else if (!_use_send_recv) {
    1140           0 :         if (!_select_check(_fd)) {
    1141           0 :             return;
    1142             :         }
    1143           0 :         int fd = _console?0:_fd;
    1144           0 :         nread = ::read(fd, buf, space);
    1145           0 :         if (nread == -1 && errno != EAGAIN && _uart_path) {
    1146           0 :             close(_fd);
    1147           0 :             _fd = -1;
    1148           0 :             _connected = false;
    1149             :         }
    1150   254130017 :     } else if (_select_check(_fd)) {
    1151     2065373 :         nread = recv(_fd, buf, space, MSG_DONTWAIT);
    1152     2065372 :         if (nread <= 0 && !_is_udp) {
    1153             :             // the socket has reached EOF
    1154         558 :             close(_fd);
    1155         558 :             _fd = -1;
    1156         558 :             _connected = false;
    1157         558 :             fprintf(stdout, "Closed connection on SERIAL%u\n", _portNumber);
    1158         558 :             fflush(stdout);
    1159             : #if defined(__CYGWIN__) || defined(__CYGWIN64__) || defined(CYGWIN_BUILD)
    1160             :             if (_portNumber == 0) {
    1161             :                 // exit on cygwin port 0 is almost certainly closing the
    1162             :                 // connection in MissionPlanner SITL. We want to exit or
    1163             :                 // we leave a stray process which confuses restart
    1164             :                 exit(0);
    1165             :             }
    1166             : #endif
    1167         558 :             return;
    1168             :         }
    1169             :     }
    1170   745909086 :     if (nread > 0) {
    1171     3439489 :         _readbuffer.write((uint8_t *)buf, nread);
    1172     3439489 :         _receive_timestamp = AP_HAL::micros64();
    1173             :     }
    1174  2434445787 : }
    1175             : 
    1176  2433580250 : void UARTDriver::_timer_tick(void)
    1177             : {
    1178  2433580250 :     handle_writing_from_writebuffer_to_device();
    1179  2433580250 :     handle_reading_from_device_to_readbuffer();
    1180  2433580249 : }
    1181             : 
    1182             : 
    1183             : /*
    1184             :   return timestamp estimate in microseconds for when the start of
    1185             :   a nbytes packet arrived on the uart. This should be treated as a
    1186             :   time constraint, not an exact time. It is guaranteed that the
    1187             :   packet did not start being received after this time, but it
    1188             :   could have been in a system buffer before the returned time.
    1189             :   
    1190             :   This takes account of the baudrate of the link. For transports
    1191             :   that have no baudrate (such as USB) the time estimate may be
    1192             :   less accurate.
    1193             :   
    1194             :   A return value of zero means the HAL does not support this API
    1195             : */
    1196      726391 : uint64_t UARTDriver::receive_time_constraint_us(uint16_t nbytes)
    1197             : {
    1198      726391 :     uint64_t last_receive_us = _receive_timestamp;
    1199      726391 :     if (_uart_baudrate > 0) {
    1200             :         // assume 10 bits per byte. 
    1201      726391 :         uint32_t transport_time_us = (1000000UL * 10UL / _uart_baudrate) * (nbytes+available());
    1202      726391 :         last_receive_us -= transport_time_us;
    1203             :     }
    1204      726391 :     return last_receive_us;
    1205             : }
    1206             : 
    1207    87644331 : ssize_t UARTDriver::get_system_outqueue_length() const
    1208             : {
    1209    87644331 :     if (!_connected) {
    1210         980 :         return 0;
    1211             :     }
    1212             : 
    1213             : #if defined(__CYGWIN__) || defined(__CYGWIN64__) || defined(CYGWIN_BUILD)
    1214             :     return 0;
    1215             : #elif defined(__APPLE__) && defined(__MACH__)
    1216             :     return 0;
    1217             : #else
    1218             :     int size;
    1219    87643351 :     if (ioctl(_fd, TIOCOUTQ, &size) == -1) {
    1220             :         // ::fprintf(stderr, "ioctl TIOCOUTQ failed: %m\n");
    1221           0 :         return 0;
    1222             :     }
    1223    87643351 :     return size;
    1224             : #endif
    1225             : }
    1226             : 
    1227    87641061 : ssize_t UARTDriver::get_system_outqueue_limit() const
    1228             : {
    1229             :     // AF_UNIX TIOCOUTQ includes per-write kernel accounting and data already
    1230             :     // delivered to the peer. Allow enough room for several full UART writes
    1231             :     // before applying the anti-lag throttle, while retaining the TCP limit.
    1232    87641061 :     return _is_unix_socket ? 65536 : 1024;
    1233             : }
    1234             : 
    1235        7012 : uint32_t UARTDriver::bw_in_bytes_per_second() const
    1236             : {
    1237             :     // if connected, assume at least a 10/100Mbps connection if not limited
    1238        7012 :     bool baud_limit = false;
    1239             : #if !defined(HAL_BUILD_AP_PERIPH)
    1240        7012 :     SITL::SIM *_sitl = AP::sitl();
    1241        7012 :     baud_limit = (_sitl != nullptr) && _sitl->telem_baudlimit_enable;
    1242             : #endif
    1243        7012 :     const uint32_t bitrate = (_connected && !baud_limit) ? 10E6 : _uart_baudrate;
    1244        7012 :     return bitrate/10; // convert bits to bytes minus overhead
    1245             : };
    1246             : 
    1247             : #if HAL_UART_STATS_ENABLED
    1248             : // request information on uart I/O for @SYS/uarts.txt for this uart
    1249       11484 : void UARTDriver::uart_info(ExpandingString &str, StatsTracker &stats, const uint32_t dt_ms)
    1250             : {
    1251       11484 :     const uint32_t tx_bytes = stats.tx.update(_tx_stats_bytes);
    1252       11484 :     const uint32_t rx_bytes = stats.rx.update(_rx_stats_bytes);
    1253             : 
    1254       11484 :     str.printf("TX=%8u RX=%8u TXBD=%6u RXBD=%6u %s (%s)\n",
    1255             :                 unsigned(tx_bytes),
    1256             :                 unsigned(rx_bytes),
    1257       11484 :                 unsigned((tx_bytes * 10000) / dt_ms),
    1258       11484 :                 unsigned((rx_bytes * 10000) / dt_ms),
    1259       11484 :                 _connected ? "connected    " : "not connected",
    1260       11484 :                 _sitlState->_serial_path[_portNumber]);
    1261       11484 : }
    1262             : #endif
    1263             : 
    1264             : #endif // CONFIG_HAL_BOARD

Generated by: LCOV version 1.14