blob: b7e64c449aef6eee9c5f12f507eabe1ae711cdd2 [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 Petti23dcf6c2013-12-19 08:56:41 -080023UartReceiver::UartReceiver(uint32_t baud_rate) :
24 baud_rate_(baud_rate) {
25 packet_size_ = DATA_STRUCT_SEND_SIZE;
Daniel Petti059be422013-12-14 19:47:42 -080026 //packet_size_ should be a multiple of four.
27 int toadd = packet_size_ % 4;
28 LOG(DEBUG, "Increasing packet size by %d bytes.\n", toadd);
29 packet_size_ += toadd;
30
31 // See cows.h for where this comes from.
32 stuffed_size_ = ((packet_size_ - 1) / (pow(2, 32) - 1) + 1) * 4 + packet_size_;
33
34 buf_ = static_cast<char *>(malloc(stuffed_size_));
35}
36
37UartReceiver::~UartReceiver() {
38 free(buf_);
39}
40
41int UartReceiver::SetUp() {
Daniel Pettib36f5b82013-12-15 08:55:04 -080042 termios options;
43 serial_struct serinfo;
Daniel Petti059be422013-12-14 19:47:42 -080044
45 if ((fd_ = open("/dev/ttyO1", O_RDWR | O_NOCTTY)) < 0) {
46 LOG(FATAL, "Open() failed with error %d.\
47 (Did you read my note in uart_receiver.cc?)\n", fd_);
48 }
Daniel Pettib36f5b82013-12-15 08:55:04 -080049
50 // Must implement an ugly custom divisor.
51 serinfo.reserved_char[0] = 0;
52 if (ioctl(fd_, TIOCGSERIAL, &serinfo) < 0)
Daniel Petti059be422013-12-14 19:47:42 -080053 return -1;
Daniel Pettib36f5b82013-12-15 08:55:04 -080054 serinfo.flags &= ~ASYNC_SPD_MASK;
55 serinfo.flags |= ASYNC_SPD_CUST;
56 serinfo.custom_divisor = (serinfo.baud_base + (baud_rate_ / 2)) / baud_rate_;
57 if (serinfo.custom_divisor < 1)
58 serinfo.custom_divisor = 1;
59 if (ioctl(fd_, TIOCSSERIAL, &serinfo) < 0)
60 LOG(ERROR, "First ioctl failed.\n");
Daniel Petti059be422013-12-14 19:47:42 -080061 return -1;
Daniel Pettib36f5b82013-12-15 08:55:04 -080062 if (ioctl(fd_, TIOCGSERIAL, &serinfo) < 0)
63 LOG(ERROR, "Second ioctl failed.\n");
64 return -1;
65 if (serinfo.custom_divisor * static_cast<int>(baud_rate_)
66 != serinfo.baud_base) {
67 LOG(WARNING, "actual baudrate is %d / %d = %f",
68 serinfo.baud_base, serinfo.custom_divisor,
69 (float)serinfo.baud_base / serinfo.custom_divisor);
Daniel Petti059be422013-12-14 19:47:42 -080070 }
71
Daniel Pettib36f5b82013-12-15 08:55:04 -080072 fcntl(fd_, F_SETFL, 0);
73 tcgetattr(fd_, &options);
74 cfsetispeed(&options, B38400);
75 cfsetospeed(&options, B38400);
76 cfmakeraw(&options);
77 options.c_cflag |= (CLOCAL | CREAD);
78 options.c_cflag &= ~CRTSCTS;
79 options.c_iflag = 0;
80 options.c_oflag = 0;
81 options.c_lflag = 0;
82 // We know the minimum size for packets.
83 // No use in having it do excessive syscalls.
84 options.c_cc[VMIN] = packet_size_;
Daniel Petti23dcf6c2013-12-19 08:56:41 -080085 options.c_cc[VTIME] = 1;
Daniel Pettib36f5b82013-12-15 08:55:04 -080086 if (tcsetattr(fd_, TCSANOW, &options) != 0)
87 LOG(ERROR, "Tcsetattr failed.\n");
88 return -1;
Daniel Petti059be422013-12-14 19:47:42 -080089
90 return 0;
91}
92
93int UartReceiver::GetPacket(DataStruct *packet) {
94 int pstarti = 0, bread, cons_zeros = 0;
95 uint32_t readi = 0;
96 bool done = false, has_packet = false;
97 uint32_t ptemp [(stuffed_size_ - 1) / 4 + 1];
98
99 while (!done && buf_used_ < stuffed_size_) {
100 if ((bread = read(fd_, buf_ + buf_used_, stuffed_size_ - buf_used_)) < 0) {
101 LOG(WARNING, "Read() failed with error %d.\n", bread);
102 return -1;
103 }
104 buf_used_ += bread;
105
106 // Find the beginning of the packet.
107 // Look for four bytes of zeros.
108 while (readi < buf_used_) {
109 if (buf_[readi] == 0) {
110 if (cons_zeros == 4) {
111 if (has_packet) {
112 // We got to the end of a packet.
113 done = true;
114 break;
115 } else {
116 // We got to the start of a packet.
117 has_packet = true;
118 pstarti = readi - 3;
119 }
120 } else {
121 ++cons_zeros;
122 }
123 } else {
124 cons_zeros = 0;
125 }
126 }
127 ++readi;
128 }
129
130 // Copy packet data to output.
131 int filled = 0;
Daniel Petti23dcf6c2013-12-19 08:56:41 -0800132 readi -= 3; // We read a little into the next packet.
Daniel Petti059be422013-12-14 19:47:42 -0800133 for (uint32_t i = pstarti; i < readi - 3; ++i) {
134 ptemp[i] = buf_[i];
135 ++filled;
136 }
137 // Move everything we didn't use to the beginning of the buffer for next time.
138 uint32_t puti = 0;
139 for (uint32_t i = readi; i < stuffed_size_; ++i) {
140 buf_[puti++] = buf_[i];
141 }
142 buf_used_ = stuffed_size_ - readi;
Daniel Petti23dcf6c2013-12-19 08:56:41 -0800143 readi = 0;
Daniel Petti059be422013-12-14 19:47:42 -0800144
145 // Cows algorithm always outputs something 4-byte aligned.
146 if (filled % 4) {
147 LOG(WARNING, "Rejecting packet due to it not being possible\
148 for cows to have created it.\n");
149 return -1;
150 }
151
152 // Unstuff our packet.
153 uint32_t ptemp_unstuffed [packet_size_];
154 uint32_t bunstuffed;
155 if ((bunstuffed = cows_unstuff(ptemp, sizeof(ptemp), ptemp_unstuffed)) == 0) {
156 LOG(WARNING, "Rejecting packet due to failure to unstuff it.\n");
157 return -1;
158 }
159 if (bunstuffed != packet_size_) {
160 LOG(WARNING, "Rejecting packet of wrong size.\
161 Expected packet of size %d, got packet of size %d.\n",
162 packet_size_, bunstuffed);
163 return -1;
164 }
165
166 // Make sure the checksum checks out.
167 uint32_t checksum = static_cast<uint32_t>(ptemp_unstuffed[packet_size_ - 4]);
Daniel Petti23dcf6c2013-12-19 08:56:41 -0800168 // Checksum only gets done on the actual datastruct part of the packet,
169 // so we'll discard everything after it.
Daniel Petti059be422013-12-14 19:47:42 -0800170 memcpy(packet, ptemp_unstuffed, sizeof(DataStruct));
171 if (cape::CalculateChecksum((uint8_t *)packet, sizeof(DataStruct)) != checksum) {
172 LOG(WARNING, "Rejecting packet due to checksum failure.\n");
173 return -1;
174 }
175
176 return 0;
177}
178
Daniel Petti23dcf6c2013-12-19 08:56:41 -0800179} // bbb
Daniel Petti059be422013-12-14 19:47:42 -0800180