bsidesbadge/src/SerialBridge.cpp

253 lines
6.1 KiB
C++

#include <cstdbool>
#include <cstddef>
#include <cstdint>
#ifdef ARDUINO_SAMD_MKRZERO
#ifndef SAMD21
#define SAMD21
#endif
#endif // ARDUINO_SAMD_MKRZERO
#ifdef ESP32
#define HAS_BRIDGELINK
#define BRIDGELINK_NEEDS_FLUSH true
#include <HardwareSerial.h>
#endif
#ifdef SAMD21
#define HAS_BRIDGELINK
#define BRIDGELINK_NEEDS_FLUSH
#include <Arduino.h>
#include "wiring_private.h" // pinPeripheral() function
#endif
#ifdef __unix__
#include <SoftwareSerial.h>
#include <sys/types.h>
#endif
#include <BadgeLog.h>
#include "SerialBridge.h"
// Include pin assignments
#include "badge_pins.h"
#ifdef SAMD21
// SAMD21 Arduino pinouts for UART bridge use -- we set up a Serial object here on the relevant pins
// UART_RXD on SERCOM4, PAD[0]
// UART_TXD on SERCOM4, PAD[2];
Uart BridgeLink(&sercom4, UART_RXD, UART_TXD, SERCOM_RX_PAD_0, UART_TX_PAD_2);
Uart DebugSerial(&sercom0, UART_SCI_RXD, UART_SCI_TXD, SERCOM_RX_PAD_3, UART_TX_PAD_2);
void SERCOM4_Handler()
{
BridgeLink.IrqHandler();
}
void SERCOM0_Handler()
{
DebugSerial.IrqHandler();
}
#endif
#ifdef ESP32
// ESP32 pinouts don't define a serial bridge on these pins, so we make our own
// We take over UART #1
HardwareSerial BridgeLink(1);
#endif
SerialBridgeLink::SerialBridgeLink()
{
}
void SerialBridgeLink::begin(unsigned long baud)
{
log(LOG_INFO, "Connecting Serial Link");
#ifdef SAMD21
pinPeripheral(UART_RXD, PIO_SERCOM_ALT);
pinPeripheral(UART_TXD, PIO_SERCOM_ALT);
pinPeripheral(UART_SCI_RXD, PIO_SERCOM);
pinPeripheral(UART_SCI_TXD, PIO_SERCOM);
BridgeLink.begin(baud);
#endif
#ifdef ESP32
BridgeLink.begin(baud, SERIAL_8N1, UART_RXD, UART_TXD);
#endif
log(LOG_INFO, "Connected");
}
void SerialBridgeLink::end(void)
{
#ifdef HAS_BRIDGELINK
return BridgeLink.end();
#endif
}
int SerialBridgeLink::available(void)
{
#ifdef HAS_BRIDGELINK
return BridgeLink.available();
#else
return 0;
#endif
}
int SerialBridgeLink::read(void)
{
#ifdef HAS_BRIDGELINK
return BridgeLink.read();
#else
return 0;
#endif
}
void SerialBridgeLink::flush(void)
{
#ifdef HAS_BRIDGELINK
return BridgeLink.flush(BRIDGELINK_NEEDS_FLUSH);
#endif
}
size_t SerialBridgeLink::write(uint8_t value)
{
#ifdef HAS_BRIDGELINK
return BridgeLink.write(value);
#else
return 0;
#endif
}
SerialBridge::SerialBridge()
{
}
void SerialBridge::connect(void)
{
this->link.begin();
}
void SerialBridge::disconnect(void)
{
this->link.end();
}
int SerialBridge::available(void)
{
return this->link.available();
}
/* These are intended to ensure we get always get the start and end of a message, but
* we don't currently do anything to mark framing past this. Any application layer
* protocol on to of this wire protocol can handle SYNACKs if need be.
*/
#define SERIALBRIDGE_SYNC (0b11011001U)
#define SERIALBRIDGE_EOF (0b00100110U)
// 1 SYNC transmits appears to be enough for a mostly reliable line during busy messaging
#define SERIALBRIDGE_SYNC_TX (1U)
bool SerialBridge::sendMessage(const SerialMessage& msg) {
// Send sync bytes
for (int i = 0; i < SERIALBRIDGE_SYNC_TX; ++i) {
if (this->link.write(SERIALBRIDGE_SYNC) != 1) {
return false;
}
}
ssize_t sent = this->send(&msg, SERIALBRIDGE_MESSAGE_SIZE);
if (sent != SERIALBRIDGE_MESSAGE_SIZE) {
return false;
}
// We send a single EOF
if (this->link.write(SERIALBRIDGE_EOF) != 1) {
return false;
}
return true;
}
void SerialBridge::sendAckMessage(uint8_t seqNo) {
SerialMessage msg;
prepareMessage(msg, Message_ACK, seqNo, NULL, 0);
log(LOG_DEBUG, "ACK message %d", seqNo);
this->sendMessage(msg);
}
void SerialBridge::sendNackMessage(uint8_t seqNo) {
SerialMessage msg;
prepareMessage(msg, Message_NACK, seqNo, NULL, 0);
log(LOG_DEBUG, "NACK message %d", seqNo);
this->sendMessage(msg);
}
bool SerialBridge::recvMessage(SerialMessage* msg) {
if (this->link.available() < (SERIALBRIDGE_MESSAGE_SIZE + (SERIALBRIDGE_SYNC_TX))) {
log(LOG_DEBUG, "Not enough bytes to be a minimally valid message");
return false;
}
// Find a SYNC byte
uint8_t sync = 0;
while ((sync != SERIALBRIDGE_SYNC) && (this->link.available() > 0)) {
sync = this->link.read();
}
// Consume SYNC bytes
while ((sync == SERIALBRIDGE_SYNC) && (this->link.available() > 0)) {
sync = this->link.read();
}
// Ensure we haven't just consumed SYNCs
if (sync == SERIALBRIDGE_SYNC) {
log(LOG_ERROR, "Failed to find message");
return false;
}
// Put the first byte in and consume the rest of the message
uint8_t *buf = (uint8_t*)msg;
buf[0] = sync;
buf++;
ssize_t recvd = this->recv((void*)buf, SERIALBRIDGE_MESSAGE_SIZE - 1) + 1;
if (recvd < (SERIALBRIDGE_MESSAGE_SIZE - 1)) {
log(LOG_DEBUG, "Not a full message: received %d expected %d", recvd, SERIALBRIDGE_MESSAGE_SIZE);
}
// Consume until EOF byte found
sync = 0;
while ((sync != SERIALBRIDGE_EOF) && (this->link.available() > 0)) {
sync = this->link.read();
}
if (!validateMessage(*msg)) {
log(LOG_ERROR, "Invalid %s message", messageType(msg->header.type));
// Don't (n)ACK a (n)ACK
if ((msg->header.type != Message_ACK)
&& (msg->header.type != Message_NACK)) {
this->sendNackMessage(msg->header.seqNo);
}
return false;
}
// Don't (n)ACK a (n)ACK
if ((msg->header.type != Message_ACK)
&& (msg->header.type != Message_NACK)) {
this->sendAckMessage(msg->header.seqNo);
}
return true;
}
ssize_t SerialBridge::send(const void *buf, size_t buf_size)
{
size_t i = -1;
#ifdef HAS_BRIDGELINK
uint8_t *p = (uint8_t*) buf;
for ( i = 0; i < buf_size; i++ ) {
if (this->link.write(p[i]) != 1) goto flush;
}
flush:
this->link.flush();
#endif
return (ssize_t)i;
}
ssize_t SerialBridge::recv(void *buf, size_t buf_size)
{
uint8_t *p = (uint8_t*)buf;
int offset = -1;
#ifdef HAS_BRIDGELINK
while (this->link.available() && offset < (int)buf_size) {
++offset;
p[offset] = this->link.read();
}
#endif
return offset;
}