blob: 81bacddb5f0ca02c4d0681ce107f9d0d624e02b3 [file] [log] [blame]
#include <fcntl.h>
#include <linux/serial.h>
#include <termio.h>
#include <unistd.h>
#include <cmath>
#include <cstring>
#include "aos/common/logging/logging_impl.h"
#include "bbb_cape/src/cape/cows.h"
#include "crc.h"
#include "uart_receiver.h"
// This is the code for receiving data from the cape via UART.
// NOTE: In order for this to work, you MUST HAVE "capemgr.enable_partno=BB_UART1"
// in your BBB's /media/BEAGLEBONE/uEnv.txt file!
// Implementation for setting custom serial baud rates based on this code:
// <http://jim.sh/ftx/files/linux-custom-baudrate.c>
namespace bbb {
UartReceiver::UartReceiver(uint32_t baud_rate) :
baud_rate_(baud_rate) {
packet_size_ = DATA_STRUCT_SEND_SIZE;
//packet_size_ should be a multiple of four.
int toadd = packet_size_ % 4;
LOG(DEBUG, "Increasing packet size by %d bytes.\n", toadd);
packet_size_ += toadd;
// See cows.h for where this comes from.
stuffed_size_ = ((packet_size_ - 1) / (pow(2, 32) - 1) + 1) * 4 + packet_size_;
buf_ = static_cast<char *>(malloc(stuffed_size_));
}
UartReceiver::~UartReceiver() {
free(buf_);
}
int UartReceiver::SetUp() {
termios options;
serial_struct serinfo;
if ((fd_ = open("/dev/ttyO1", O_RDWR | O_NOCTTY)) < 0) {
LOG(FATAL, "Open() failed with error %d.\
(Did you read my note in uart_receiver.cc?)\n", fd_);
}
// Must implement an ugly custom divisor.
serinfo.reserved_char[0] = 0;
if (ioctl(fd_, TIOCGSERIAL, &serinfo) < 0)
return -1;
serinfo.flags &= ~ASYNC_SPD_MASK;
serinfo.flags |= ASYNC_SPD_CUST;
serinfo.custom_divisor = (serinfo.baud_base + (baud_rate_ / 2)) / baud_rate_;
if (serinfo.custom_divisor < 1)
serinfo.custom_divisor = 1;
if (ioctl(fd_, TIOCSSERIAL, &serinfo) < 0)
LOG(ERROR, "First ioctl failed.\n");
return -1;
if (ioctl(fd_, TIOCGSERIAL, &serinfo) < 0)
LOG(ERROR, "Second ioctl failed.\n");
return -1;
if (serinfo.custom_divisor * static_cast<int>(baud_rate_)
!= serinfo.baud_base) {
LOG(WARNING, "actual baudrate is %d / %d = %f",
serinfo.baud_base, serinfo.custom_divisor,
(float)serinfo.baud_base / serinfo.custom_divisor);
}
fcntl(fd_, F_SETFL, 0);
tcgetattr(fd_, &options);
cfsetispeed(&options, B38400);
cfsetospeed(&options, B38400);
cfmakeraw(&options);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~CRTSCTS;
options.c_iflag = 0;
options.c_oflag = 0;
options.c_lflag = 0;
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 1;
if (tcsetattr(fd_, TCSANOW, &options) != 0)
LOG(ERROR, "Tcsetattr failed.\n");
return -1;
return 0;
}
int UartReceiver::GetPacket(DataStruct *packet) {
int pstarti = 0, bread, cons_zeros = 0;
uint32_t readi = 0;
bool done = false, has_packet = false;
uint32_t ptemp [(stuffed_size_ - 1) / 4 + 1];
while (!done && buf_used_ < stuffed_size_) {
if ((bread = read(fd_, buf_ + buf_used_, stuffed_size_ - buf_used_)) < 0) {
LOG(WARNING, "Read() failed with error %d.\n", bread);
return -1;
}
buf_used_ += bread;
// Find the beginning of the packet.
// Look for four bytes of zeros.
while (readi < buf_used_) {
if (buf_[readi] == 0) {
if (cons_zeros == 4) {
if (has_packet) {
// We got to the end of a packet.
done = true;
break;
} else {
// We got to the start of a packet.
has_packet = true;
pstarti = readi - 3;
}
} else {
++cons_zeros;
}
} else {
cons_zeros = 0;
}
}
++readi;
}
// Copy packet data to output.
int filled = 0;
readi -= 3; // We read a little into the next packet.
for (uint32_t i = pstarti; i < readi - 3; ++i) {
ptemp[i] = buf_[i];
++filled;
}
// Move everything we didn't use to the beginning of the buffer for next time.
uint32_t puti = 0;
for (uint32_t i = readi; i < stuffed_size_; ++i) {
buf_[puti++] = buf_[i];
}
buf_used_ = stuffed_size_ - readi;
readi = 0;
// Cows algorithm always outputs something 4-byte aligned.
if (filled % 4) {
LOG(WARNING, "Rejecting packet due to it not being possible\
for cows to have created it.\n");
return -1;
}
// Unstuff our packet.
uint32_t ptemp_unstuffed [packet_size_];
uint32_t bunstuffed;
if ((bunstuffed = cows_unstuff(ptemp, sizeof(ptemp), ptemp_unstuffed)) == 0) {
LOG(WARNING, "Rejecting packet due to failure to unstuff it.\n");
return -1;
}
if (bunstuffed != packet_size_) {
LOG(WARNING, "Rejecting packet of wrong size.\
Expected packet of size %d, got packet of size %d.\n",
packet_size_, bunstuffed);
return -1;
}
// Make sure the checksum checks out.
uint32_t checksum = static_cast<uint32_t>(ptemp_unstuffed[packet_size_ - 4]);
// Checksum only gets done on the actual datastruct part of the packet,
// so we'll discard everything after it.
memcpy(packet, ptemp_unstuffed, sizeof(DataStruct));
if (cape::CalculateChecksum((uint8_t *)packet, sizeof(DataStruct)) != checksum) {
LOG(WARNING, "Rejecting packet due to checksum failure.\n");
return -1;
}
return 0;
}
} // bbb