• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mavlink / MAVSDK / 7965506471

19 Feb 2024 09:51PM UTC coverage: 36.22% (+0.008%) from 36.212%
7965506471

push

github

web-flow
Merge pull request #2223 from mavlink/pr-absl-fix

Fix illegal instruction on RPi 4

10035 of 27706 relevant lines covered (36.22%)

127.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/mavsdk/core/serial_connection.cpp
1
#include "serial_connection.h"
2
#include "log.h"
3

4
#if defined(APPLE) || defined(LINUX)
5
#include <unistd.h>
6
#include <fcntl.h>
7
#include <termios.h>
8
#include <poll.h>
9

10
#include <utility>
11
#endif
12

13
namespace mavsdk {
14

15
#ifndef WINDOWS
16
#define GET_ERROR() strerror(errno)
17
#else
18
#define GET_ERROR() GetLastErrorStdStr()
19
// Taken from:
20
// https://coolcowstudio.wordpress.com/2012/10/19/getlasterror-as-stdstring/
21
std::string GetLastErrorStdStr()
22
{
23
    DWORD error = GetLastError();
24
    if (error) {
25
        LPVOID lpMsgBuf;
26
        DWORD bufLen = FormatMessage(
27
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
28
                FORMAT_MESSAGE_IGNORE_INSERTS,
29
            NULL,
30
            error,
31
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
32
            (LPTSTR)&lpMsgBuf,
33
            0,
34
            NULL);
35
        if (bufLen) {
36
            LPCSTR lpMsgStr = (LPCSTR)lpMsgBuf;
37
            std::string result(lpMsgStr, lpMsgStr + bufLen);
38

39
            LocalFree(lpMsgBuf);
40

41
            return result;
42
        }
43
    }
44
    return std::string();
45
}
46
#endif
47

48
SerialConnection::SerialConnection(
×
49
    Connection::ReceiverCallback receiver_callback,
50
    std::string path,
51
    int baudrate,
52
    bool flow_control,
53
    ForwardingOption forwarding_option) :
×
54
    Connection(std::move(receiver_callback), forwarding_option),
×
55
    _serial_node(std::move(path)),
×
56
    _baudrate(baudrate),
57
    _flow_control(flow_control)
×
58
{}
×
59

60
SerialConnection::~SerialConnection()
×
61
{
62
    // If no one explicitly called stop before, we should at least do it.
63
    stop();
×
64
}
×
65

66
ConnectionResult SerialConnection::start()
×
67
{
68
    if (!start_mavlink_receiver()) {
×
69
        return ConnectionResult::ConnectionsExhausted;
×
70
    }
71

72
    ConnectionResult ret = setup_port();
×
73
    if (ret != ConnectionResult::Success) {
×
74
        return ret;
×
75
    }
76

77
    start_recv_thread();
×
78

79
    return ConnectionResult::Success;
×
80
}
81

82
ConnectionResult SerialConnection::setup_port()
×
83
{
84
#if defined(LINUX) || defined(APPLE)
85
    // open() hangs on macOS or Linux devices(e.g. pocket beagle) unless you give it O_NONBLOCK
86
    _fd = open(_serial_node.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
×
87
    if (_fd == -1) {
×
88
        LogErr() << "open failed: " << GET_ERROR();
×
89
        return ConnectionResult::ConnectionError;
×
90
    }
91
    // We need to clear the O_NONBLOCK again because we can block while reading
92
    // as we do it in a separate thread.
93
    if (fcntl(_fd, F_SETFL, 0) == -1) {
×
94
        LogErr() << "fcntl failed: " << GET_ERROR();
×
95
        return ConnectionResult::ConnectionError;
×
96
    }
97
#elif defined(WINDOWS)
98
    // Required for COM ports > 9.
99
    const auto full_serial_path = "\\\\.\\" + _serial_node;
100

101
    _handle = CreateFile(
102
        full_serial_path.c_str(),
103
        GENERIC_READ | GENERIC_WRITE,
104
        0, // exclusive-access
105
        NULL, //  default security attributes
106
        OPEN_EXISTING,
107
        0, //  not overlapped I/O
108
        NULL); //  hTemplate must be NULL for comm devices
109

110
    if (_handle == INVALID_HANDLE_VALUE) {
111
        LogErr() << "CreateFile failed with: " << GET_ERROR();
112
        return ConnectionResult::ConnectionError;
113
    }
114
#endif
115

116
#if defined(LINUX) || defined(APPLE)
117
    struct termios tc;
×
118
    bzero(&tc, sizeof(tc));
×
119

120
    if (tcgetattr(_fd, &tc) != 0) {
×
121
        LogErr() << "tcgetattr failed: " << GET_ERROR();
×
122
        close(_fd);
×
123
        return ConnectionResult::ConnectionError;
×
124
    }
125
#endif
126

127
#if defined(LINUX) || defined(APPLE)
128
    tc.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON);
×
129
    tc.c_oflag &= ~(OCRNL | ONLCR | ONLRET | ONOCR | OFILL | OPOST);
×
130
    tc.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG | TOSTOP);
×
131
    tc.c_cflag &= ~(CSIZE | PARENB | CRTSCTS);
×
132
    tc.c_cflag |= CS8;
×
133

134
    tc.c_cc[VMIN] = 0; // We are ok with 0 bytes.
×
135
    tc.c_cc[VTIME] = 10; // Timeout after 1 second.
×
136

137
    if (_flow_control) {
×
138
        tc.c_cflag |= CRTSCTS;
×
139
    }
140
#endif
141

142
#if defined(LINUX) || defined(APPLE)
143
    tc.c_cflag |= CLOCAL; // Without this a write() blocks indefinitely.
×
144

145
#if defined(LINUX)
146
    const int baudrate_or_define = define_from_baudrate(_baudrate);
×
147
#elif defined(APPLE)
148
    const int baudrate_or_define = _baudrate;
149
#endif
150

151
    if (baudrate_or_define == -1) {
×
152
        return ConnectionResult::BaudrateUnknown;
×
153
    }
154

155
    if (cfsetispeed(&tc, baudrate_or_define) != 0) {
×
156
        LogErr() << "cfsetispeed failed: " << GET_ERROR();
×
157
        close(_fd);
×
158
        return ConnectionResult::ConnectionError;
×
159
    }
160

161
    if (cfsetospeed(&tc, baudrate_or_define) != 0) {
×
162
        LogErr() << "cfsetospeed failed: " << GET_ERROR();
×
163
        close(_fd);
×
164
        return ConnectionResult::ConnectionError;
×
165
    }
166

167
    if (tcsetattr(_fd, TCSANOW, &tc) != 0) {
×
168
        LogErr() << "tcsetattr failed: " << GET_ERROR();
×
169
        close(_fd);
×
170
        return ConnectionResult::ConnectionError;
×
171
    }
172
#endif
173

174
#if defined(WINDOWS)
175
    DCB dcb;
176
    SecureZeroMemory(&dcb, sizeof(DCB));
177
    dcb.DCBlength = sizeof(DCB);
178

179
    if (!GetCommState(_handle, &dcb)) {
180
        LogErr() << "GetCommState failed with error: " << GET_ERROR();
181
        return ConnectionResult::ConnectionError;
182
    }
183

184
    dcb.BaudRate = _baudrate;
185
    dcb.ByteSize = 8;
186
    dcb.Parity = NOPARITY;
187
    dcb.StopBits = ONESTOPBIT;
188
    if (_flow_control) {
189
        dcb.fOutxCtsFlow = TRUE;
190
        dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
191
        dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
192
    } else {
193
        dcb.fDtrControl = DTR_CONTROL_DISABLE;
194
        dcb.fRtsControl = RTS_CONTROL_DISABLE;
195
    }
196
    dcb.fOutX = FALSE;
197
    dcb.fInX = FALSE;
198
    dcb.fBinary = TRUE;
199
    dcb.fNull = FALSE;
200
    dcb.fDsrSensitivity = FALSE;
201

202
    if (!SetCommState(_handle, &dcb)) {
203
        LogErr() << "SetCommState failed with error: " << GET_ERROR();
204
        return ConnectionResult::ConnectionError;
205
    }
206

207
    COMMTIMEOUTS timeout = {0, 0, 0, 0, 0};
208
    timeout.ReadIntervalTimeout = 1;
209
    timeout.ReadTotalTimeoutConstant = 1;
210
    timeout.ReadTotalTimeoutMultiplier = 1;
211
    timeout.WriteTotalTimeoutConstant = 1;
212
    timeout.WriteTotalTimeoutMultiplier = 1;
213
    SetCommTimeouts(_handle, &timeout);
214

215
    if (!SetCommTimeouts(_handle, &timeout)) {
216
        LogErr() << "SetCommTimeouts failed with error: " << GET_ERROR();
217
        return ConnectionResult::ConnectionError;
218
    }
219

220
#endif
221

222
    return ConnectionResult::Success;
×
223
}
224

