blob: 981e40ba65723f46a543d5c9256d36938b670fe2 [file] [log] [blame]
Daniel Pettib36f5b82013-12-15 08:55:04 -08001#include <fcntl.h>
2#include <linux/serial.h>
3#include <termio.h>
4#include <unistd.h>
5
Daniel Petti059be422013-12-14 19:47:42 -08006#include <cmath>
7#include <cstring>
Daniel Petti059be422013-12-14 19:47:42 -08008
9#include "aos/common/logging/logging_impl.h"
10#include "bbb_cape/src/cape/cows.h"
11#include "crc.h"
12#include "uart_receiver.h"
13
14// This is the code for receiving data from the cape via UART.
15// NOTE: In order for this to work, you MUST HAVE "capemgr.enable_partno=BB_UART1"
16// in your BBB's /media/BEAGLEBONE/uEnv.txt file!
17
Daniel Pettib36f5b82013-12-15 08:55:04 -080018// Implementation for setting custom serial baud rates based on this code:
19// <http://jim.sh/ftx/files/linux-custom-baudrate.c>
20
Daniel Petti059be422013-12-14 19:47:42 -080021namespace bbb {
22
Daniel Pettib36f5b82013-12-15 08:55:04 -080023UartReceiver::UartReceiver(uint32_t baud_rate, size_t packet_size) :
Daniel Petti059be422013-12-14 19:47:42 -080024 baud_rate_(baud_rate), packet_size_(packet_size) {
25 //packet_size_ should be a multiple of four.
26 int toadd = packet_size_ % 4;
27 LOG(DEBUG, "Increasing packet size by %d bytes.\n", toadd);
28 packet_size_ += toadd;
29
30 // See cows.h for where this comes from.
31 stuffed_size_ = ((packet_size_ - 1) / (pow(2, 32) - 1) + 1) * 4 + packet_size_;
32
33 buf_ = static_cast<char *>(malloc(stuffed_size_));
34}
35
36UartReceiver::~UartReceiver() {
37 free(buf_);
38}
39
40int UartReceiver::SetUp() {
Daniel Pettib36f5b82013-12-15 08:55:04 -080041 termios options;
42 serial_struct serinfo;
Daniel Petti059be422013-12-14 19:47:42 -080043
44 if ((fd_ = open("/dev/ttyO1", O_RDWR | O_NOCTTY)) < 0) {
45 LOG(FATAL, "Open() failed with error %d.\
46 (Did you read my note in uart_receiver.cc?)\n", fd_);
47 }
Daniel Pettib36f5b82013-12-15 08:55:04 -080048
49 // Must implement an ugly custom divisor.
50 serinfo.reserved_char[0] = 0;
51 if (ioctl(fd_, TIOCGSERIAL, &serinfo) < 0)
Daniel Petti059be422013-12-14 19:47:42 -080052 return -1;
Daniel Pettib36f5b82013-12-15 08:55:04 -080053 serinfo.flags &= ~ASYNC_SPD_MASK;
54 serinfo.flags |= ASYNC_SPD_CUST;
55 serinfo.custom_divisor = (serinfo.baud_base + (baud_rate_ / 2)) / baud_rate_;
56 if (serinfo.custom_divisor < 1)
57 serinfo.custom_divisor = 1;
58 if (ioctl(fd_, TIOCSSERIAL, &serinfo) < 0)
59 LOG(ERROR, "First ioctl failed.\n");
Daniel Petti059be422013-12-14 19:47:42 -080060 return -1;
Daniel Pettib36f5b82013-12-15 08:55:04 -080061 if (ioctl(fd_, TIOCGSERIAL, &serinfo) < 0)
62 LOG(ERROR, "Second ioctl failed.\n");
63 return -1;
64 if (serinfo.custom_divisor * static_cast<int>(baud_rate_)
65 != serinfo.baud_base) {
66 LOG(WARNING, "actual baudrate is %d / %d = %f",
67 serinfo.baud_base, serinfo.custom_divisor,
68 (float)serinfo.baud_base / serinfo.custom_divisor);
Daniel Petti059be422013-12-14 19:47:42 -080069 }
70
Daniel Pettib36f5b82013-12-15 08:55:04 -080071 fcntl(fd_, F_SETFL, 0);
72 tcgetattr(fd_, &options);
73 cfsetispeed(&options, B38400);
74 cfsetospeed(&options, B38400);
75 cfmakeraw(&options);
76 options.c_cflag |= (CLOCAL | CREAD);
77 options.c_cflag &= ~CRTSCTS;
78 options.c_iflag = 0;
79 options.c_oflag = 0;
80 options.c_lflag = 0;
81 // We know the minimum size for packets.
82 // No use in having it do excessive syscalls.
83 options.c_cc[VMIN] = packet_size_;
84 options.c_cc[VTIME] = 0;
85 if (tcsetattr(fd_, TCSANOW, &options) != 0)
86 LOG(ERROR, "Tcsetattr failed.\n");
87 return -1;
Daniel Petti059be422013-12-14 19:47:42 -080088
89 return 0;
90}
91
92int UartReceiver::GetPacket(DataStruct *packet) {
93 int pstarti = 0, bread, cons_zeros = 0;
94 uint32_t readi = 0;
95 bool done = false, has_packet = false;
96 uint32_t ptemp [(stuffed_size_ - 1) / 4 + 1];
97
98 while (!done && buf_used_ < stuffed_size_) {
99 if ((bread = read(fd_, buf_ + buf_used_, stuffed_size_ - buf_used_)) < 0) {
100 LOG(WARNING, "Read() failed with error %d.\n", bread);
101 return -1;
102 }
103 buf_used_ += bread;
104
105 // Find the beginning of the packet.
106 // Look for four bytes of zeros.
107 while (readi < buf_used_) {
108 if (buf_[readi] == 0) {
109 if (cons_zeros == 4) {
110 if (has_packet) {
111 // We got to the end of a packet.
112 done = true;
113 break;
114 } else {
115 // We got to the start of a packet.
116 has_packet = true;
117 pstarti = readi - 3;
118 }
119 } else {
120 ++cons_zeros;
121 }
122 } else {
123 cons_zeros = 0;
124 }
125 }
126 ++readi;
127 }
128
129 // Copy packet data to output.
130 int filled = 0;
131 readi -= 3;
132 for (uint32_t i = pstarti; i < readi - 3; ++i) {
133 ptemp[i] = buf_[i];
134 ++filled;
135 }
136 // Move everything we didn't use to the beginning of the buffer for next time.
137 uint32_t puti = 0;
138 for (uint32_t i = readi; i < stuffed_size_; ++i) {
139 buf_[puti++] = buf_[i];
140 }
141 buf_used_ = stuffed_size_ - readi;
142
143 // Cows algorithm always outputs something 4-byte aligned.
144 if (filled % 4) {
145 LOG(WARNING, "Rejecting packet due to it not being possible\
146 for cows to have created it.\n");
147 return -1;
148 }
149
150 // Unstuff our packet.
151 uint32_t ptemp_unstuffed [packet_size_];
152 uint32_t bunstuffed;
153 if ((bunstuffed = cows_unstuff(ptemp, sizeof(ptemp), ptemp_unstuffed)) == 0) {
154 LOG(WARNING, "Rejecting packet due to failure to unstuff it.\n");
155 return -1;
156 }
157 if (bunstuffed != packet_size_) {
158 LOG(WARNING, "Rejecting packet of wrong size.\
159 Expected packet of size %d, got packet of size %d.\n",
160 packet_size_, bunstuffed);
161 return -1;
162 }
163
164 // Make sure the checksum checks out.
165 uint32_t checksum = static_cast<uint32_t>(ptemp_unstuffed[packet_size_ - 4]);
166 memcpy(packet, ptemp_unstuffed, sizeof(DataStruct));
167 if (cape::CalculateChecksum((uint8_t *)packet, sizeof(DataStruct)) != checksum) {
168 LOG(WARNING, "Rejecting packet due to checksum failure.\n");
169 return -1;
170 }
171
172 return 0;
173}
174
175} //bbb
176