Apollo 11.0
自动驾驶开放平台
serial_stream.cc
浏览该文件的文档.
1/******************************************************************************
2 * Copyright 2017 The Apollo Authors. All Rights Reserved.
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#include <fcntl.h>
18#include <sys/ioctl.h>
19#include <sys/select.h>
20#include <sys/socket.h>
21#include <sys/time.h>
22#include <termios.h>
23#include <unistd.h>
24
25#include <cerrno>
26#include <cstring>
27#include <ctime>
28#include <thread>
29
30#include "cyber/cyber.h"
32
33namespace apollo {
34namespace drivers {
35namespace gnss {
36
37speed_t get_serial_baudrate(uint32_t rate) {
38 switch (rate) {
39 case 9600:
40 return B9600;
41 case 19200:
42 return B19200;
43 case 38400:
44 return B38400;
45 case 57600:
46 return B57600;
47 case 115200:
48 return B115200;
49 case 230400:
50 return B230400;
51 case 460800:
52 return B460800;
53 case 921600:
54 return B921600;
55 default:
56 return 0;
57 }
58}
59
60class SerialStream : public Stream {
61 public:
62 SerialStream(const char* device_name, speed_t baud_rate,
63 uint32_t timeout_usec);
65
66 virtual bool Connect();
67 virtual bool Disconnect();
68 virtual size_t read(uint8_t* buffer, size_t max_length);
69 virtual size_t write(const uint8_t* data, size_t length);
70
71 private:
72 SerialStream() {}
73 void open();
74 void close();
75 bool configure_port(int fd);
76 bool wait_readable(uint32_t timeout_us);
77 bool wait_writable(uint32_t timeout_us);
78 void check_remove();
79
80 std::string device_name_;
81 speed_t baud_rate_;
82 uint32_t bytesize_;
83 uint32_t parity_;
84 uint32_t stopbits_;
85 uint32_t flowcontrol_;
86 uint32_t byte_time_us_;
87
88 uint32_t timeout_usec_;
89 int fd_;
90 int errno_;
91 bool is_open_;
92};
93
94SerialStream::SerialStream(const char* device_name, speed_t baud_rate,
95 uint32_t timeout_usec)
96 : device_name_(device_name),
97 baud_rate_(baud_rate),
98 bytesize_(8),
99 parity_(0),
100 stopbits_(1),
101 flowcontrol_(0),
102 timeout_usec_(timeout_usec),
103 fd_(-1),
104 errno_(0),
105 is_open_(false) {
106 if (device_name_.empty()) {
108 }
109}
110
111SerialStream::~SerialStream() { this->close(); }
112
113void SerialStream::open(void) {
114 int fd = 0;
115 fd = ::open(device_name_.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
116 if (fd == -1) {
117 switch (errno) {
118 case EINTR:
119 // Recurse because this is a recoverable error.
120 return open();
121
122 case ENFILE:
123 case EMFILE:
124 default:
125 AERROR << "Open device " << device_name_
126 << " failed, error: " << strerror(errno);
127 return;
128 }
129 }
130
131 if (!configure_port(fd)) {
132 ::close(fd);
133 return;
134 }
135
136 fd_ = fd;
137 is_open_ = true;
138}
139
140bool SerialStream::configure_port(int fd) {
141 if (fd < 0) {
142 return false;
143 }
144
145 struct termios options; // The options for the file descriptor
146 if (tcgetattr(fd, &options) == -1) {
147 AERROR << "tcgetattr failed.";
148 return false;
149 }
150
151 // set up raw mode / no echo / binary
152 options.c_cflag |= (tcflag_t)(CLOCAL | CREAD);
153 options.c_lflag &= (tcflag_t) ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL |
154 ISIG | IEXTEN); // |ECHOPRT
155
156 options.c_oflag &= (tcflag_t) ~(OPOST);
157 options.c_iflag &= (tcflag_t) ~(INLCR | IGNCR | ICRNL | IGNBRK);
158
159#ifdef IUCLC
160 options.c_iflag &= (tcflag_t)~IUCLC;
161#endif
162
163#ifdef PARMRK
164 options.c_iflag &= (tcflag_t)~PARMRK;
165#endif
166
167#ifdef BSD_SOURCE_ // depend glibc
168 ::cfsetspeed(&options, baud_rate_);
169#else
170 ::cfsetispeed(&options, baud_rate_);
171 ::cfsetospeed(&options, baud_rate_);
172#endif
173
174 // setup char len
175 options.c_cflag &= (tcflag_t)~CSIZE;
176
177 // eight bits
178 options.c_cflag |= CS8;
179
180 // setup stopbits:stopbits_one
181 options.c_cflag &= (tcflag_t) ~(CSTOPB);
182
183 // setup parity: parity_none
184 options.c_iflag &= (tcflag_t) ~(INPCK | ISTRIP);
185 options.c_cflag &= (tcflag_t) ~(PARENB | PARODD);
186
187// setup flow control : flowcontrol_none
188// xonxoff
189#ifdef IXANY
190 options.c_iflag &= (tcflag_t) ~(IXON | IXOFF | IXANY);
191#else
192 options.c_iflag &= (tcflag_t) ~(IXON | IXOFF);
193#endif
194
195// rtscts
196#ifdef CRTSCTS
197 options.c_cflag &= static_cast<uint64_t>(~(CRTSCTS));
198#elif defined CNEW_RTSCTS
199 options.c_cflag &= static_cast<uint64_t>(~(CNEW_RTSCTS));
200#else
201#error "OS Support seems wrong."
202#endif
203
204 // http://www.unixwiz.net/techtips/termios-vmin-vtime.html
205 // this basically sets the read call up to be a polling read,
206 // but we are using select to ensure there is data available
207 // to read before each call, so we should never needlessly poll
208 options.c_cc[VMIN] = 0;
209 options.c_cc[VTIME] = 0;
210
211 // activate settings
212 ::tcsetattr(fd, TCSANOW, &options);
213
214 // Update byte_time_ based on the new settings.
215 uint32_t bit_time_us = static_cast<uint32_t>(1e6 / 115200);
216 byte_time_us_ = bit_time_us * (1 + bytesize_ + parity_ + stopbits_);
217 return true;
218}
219
221 if (!is_open_) {
222 this->open();
223 if (!is_open_) {
225 errno_ = errno;
226 return false;
227 }
228 }
229
231 return true;
232 }
233
235 Login();
236 return true;
237}
238
239void SerialStream::close(void) {
240 if (is_open_) {
241 ::close(fd_);
242 fd_ = -1;
243 is_open_ = false;
245 }
246}
247
249 if (!is_open_) {
250 // not open
251 return false;
252 }
253
254 this->close();
255 return true;
256}
257
258void SerialStream::check_remove() {
259 char data = 0;
260 ssize_t nsent = ::write(fd_, &data, 0);
261 if (nsent < 0) {
262 AERROR << "Serial stream detect write failed, error: " << strerror(errno);
263 switch (errno) {
264 case EBADF:
265 case EIO:
267 AERROR << "Device " << device_name_ << " removed.";
268 Disconnect();
269 break;
270 }
271 }
272}
273
274size_t SerialStream::read(uint8_t* buffer, size_t max_length) {
275 if (!is_open_) {
276 if (!Connect()) {
277 return 0;
278 }
279 AINFO << "Connect " << device_name_ << " success.";
280 }
281
282 ssize_t bytes_read = 0;
283 ssize_t bytes_current_read = 0;
284
285 wait_readable(5000); // wait 5ms
286
287 while (max_length > 0) {
288 bytes_current_read = ::read(fd_, buffer, max_length);
289 if (bytes_current_read < 0) {
290 switch (errno) {
291 case EAGAIN:
292 case EINVAL:
293 bytes_current_read = 0;
294 break;
295
296 case EBADF:
297 case EIO:
298 AERROR << "Serial stream read data failed, error: "
299 << strerror(errno);
300 Disconnect();
301 if (Connect()) {
302 AINFO << "Reconnect " << device_name_ << " success.";
303 bytes_current_read = 0;
304 break; // has recoverable
305 }
306
307 default:
308 AERROR << "Serial stream read data failed, error: " << strerror(errno)
309 << ", errno: " << errno;
311 errno_ = errno;
312 return bytes_read;
313 }
314 }
315
316 if (bytes_current_read == 0) {
317 if (!bytes_read) {
318 check_remove();
319 return 0;
320 }
321 return bytes_read;
322 }
323 max_length -= bytes_current_read;
324 buffer += bytes_current_read;
325 bytes_read += bytes_current_read;
326 }
327
328 return bytes_read;
329}
330
331size_t SerialStream::write(const uint8_t* data, size_t length) {
332 if (!is_open_) {
333 if (!Connect()) {
334 return 0;
335 }
336 AINFO << "Connect " << device_name_ << " success.";
337 }
338
339 size_t total_nsent = 0;
340 size_t delay_times = 0;
341
342 while ((length > 0) && (delay_times < 5)) {
343 ssize_t nsent = ::write(fd_, data, length);
344 if (nsent < 0) {
345 AERROR << "Serial stream write data failed, error: " << strerror(errno);
346 switch (errno) {
347 case EAGAIN:
348 case EINVAL:
349 nsent = 0;
350 break;
351
352 case EBADF:
353 case EIO:
354 Disconnect();
355 if (Connect()) {
356 AINFO << "Reconnect " << device_name_ << "success.";
357 nsent = 0;
358 break; // has recoverable
359 }
360
361 default:
363 errno_ = errno;
364 return total_nsent;
365 }
366 }
367
368 if (nsent == 0) {
369 if (!wait_writable(byte_time_us_)) {
370 break;
371 }
372 ++delay_times;
373 continue;
374 }
375
376 total_nsent += nsent;
377 length -= nsent;
378 data += nsent;
379 }
380
381 return total_nsent;
382}
383
384bool SerialStream::wait_readable(uint32_t timeout_us) {
385 // Setup a select call to block for serial data or a timeout
386 timespec timeout_ts;
387 fd_set readfds;
388 FD_ZERO(&readfds);
389 FD_SET(fd_, &readfds);
390
391 timeout_ts.tv_sec = timeout_us / 1000000;
392 timeout_ts.tv_nsec = (timeout_us % 1000000) * 1000;
393 int r = pselect(fd_ + 1, &readfds, NULL, NULL, &timeout_ts, NULL);
394 if (r <= 0) {
395 return false;
396 }
397
398 // This shouldn't happen, if r > 0 our fd has to be in the list!
399 if (!FD_ISSET(fd_, &readfds)) {
400 return false;
401 }
402 // Data available to read.
403 return true;
404}
405
406bool SerialStream::wait_writable(uint32_t timeout_us) {
407 // Setup a select call to block for serial data or a timeout
408 timespec timeout_ts;
409 fd_set writefds;
410 FD_ZERO(&writefds);
411 FD_SET(fd_, &writefds);
412
413 timeout_ts.tv_sec = timeout_us / 1000000;
414 timeout_ts.tv_nsec = (timeout_us % 1000000) * 1000;
415 int r = pselect(fd_ + 1, NULL, &writefds, NULL, &timeout_ts, NULL);
416 if (r <= 0) {
417 return false;
418 }
419
420 // This shouldn't happen, if r > 0 our fd has to be in the list!
421 if (!FD_ISSET(fd_, &writefds)) {
422 return false;
423 }
424 // Data available to write.
425 return true;
426}
427
428Stream* Stream::create_serial(const char* device_name, uint32_t baud_rate,
429 uint32_t timeout_usec) {
430 speed_t baud = get_serial_baudrate(baud_rate);
431 return baud == 0 ? nullptr
432 : new SerialStream(device_name, baud, timeout_usec);
433}
434
435} // namespace gnss
436} // namespace drivers
437} // namespace apollo
virtual size_t write(const uint8_t *data, size_t length)
virtual size_t read(uint8_t *buffer, size_t max_length)
static Stream * create_serial(const char *device_name, uint32_t baud_rate, uint32_t timeout_usec=0)
#define AERROR
Definition log.h:44
#define AINFO
Definition log.h:42
speed_t get_serial_baudrate(uint32_t rate)
class register implement
Definition arena_queue.h:37