blob: 7b5e1163537693528b80e14478c9eaade1127a26 [file] [log] [blame]
Brian Silvermanf6b68842013-12-20 12:34:58 -08001#include "bbb/uart_reader.h"
Brian Silverman1662a0e2013-12-19 17:50:01 -08002
Daniel Pettib36f5b82013-12-15 08:55:04 -08003#include <fcntl.h>
4#include <linux/serial.h>
5#include <termio.h>
6#include <unistd.h>
Brian Silverman1662a0e2013-12-19 17:50:01 -08007#include <errno.h>
8#include <string.h>
9#include <inttypes.h>
Daniel Pettib36f5b82013-12-15 08:55:04 -080010
Brian Silverman1662a0e2013-12-19 17:50:01 -080011#include <algorithm>
Daniel Petti059be422013-12-14 19:47:42 -080012
Brian Silverman1662a0e2013-12-19 17:50:01 -080013#include "aos/common/logging/logging.h"
Daniel Petti059be422013-12-14 19:47:42 -080014#include "bbb_cape/src/cape/cows.h"
Brian Silverman1662a0e2013-12-19 17:50:01 -080015#include "bbb/crc.h"
Daniel Petti059be422013-12-14 19:47:42 -080016
Brian Silverman53f29182013-12-21 15:16:27 -080017#define PACKET_SIZE (DATA_STRUCT_SEND_SIZE - 4)
18
Daniel Petti059be422013-12-14 19:47:42 -080019// This is the code for receiving data from the cape via UART.
Brian Silverman1662a0e2013-12-19 17:50:01 -080020// NOTE: In order for this to work, you MUST HAVE
21// "capemgr.enable_partno=BB_UART1"
Daniel Petti059be422013-12-14 19:47:42 -080022// in your BBB's /media/BEAGLEBONE/uEnv.txt file!
23
Daniel Pettib36f5b82013-12-15 08:55:04 -080024// Implementation for setting custom serial baud rates based on this code:
25// <http://jim.sh/ftx/files/linux-custom-baudrate.c>
26
Daniel Petti059be422013-12-14 19:47:42 -080027namespace bbb {
Brian Silverman1662a0e2013-12-19 17:50:01 -080028namespace {
29// TODO(brians): Determine this in some way that allows easy switching for
30// testing with /dev/ttyUSB0 for example.
Brian Silvermaned030062013-12-20 21:03:47 -080031// TODO(brians): Change back to /dev/ttyO1.
32const char *device = "/dev/ttyUSB0";
Brian Silverman1662a0e2013-12-19 17:50:01 -080033} // namespace
Daniel Petti059be422013-12-14 19:47:42 -080034
Brian Silvermanf6b68842013-12-20 12:34:58 -080035UartReader::UartReader(int32_t baud_rate)
Brian Silverman1662a0e2013-12-19 17:50:01 -080036 : baud_rate_(baud_rate),
Brian Silverman53f29182013-12-21 15:16:27 -080037 buf_(new AlignedChar[PACKET_SIZE]),
38 unstuffed_data_(new AlignedChar[PACKET_SIZE - 4]),
Brian Silverman1662a0e2013-12-19 17:50:01 -080039 fd_(open(device, O_RDWR | O_NOCTTY)) {
Brian Silverman53f29182013-12-21 15:16:27 -080040 static_assert((PACKET_SIZE % 4) == 0,
Brian Silverman1662a0e2013-12-19 17:50:01 -080041 "We can't do checksums of lengths that aren't multiples of 4.");
Daniel Petti059be422013-12-14 19:47:42 -080042
Brian Silverman1662a0e2013-12-19 17:50:01 -080043 if (fd_ < 0) {
44 LOG(FATAL, "open(%s, O_RDWR | O_NOCTTY) failed with %d: %s."
Brian Silvermanf6b68842013-12-20 12:34:58 -080045 " Did you read my note in bbb/uart_reader.cc?\n",
Brian Silverman1662a0e2013-12-19 17:50:01 -080046 device, errno, strerror(errno));
Daniel Petti059be422013-12-14 19:47:42 -080047 }
Brian Silvermaned030062013-12-20 21:03:47 -080048
49 {
50 termios options;
51 if (tcgetattr(fd_, &options) != 0) {
52 LOG(FATAL, "tcgetattr(%d, %p) failed with %d: %s\n",
53 fd_, &options, errno, strerror(errno));
54 }
55 if (cfsetispeed(&options, B38400) != 0) {
56 LOG(FATAL, "cfsetispeed(%p, B38400) failed with %d: %s\n",
57 &options, errno, strerror(errno));
58 }
59 if (cfsetospeed(&options, B38400) != 0) {
60 LOG(FATAL, "cfsetospeed(%p, B38400) failed with %d: %s\n",
61 &options, errno, strerror(errno));
62 }
63 cfmakeraw(&options);
64 options.c_cflag |= CLOCAL; // ignore modem flow control
65 options.c_cflag |= CREAD; // enable receiver
66 options.c_cflag &= ~CRTSCTS; // disable flow control
67 //options.c_cflag |= PARENB; // enable parity; defaults to even
68 options.c_cflag = (options.c_cflag & ~CSIZE) | CS8; // 8 bits
69 options.c_cflag &= ~CSTOPB; // 1 stop bit
70 options.c_iflag = 0;
71 // Replace any received characters with parity or framing errors with 0s.
72 options.c_iflag = (options.c_iflag & ~(IGNPAR | PARMRK)) | INPCK;
73 //options.c_iflag |= IGNCR | PARMRK;
74 options.c_oflag = 0;
75 options.c_lflag = 0;
76 options.c_cc[VMIN] = 0;
Brian Silverman53f29182013-12-21 15:16:27 -080077 options.c_cc[VTIME] = 10;
Brian Silvermaned030062013-12-20 21:03:47 -080078 if (tcsetattr(fd_, TCSANOW, &options) != 0) {
79 LOG(FATAL, "tcsetattr(%d, TCSANOW, %p) failed with %d: %s\n",
80 fd_, &options, errno, strerror(errno));
81 }
82 }
Daniel Pettib36f5b82013-12-15 08:55:04 -080083
Brian Silvermaned030062013-12-20 21:03:47 -080084 {
85 serial_struct serinfo;
86 if (ioctl(fd_, TIOCGSERIAL, &serinfo) != 0) {
87 LOG(FATAL, "ioctl(%d, TIOCGSERIAL, %p) failed with %d: %s\n",
88 fd_, &serinfo, errno, strerror(errno));
89 }
90 serinfo.reserved_char[0] = 0;
91 serinfo.flags &= ~ASYNC_SPD_MASK;
92 serinfo.flags |= ASYNC_SPD_CUST;
93 //serinfo.flags |= ASYNC_LOW_LATENCY;
94 serinfo.custom_divisor = static_cast<int>(
95 static_cast<double>(serinfo.baud_base) / baud_rate_ + 0.5);
96 if (serinfo.custom_divisor < 1) serinfo.custom_divisor = 1;
97 if (ioctl(fd_, TIOCSSERIAL, &serinfo) < 0) {
98 LOG(FATAL, "ioctl(%d, TIOCSSERIAL, %p) failed with %d: %s\n",
99 fd_, &serinfo, errno, strerror(errno));
100 }
101 if (ioctl(fd_, TIOCGSERIAL, &serinfo) < 0) {
102 LOG(FATAL, "ioctl(%d, TIOCGSERIAL, %p) failed with %d: %s\n",
103 fd_, &serinfo, errno, strerror(errno));
104 }
105 if ((serinfo.flags & ASYNC_SPD_CUST) == 0) {
106 LOG(FATAL, "can not set custom baud rate\n");
107 }
108 if (serinfo.custom_divisor * baud_rate_ != serinfo.baud_base) {
109 LOG(WARNING, "actual baudrate is %d / %d = %f\n",
110 serinfo.baud_base, serinfo.custom_divisor,
111 static_cast<double>(serinfo.baud_base) / serinfo.custom_divisor);
112 }
Daniel Petti059be422013-12-14 19:47:42 -0800113 }
114
Brian Silverman1662a0e2013-12-19 17:50:01 -0800115 if (fcntl(fd_, F_SETFL, 0) != 0) {
116 LOG(FATAL, "fcntl(%d, F_SETFL, 0) failed with %d: %s\n",
117 fd_, errno, strerror(errno));
118 }
Daniel Petti059be422013-12-14 19:47:42 -0800119}
120
Brian Silvermanf6b68842013-12-20 12:34:58 -0800121UartReader::~UartReader() {
Brian Silverman1662a0e2013-12-19 17:50:01 -0800122 delete buf_;
Brian Silverman53f29182013-12-21 15:16:27 -0800123 delete unstuffed_data_;
Brian Silverman1662a0e2013-12-19 17:50:01 -0800124 if (fd_ > 0) close(fd_);
125}
Daniel Petti059be422013-12-14 19:47:42 -0800126
Brian Silverman53f29182013-12-21 15:16:27 -0800127// TODO(brians): Figure out why this (sometimes?) gets confused right after
128// flashing the cape.
Brian Silvermanf6b68842013-12-20 12:34:58 -0800129bool UartReader::FindPacket() {
Brian Silverman1662a0e2013-12-19 17:50:01 -0800130 // How many 0 bytes we've found at the front so far.
131 int zeros_found = 0;
Brian Silverman1662a0e2013-12-19 17:50:01 -0800132 while (true) {
Brian Silverman53f29182013-12-21 15:16:27 -0800133 size_t already_read = ::std::max(0, packet_bytes_);
Brian Silverman1662a0e2013-12-19 17:50:01 -0800134 ssize_t new_bytes =
Brian Silverman53f29182013-12-21 15:16:27 -0800135 read(fd_, buf_ + already_read, PACKET_SIZE - already_read);
Brian Silverman1662a0e2013-12-19 17:50:01 -0800136 if (new_bytes < 0) {
137 if (errno == EINTR) continue;
Brian Silverman53f29182013-12-21 15:16:27 -0800138 LOG(FATAL, "read(%d, %p, %zd) failed with %d: %s\n",
139 fd_, buf_ + already_read, PACKET_SIZE - already_read,
Brian Silverman1662a0e2013-12-19 17:50:01 -0800140 errno, strerror(errno));
141 return false;
Daniel Petti059be422013-12-14 19:47:42 -0800142 }
Daniel Petti059be422013-12-14 19:47:42 -0800143
Brian Silverman53f29182013-12-21 15:16:27 -0800144 if (packet_bytes_ == -1) {
145 for (size_t to_check = already_read; to_check < already_read + new_bytes;
146 ++to_check) {
Brian Silvermanbdc2bd42013-12-20 12:38:14 -0800147 if (buf_[to_check] == 0) {
Brian Silverman1662a0e2013-12-19 17:50:01 -0800148 ++zeros_found;
149 if (zeros_found == 4) {
Brian Silverman53f29182013-12-21 15:16:27 -0800150 packet_bytes_ = 0;
151 zeros_found = 0;
152 new_bytes -= to_check + 1;
153 memmove(buf_, buf_ + to_check + 1, new_bytes);
154 to_check = 0;
Daniel Petti059be422013-12-14 19:47:42 -0800155 }
156 } else {
Brian Silverman1662a0e2013-12-19 17:50:01 -0800157 zeros_found = 0;
Daniel Petti059be422013-12-14 19:47:42 -0800158 }
Daniel Petti059be422013-12-14 19:47:42 -0800159 }
160 }
Brian Silverman53f29182013-12-21 15:16:27 -0800161 if (packet_bytes_ != -1) { // if we decided that these are good bytes
162 packet_bytes_ += new_bytes;
163 if (packet_bytes_ == PACKET_SIZE) return true;
164 }
Daniel Petti059be422013-12-14 19:47:42 -0800165 }
Brian Silverman1662a0e2013-12-19 17:50:01 -0800166}
Daniel Petti059be422013-12-14 19:47:42 -0800167
Brian Silverman53f29182013-12-21 15:16:27 -0800168bool UartReader::ProcessPacket() {
169 uint32_t unstuffed =
170 cows_unstuff(reinterpret_cast<uint32_t *>(buf_), PACKET_SIZE,
171 reinterpret_cast<uint32_t *>(unstuffed_data_));
Brian Silverman1662a0e2013-12-19 17:50:01 -0800172 if (unstuffed == 0) {
173 LOG(WARNING, "invalid packet\n");
Brian Silverman53f29182013-12-21 15:16:27 -0800174 return false;
175 } else if (unstuffed != (PACKET_SIZE - 4) / 4) {
Brian Silverman1662a0e2013-12-19 17:50:01 -0800176 LOG(WARNING, "packet is %" PRIu32 " words instead of %" PRIu32 "\n",
Brian Silverman53f29182013-12-21 15:16:27 -0800177 unstuffed, (PACKET_SIZE - 4) / 4);
178 return false;
Daniel Petti059be422013-12-14 19:47:42 -0800179 }
180
181 // Make sure the checksum checks out.
Brian Silverman53f29182013-12-21 15:16:27 -0800182 uint32_t sent_checksum;
183 memcpy(&sent_checksum, unstuffed_data_ + PACKET_SIZE - 8, 4);
184 uint32_t calculated_checksum = cape::CalculateChecksum(
185 reinterpret_cast<uint8_t *>(unstuffed_data_), PACKET_SIZE - 8);
186 if (sent_checksum != calculated_checksum) {
187 LOG(WARNING, "sent checksum: %" PRIx32 " vs calculated: %" PRIx32"\n",
188 sent_checksum, calculated_checksum);
Brian Silverman1662a0e2013-12-19 17:50:01 -0800189 return false;
Daniel Petti059be422013-12-14 19:47:42 -0800190 }
191
Brian Silverman1662a0e2013-12-19 17:50:01 -0800192 return true;
Daniel Petti059be422013-12-14 19:47:42 -0800193}
194
Brian Silverman53f29182013-12-21 15:16:27 -0800195bool UartReader::GetPacket(DataStruct *packet) {
196 static_assert(sizeof(*packet) <= PACKET_SIZE - 8,
197 "output data type is too big");
198
199 if (!FindPacket()) return false;
200
201 if (!ProcessPacket()) {
202 packet_bytes_ = -1;
203 int zeros = 0;
204 for (int i = 0; i < PACKET_SIZE; ++i) {
205 if (buf_[i] == 0) {
206 ++zeros;
207 if (zeros == 4) {
208 LOG(INFO, "found another packet start at %d\n", i);
209 packet_bytes_ = PACKET_SIZE - (i + 1);
210 memmove(buf_, buf_ + i + 1, packet_bytes_);
211 return false;
212 }
213 } else {
214 zeros = 0;
215 }
216 }
217 return false;
218 } else {
219 packet_bytes_ = -1;
220 }
221 memcpy(packet, unstuffed_data_, sizeof(*packet));
222
223 return true;
224}
225
Brian Silverman1662a0e2013-12-19 17:50:01 -0800226} // namespace bbb