blob: f7a7d03a09f8cbac986720f80aac1bf6b18bea78 [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
17// This is the code for receiving data from the cape via UART.
Brian Silverman1662a0e2013-12-19 17:50:01 -080018// NOTE: In order for this to work, you MUST HAVE
19// "capemgr.enable_partno=BB_UART1"
Daniel Petti059be422013-12-14 19:47:42 -080020// in your BBB's /media/BEAGLEBONE/uEnv.txt file!
21
Daniel Pettib36f5b82013-12-15 08:55:04 -080022// Implementation for setting custom serial baud rates based on this code:
23// <http://jim.sh/ftx/files/linux-custom-baudrate.c>
24
Daniel Petti059be422013-12-14 19:47:42 -080025namespace bbb {
Brian Silverman1662a0e2013-12-19 17:50:01 -080026namespace {
27// TODO(brians): Determine this in some way that allows easy switching for
28// testing with /dev/ttyUSB0 for example.
29const char *device = "/dev/ttyO1";
30} // namespace
Daniel Petti059be422013-12-14 19:47:42 -080031
Brian Silvermanf6b68842013-12-20 12:34:58 -080032UartReader::UartReader(int32_t baud_rate)
Brian Silverman1662a0e2013-12-19 17:50:01 -080033 : baud_rate_(baud_rate),
34 buf_(new AlignedChar[DATA_STRUCT_SEND_SIZE]),
35 fd_(open(device, O_RDWR | O_NOCTTY)) {
36 static_assert((DATA_STRUCT_SEND_SIZE % 4) == 0,
37 "We can't do checksums of lengths that aren't multiples of 4.");
Daniel Petti059be422013-12-14 19:47:42 -080038
Brian Silverman1662a0e2013-12-19 17:50:01 -080039 if (fd_ < 0) {
40 LOG(FATAL, "open(%s, O_RDWR | O_NOCTTY) failed with %d: %s."
Brian Silvermanf6b68842013-12-20 12:34:58 -080041 " Did you read my note in bbb/uart_reader.cc?\n",
Brian Silverman1662a0e2013-12-19 17:50:01 -080042 device, errno, strerror(errno));
Daniel Petti059be422013-12-14 19:47:42 -080043 }
Daniel Pettib36f5b82013-12-15 08:55:04 -080044
Brian Silverman1662a0e2013-12-19 17:50:01 -080045 serial_struct serinfo;
Daniel Pettib36f5b82013-12-15 08:55:04 -080046 // Must implement an ugly custom divisor.
47 serinfo.reserved_char[0] = 0;
Brian Silverman1662a0e2013-12-19 17:50:01 -080048 if (ioctl(fd_, TIOCGSERIAL, &serinfo) < 0) {
49 LOG(FATAL, "ioctl(%d, TIOCGSERIAL, %p) failed with %d: %s\n",
50 fd_, &serinfo, errno, strerror(errno));
51 }
Daniel Pettib36f5b82013-12-15 08:55:04 -080052 serinfo.flags &= ~ASYNC_SPD_MASK;
53 serinfo.flags |= ASYNC_SPD_CUST;
54 serinfo.custom_divisor = (serinfo.baud_base + (baud_rate_ / 2)) / baud_rate_;
Brian Silverman1662a0e2013-12-19 17:50:01 -080055 if (serinfo.custom_divisor < 1) serinfo.custom_divisor = 1;
56 if (ioctl(fd_, TIOCSSERIAL, &serinfo) < 0) {
57 LOG(FATAL, "ioctl(%d, TIOCSSERIAL, %p) failed with %d: %s\n",
58 fd_, &serinfo, errno, strerror(errno));
59 }
60 if (ioctl(fd_, TIOCGSERIAL, &serinfo) < 0) {
61 LOG(FATAL, "ioctl(%d, TIOCGSERIAL, %p) failed with %d: %s\n",
62 fd_, &serinfo, errno, strerror(errno));
63 }
64 if (serinfo.custom_divisor * baud_rate_ != serinfo.baud_base) {
65 LOG(WARNING, "actual baudrate is %d / %d = %f\n",
66 serinfo.baud_base, serinfo.custom_divisor,
67 static_cast<double>(serinfo.baud_base) / serinfo.custom_divisor);
Daniel Petti059be422013-12-14 19:47:42 -080068 }
69
Brian Silverman1662a0e2013-12-19 17:50:01 -080070 if (fcntl(fd_, F_SETFL, 0) != 0) {
71 LOG(FATAL, "fcntl(%d, F_SETFL, 0) failed with %d: %s\n",
72 fd_, errno, strerror(errno));
73 }
74
75 termios options;
76 if (tcgetattr(fd_, &options) != 0) {
77 LOG(FATAL, "tcgetattr(%d, %p) failed with %d: %s\n",
78 fd_, &options, errno, strerror(errno));
79 }
80 if (cfsetispeed(&options, B38400) != 0) {
81 LOG(FATAL, "cfsetispeed(%p, B38400) failed with %d: %s\n",
82 &options, errno, strerror(errno));
83 }
84 if (cfsetospeed(&options, B38400) != 0) {
85 LOG(FATAL, "cfsetospeed(%p, B38400) failed with %d: %s\n",
86 &options, errno, strerror(errno));
87 }
Daniel Pettib36f5b82013-12-15 08:55:04 -080088 cfmakeraw(&options);
89 options.c_cflag |= (CLOCAL | CREAD);
90 options.c_cflag &= ~CRTSCTS;
91 options.c_iflag = 0;
92 options.c_oflag = 0;
93 options.c_lflag = 0;
Daniel Petti067e0612013-12-19 09:09:09 -080094 options.c_cc[VMIN] = 0;
Daniel Petti23dcf6c2013-12-19 08:56:41 -080095 options.c_cc[VTIME] = 1;
Brian Silverman1662a0e2013-12-19 17:50:01 -080096 if (tcsetattr(fd_, TCSANOW, &options) != 0) {
97 LOG(FATAL, "tcsetattr(%d, TCSANOW, %p) failed with %d: %s\n",
98 fd_, &options, errno, strerror(errno));
99 }
Daniel Petti059be422013-12-14 19:47:42 -0800100}
101
Brian Silvermanf6b68842013-12-20 12:34:58 -0800102UartReader::~UartReader() {
Brian Silverman1662a0e2013-12-19 17:50:01 -0800103 delete buf_;
104 if (fd_ > 0) close(fd_);
105}
Daniel Petti059be422013-12-14 19:47:42 -0800106
Brian Silvermanf6b68842013-12-20 12:34:58 -0800107bool UartReader::FindPacket() {
Brian Silverman1662a0e2013-12-19 17:50:01 -0800108 // How many 0 bytes we've found at the front so far.
109 int zeros_found = 0;
110 // How many bytes of the packet we've read in (or -1 if we don't know where
111 // the packet is).
112 int packet_bytes = -1;
113 while (true) {
114 size_t already_read = ::std::max(packet_bytes, 0);
115 ssize_t new_bytes =
116 read(fd_, buf_ + already_read, DATA_STRUCT_SEND_SIZE - already_read);
117 if (new_bytes < 0) {
118 if (errno == EINTR) continue;
119 LOG(WARNING, "read(%d, %p, %zd) failed with %d: %s\n",
120 fd_, buf_ + already_read, DATA_STRUCT_SEND_SIZE - already_read,
121 errno, strerror(errno));
122 return false;
Daniel Petti059be422013-12-14 19:47:42 -0800123 }
Daniel Petti059be422013-12-14 19:47:42 -0800124
Brian Silverman1662a0e2013-12-19 17:50:01 -0800125 if (packet_bytes == -1) {
126 // Find the beginning of the packet (aka look for four zero bytes).
127 for (ssize_t checked = 0; checked < new_bytes; ++checked) {
128 if (buf_[checked] == 0) {
129 ++zeros_found;
130 if (zeros_found == 4) {
131 packet_bytes = new_bytes - checked - 1;
132 memmove(buf_, buf_ + checked + 1, packet_bytes);
Daniel Petti059be422013-12-14 19:47:42 -0800133 break;
Daniel Petti059be422013-12-14 19:47:42 -0800134 }
135 } else {
Brian Silverman1662a0e2013-12-19 17:50:01 -0800136 zeros_found = 0;
Daniel Petti059be422013-12-14 19:47:42 -0800137 }
Daniel Petti059be422013-12-14 19:47:42 -0800138 }
Brian Silverman1662a0e2013-12-19 17:50:01 -0800139 } else { // we think there's a packet at the beginning of our buffer
140 for (int to_check = packet_bytes; packet_bytes + new_bytes; ++to_check) {
141 // We shouldn't find any 0s in the middle of what should be a packet.
142 if (buf_[to_check] == 0) {
143 packet_bytes = -1;
144 break;
145 }
146 }
147 packet_bytes += new_bytes;
148 if (packet_bytes == DATA_STRUCT_SEND_SIZE) return true;
Daniel Petti059be422013-12-14 19:47:42 -0800149 }
Daniel Petti059be422013-12-14 19:47:42 -0800150 }
Brian Silverman1662a0e2013-12-19 17:50:01 -0800151}
Daniel Petti059be422013-12-14 19:47:42 -0800152
Brian Silvermanf6b68842013-12-20 12:34:58 -0800153bool UartReader::GetPacket(DataStruct *packet) {
Brian Silverman1662a0e2013-12-19 17:50:01 -0800154 if (!FindPacket()) return false;
Daniel Petti059be422013-12-14 19:47:42 -0800155
Brian Silverman1662a0e2013-12-19 17:50:01 -0800156 uint32_t unstuffed = cows_unstuff(reinterpret_cast<uint32_t *>(buf_),
157 DATA_STRUCT_SEND_SIZE / 4,
158 reinterpret_cast<uint32_t *>(packet));
159 if (unstuffed == 0) {
160 LOG(WARNING, "invalid packet\n");
161 } else if (unstuffed != sizeof(packet)) {
162 LOG(WARNING, "packet is %" PRIu32 " words instead of %" PRIu32 "\n",
163 unstuffed, DATA_STRUCT_SEND_SIZE / 4);
Daniel Petti059be422013-12-14 19:47:42 -0800164 }
165
166 // Make sure the checksum checks out.
Brian Silverman1662a0e2013-12-19 17:50:01 -0800167 uint32_t checksum;
168 memcpy(&checksum, buf_ + DATA_STRUCT_SEND_SIZE - 4, 4);
169 if (cape::CalculateChecksum(reinterpret_cast<uint8_t *>(packet),
170 sizeof(DataStruct)) != checksum) {
Daniel Petti059be422013-12-14 19:47:42 -0800171 LOG(WARNING, "Rejecting packet due to checksum failure.\n");
Brian Silverman1662a0e2013-12-19 17:50:01 -0800172 return false;
Daniel Petti059be422013-12-14 19:47:42 -0800173 }
174
Brian Silverman1662a0e2013-12-19 17:50:01 -0800175 return true;
Daniel Petti059be422013-12-14 19:47:42 -0800176}
177
Brian Silverman1662a0e2013-12-19 17:50:01 -0800178} // namespace bbb