blob: 0a3f3c6877b07880a1c4bf6be596573d0d2f5e96 [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.
Brian Silvermaned030062013-12-20 21:03:47 -080029// TODO(brians): Change back to /dev/ttyO1.
30const char *device = "/dev/ttyUSB0";
Brian Silverman1662a0e2013-12-19 17:50:01 -080031} // namespace
Daniel Petti059be422013-12-14 19:47:42 -080032
Brian Silvermanf6b68842013-12-20 12:34:58 -080033UartReader::UartReader(int32_t baud_rate)
Brian Silverman1662a0e2013-12-19 17:50:01 -080034 : baud_rate_(baud_rate),
35 buf_(new AlignedChar[DATA_STRUCT_SEND_SIZE]),
36 fd_(open(device, O_RDWR | O_NOCTTY)) {
37 static_assert((DATA_STRUCT_SEND_SIZE % 4) == 0,
38 "We can't do checksums of lengths that aren't multiples of 4.");
Daniel Petti059be422013-12-14 19:47:42 -080039
Brian Silverman1662a0e2013-12-19 17:50:01 -080040 if (fd_ < 0) {
41 LOG(FATAL, "open(%s, O_RDWR | O_NOCTTY) failed with %d: %s."
Brian Silvermanf6b68842013-12-20 12:34:58 -080042 " Did you read my note in bbb/uart_reader.cc?\n",
Brian Silverman1662a0e2013-12-19 17:50:01 -080043 device, errno, strerror(errno));
Daniel Petti059be422013-12-14 19:47:42 -080044 }
Brian Silvermaned030062013-12-20 21:03:47 -080045
46 {
47 termios options;
48 if (tcgetattr(fd_, &options) != 0) {
49 LOG(FATAL, "tcgetattr(%d, %p) failed with %d: %s\n",
50 fd_, &options, errno, strerror(errno));
51 }
52 if (cfsetispeed(&options, B38400) != 0) {
53 LOG(FATAL, "cfsetispeed(%p, B38400) failed with %d: %s\n",
54 &options, errno, strerror(errno));
55 }
56 if (cfsetospeed(&options, B38400) != 0) {
57 LOG(FATAL, "cfsetospeed(%p, B38400) failed with %d: %s\n",
58 &options, errno, strerror(errno));
59 }
60 cfmakeraw(&options);
61 options.c_cflag |= CLOCAL; // ignore modem flow control
62 options.c_cflag |= CREAD; // enable receiver
63 options.c_cflag &= ~CRTSCTS; // disable flow control
64 //options.c_cflag |= PARENB; // enable parity; defaults to even
65 options.c_cflag = (options.c_cflag & ~CSIZE) | CS8; // 8 bits
66 options.c_cflag &= ~CSTOPB; // 1 stop bit
67 options.c_iflag = 0;
68 // Replace any received characters with parity or framing errors with 0s.
69 options.c_iflag = (options.c_iflag & ~(IGNPAR | PARMRK)) | INPCK;
70 //options.c_iflag |= IGNCR | PARMRK;
71 options.c_oflag = 0;
72 options.c_lflag = 0;
73 options.c_cc[VMIN] = 0;
74 options.c_cc[VTIME] = 1;
75 if (tcsetattr(fd_, TCSANOW, &options) != 0) {
76 LOG(FATAL, "tcsetattr(%d, TCSANOW, %p) failed with %d: %s\n",
77 fd_, &options, errno, strerror(errno));
78 }
79 }
Daniel Pettib36f5b82013-12-15 08:55:04 -080080
Brian Silvermaned030062013-12-20 21:03:47 -080081 {
82 serial_struct serinfo;
83 if (ioctl(fd_, TIOCGSERIAL, &serinfo) != 0) {
84 LOG(FATAL, "ioctl(%d, TIOCGSERIAL, %p) failed with %d: %s\n",
85 fd_, &serinfo, errno, strerror(errno));
86 }
87 serinfo.reserved_char[0] = 0;
88 serinfo.flags &= ~ASYNC_SPD_MASK;
89 serinfo.flags |= ASYNC_SPD_CUST;
90 //serinfo.flags |= ASYNC_LOW_LATENCY;
91 serinfo.custom_divisor = static_cast<int>(
92 static_cast<double>(serinfo.baud_base) / baud_rate_ + 0.5);
93 if (serinfo.custom_divisor < 1) serinfo.custom_divisor = 1;
94 if (ioctl(fd_, TIOCSSERIAL, &serinfo) < 0) {
95 LOG(FATAL, "ioctl(%d, TIOCSSERIAL, %p) failed with %d: %s\n",
96 fd_, &serinfo, errno, strerror(errno));
97 }
98 if (ioctl(fd_, TIOCGSERIAL, &serinfo) < 0) {
99 LOG(FATAL, "ioctl(%d, TIOCGSERIAL, %p) failed with %d: %s\n",
100 fd_, &serinfo, errno, strerror(errno));
101 }
102 if ((serinfo.flags & ASYNC_SPD_CUST) == 0) {
103 LOG(FATAL, "can not set custom baud rate\n");
104 }
105 if (serinfo.custom_divisor * baud_rate_ != serinfo.baud_base) {
106 LOG(WARNING, "actual baudrate is %d / %d = %f\n",
107 serinfo.baud_base, serinfo.custom_divisor,
108 static_cast<double>(serinfo.baud_base) / serinfo.custom_divisor);
109 }
Daniel Petti059be422013-12-14 19:47:42 -0800110 }
111
Brian Silverman1662a0e2013-12-19 17:50:01 -0800112 if (fcntl(fd_, F_SETFL, 0) != 0) {
113 LOG(FATAL, "fcntl(%d, F_SETFL, 0) failed with %d: %s\n",
114 fd_, errno, strerror(errno));
115 }
Daniel Petti059be422013-12-14 19:47:42 -0800116}
117
Brian Silvermanf6b68842013-12-20 12:34:58 -0800118UartReader::~UartReader() {
Brian Silverman1662a0e2013-12-19 17:50:01 -0800119 delete buf_;
120 if (fd_ > 0) close(fd_);
121}
Daniel Petti059be422013-12-14 19:47:42 -0800122
Brian Silvermanf6b68842013-12-20 12:34:58 -0800123bool UartReader::FindPacket() {
Brian Silverman1662a0e2013-12-19 17:50:01 -0800124 // How many 0 bytes we've found at the front so far.
125 int zeros_found = 0;
126 // How many bytes of the packet we've read in (or -1 if we don't know where
127 // the packet is).
128 int packet_bytes = -1;
129 while (true) {
130 size_t already_read = ::std::max(packet_bytes, 0);
131 ssize_t new_bytes =
132 read(fd_, buf_ + already_read, DATA_STRUCT_SEND_SIZE - already_read);
Brian Silvermaned030062013-12-20 21:03:47 -0800133 LOG(DEBUG, "read %zd, wanted %d\n", new_bytes,
134 DATA_STRUCT_SEND_SIZE - already_read);
135 for (int i = 0; i < new_bytes; ++i) {
136 LOG(DEBUG, "%x\n", buf_[i]);
137 }
Brian Silverman1662a0e2013-12-19 17:50:01 -0800138 if (new_bytes < 0) {
139 if (errno == EINTR) continue;
140 LOG(WARNING, "read(%d, %p, %zd) failed with %d: %s\n",
141 fd_, buf_ + already_read, DATA_STRUCT_SEND_SIZE - already_read,
142 errno, strerror(errno));
143 return false;
Daniel Petti059be422013-12-14 19:47:42 -0800144 }
Daniel Petti059be422013-12-14 19:47:42 -0800145
Brian Silvermanbdc2bd42013-12-20 12:38:14 -0800146 if (packet_bytes != -1) { // we think there's a packet at the beginning of
147 // our buffer
148 for (int to_check = packet_bytes; packet_bytes + new_bytes; ++to_check) {
149 // We shouldn't find any 0s in the middle of what should be a packet.
150 if (buf_[to_check] == 0) {
151 packet_bytes = -1;
152 memmove(buf_, buf_ + to_check, new_bytes - to_check);
153 new_bytes -= to_check;
154 break;
155 }
156 }
157 if (packet_bytes != -1) {
158 packet_bytes += new_bytes;
159 if (packet_bytes == DATA_STRUCT_SEND_SIZE) return true;
160 }
161 }
162 // This can't just be an else because the above code might set it to -1 if
163 // it finds 0s in the middle of a packet.
Brian Silverman1662a0e2013-12-19 17:50:01 -0800164 if (packet_bytes == -1) {
165 // Find the beginning of the packet (aka look for four zero bytes).
166 for (ssize_t checked = 0; checked < new_bytes; ++checked) {
167 if (buf_[checked] == 0) {
168 ++zeros_found;
169 if (zeros_found == 4) {
170 packet_bytes = new_bytes - checked - 1;
171 memmove(buf_, buf_ + checked + 1, packet_bytes);
Daniel Petti059be422013-12-14 19:47:42 -0800172 break;
Daniel Petti059be422013-12-14 19:47:42 -0800173 }
174 } else {
Brian Silverman1662a0e2013-12-19 17:50:01 -0800175 zeros_found = 0;
Daniel Petti059be422013-12-14 19:47:42 -0800176 }
Daniel Petti059be422013-12-14 19:47:42 -0800177 }
178 }
Daniel Petti059be422013-12-14 19:47:42 -0800179 }
Brian Silverman1662a0e2013-12-19 17:50:01 -0800180}
Daniel Petti059be422013-12-14 19:47:42 -0800181
Brian Silvermanf6b68842013-12-20 12:34:58 -0800182bool UartReader::GetPacket(DataStruct *packet) {
Brian Silverman1662a0e2013-12-19 17:50:01 -0800183 if (!FindPacket()) return false;
Daniel Petti059be422013-12-14 19:47:42 -0800184
Brian Silverman1662a0e2013-12-19 17:50:01 -0800185 uint32_t unstuffed = cows_unstuff(reinterpret_cast<uint32_t *>(buf_),
186 DATA_STRUCT_SEND_SIZE / 4,
187 reinterpret_cast<uint32_t *>(packet));
188 if (unstuffed == 0) {
189 LOG(WARNING, "invalid packet\n");
190 } else if (unstuffed != sizeof(packet)) {
191 LOG(WARNING, "packet is %" PRIu32 " words instead of %" PRIu32 "\n",
192 unstuffed, DATA_STRUCT_SEND_SIZE / 4);
Daniel Petti059be422013-12-14 19:47:42 -0800193 }
194
195 // Make sure the checksum checks out.
Brian Silverman1662a0e2013-12-19 17:50:01 -0800196 uint32_t checksum;
197 memcpy(&checksum, buf_ + DATA_STRUCT_SEND_SIZE - 4, 4);
198 if (cape::CalculateChecksum(reinterpret_cast<uint8_t *>(packet),
199 sizeof(DataStruct)) != checksum) {
Daniel Petti059be422013-12-14 19:47:42 -0800200 LOG(WARNING, "Rejecting packet due to checksum failure.\n");
Brian Silverman1662a0e2013-12-19 17:50:01 -0800201 return false;
Daniel Petti059be422013-12-14 19:47:42 -0800202 }
203
Brian Silverman1662a0e2013-12-19 17:50:01 -0800204 return true;
Daniel Petti059be422013-12-14 19:47:42 -0800205}
206
Brian Silverman1662a0e2013-12-19 17:50:01 -0800207} // namespace bbb