225
void SerialConnection::start_recv_thread()
×
226
{
227
    _recv_thread = std::make_unique<std::thread>(&SerialConnection::receive, this);
×
228
}
×
229

230
ConnectionResult SerialConnection::stop()
×
231
{
232
    _should_exit = true;
×
233

234
    if (_recv_thread) {
×
235
        _recv_thread->join();
×
236
        _recv_thread.reset();
×
237
    }
238

239
#if defined(LINUX) || defined(APPLE)
240
    close(_fd);
×
241
#elif defined(WINDOWS)
242
    CloseHandle(_handle);
243
#endif
244

245
    // We need to stop this after stopping the receive thread, otherwise
246
    // it can happen that we interfere with the parsing of a message.
247
    stop_mavlink_receiver();
×
248

249
    return ConnectionResult::Success;
×
250
}
251

252
bool SerialConnection::send_message(const mavlink_message_t& message)
×
253
{
254
    if (_serial_node.empty()) {
×
255
        LogErr() << "Dev Path unknown";
×
256
        return false;
×
257
    }
258

259
    if (_baudrate == 0) {
×
260
        LogErr() << "Baudrate unknown";
×
261
        return false;
×
262
    }
263

264
    uint8_t buffer[MAVLINK_MAX_PACKET_LEN];
×
265
    uint16_t buffer_len = mavlink_msg_to_send_buffer(buffer, &message);
×
266

267
    int send_len;
268
#if defined(LINUX) || defined(APPLE)
269
    send_len = static_cast<int>(write(_fd, buffer, buffer_len));
×
270
#else
271
    if (!WriteFile(_handle, buffer, buffer_len, LPDWORD(&send_len), NULL)) {
272
        LogErr() << "WriteFile failure: " << GET_ERROR();
273
        return false;
274
    }
275
#endif
276

277
    if (send_len != buffer_len) {
×
278
        LogErr() << "write failure: " << GET_ERROR();
×
279
        return false;
×
280
    }
281

282
    return true;
×
283
}
284

