LCOV - code coverage report
Current view: top level - wirestead/transport/serial - boost_serial_port.hpp (source / functions) Coverage Total Hit
Test: Wirestead Coverage Report Lines: 45.9 % 61 28
Test Date: 2026-08-30 10:35:09 Functions: 75.0 % 16 12
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 6.2 % 16 1

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * Copyright 2025 Jinwoo Sung
       3                 :             :  *
       4                 :             :  * Licensed under the Apache License, Version 2.0 (the "License");
       5                 :             :  * you may not use this file except in compliance with the License.
       6                 :             :  * You may obtain a copy of the License at
       7                 :             :  *
       8                 :             :  *     http://www.apache.org/licenses/LICENSE-2.0
       9                 :             :  *
      10                 :             :  * Unless required by applicable law or agreed to in writing, software
      11                 :             :  * distributed under the License is distributed on an "AS IS" BASIS,
      12                 :             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13                 :             :  * See the License for the specific language governing permissions and
      14                 :             :  * limitations under the License.
      15                 :             :  */
      16                 :             : 
      17                 :             : #pragma once
      18                 :             : 
      19                 :             : #include "wirestead/base/visibility.hpp"
      20                 :             : #include "wirestead/interface/iserial_port.hpp"
      21                 :             : 
      22                 :             : #ifdef __linux__
      23                 :             : #include <linux/serial.h>
      24                 :             : #include <sys/ioctl.h>
      25                 :             : #endif
      26                 :             : #if defined(__APPLE__)
      27                 :             : #include <sys/ioctl.h>
      28                 :             : #endif
      29                 :             : 
      30                 :             : #include <optional>
      31                 :             : 
      32                 :             : namespace wirestead {
      33                 :             : namespace transport {
      34                 :             : 
      35                 :             : namespace net = boost::asio;
      36                 :             : 
      37                 :             : class WIRESTEAD_API BoostSerialPort : public interface::SerialPortInterface {
      38                 :             :  public:
      39                 :          15 :   explicit BoostSerialPort(net::io_context& ioc) : port_(ioc) {}
      40                 :             : 
      41                 :          17 :   void open(const std::string& device, boost::system::error_code& ec) override { port_.open(device, ec); }
      42                 :          41 :   bool is_open() const override { return port_.is_open(); }
      43                 :           1 :   void close(boost::system::error_code& ec) override { port_.close(ec); }
      44                 :             : 
      45                 :           1 :   void set_option(const net::serial_port_base::baud_rate& option, boost::system::error_code& ec) override {
      46                 :           1 :     port_.set_option(option, ec);
      47                 :           1 :   }
      48                 :           1 :   void set_option(const net::serial_port_base::character_size& option, boost::system::error_code& ec) override {
      49                 :           1 :     port_.set_option(option, ec);
      50                 :           1 :   }
      51                 :           1 :   void set_option(const net::serial_port_base::stop_bits& option, boost::system::error_code& ec) override {
      52                 :           1 :     port_.set_option(option, ec);
      53                 :           1 :   }
      54                 :           1 :   void set_option(const net::serial_port_base::parity& option, boost::system::error_code& ec) override {
      55                 :           1 :     port_.set_option(option, ec);
      56                 :           1 :   }
      57                 :           1 :   void set_option(const net::serial_port_base::flow_control& option, boost::system::error_code& ec) override {
      58                 :           1 :     port_.set_option(option, ec);
      59                 :           1 :   }
      60                 :             : 
      61                 :             :   // Linux only. TIOCGSERIAL fails with ENOTTY on drivers that have no latency
      62                 :             :   // timer to clear, which is the common case for native UARTs and CDC-ACM, so
      63                 :             :   // the caller treats false as "nothing to do" rather than as a failure.
      64                 :           1 :   bool set_low_latency() override {
      65                 :             : #ifdef __linux__
      66                 :             :     struct serial_struct info;
      67                 :           1 :     const int fd = port_.native_handle();
      68         [ +  - ]:           1 :     if (::ioctl(fd, TIOCGSERIAL, &info) != 0) return false;
      69         [ #  # ]:           0 :     if (info.flags & ASYNC_LOW_LATENCY) return true;
      70                 :           0 :     info.flags |= ASYNC_LOW_LATENCY;
      71                 :           0 :     return ::ioctl(fd, TIOCSSERIAL, &info) == 0;
      72                 :             : #else
      73                 :             :     return false;
      74                 :             : #endif
      75                 :             :   }
      76                 :             : 
      77                 :             :   // Linux only. A driver with no RS-485 support answers ENOTTY, which the
      78                 :             :   // caller treats as "this adapter does the switching itself".
      79                 :           0 :   bool set_rs485(bool rts_on_send, bool rx_during_tx, unsigned delay_before_ms, unsigned delay_after_ms) override {
      80                 :             : #ifdef __linux__
      81                 :           0 :     struct serial_rs485 rs485 {};
      82                 :           0 :     const int fd = port_.native_handle();
      83                 :             :     // Read first: some drivers carry board-specific bits in flags that a
      84                 :             :     // blind write would clear.
      85         [ #  # ]:           0 :     if (::ioctl(fd, TIOCGRS485, &rs485) != 0) return false;
      86                 :           0 :     rs485.flags |= SER_RS485_ENABLED;
      87         [ #  # ]:           0 :     if (rts_on_send) {
      88                 :           0 :       rs485.flags |= SER_RS485_RTS_ON_SEND;
      89                 :           0 :       rs485.flags &= ~static_cast<decltype(rs485.flags)>(SER_RS485_RTS_AFTER_SEND);
      90                 :             :     } else {
      91                 :           0 :       rs485.flags &= ~static_cast<decltype(rs485.flags)>(SER_RS485_RTS_ON_SEND);
      92                 :           0 :       rs485.flags |= SER_RS485_RTS_AFTER_SEND;
      93                 :             :     }
      94         [ #  # ]:           0 :     if (rx_during_tx) {
      95                 :           0 :       rs485.flags |= SER_RS485_RX_DURING_TX;
      96                 :             :     } else {
      97                 :           0 :       rs485.flags &= ~static_cast<decltype(rs485.flags)>(SER_RS485_RX_DURING_TX);
      98                 :             :     }
      99                 :           0 :     rs485.delay_rts_before_send = delay_before_ms;
     100                 :           0 :     rs485.delay_rts_after_send = delay_after_ms;
     101                 :           0 :     return ::ioctl(fd, TIOCSRS485, &rs485) == 0;
     102                 :             : #else
     103                 :             :     (void)rts_on_send;
     104                 :             :     (void)rx_during_tx;
     105                 :             :     (void)delay_before_ms;
     106                 :             :     (void)delay_after_ms;
     107                 :             :     return false;
     108                 :             : #endif
     109                 :             :   }
     110                 :             : 
     111                 :           0 :   bool set_modem_lines(std::optional<bool> dtr, std::optional<bool> rts) override {
     112                 :             : #if defined(__linux__) || defined(__APPLE__)
     113                 :           0 :     const int fd = port_.native_handle();
     114                 :           0 :     bool ok = true;
     115                 :           0 :     auto apply = [&](std::optional<bool> want, int bit) {
     116         [ #  # ]:           0 :       if (!want) return;
     117                 :           0 :       int lines = bit;
     118   [ #  #  #  # ]:           0 :       if (::ioctl(fd, *want ? TIOCMBIS : TIOCMBIC, &lines) != 0) ok = false;
     119                 :           0 :     };
     120                 :           0 :     apply(dtr, TIOCM_DTR);
     121                 :           0 :     apply(rts, TIOCM_RTS);
     122                 :           0 :     return ok;
     123                 :             : #else
     124                 :             :     (void)dtr;
     125                 :             :     (void)rts;
     126                 :             :     return false;
     127                 :             : #endif
     128                 :             :   }
     129                 :             : 
     130                 :           1 :   void async_read_some(const net::mutable_buffer& buffer,
     131                 :             :                        std::function<void(const boost::system::error_code&, std::size_t)> handler) override {
     132                 :           1 :     port_.async_read_some(buffer, std::move(handler));
     133                 :           1 :   }
     134                 :             : 
     135                 :           0 :   void async_write(const net::const_buffer& buffer,
     136                 :             :                    std::function<void(const boost::system::error_code&, std::size_t)> handler) override {
     137                 :           0 :     net::async_write(port_, buffer, std::move(handler));
     138                 :           0 :   }
     139                 :             : 
     140                 :             :   // Real scatter-gather write: hands the sequence to asio, which issues one
     141                 :             :   // sendmsg/writev instead of one send per queued buffer.
     142                 :           1 :   void async_write(const std::vector<net::const_buffer>& buffers,
     143                 :             :                    std::function<void(const boost::system::error_code&, std::size_t)> handler) override {
     144                 :           1 :     net::async_write(port_, buffers, std::move(handler));
     145                 :           1 :   }
     146                 :             : 
     147                 :             :  private:
     148                 :             :   net::serial_port port_;
     149                 :             : };
     150                 :             : 
     151                 :             : }  // namespace transport
     152                 :             : }  // namespace wirestead
        

Generated by: LCOV version 2.0-1