mirror of
https://github.com/halleysfifthinc/AVCLAN-Mockingboard.git
synced 2026-08-07 01:13:18 +00:00
Add C++ Peripheral class
This commit is contained in:
+13
-10
@@ -33,22 +33,25 @@ set(AVCLAN_TARGET avr-attiny3216 CACHE STRING "Hardware target (port) to build")
|
|||||||
add_library(avclan STATIC
|
add_library(avclan STATIC
|
||||||
src/avclan/avclan_frame.c
|
src/avclan/avclan_frame.c
|
||||||
src/avclan/avclan_protocol.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
|
# avclan exports its public generic headers (src/avclan) to consumers and
|
||||||
# reaches into src/ for sibling headers (com232.h, board.h) during its own
|
# reaches into src/ for sibling headers (com232.h, board.h) during its own
|
||||||
# build. The selected target adds its own per-target include path.
|
# build. The selected target adds its own per-target include path.
|
||||||
target_include_directories(avclan
|
target_include_directories(avclan PUBLIC
|
||||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src/avclan
|
${CMAKE_CURRENT_SOURCE_DIR}/src/avclan
|
||||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||||
|
|
||||||
target_link_libraries(mockingboard avclan)
|
|
||||||
|
|
||||||
# Pull in the selected hardware target: its port sources, per-target headers,
|
# Pull in the selected hardware target: its port sources, per-target headers,
|
||||||
# hardware-specific compile options/definitions, device-pack handling, and the
|
# hardware-specific compile options/definitions, device-pack handling, and the
|
||||||
# flashing target. Added after the targets above so it can extend them.
|
# flashing target. Added after the targets above so it can extend them.
|
||||||
add_subdirectory(src/avclan/target/${AVCLAN_TARGET})
|
add_subdirectory(src/avclan/target/${AVCLAN_TARGET})
|
||||||
|
|
||||||
|
add_executable(mockingboard
|
||||||
|
src/sniffer.cc
|
||||||
|
src/com232.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(mockingboard avclan)
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ typedef enum avclan_bit : uint8_t {
|
|||||||
// progress/success before an error occured, with 0 being no errors
|
// progress/success before an error occured, with 0 being no errors
|
||||||
typedef enum : uint8_t {
|
typedef enum : uint8_t {
|
||||||
rNO_ERROR = 0x00,
|
rNO_ERROR = 0x00,
|
||||||
rBAD_DATA_PARITY,
|
rBAD_DATA_PARITY = 0x01,
|
||||||
rBAD_LENGTH_RANGE,
|
rBAD_LENGTH_RANGE,
|
||||||
rBAD_LENGTH_PARITY,
|
rBAD_LENGTH_PARITY,
|
||||||
rBAD_PERIPHERAL_PARITY,
|
rBAD_PERIPHERAL_PARITY,
|
||||||
@@ -150,7 +150,7 @@ typedef enum : uint8_t {
|
|||||||
|
|
||||||
typedef enum : uint8_t {
|
typedef enum : uint8_t {
|
||||||
sNO_ERROR = 0x00,
|
sNO_ERROR = 0x00,
|
||||||
sNAK_DATA,
|
sNAK_DATA = 0x01,
|
||||||
sNAK_MESSAGE_LENGTH,
|
sNAK_MESSAGE_LENGTH,
|
||||||
sNAK_CONTROL,
|
sNAK_CONTROL,
|
||||||
sNAK_ADDRESS,
|
sNAK_ADDRESS,
|
||||||
|
|||||||
@@ -10,262 +10,6 @@
|
|||||||
#include "avclan_phy.h" // bus symbol I/O + transaction guard (target-provided)
|
#include "avclan_phy.h" // bus symbol I/O + transaction guard (target-provided)
|
||||||
#include "com232.h" // error logging
|
#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) {
|
void AVCLAN_printframe(const AVCLAN_frame_t *frame, bool binary) {
|
||||||
if (binary) {
|
if (binary) {
|
||||||
uint8_t buffer[8];
|
uint8_t buffer[8];
|
||||||
|
|||||||
@@ -39,8 +39,6 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#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);
|
void AVCLAN_printframe(const AVCLAN_frame_t *frame, bool binary);
|
||||||
uint8_t AVCLAN_parseframe(const uint8_t *bytes, uint8_t len,
|
uint8_t AVCLAN_parseframe(const uint8_t *bytes, uint8_t len,
|
||||||
AVCLAN_frame_t *frame);
|
AVCLAN_frame_t *frame);
|
||||||
|
|||||||
+35
-14
@@ -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_readbitsl(uint16_t *bits, int8_t len);
|
||||||
uint8_t AVCLAN_readbyte(uint8_t *byte);
|
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
|
#ifndef NDEBUG
|
||||||
// Sample and dump bus bit timing over the serial link (REPL `M`).
|
// Sample and dump bus bit timing over the serial link (REPL `M`).
|
||||||
void AVCLan_Measure(void);
|
void AVCLan_Measure(void);
|
||||||
@@ -75,3 +61,38 @@ void AVCLan_Measure(void);
|
|||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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)
|
add_library(libstdcpp INTERFACE)
|
||||||
target_include_directories(libstdcpp SYSTEM
|
target_include_directories(libstdcpp SYSTEM
|
||||||
INTERFACE ${avr_libstdcpp_SOURCE_DIR}/include)
|
INTERFACE ${avr_libstdcpp_SOURCE_DIR}/include)
|
||||||
target_link_libraries(avclan INTERFACE libstdcpp)
|
target_link_libraries(avclan PUBLIC libstdcpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+16
-13
@@ -10,6 +10,7 @@
|
|||||||
#include "avclandrv.h"
|
#include "avclandrv.h"
|
||||||
#include "board.h"
|
#include "board.h"
|
||||||
#include "com232.h"
|
#include "com232.h"
|
||||||
|
#include "peripheral.hpp"
|
||||||
#include "queue.hpp"
|
#include "queue.hpp"
|
||||||
|
|
||||||
const char *const offon[] = {"OFF", "ON"};
|
const char *const offon[] = {"OFF", "ON"};
|
||||||
@@ -59,7 +60,6 @@ int main() {
|
|||||||
uint8_t data_tmp[MAXMSGLEN + sizeof(AVCLAN_frame_t)];
|
uint8_t data_tmp[MAXMSGLEN + sizeof(AVCLAN_frame_t)];
|
||||||
uint8_t seqIdx = 0; // current index in data_tmp
|
uint8_t seqIdx = 0; // current index in data_tmp
|
||||||
|
|
||||||
uint8_t err = 0;
|
|
||||||
uint8_t failedStatusReports = 0;
|
uint8_t failedStatusReports = 0;
|
||||||
|
|
||||||
// Temporary, direct access is questionable since cache has ownership
|
// Temporary, direct access is questionable since cache has ownership
|
||||||
@@ -69,16 +69,19 @@ int main() {
|
|||||||
|
|
||||||
const AVCLAN_frame_t *lastStatus = nullptr;
|
const AVCLAN_frame_t *lastStatus = nullptr;
|
||||||
|
|
||||||
|
avclan::Peripheral cd_changer(0x360);
|
||||||
|
using Error = avclan::Peripheral::Error;
|
||||||
|
|
||||||
Setup();
|
Setup();
|
||||||
print_help();
|
print_help();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (AVCLAN_busActive()) {
|
if (AVCLAN_busActive()) {
|
||||||
if (auto msg = cache.pop()) {
|
if (auto msg = cache.pop()) {
|
||||||
err = AVCLAN_readframe(msg.get(), (log_t){.print = printAllFrames,
|
auto err = cd_changer.read(msg.get(), (log_t){.print = printAllFrames,
|
||||||
.binary = printBinary,
|
.binary = printBinary,
|
||||||
.verbose = verbose});
|
.verbose = verbose});
|
||||||
if (!err)
|
if (err == Error::Read{0x00})
|
||||||
incoming.push(std::move(msg));
|
incoming.push(std::move(msg));
|
||||||
} else {
|
} else {
|
||||||
RS232_Print("!! Dropping an incoming message; cache is empty !!\n");
|
RS232_Print("!! Dropping an incoming message; cache is empty !!\n");
|
||||||
@@ -108,18 +111,18 @@ int main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (auto out = outgoing.pop()) {
|
if (auto out = outgoing.pop()) {
|
||||||
err = AVCLAN_sendframe(
|
auto err = cd_changer.send(
|
||||||
out.get(), (log_t){.print = printAllFrames, .binary = printBinary});
|
out.get(), (log_t){.print = printAllFrames, .binary = printBinary});
|
||||||
if (err || (reaction_t)out->reaction == r_SendOnly) {
|
if (err == Error::Send{0x00} && (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 {
|
|
||||||
AVCLAN_statemachine(out.get());
|
AVCLAN_statemachine(out.get());
|
||||||
if (out->reaction)
|
if (out->reaction)
|
||||||
outgoing.push(std::move(out));
|
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?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user