285
void SerialConnection::receive()
×
286
{
287
    // Enough for MTU 1500 bytes.
288
    char buffer[2048];
×
289

290
#if defined(LINUX) || defined(APPLE)
291
    struct pollfd fds[1];
×
292
    fds[0].fd = _fd;
×
293
    fds[0].events = POLLIN;
×
294
#endif
295

296
    while (!_should_exit) {
×
297
        int recv_len;
298
#if defined(LINUX) || defined(APPLE)
299
        int pollrc = poll(fds, 1, 1000);
×
300
        if (pollrc == 0 || !(fds[0].revents & POLLIN)) {
×
301
            continue;
×
302
        } else if (pollrc == -1) {
×
303
            LogErr() << "read poll failure: " << GET_ERROR();
×
304
        }
305
        // We enter here if (fds[0].revents & POLLIN) == true
306
        recv_len = static_cast<int>(read(_fd, buffer, sizeof(buffer)));
×
307
        if (recv_len < -1) {
×
308
            LogErr() << "read failure: " << GET_ERROR();
×
309
        }
310
#else
311
        if (!ReadFile(_handle, buffer, sizeof(buffer), LPDWORD(&recv_len), NULL)) {
312
            LogErr() << "ReadFile failure: " << GET_ERROR();
313
            continue;
314
        }
315
#endif
316
        if (recv_len > static_cast<int>(sizeof(buffer)) || recv_len == 0) {
×
317
            continue;
×
318
        }
319
        _mavlink_receiver->set_new_datagram(buffer, recv_len);
×
320
        // Parse all mavlink messages in one data packet. Once exhausted, we'll exit while.
321
        while (_mavlink_receiver->parse_message()) {
×
322
            receive_message(_mavlink_receiver->get_last_message(), this);
×
323
        }
324
    }
325
}
×
326

327
#if defined(LINUX)
328
int SerialConnection::define_from_baudrate(int baudrate)
×
329
{
330
    switch (baudrate) {
×
331
        case 9600:
×
332
            return B9600;
×
333
        case 19200:
×
334
            return B19200;
×
335
        case 38400:
×
336
            return B38400;
×
337
        case 57600:
×
338
            return B57600;
×
339
        case 115200:
×
340
            return B115200;
×
341
        case 230400:
×
342
            return B230400;
×
343
        case 460800:
×
344
            return B460800;
×
345
        case 500000:
×
346
            return B500000;
×
347
        case 576000:
×
348
            return B576000;
×
349
        case 921600:
×
350
            return B921600;
×
351
        case 1000000:
×
352
            return B1000000;
×
353
        case 1152000:
×
354
            return B1152000;
×
355
        case 1500000:
×
356
            return B1500000;
×
357
        case 2000000:
×
358
            return B2000000;
×
359
        case 2500000:
×
360
            return B2500000;
×
361
        case 3000000:
×
362
            return B3000000;
×
363
        case 3500000:
×
364
            return B3500000;
×
365
        case 4000000:
×
366
            return B4000000;
×
367
        default: {
×
368
            LogErr() << "Unknown baudrate";
×
369
            return -1;
×
370
        }
371
    }
372
}
373
#endif
374

375
} // namespace mavsdk
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc