Add C++ Peripheral class

This commit is contained in:
Allen Hill
2026-06-25 18:16:38 -07:00
parent 02b370dd06
commit 1877bb0658
9 changed files with 391 additions and 298 deletions
+13 -10
View File
@@ -33,22 +33,25 @@ set(AVCLAN_TARGET avr-attiny3216 CACHE STRING "Hardware target (port) to build")
add_library(avclan STATIC
src/avclan/avclan_frame.c
src/avclan/avclan_protocol.c
src/avclan/cdchanger.c)
src/avclan/cdchanger.c
src/avclan/peripheral.cc
)
add_executable(mockingboard
src/sniffer.cc
src/com232.c)
# avclan exports its public generic headers (src/avclan) to consumers and
# reaches into src/ for sibling headers (com232.h, board.h) during its own
# build. The selected target adds its own per-target include path.
target_include_directories(avclan
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/avclan
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(mockingboard avclan)
target_include_directories(avclan PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/avclan
${CMAKE_CURRENT_SOURCE_DIR}/src)
# Pull in the selected hardware target: its port sources, per-target headers,
# hardware-specific compile options/definitions, device-pack handling, and the
# flashing target. Added after the targets above so it can extend them.
add_subdirectory(src/avclan/target/${AVCLAN_TARGET})
add_executable(mockingboard
src/sniffer.cc
src/com232.c
)
target_link_libraries(mockingboard avclan)
+2 -2
View File
@@ -137,7 +137,7 @@ typedef enum avclan_bit : uint8_t {
// progress/success before an error occured, with 0 being no errors
typedef enum : uint8_t {
rNO_ERROR = 0x00,
rBAD_DATA_PARITY,
rBAD_DATA_PARITY = 0x01,
rBAD_LENGTH_RANGE,
rBAD_LENGTH_PARITY,
rBAD_PERIPHERAL_PARITY,
@@ -150,7 +150,7 @@ typedef enum : uint8_t {
typedef enum : uint8_t {
sNO_ERROR = 0x00,
sNAK_DATA,
sNAK_DATA = 0x01,
sNAK_MESSAGE_LENGTH,
sNAK_CONTROL,
sNAK_ADDRESS,
-256
View File
@@ -10,262 +10,6 @@
#include "avclan_phy.h" // bus symbol I/O + transaction guard (target-provided)
#include "com232.h" // error logging
avclan_readerr_t AVCLAN_readframe(AVCLAN_frame_t *frame, log_t print) {
struct errtype {
avclan_readerr_t errno;
union {
uint8_t val; // BAD_LENGTH_RANGE: the out-of-range length value
struct {
uint16_t read_val;
uint8_t parity; // received (bad) parity bit
};
};
} err = {0};
AVCLAN_stopEvent(); // quiesce contending sources during the read
uint8_t tmp = 0;
err.errno = AVCLAN_readstartbit();
if (err.errno)
goto handle_err;
AVCLAN_readbits(&tmp, 1);
frame->is_unicast = tmp;
uint8_t parity = AVCLAN_readbits(&frame->controller_addr, 12);
AVCLAN_readbits(&tmp, 1);
if (parity != (tmp &= 1)) {
err.errno = rBAD_CONTROLLER_PARITY;
if (print.verbose) {
err.read_val = frame->controller_addr;
err.parity = tmp;
}
goto handle_err;
}
parity = AVCLAN_readbits(&frame->peripheral_addr, 12);
AVCLAN_readbits(&tmp, 1);
if (parity != (tmp &= 1)) {
err.errno = rBAD_PERIPHERAL_PARITY;
if (print.verbose) {
err.read_val = frame->peripheral_addr;
err.parity = tmp;
}
goto handle_err;
}
bool shouldACK = !AVCLAN_ismuted() && (frame->peripheral_addr == DEVICE_ADDR);
if (shouldACK)
AVCLAN_sendbit_ACK();
else
AVCLAN_readbits(&tmp, 1);
parity = AVCLAN_readbits(&frame->control, 4);
AVCLAN_readbits(&tmp, 1);
if (parity != (tmp &= 1)) {
err.errno = rBAD_CONTROL_PARITY;
if (print.verbose) {
err.read_val = frame->control;
err.parity = tmp;
}
goto handle_err;
} else if (shouldACK) {
AVCLAN_sendbit_ACK();
} else {
AVCLAN_readbits(&tmp, 1);
}
parity = AVCLAN_readbyte(&frame->length);
AVCLAN_readbits(&tmp, 1);
if (parity != (tmp &= 1)) {
err.errno = rBAD_LENGTH_PARITY;
if (print.verbose) {
err.read_val = frame->length;
err.parity = tmp;
}
goto handle_err;
} else if (shouldACK) {
AVCLAN_sendbit_ACK();
} else {
AVCLAN_readbits(&tmp, 1);
}
if (frame->length == 0 || frame->length > MAXMSGLEN) {
err.errno = rBAD_LENGTH_RANGE;
err.val = frame->length;
goto handle_err;
}
for (uint8_t i = 0; i < frame->length; i++) {
parity = AVCLAN_readbyte(&frame->data[i]);
AVCLAN_readbits(&tmp, 1);
if (parity != (tmp &= 1)) {
err.errno = rBAD_DATA_PARITY;
if (print.verbose) {
err.read_val = frame->data[i];
err.parity = tmp;
}
goto handle_err;
} else if (shouldACK) {
AVCLAN_sendbit_ACK();
} else {
AVCLAN_readbits(&tmp, 1);
}
}
if (false) {
handle_err:;
AVCLAN_startEvent();
RS232_Print("ERR(read): ");
switch (err.errno) {
case rLATCHED_COMPARATOR: RS232_Print("latched comparator"); break;
case rSTARTBIT_TOO_SHORT: RS232_Print("start bit too short"); break;
case rSTARTBIT_TOO_LONG: RS232_Print("start bit too long"); break;
case rBAD_CONTROLLER_PARITY:
RS232_Print("reading controller addr.");
goto VERBOSE;
case rBAD_PERIPHERAL_PARITY:
RS232_Print("reading peripheral addr.");
goto VERBOSE;
case rBAD_CONTROL_PARITY: RS232_Print("reading control"); goto VERBOSE;
case rBAD_LENGTH_PARITY: RS232_Print("reading length"); goto VERBOSE;
case rBAD_LENGTH_RANGE:
RS232_Print("bad length 0x");
RS232_PrintHex4(err.val);
break;
case rBAD_DATA_PARITY: RS232_Print("reading data"); goto VERBOSE;
case rNO_ERROR:
__builtin_unreachable();
VERBOSE:
if (print.verbose) {
RS232_Print("; read 0x");
RS232_PrintHex(err.read_val);
RS232_Print(" and got bad parity ");
RS232_PrintHex4(err.parity);
}
}
RS232_Print("\n");
} else {
AVCLAN_startEvent();
}
// Only print if some data has been correctly received
if (print.print && (err.errno < rSTARTBIT_TOO_SHORT)) {
if (err.errno > rBAD_DATA_PARITY)
frame->length = 0;
AVCLAN_printframe(frame, print.binary);
}
return err.errno;
}
avclan_senderr_t AVCLAN_sendframe(const AVCLAN_frame_t *frame, log_t print) {
struct errtype {
// Error enum is ordered such that a lower numeric value corresponds to more
// success
avclan_senderr_t errno;
uint8_t val;
} err = {0};
if (AVCLAN_ismuted()) {
err.errno = sMUTED;
goto handle_err;
}
AVCLAN_stopEvent();
if (!AVCLAN_sendstartbit()) {
// Some other device is already driving the bus
err.errno = sBUSY;
goto handle_err;
}
AVCLAN_sendbits(&(uint8_t){frame->is_unicast}, 1);
avclan_bit_t parity = AVCLAN_sendbits(&frame->controller_addr, 12);
AVCLAN_sendbit(parity);
parity = AVCLAN_sendbits(&frame->peripheral_addr, 12);
AVCLAN_sendbit(parity);
if (frame->is_unicast && !AVCLAN_readbit_ACK()) {
err.errno = sNAK_ADDRESS;
goto handle_err;
}
parity = AVCLAN_sendbits(&frame->control, 4);
AVCLAN_sendbit(parity);
if (frame->is_unicast && !AVCLAN_readbit_ACK()) {
err.errno = sNAK_CONTROL;
goto handle_err;
}
parity = AVCLAN_sendbyte(&frame->length); // data length
AVCLAN_sendbit(parity);
if (frame->is_unicast && !AVCLAN_readbit_ACK()) {
err.errno = sNAK_MESSAGE_LENGTH;
goto handle_err;
}
for (uint8_t i = 0; i < frame->length; i++) {
parity = AVCLAN_sendbyte(&frame->data[i]);
AVCLAN_sendbit(parity);
// Based on the µPD6708 datasheet, ACK bit for broadcast doesn't seem
// necessary (i.e. This deviates from the previous broadcast specific
// function that sent an extra `1` bit after each byte/parity)
if (frame->is_unicast && !AVCLAN_readbit_ACK()) {
err.errno = sNAK_DATA;
err.val = i;
goto handle_err;
}
// else
// AVCLAN_sendbit_1();
}
// back to read mode
if (false) {
handle_err:;
AVCLAN_startEvent();
RS232_Print("Error");
switch (err.errno) {
case sMUTED: RS232_Print(": Device muted"); break;
case sBUSY: RS232_Print(": Busy bus"); break;
case sNAK_ADDRESS:
case sNAK_CONTROL:
case sNAK_MESSAGE_LENGTH:
case sNAK_DATA:
RS232_Print(" NAK: ");
switch (err.errno) {
case sNAK_ADDRESS: RS232_Print("address"); break;
case sNAK_CONTROL: RS232_Print("Control"); break;
case sNAK_MESSAGE_LENGTH: RS232_Print("Message length"); break;
case sNAK_DATA:
RS232_Print(" data[");
RS232_PrintDec(err.val);
RS232_Print("]");
break;
case sNO_ERROR:
case sMUTED:
case sBUSY: __builtin_unreachable();
}
break;
case sNO_ERROR: __builtin_unreachable();
}
RS232_Print("\n");
} else {
AVCLAN_startEvent();
}
if (print.print)
AVCLAN_printframe(frame, print.binary);
return err.errno;
}
void AVCLAN_printframe(const AVCLAN_frame_t *frame, bool binary) {
if (binary) {
uint8_t buffer[8];
-2
View File
@@ -39,8 +39,6 @@
extern "C" {
#endif
avclan_readerr_t AVCLAN_readframe(AVCLAN_frame_t *frame, log_t print);
avclan_senderr_t AVCLAN_sendframe(const AVCLAN_frame_t *frame, log_t print);
void AVCLAN_printframe(const AVCLAN_frame_t *frame, bool binary);
uint8_t AVCLAN_parseframe(const uint8_t *bytes, uint8_t len,
AVCLAN_frame_t *frame);
+35 -14
View File
@@ -53,20 +53,6 @@ uint8_t AVCLAN_readbitsi(uint8_t *bits, uint8_t len);
uint8_t AVCLAN_readbitsl(uint16_t *bits, int8_t len);
uint8_t AVCLAN_readbyte(uint8_t *byte);
#define AVCLAN_sendbits(bits, len) \
_Generic((bits), \
const uint16_t *: AVCLAN_sendbitsl, \
uint16_t *: AVCLAN_sendbitsl, \
const uint8_t *: AVCLAN_sendbitsi, \
uint8_t *: AVCLAN_sendbitsi)(bits, len)
#define AVCLAN_readbits(bits, len) \
_Generic((bits), \
const uint16_t *: AVCLAN_readbitsl, \
uint16_t *: AVCLAN_readbitsl, \
const uint8_t *: AVCLAN_readbitsi, \
uint8_t *: AVCLAN_readbitsi)(bits, len)
#ifndef NDEBUG
// Sample and dump bus bit timing over the serial link (REPL `M`).
void AVCLan_Measure(void);
@@ -75,3 +61,38 @@ void AVCLan_Measure(void);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
template <class T, auto N> avclan_bit_t AVCLAN_sendbits(T bits);
template <class T, auto N> avclan_bit_t AVCLAN_readbits(T *bits);
// Temporary specializations bridging to legacy C API
// Replace with proper (single?) template when phy has been ported
template <auto N>
requires(N <= 8)
avclan_bit_t AVCLAN_sendbits(uint8_t bits) {
return AVCLAN_sendbitsi(&bits, N);
}
template <auto N>
requires(N <= 16)
avclan_bit_t AVCLAN_sendbits(uint16_t bits) {
return AVCLAN_sendbitsl(&bits, N);
}
template <> inline avclan_bit_t AVCLAN_sendbits<8>(uint8_t byte) {
return AVCLAN_sendbyte(&byte);
}
template <auto N>
requires(N <= 8)
avclan_bit_t AVCLAN_readbits(uint8_t *bits) {
return static_cast<avclan_bit_t>(AVCLAN_readbitsi(bits, N));
}
template <auto N>
requires(N <= 16)
avclan_bit_t AVCLAN_readbits(uint16_t *bits) {
return static_cast<avclan_bit_t>(AVCLAN_readbitsl(bits, N));
}
template <> inline avclan_bit_t AVCLAN_readbits<8>(uint8_t *byte) {
return static_cast<avclan_bit_t>(AVCLAN_readbyte(byte));
}
#endif
+278
View File
@@ -0,0 +1,278 @@
// copyright (C) 2006 Marcin Slonicki <marcin@softservice.com.pl>
// copyright (C) 2007 Louis Frigon
// Copyright (C) 2015 Allen Hill <allenofthehills@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "peripheral.hpp"
#include "avclan_defs.h"
#include "avclan_frame.h"
#include "avclan_phy.h" // bus symbol I/O + transaction guard (target-provided)
#include "com232.h" // error logging
#include <cstdint>
namespace avclan {
Peripheral::Error::Read Peripheral::read(AVCLAN_frame_t *in, log_t print) {
struct errtype {
Error::Read errno;
union {
uint8_t val; // BAD_LENGTH_RANGE: the out-of-range length value
struct {
uint8_t parity; // received (bad) parity bit
uint16_t read_val;
};
};
} err = {};
using enum Error::Read;
AVCLAN_stopEvent(); // quiesce contending sources during the read
bool shouldACK = false;
uint8_t tmp = 0, parity = 0;
err.errno = Error::Read{AVCLAN_readstartbit()};
if (static_cast<uint8_t>(err.errno))
goto handle_err;
AVCLAN_readbits<1>(&tmp);
in->is_unicast = tmp;
parity = AVCLAN_readbits<12>(&in->controller_addr);
AVCLAN_readbits<1>(&tmp);
if (parity != (tmp &= 1)) {
err.errno = BAD_CONTROLLER_PARITY;
if (print.verbose) {
err.read_val = in->controller_addr;
err.parity = tmp;
}
goto handle_err;
}
parity = AVCLAN_readbits<12>(&in->peripheral_addr);
AVCLAN_readbits<1>(&tmp);
if (parity != (tmp &= 1)) {
err.errno = BAD_PERIPHERAL_PARITY;
if (print.verbose) {
err.read_val = in->peripheral_addr;
err.parity = tmp;
}
goto handle_err;
}
shouldACK = !AVCLAN_ismuted() && (in->peripheral_addr == address);
if (shouldACK)
AVCLAN_sendbit_ACK();
else
AVCLAN_readbits<1>(&tmp);
parity = AVCLAN_readbits<4>(&in->control);
AVCLAN_readbits<1>(&tmp);
if (parity != (tmp &= 1)) {
err.errno = BAD_CONTROL_PARITY;
if (print.verbose) {
err.read_val = in->control;
err.parity = tmp;
}
goto handle_err;
} else if (shouldACK) {
AVCLAN_sendbit_ACK();
} else {
AVCLAN_readbits<1>(&tmp);
}
parity = AVCLAN_readbyte(&in->length);
AVCLAN_readbits<1>(&tmp);
if (parity != (tmp &= 1)) {
err.errno = BAD_LENGTH_PARITY;
if (print.verbose) {
err.read_val = in->length;
err.parity = tmp;
}
goto handle_err;
} else if (shouldACK) {
AVCLAN_sendbit_ACK();
} else {
AVCLAN_readbits<1>(&tmp);
}
if (in->length == 0 || in->length > MAXMSGLEN) {
err.errno = BAD_LENGTH_RANGE;
err.val = in->length;
goto handle_err;
}
for (uint8_t i = 0; i < in->length; i++) {
parity = AVCLAN_readbits<8>(&in->data[i]);
AVCLAN_readbits<1>(&tmp);
if (parity != (tmp &= 1)) {
err.errno = BAD_DATA_PARITY;
if (print.verbose) {
err.read_val = in->data[i];
err.parity = tmp;
}
goto handle_err;
} else if (shouldACK) {
AVCLAN_sendbit_ACK();
} else {
AVCLAN_readbits<1>(&tmp);
}
}
if (false) {
handle_err:;
AVCLAN_startEvent();
RS232_Print("ERR(read): ");
switch (err.errno) {
case BAD_STARTBIT: RS232_Print("bad start bit (other)"); break;
case STARTBIT_TOO_SHORT: RS232_Print("bad start bit (short)"); break;
case STARTBIT_TOO_LONG: RS232_Print("bad start bit (long)"); break;
case BAD_CONTROLLER_PARITY:
RS232_Print("reading controller addr.");
goto VERBOSE;
case BAD_PERIPHERAL_PARITY:
RS232_Print("reading peripheral addr.");
goto VERBOSE;
case BAD_CONTROL_PARITY: RS232_Print("reading control"); goto VERBOSE;
case BAD_LENGTH_PARITY: RS232_Print("reading length"); goto VERBOSE;
case BAD_LENGTH_RANGE:
RS232_Print("bad length 0x");
RS232_PrintHex4(err.val);
break;
case BAD_DATA_PARITY:
RS232_Print("reading data");
goto VERBOSE;
VERBOSE:
if (print.verbose) {
RS232_Print("; read 0x");
RS232_PrintHex(err.read_val);
RS232_Print(" and got bad parity ");
RS232_PrintHex4(err.parity);
}
}
RS232_Print("\n");
} else {
AVCLAN_startEvent();
}
// Only print if some data has been correctly received
if (print.print && (err.errno < STARTBIT_TOO_SHORT)) {
if (err.errno > BAD_DATA_PARITY)
in->length = 0;
AVCLAN_printframe(in, print.binary);
}
return err.errno;
}
Peripheral::Error::Send Peripheral::send(const AVCLAN_frame_t *out,
log_t print) {
struct errtype {
// Error enum is ordered such that a lower numeric value corresponds to
// more success
Error::Send errno;
uint8_t val;
} err = {};
using enum Error::Send;
avclan_bit_t parity;
if (AVCLAN_ismuted()) {
err.errno = MUTED;
goto handle_err;
}
AVCLAN_stopEvent();
if (!AVCLAN_sendstartbit()) {
// Some other device is already driving the bus
err.errno = BUSY;
goto handle_err;
}
AVCLAN_sendbits<1>(static_cast<uint8_t>(out->is_unicast));
parity = AVCLAN_sendbits<12>(out->controller_addr);
AVCLAN_sendbit(parity);
parity = AVCLAN_sendbits<12>(address);
AVCLAN_sendbit(parity);
if (out->is_unicast && !AVCLAN_readbit_ACK()) {
err.errno = NAK_ADDRESS;
goto handle_err;
}
parity = AVCLAN_sendbits<4>(out->control);
AVCLAN_sendbit(parity);
if (out->is_unicast && !AVCLAN_readbit_ACK()) {
err.errno = NAK_CONTROL;
goto handle_err;
}
parity = AVCLAN_sendbits<8>(out->length); // data length
AVCLAN_sendbit(parity);
if (out->is_unicast && !AVCLAN_readbit_ACK()) {
err.errno = NAK_MESSAGE_LENGTH;
goto handle_err;
}
for (uint8_t i = 0; i < out->length; i++) {
parity = AVCLAN_sendbits<8>(out->data[i]);
AVCLAN_sendbit(parity);
// Based on the µPD6708 datasheet, ACK bit for broadcast doesn't seem
// necessary (i.e. This deviates from the previous broadcast specific
// function that sent an extra `1` bit after each byte/parity)
if (out->is_unicast && !AVCLAN_readbit_ACK()) {
err.errno = NAK_DATA;
err.val = i;
goto handle_err;
}
// else
// AVCLAN_sendbit_1();
}
// back to read mode
if (false) {
handle_err:;
AVCLAN_startEvent();
RS232_Print("Error");
switch (err.errno) {
case MUTED: RS232_Print(": Device muted"); break;
case BUSY: RS232_Print(": Busy bus"); break;
case NAK_ADDRESS:
case NAK_CONTROL:
case NAK_MESSAGE_LENGTH:
case NAK_DATA:
RS232_Print(" NAK: ");
switch (err.errno) {
case NAK_ADDRESS: RS232_Print("address"); break;
case NAK_CONTROL: RS232_Print("Control"); break;
case NAK_MESSAGE_LENGTH: RS232_Print("Message length"); break;
case NAK_DATA:
RS232_Print(" data[");
RS232_PrintDec(err.val);
RS232_Print("]");
break;
case MUTED:
case BUSY: __builtin_unreachable();
}
break;
}
RS232_Print("\n");
} else {
AVCLAN_startEvent();
}
if (print.print)
AVCLAN_printframe(out, print.binary);
return err.errno;
}
} // namespace avclan
+46
View File
@@ -0,0 +1,46 @@
// copyright (C) 2006 Marcin Slonicki <marcin@softservice.com.pl>
// copyright (C) 2007 Louis Frigon
// Copyright (C) 2015 Allen Hill <allenofthehills@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "avclan_defs.h"
#include <cstdint>
namespace avclan {
class Peripheral {
public:
struct Error {
// Error enums are ordered such that a lower numeric value corresponds to
// more progress/success before an error occured, with 0 being no errors
enum class Read : uint8_t {
BAD_DATA_PARITY = rBAD_DATA_PARITY, // = 0x01
BAD_LENGTH_RANGE = rBAD_LENGTH_RANGE,
BAD_LENGTH_PARITY = rBAD_LENGTH_PARITY,
BAD_PERIPHERAL_PARITY = rBAD_PERIPHERAL_PARITY,
BAD_CONTROLLER_PARITY = rBAD_CONTROLLER_PARITY,
BAD_CONTROL_PARITY = rBAD_CONTROL_PARITY,
STARTBIT_TOO_SHORT = rSTARTBIT_TOO_SHORT,
STARTBIT_TOO_LONG = rSTARTBIT_TOO_LONG,
BAD_STARTBIT = rLATCHED_COMPARATOR,
};
enum class Send : uint8_t {
NAK_DATA = sNAK_DATA, // = 0x01
NAK_MESSAGE_LENGTH = sNAK_MESSAGE_LENGTH,
NAK_CONTROL = sNAK_CONTROL,
NAK_ADDRESS = sNAK_ADDRESS,
BUSY = sBUSY,
MUTED = sMUTED,
};
};
Peripheral(uint16_t address) : address{address} {}
Error::Read read(AVCLAN_frame_t *in, log_t print);
Error::Send send(const AVCLAN_frame_t *out, log_t print);
private:
const uint16_t address;
};
} // namespace avclan
@@ -89,7 +89,7 @@ FetchContent_MakeAvailable(avr_libstdcpp)
add_library(libstdcpp INTERFACE)
target_include_directories(libstdcpp SYSTEM
INTERFACE ${avr_libstdcpp_SOURCE_DIR}/include)
target_link_libraries(avclan INTERFACE libstdcpp)
target_link_libraries(avclan PUBLIC libstdcpp)
endif()
+16 -13
View File
@@ -10,6 +10,7 @@
#include "avclandrv.h"
#include "board.h"
#include "com232.h"
#include "peripheral.hpp"
#include "queue.hpp"
const char *const offon[] = {"OFF", "ON"};
@@ -59,7 +60,6 @@ int main() {
uint8_t data_tmp[MAXMSGLEN + sizeof(AVCLAN_frame_t)];
uint8_t seqIdx = 0; // current index in data_tmp
uint8_t err = 0;
uint8_t failedStatusReports = 0;
// Temporary, direct access is questionable since cache has ownership
@@ -69,16 +69,19 @@ int main() {
const AVCLAN_frame_t *lastStatus = nullptr;
avclan::Peripheral cd_changer(0x360);
using Error = avclan::Peripheral::Error;
Setup();
print_help();
while (true) {
if (AVCLAN_busActive()) {
if (auto msg = cache.pop()) {
err = AVCLAN_readframe(msg.get(), (log_t){.print = printAllFrames,
.binary = printBinary,
.verbose = verbose});
if (!err)
auto err = cd_changer.read(msg.get(), (log_t){.print = printAllFrames,
.binary = printBinary,
.verbose = verbose});
if (err == Error::Read{0x00})
incoming.push(std::move(msg));
} else {
RS232_Print("!! Dropping an incoming message; cache is empty !!\n");
@@ -108,18 +111,18 @@ int main() {
}
if (auto out = outgoing.pop()) {
err = AVCLAN_sendframe(
auto err = cd_changer.send(
out.get(), (log_t){.print = printAllFrames, .binary = printBinary});
if (err || (reaction_t)out->reaction == r_SendOnly) {
if (err && out.get() == lastStatus && ++failedStatusReports > 1) {
failedStatusReports = 0;
AVCLAN_stopPlaying(); // Disable periodic updates if e.g. no-one's
// listening (car was turned off?)
}
} else {
if (err == Error::Send{0x00} && (reaction_t)out->reaction > r_SendOnly) {
AVCLAN_statemachine(out.get());
if (out->reaction)
outgoing.push(std::move(out));
} else if (err == Error::Send::NAK_ADDRESS && out.get() == lastStatus &&
++failedStatusReports > 1) {
failedStatusReports = 0;
AVCLAN_stopPlaying(); // Disable periodic updates if e.g. no-one's
// listening (car was turned off?)
}
}