blob: ef90635c50a403baeca7d4206c275adc3a034df5 [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 Silvermanbdc2bd42013-12-20 12:38:14 -0800125 if (packet_bytes != -1) { // we think there's a packet at the beginning of
126 // our buffer
127 for (int to_check = packet_bytes; packet_bytes + new_bytes; ++to_check) {
128 // We shouldn't find any 0s in the middle of what should be a packet.
129 if (buf_[to_check] == 0) {
130 packet_bytes = -1;
131 memmove(buf_, buf_ + to_check, new_bytes - to_check);
132 new_bytes -= to_check;
133 break;
134 }
135 }
136 if (packet_bytes != -1) {
137 packet_bytes += new_bytes;
138 if (packet_bytes == DATA_STRUCT_SEND_SIZE) return true;
139 }
140 }
141 // This can't just be an else because the above code might set it to -1 if
142 // it finds 0s in the middle of a packet.
Brian Silverman1662a0e2013-12-19 17:50:01 -0800143 if (packet_bytes == -1) {
144 // Find the beginning of the packet (aka look for four zero bytes).
145 for (ssize_t checked = 0; checked < new_bytes; ++checked) {
146 if (buf_[checked] == 0) {
147 ++zeros_found;
148 if (zeros_found == 4) {
149 packet_bytes = new_bytes - checked - 1;
150 memmove(buf_, buf_ + checked + 1, packet_bytes);
Daniel Petti059be422013-12-14 19:47:42 -0800151 break;
Daniel Petti059be422013-12-14 19:47:42 -0800152 }
153 } else {
Brian Silverman1662a0e2013-12-19 17:50:01 -0800154 zeros_found = 0;
Daniel Petti059be422013-12-14 19:47:42 -0800155 }
Daniel Petti059be422013-12-14 19:47:42 -0800156 }
157 }
Daniel Petti059be422013-12-14 19:47:42 -0800158 }
Brian Silverman1662a0e2013-12-19 17:50:01 -0800159}
Daniel Petti059be422013-12-14 19:47:42 -0800160
Brian Silvermanf6b68842013-12-20 12:34:58 -0800161bool UartReader::GetPacket(DataStruct *packet) {
Brian Silverman1662a0e2013-12-19 17:50:01 -0800162 if (!FindPacket()) return false;
Daniel Petti059be422013-12-14 19:47:42 -0800163
Brian Silverman1662a0e2013-12-19 17:50:01 -0800164 uint32_t unstuffed = cows_unstuff(reinterpret_cast<uint32_t *>(buf_),
165 DATA_STRUCT_SEND_SIZE / 4,
166 reinterpret_cast<uint32_t *>(packet));
167 if (unstuffed == 0) {
168 LOG(WARNING, "invalid packet\n");
169 } else if (unstuffed != sizeof(packet)) {
170 LOG(WARNING, "packet is %" PRIu32 " words instead of %" PRIu32 "\n",
171 unstuffed, DATA_STRUCT_SEND_SIZE / 4);
Daniel Petti059be422013-12-14 19:47:42 -0800172 }
173
174 // Make sure the checksum checks out.
Brian Silverman1662a0e2013-12-19 17:50:01 -0800175 uint32_t checksum;
176 memcpy(&checksum, buf_ + DATA_STRUCT_SEND_SIZE - 4, 4);
177 if (cape::CalculateChecksum(reinterpret_cast<uint8_t *>(packet),
178 sizeof(DataStruct)) != checksum) {
Daniel Petti059be422013-12-14 19:47:42 -0800179 LOG(WARNING, "Rejecting packet due to checksum failure.\n");
Brian Silverman1662a0e2013-12-19 17:50:01 -0800180 return false;
Daniel Petti059be422013-12-14 19:47:42 -0800181 }
182
Brian Silverman1662a0e2013-12-19 17:50:01 -0800183 return true;
Daniel Petti059be422013-12-14 19:47:42 -0800184}
185
Brian Silverman1662a0e2013-12-19 17:50:01 -0800186} // namespace bbb