mirror of
https://github.com/halleysfifthinc/AVCLAN-Mockingboard.git
synced 2026-08-07 01:13:18 +00:00
Port Frame object to C++
This commit is contained in:
+1
-1
@@ -32,7 +32,7 @@ set(AVCLAN_TARGET avr-attiny3216 CACHE STRING "Hardware target (port) to build")
|
|||||||
# <avr/...>, no register access). The selected target's port sources and flags
|
# <avr/...>, no register access). The selected target's port sources and flags
|
||||||
# are contributed by its subdirectory.
|
# are contributed by its subdirectory.
|
||||||
add_library(avclan STATIC
|
add_library(avclan STATIC
|
||||||
src/avclan/avclan_frame.c
|
src/avclan/frame.cc
|
||||||
src/avclan/cdchanger.cc
|
src/avclan/cdchanger.cc
|
||||||
src/avclan/bus.cc
|
src/avclan/bus.cc
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,11 +11,6 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define MAXMSGLEN 32
|
|
||||||
|
|
||||||
#define DEVICE_ADDR 0x360 // CD Changer address
|
|
||||||
#define HU_ADDR 0x190 // Head-unit address
|
|
||||||
|
|
||||||
typedef enum : uint8_t {
|
typedef enum : uint8_t {
|
||||||
dev_LAN = 0x00,
|
dev_LAN = 0x00,
|
||||||
dev_COMM_CTRL = 0x01,
|
dev_COMM_CTRL = 0x01,
|
||||||
@@ -109,23 +104,6 @@ typedef enum : uint8_t {
|
|||||||
Report_TOC = 0xf9,
|
Report_TOC = 0xf9,
|
||||||
} actions;
|
} actions;
|
||||||
|
|
||||||
typedef struct print_struct {
|
|
||||||
bool print : 1; // print at all
|
|
||||||
bool binary : 1; // when also printing, format as binary instead of text
|
|
||||||
bool verbose : 1; // include extra context in error reports
|
|
||||||
} log_t;
|
|
||||||
|
|
||||||
typedef struct AVCLAN_frame_struct {
|
|
||||||
uint8_t reaction;
|
|
||||||
uint8_t owning_device;
|
|
||||||
bool is_unicast;
|
|
||||||
uint16_t controller_addr; // formerly "master"
|
|
||||||
uint16_t peripheral_addr; // formerly "slave"
|
|
||||||
uint8_t control;
|
|
||||||
uint8_t length;
|
|
||||||
uint8_t data[MAXMSGLEN];
|
|
||||||
} AVCLAN_frame_t;
|
|
||||||
|
|
||||||
// A single bus symbol. bit_zero/bit_one carry data (and double as parity
|
// A single bus symbol. bit_zero/bit_one carry data (and double as parity
|
||||||
// values); bit_start marks a frame start bit.
|
// values); bit_start marks a frame start bit.
|
||||||
typedef enum avclan_bit : uint8_t {
|
typedef enum avclan_bit : uint8_t {
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
// 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 <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "avclan_frame.h"
|
|
||||||
#include "avclan_phy.h" // bus symbol I/O + transaction guard (target-provided)
|
|
||||||
#include "com232.h" // error logging
|
|
||||||
|
|
||||||
void AVCLAN_printframe(const AVCLAN_frame_t *frame, bool binary) {
|
|
||||||
if (binary) {
|
|
||||||
uint8_t buffer[8];
|
|
||||||
buffer[0] = 0x10; // Data Link Escape, signaling binary data forthcoming
|
|
||||||
buffer[1] = frame->is_unicast;
|
|
||||||
|
|
||||||
// Send addresses in big-endian order
|
|
||||||
buffer[2] = (uint8_t)(frame->controller_addr >> 8);
|
|
||||||
buffer[3] = (uint8_t)frame->controller_addr;
|
|
||||||
buffer[4] = (uint8_t)(frame->peripheral_addr >> 8);
|
|
||||||
buffer[5] = (uint8_t)frame->peripheral_addr;
|
|
||||||
|
|
||||||
buffer[6] = frame->control;
|
|
||||||
buffer[7] = frame->length;
|
|
||||||
RS232_sendbytes((uint8_t *)&buffer, 8);
|
|
||||||
RS232_sendbytes(frame->data, frame->length);
|
|
||||||
|
|
||||||
buffer[0] = 0x17; // End of transmission block
|
|
||||||
buffer[1] = 0x0D; // \r
|
|
||||||
buffer[2] = 0x0A; // \n
|
|
||||||
RS232_sendbytes((uint8_t *)&buffer, 3);
|
|
||||||
} else {
|
|
||||||
RS232_PrintHex4(frame->is_unicast);
|
|
||||||
|
|
||||||
RS232_Print(" 0x");
|
|
||||||
RS232_PrintHex12(frame->controller_addr);
|
|
||||||
RS232_Print(" 0x");
|
|
||||||
RS232_PrintHex12(frame->peripheral_addr);
|
|
||||||
|
|
||||||
RS232_Print(" 0x");
|
|
||||||
RS232_PrintHex4(frame->control);
|
|
||||||
|
|
||||||
RS232_Print(" 0x");
|
|
||||||
RS232_PrintHex4(frame->length);
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < frame->length; i++) {
|
|
||||||
RS232_Print(" 0x");
|
|
||||||
RS232_PrintHex8(frame->data[i]);
|
|
||||||
}
|
|
||||||
RS232_Print("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t AVCLAN_parseframe(const uint8_t *bytes, uint8_t len,
|
|
||||||
AVCLAN_frame_t *frame) {
|
|
||||||
struct errtype {
|
|
||||||
enum : uint8_t {
|
|
||||||
TOO_SHORT = 0x01,
|
|
||||||
MISMATCH_LENGTH,
|
|
||||||
LENGTH_TOO_BIG,
|
|
||||||
} errno;
|
|
||||||
uint8_t val;
|
|
||||||
} err = {0};
|
|
||||||
|
|
||||||
if (len < sizeof(AVCLAN_frame_t)) {
|
|
||||||
err.errno = TOO_SHORT;
|
|
||||||
goto handle_err;
|
|
||||||
}
|
|
||||||
const uint8_t *last = bytes + len;
|
|
||||||
|
|
||||||
frame->is_unicast = *bytes++;
|
|
||||||
frame->controller_addr = bytes[0] | ((uint16_t)bytes[1] << 8);
|
|
||||||
bytes += 2;
|
|
||||||
frame->peripheral_addr = bytes[0] | ((uint16_t)bytes[1] << 8);
|
|
||||||
bytes += 2;
|
|
||||||
frame->control = *bytes++;
|
|
||||||
frame->length = *bytes++;
|
|
||||||
|
|
||||||
if (frame->length > MAXMSGLEN) {
|
|
||||||
err.errno = LENGTH_TOO_BIG;
|
|
||||||
err.val = frame->length;
|
|
||||||
goto handle_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((bytes + frame->length) <= last) {
|
|
||||||
memcpy(frame->data, bytes, frame->length);
|
|
||||||
} else {
|
|
||||||
err.errno = MISMATCH_LENGTH;
|
|
||||||
goto handle_err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (false) {
|
|
||||||
handle_err:;
|
|
||||||
RS232_Print("ERR(parse): ");
|
|
||||||
switch (err.errno) {
|
|
||||||
case TOO_SHORT:
|
|
||||||
RS232_Print("not enough bytes too fill AVCLAN frame");
|
|
||||||
break;
|
|
||||||
case MISMATCH_LENGTH:
|
|
||||||
RS232_Print("frame->length is longer than remaining data");
|
|
||||||
break;
|
|
||||||
case LENGTH_TOO_BIG:
|
|
||||||
RS232_Print("frame->length exceeds MAXMSGLEN: 0x");
|
|
||||||
RS232_PrintHex8(err.val);
|
|
||||||
break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
RS232_Print("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return err.errno;
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
// 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
|
|
||||||
|
|
||||||
/*
|
|
||||||
AVC LAN Frame Format
|
|
||||||
│ Bits │ Description
|
|
||||||
────────────────────────────────────────
|
|
||||||
| 1 │ Start bit
|
|
||||||
| 1 │ Direct/broadcast
|
|
||||||
| 12 │ Controller address
|
|
||||||
| 1 │ Parity
|
|
||||||
| 12 │ Peripheral address
|
|
||||||
| 1 │ Parity
|
|
||||||
| 1 │ *Acknowledge* (read below)
|
|
||||||
| 4 │ Control
|
|
||||||
| 1 │ Parity
|
|
||||||
| 1 │ *Acknowledge*
|
|
||||||
| 8 │ Message length (n)
|
|
||||||
| 1 │ Parity
|
|
||||||
| 1 │ *Acknowledge*
|
|
||||||
────────
|
|
||||||
| 8 │ Data
|
|
||||||
| 1 │ Parity
|
|
||||||
| 1 │ *Acknowledge*
|
|
||||||
*repeat `n` times*
|
|
||||||
|
|
||||||
No acknowledge bits are sent for broadcast frames.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "avclan_defs.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -9,7 +9,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "avclan_defs.h"
|
#include "avclan_defs.h"
|
||||||
#include "avclan_frame.h"
|
|
||||||
#include "avclan_phy.h"
|
#include "avclan_phy.h"
|
||||||
#include "mediacontrol.h"
|
#include "mediacontrol.h"
|
||||||
#include "statustimer.h"
|
#include "statustimer.h"
|
||||||
|
|||||||
+6
-7
@@ -32,9 +32,9 @@
|
|||||||
#include "bus.hpp"
|
#include "bus.hpp"
|
||||||
#include "avclan.hpp"
|
#include "avclan.hpp"
|
||||||
#include "avclan_defs.h"
|
#include "avclan_defs.h"
|
||||||
#include "avclan_frame.h"
|
|
||||||
#include "avclan_phy.h" // bridge until phy has been ported
|
#include "avclan_phy.h" // bridge until phy has been ported
|
||||||
#include "com232.h"
|
#include "com232.h"
|
||||||
|
#include "frame.hpp"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int ADDR_WIDTH = 12;
|
constexpr int ADDR_WIDTH = 12;
|
||||||
@@ -45,8 +45,7 @@ void Bus::init() { AVCLAN_busInit(); };
|
|||||||
void Bus::mute(bool mute) { AVCLAN_muteDevice(mute); };
|
void Bus::mute(bool mute) { AVCLAN_muteDevice(mute); };
|
||||||
bool Bus::is_muted() const { return AVCLAN_ismuted(); };
|
bool Bus::is_muted() const { return AVCLAN_ismuted(); };
|
||||||
|
|
||||||
auto Bus::read(uint16_t address, AVCLAN_frame_t *in, log_t print)
|
auto Bus::read(uint16_t address, Frame *in, Frame::Print print) -> Error::Read {
|
||||||
-> Error::Read {
|
|
||||||
struct errtype {
|
struct errtype {
|
||||||
Error::Read errno;
|
Error::Read errno;
|
||||||
union {
|
union {
|
||||||
@@ -113,7 +112,7 @@ auto Bus::read(uint16_t address, AVCLAN_frame_t *in, log_t print)
|
|||||||
goto handle_err;
|
goto handle_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in->length == 0 || in->length > MAXMSGLEN) {
|
if (in->length == 0 || in->length > Frame::MAXLENGTH) {
|
||||||
err.errno = BAD_LENGTH_RANGE;
|
err.errno = BAD_LENGTH_RANGE;
|
||||||
err.val = in->length;
|
err.val = in->length;
|
||||||
goto handle_err;
|
goto handle_err;
|
||||||
@@ -169,13 +168,13 @@ auto Bus::read(uint16_t address, AVCLAN_frame_t *in, log_t print)
|
|||||||
if (print.print && (err.errno < STARTBIT_TOO_SHORT)) {
|
if (print.print && (err.errno < STARTBIT_TOO_SHORT)) {
|
||||||
if (err.errno > BAD_DATA_PARITY)
|
if (err.errno > BAD_DATA_PARITY)
|
||||||
in->length = 0;
|
in->length = 0;
|
||||||
AVCLAN_printframe(in, print.binary);
|
in->print(print);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err.errno;
|
return err.errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Bus::send(const AVCLAN_frame_t *out, log_t print) -> Error::Send {
|
auto Bus::send(const Frame *out, Frame::Print print) -> Error::Send {
|
||||||
struct errtype {
|
struct errtype {
|
||||||
// Error enum is ordered such that a lower numeric value corresponds to
|
// Error enum is ordered such that a lower numeric value corresponds to
|
||||||
// more success
|
// more success
|
||||||
@@ -267,7 +266,7 @@ auto Bus::send(const AVCLAN_frame_t *out, log_t print) -> Error::Send {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (print.print)
|
if (print.print)
|
||||||
AVCLAN_printframe(out, print.binary);
|
out->print(print);
|
||||||
|
|
||||||
return err.errno;
|
return err.errno;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -52,6 +52,7 @@
|
|||||||
#include "avclan.hpp"
|
#include "avclan.hpp"
|
||||||
#include "avclan_defs.h"
|
#include "avclan_defs.h"
|
||||||
#include "avclan_phy.h" // bridge until phy has been ported
|
#include "avclan_phy.h" // bridge until phy has been ported
|
||||||
|
#include "frame.hpp"
|
||||||
|
|
||||||
namespace avclan {
|
namespace avclan {
|
||||||
class Bus {
|
class Bus {
|
||||||
@@ -63,8 +64,8 @@ public:
|
|||||||
void mute(bool mute);
|
void mute(bool mute);
|
||||||
bool is_muted() const;
|
bool is_muted() const;
|
||||||
|
|
||||||
Error::Read read(uint16_t address, AVCLAN_frame_t *in, log_t print);
|
Error::Read read(uint16_t address, Frame *in, Frame::Print print);
|
||||||
Error::Send send(const AVCLAN_frame_t *out, log_t print);
|
Error::Send send(const Frame *out, Frame::Print print);
|
||||||
|
|
||||||
static Handle get();
|
static Handle get();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "avclan_defs.h"
|
#include "avclan_defs.h"
|
||||||
#include "cdchanger.hpp"
|
#include "cdchanger.hpp"
|
||||||
|
#include "frame.hpp"
|
||||||
#include "mediacontrol.h"
|
#include "mediacontrol.h"
|
||||||
#include "statustimer.h"
|
#include "statustimer.h"
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@ void CDChanger::init() {
|
|||||||
statustimer_init(this, &incrementTime_callback, &isPlaying_callback);
|
statustimer_init(this, &incrementTime_callback, &isPlaying_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDChanger::handle(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) {
|
void CDChanger::handle(const Frame *in, Frame *out) {
|
||||||
const uint8_t *data = &in->data[1];
|
const uint8_t *data = &in->data[1];
|
||||||
const auto from = static_cast<devices>(*data++);
|
const auto from = static_cast<devices>(*data++);
|
||||||
/* const auto to = */ data++;
|
/* const auto to = */ data++;
|
||||||
@@ -244,7 +245,7 @@ void CDChanger::handle(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDChanger::react(AVCLAN_frame_t *out, detail::Error::Send err) {
|
void CDChanger::react(Frame *out, detail::Error::Send err) {
|
||||||
auto resp = static_cast<reaction_t>(out->reaction);
|
auto resp = static_cast<reaction_t>(out->reaction);
|
||||||
out->reaction = r_Nothing;
|
out->reaction = r_Nothing;
|
||||||
switch (resp) {
|
switch (resp) {
|
||||||
@@ -302,7 +303,7 @@ void CDChanger::react(AVCLAN_frame_t *out, detail::Error::Send err) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDChanger::enable(AVCLAN_frame_t *out) {
|
void CDChanger::enable(Frame *out) {
|
||||||
if (!isPlaying()) {
|
if (!isPlaying()) {
|
||||||
if (mins > TWODIGIT_MAX)
|
if (mins > TWODIGIT_MAX)
|
||||||
mins = 0;
|
mins = 0;
|
||||||
@@ -318,7 +319,7 @@ void CDChanger::enable(AVCLAN_frame_t *out) {
|
|||||||
bool CDChanger::pending() { return statustimer_tickPending(); }
|
bool CDChanger::pending() { return statustimer_tickPending(); }
|
||||||
void CDChanger::resolvepending() { statustimer_clearTick(); }
|
void CDChanger::resolvepending() { statustimer_clearTick(); }
|
||||||
|
|
||||||
void CDChanger::emit(AVCLAN_frame_t *out) {
|
void CDChanger::emit(Frame *out) {
|
||||||
generateStatus(out, true, dev_STATUS);
|
generateStatus(out, true, dev_STATUS);
|
||||||
out->reaction = r_StateReport;
|
out->reaction = r_StateReport;
|
||||||
}
|
}
|
||||||
@@ -377,7 +378,7 @@ void CDChanger::incrementTime() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Used for changed status messages
|
// Used for changed status messages
|
||||||
void CDChanger::generateStatus(AVCLAN_frame_t *status, bool is_unicast,
|
void CDChanger::generateStatus(Frame *status, bool is_unicast,
|
||||||
devices to) const {
|
devices to) const {
|
||||||
status->is_unicast = is_unicast;
|
status->is_unicast = is_unicast;
|
||||||
if (!is_unicast)
|
if (!is_unicast)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "avclan.hpp"
|
#include "avclan.hpp"
|
||||||
#include "avclan_defs.h"
|
#include "avclan_defs.h"
|
||||||
|
#include "frame.hpp"
|
||||||
|
|
||||||
namespace avclan {
|
namespace avclan {
|
||||||
|
|
||||||
@@ -59,13 +60,13 @@ public:
|
|||||||
static constexpr uint8_t id = dev_CD_CHANGER;
|
static constexpr uint8_t id = dev_CD_CHANGER;
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
void handle(const AVCLAN_frame_t *in, AVCLAN_frame_t *out);
|
void handle(const Frame *in, Frame *out);
|
||||||
void react(AVCLAN_frame_t *out, detail::Error::Send err);
|
void react(Frame *out, detail::Error::Send err);
|
||||||
void enable(AVCLAN_frame_t *out);
|
void enable(Frame *out);
|
||||||
void disable(AVCLAN_frame_t *out);
|
void disable(Frame *out);
|
||||||
static bool pending();
|
static bool pending();
|
||||||
static void resolvepending();
|
static void resolvepending();
|
||||||
void emit(AVCLAN_frame_t *out);
|
void emit(Frame *out);
|
||||||
void incrementTime();
|
void incrementTime();
|
||||||
bool isPlaying() const;
|
bool isPlaying() const;
|
||||||
|
|
||||||
@@ -74,8 +75,7 @@ private:
|
|||||||
void stopPlaying();
|
void stopPlaying();
|
||||||
void serialize(uint8_t *dst) const;
|
void serialize(uint8_t *dst) const;
|
||||||
void setTime(uint8_t mins, uint8_t secs);
|
void setTime(uint8_t mins, uint8_t secs);
|
||||||
void generateStatus(AVCLAN_frame_t *status, bool is_unicast,
|
void generateStatus(Frame *status, bool is_unicast, devices to) const;
|
||||||
devices to) const;
|
|
||||||
void normalizeState();
|
void normalizeState();
|
||||||
|
|
||||||
bool playing = false;
|
bool playing = false;
|
||||||
|
|||||||
@@ -4,16 +4,16 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "avclan.hpp"
|
#include "avclan.hpp"
|
||||||
#include "avclan_defs.h"
|
#include "frame.hpp"
|
||||||
|
|
||||||
#include <concepts>
|
#include <concepts>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace avclan {
|
namespace avclan {
|
||||||
template <class T>
|
template <class T>
|
||||||
concept Device = requires { std::integral_constant<uint8_t, T::id>{}; } &&
|
concept Device = requires {
|
||||||
requires(T dev, const AVCLAN_frame_t *in, AVCLAN_frame_t *out,
|
std::integral_constant<uint8_t, T::id>{};
|
||||||
detail::Error::Send err) {
|
} && requires(T dev, const Frame *in, Frame *out, detail::Error::Send err) {
|
||||||
dev.init();
|
dev.init();
|
||||||
dev.handle(in, out);
|
dev.handle(in, out);
|
||||||
dev.enable(out);
|
dev.enable(out);
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
// 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 <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "com232.h" // error logging
|
||||||
|
#include "frame.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
using Error = avclan::Frame::Error;
|
||||||
|
using enum Error::Parse;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace avclan {
|
||||||
|
void Frame::print(Frame::Print print) const {
|
||||||
|
if (print.binary) {
|
||||||
|
uint8_t buffer[8];
|
||||||
|
uint8_t *bptr = buffer;
|
||||||
|
*bptr++ = 0x10; // Data Link Escape, signaling binary data forthcoming
|
||||||
|
*bptr++ = static_cast<uint8_t>(is_unicast);
|
||||||
|
|
||||||
|
// Send addresses in big-endian order
|
||||||
|
*bptr++ = static_cast<uint8_t>(controller_addr >> 8);
|
||||||
|
*bptr++ = static_cast<uint8_t>(controller_addr);
|
||||||
|
*bptr++ = static_cast<uint8_t>(peripheral_addr >> 8);
|
||||||
|
*bptr++ = static_cast<uint8_t>(peripheral_addr);
|
||||||
|
|
||||||
|
*bptr++ = control;
|
||||||
|
*bptr++ = length;
|
||||||
|
RS232_sendbytes(buffer, 8);
|
||||||
|
RS232_sendbytes(data, length);
|
||||||
|
|
||||||
|
bptr = buffer;
|
||||||
|
*bptr++ = 0x17; // End of transmission block
|
||||||
|
*bptr++ = 0x0D; // \r
|
||||||
|
*bptr++ = 0x0A; // \n
|
||||||
|
RS232_sendbytes(buffer, 3);
|
||||||
|
} else {
|
||||||
|
RS232_PrintHex4(static_cast<uint8_t>(is_unicast));
|
||||||
|
|
||||||
|
RS232_Print(" 0x");
|
||||||
|
RS232_PrintHex12(controller_addr);
|
||||||
|
RS232_Print(" 0x");
|
||||||
|
RS232_PrintHex12(peripheral_addr);
|
||||||
|
|
||||||
|
RS232_Print(" 0x");
|
||||||
|
RS232_PrintHex4(control);
|
||||||
|
|
||||||
|
RS232_Print(" 0x");
|
||||||
|
RS232_PrintHex4(length);
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < length; i++) {
|
||||||
|
RS232_Print(" 0x");
|
||||||
|
RS232_PrintHex8(data[i]);
|
||||||
|
}
|
||||||
|
RS232_Print("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Error::Parse Frame::parse(const uint8_t *bytes, uint8_t len) {
|
||||||
|
struct errtype {
|
||||||
|
Error::Parse errno;
|
||||||
|
uint8_t val;
|
||||||
|
} err = {};
|
||||||
|
|
||||||
|
const uint8_t *last = bytes + len;
|
||||||
|
|
||||||
|
if (len < sizeof(avclan::Frame)) {
|
||||||
|
err.errno = TOO_SHORT;
|
||||||
|
goto handle_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_unicast = (*bytes++ != 0U);
|
||||||
|
controller_addr = bytes[0] | ((uint16_t)bytes[1] << 8);
|
||||||
|
bytes += 2;
|
||||||
|
peripheral_addr = bytes[0] | ((uint16_t)bytes[1] << 8);
|
||||||
|
bytes += 2;
|
||||||
|
control = *bytes++;
|
||||||
|
length = *bytes++;
|
||||||
|
|
||||||
|
if (length > MAXLENGTH) {
|
||||||
|
err.errno = LENGTH_TOO_BIG;
|
||||||
|
err.val = length;
|
||||||
|
goto handle_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((bytes + length) <= last) {
|
||||||
|
memcpy(data, bytes, length);
|
||||||
|
} else {
|
||||||
|
err.errno = MISMATCH_LENGTH;
|
||||||
|
goto handle_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false) { // NOLINT(readability-simplify-boolean-expr)
|
||||||
|
handle_err:;
|
||||||
|
RS232_Print("ERR(parse): ");
|
||||||
|
switch (err.errno) {
|
||||||
|
case TOO_SHORT:
|
||||||
|
RS232_Print("not enough bytes too fill AVCLAN frame");
|
||||||
|
break;
|
||||||
|
case MISMATCH_LENGTH:
|
||||||
|
RS232_Print("frame->length is longer than remaining data");
|
||||||
|
break;
|
||||||
|
case LENGTH_TOO_BIG:
|
||||||
|
RS232_Print("frame->length exceeds MAXLENGTH: 0x");
|
||||||
|
RS232_PrintHex8(err.val);
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
RS232_Print("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return err.errno;
|
||||||
|
}
|
||||||
|
} // namespace avclan
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// 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 <cstdint>
|
||||||
|
|
||||||
|
namespace avclan {
|
||||||
|
struct Frame {
|
||||||
|
struct Print {
|
||||||
|
bool print : 1 = false; // print at all
|
||||||
|
bool binary : 1 = false; // if printing, format as binary instead of text
|
||||||
|
bool verbose : 1 = false; // include extra context in error reports
|
||||||
|
};
|
||||||
|
struct Error {
|
||||||
|
enum class Parse : uint8_t {
|
||||||
|
TOO_SHORT = 0x01,
|
||||||
|
MISMATCH_LENGTH,
|
||||||
|
LENGTH_TOO_BIG,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
static constexpr int MAXLENGTH = 32;
|
||||||
|
|
||||||
|
Error::Parse parse(const uint8_t *bytes, uint8_t len);
|
||||||
|
void print(Print print) const;
|
||||||
|
|
||||||
|
uint8_t reaction;
|
||||||
|
uint8_t owning_device;
|
||||||
|
bool is_unicast;
|
||||||
|
uint16_t controller_addr; // formerly "master"
|
||||||
|
uint16_t peripheral_addr; // formerly "slave"
|
||||||
|
uint8_t control = 0xF;
|
||||||
|
uint8_t length;
|
||||||
|
uint8_t data[MAXLENGTH];
|
||||||
|
};
|
||||||
|
} // namespace avclan
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "avclan_defs.h"
|
#include "avclan_defs.h"
|
||||||
#include "bus.hpp"
|
#include "bus.hpp"
|
||||||
#include "device.hpp"
|
#include "device.hpp"
|
||||||
|
#include "frame.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -24,16 +25,16 @@ public:
|
|||||||
|
|
||||||
uint16_t address() const { return address_; };
|
uint16_t address() const { return address_; };
|
||||||
|
|
||||||
Error::Read read(AVCLAN_frame_t *in, log_t print) {
|
Error::Read read(Frame *in, Frame::Print print) {
|
||||||
return bus.read(address_, in, print);
|
return bus.read(address_, in, print);
|
||||||
};
|
};
|
||||||
Error::Send send(const AVCLAN_frame_t *out, log_t print) {
|
Error::Send send(const Frame *out, Frame::Print print) {
|
||||||
return bus.send(out, print);
|
return bus.send(out, print);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define PACK3(a, b, c) (((uint32_t)(a) << 16) | ((uint32_t)(b) << 8) | (c))
|
#define PACK3(a, b, c) (((uint32_t)(a) << 16) | ((uint32_t)(b) << 8) | (c))
|
||||||
|
|
||||||
void route(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) {
|
void route(const Frame *in, Frame *out) {
|
||||||
out->reaction = 0;
|
out->reaction = 0;
|
||||||
|
|
||||||
if (AVCLAN_ismuted() || in->length < 3)
|
if (AVCLAN_ismuted() || in->length < 3)
|
||||||
@@ -119,7 +120,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void react(AVCLAN_frame_t *out, Error::Send err) {
|
void react(Frame *out, Error::Send err) {
|
||||||
if (((Devs::id == out->owning_device) || ...))
|
if (((Devs::id == out->owning_device) || ...))
|
||||||
((Devs::id == out->owning_device
|
((Devs::id == out->owning_device
|
||||||
? std::get<Devs>(devices_).react(out, err),
|
? std::get<Devs>(devices_).react(out, err),
|
||||||
@@ -141,7 +142,7 @@ private:
|
|||||||
dev.resolvepending();
|
dev.resolvepending();
|
||||||
}
|
}
|
||||||
template <Device Dev>
|
template <Device Dev>
|
||||||
void device_preroute(Dev dev, const AVCLAN_frame_t *in, AVCLAN_frame_t *out) {
|
void device_preroute(Dev dev, const Frame *in, Frame *out) {
|
||||||
out->owning_device = Dev::id;
|
out->owning_device = Dev::id;
|
||||||
dev.handle(in, out);
|
dev.handle(in, out);
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-24
@@ -11,6 +11,7 @@
|
|||||||
#include "board.h"
|
#include "board.h"
|
||||||
#include "cdchanger.hpp"
|
#include "cdchanger.hpp"
|
||||||
#include "com232.h"
|
#include "com232.h"
|
||||||
|
#include "frame.hpp"
|
||||||
#include "peripheral.hpp"
|
#include "peripheral.hpp"
|
||||||
#include "queue.hpp"
|
#include "queue.hpp"
|
||||||
|
|
||||||
@@ -18,8 +19,10 @@ const char *const offon[] = {"OFF", "ON"};
|
|||||||
|
|
||||||
constexpr uint8_t CACHE_SIZE = 32;
|
constexpr uint8_t CACHE_SIZE = 32;
|
||||||
|
|
||||||
|
using namespace avclan;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
AVCLAN_frame_t frames[CACHE_SIZE];
|
Frame frames[CACHE_SIZE];
|
||||||
|
|
||||||
constinit Queue cache(frames);
|
constinit Queue cache(frames);
|
||||||
constinit Queue incoming = cache;
|
constinit Queue incoming = cache;
|
||||||
@@ -60,17 +63,13 @@ int main() {
|
|||||||
|
|
||||||
// Binary-mode REPL includes the full wire preamble (broadcast + 2*addr +
|
// Binary-mode REPL includes the full wire preamble (broadcast + 2*addr +
|
||||||
// control + length), so size for the worst case.
|
// control + length), so size for the worst case.
|
||||||
uint8_t data_tmp[MAXMSGLEN + sizeof(AVCLAN_frame_t)];
|
uint8_t data_tmp[Frame::MAXLENGTH + sizeof(Frame)];
|
||||||
uint8_t seqIdx = 0; // current index in data_tmp
|
uint8_t seqIdx = 0; // current index in data_tmp
|
||||||
|
|
||||||
// Temporary, direct access is questionable since cache has ownership
|
Bus phy;
|
||||||
for (auto &frame : frames) {
|
Peripheral<CDChanger> peripheral(phy, 0x360);
|
||||||
frame.control = 0x0f;
|
using Error = decltype(peripheral)::Error;
|
||||||
}
|
using Print = Frame::Print;
|
||||||
|
|
||||||
avclan::Bus phy;
|
|
||||||
avclan::Peripheral<avclan::CDChanger> cd_changer(phy, 0x360);
|
|
||||||
using Error = decltype(cd_changer)::Error;
|
|
||||||
|
|
||||||
Setup();
|
Setup();
|
||||||
print_help();
|
print_help();
|
||||||
@@ -78,7 +77,7 @@ int main() {
|
|||||||
while (true) {
|
while (true) {
|
||||||
if (AVCLAN_busActive()) {
|
if (AVCLAN_busActive()) {
|
||||||
if (auto msg = cache.pop()) {
|
if (auto msg = cache.pop()) {
|
||||||
auto err = cd_changer.read(msg.get(), (log_t){.print = printAllFrames,
|
auto err = peripheral.read(msg.get(), Print{.print = printAllFrames,
|
||||||
.binary = printBinary,
|
.binary = printBinary,
|
||||||
.verbose = verbose});
|
.verbose = verbose});
|
||||||
if (err == Error::Read{0x00})
|
if (err == Error::Read{0x00})
|
||||||
@@ -90,7 +89,7 @@ int main() {
|
|||||||
|
|
||||||
if (const auto *in = incoming.peek()) {
|
if (const auto *in = incoming.peek()) {
|
||||||
if (auto out = cache.pop()) {
|
if (auto out = cache.pop()) {
|
||||||
cd_changer.route(in, out.get());
|
peripheral.route(in, out.get());
|
||||||
incoming.pop();
|
incoming.pop();
|
||||||
|
|
||||||
if (out->reaction > 0)
|
if (out->reaction > 0)
|
||||||
@@ -100,7 +99,7 @@ int main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cd_changer.poll_devices([&](auto dev) {
|
peripheral.poll_devices([&](auto dev) {
|
||||||
if (auto status = cache.pop()) {
|
if (auto status = cache.pop()) {
|
||||||
dev.emit(status.get());
|
dev.emit(status.get());
|
||||||
outgoing.push(std::move(status));
|
outgoing.push(std::move(status));
|
||||||
@@ -110,9 +109,9 @@ int main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (auto out = outgoing.pop()) {
|
if (auto out = outgoing.pop()) {
|
||||||
auto err = cd_changer.send(
|
auto err = peripheral.send(
|
||||||
out.get(), (log_t){.print = printAllFrames, .binary = printBinary});
|
out.get(), Print{.print = printAllFrames, .binary = printBinary});
|
||||||
cd_changer.react(out.get(), err);
|
peripheral.react(out.get(), err);
|
||||||
if (out->reaction > 0)
|
if (out->reaction > 0)
|
||||||
outgoing.push(std::move(out));
|
outgoing.push(std::move(out));
|
||||||
}
|
}
|
||||||
@@ -139,8 +138,8 @@ int main() {
|
|||||||
case 'E': // Beep
|
case 'E': // Beep
|
||||||
if (auto out = cache.pop()) {
|
if (auto out = cache.pop()) {
|
||||||
out->is_unicast = true;
|
out->is_unicast = true;
|
||||||
out->controller_addr = cd_changer.address();
|
out->controller_addr = peripheral.address();
|
||||||
out->peripheral_addr = HU_ADDR;
|
out->peripheral_addr = peripheral.controller();
|
||||||
{
|
{
|
||||||
const uint8_t beep[] = {0x00, dev_CD_CHANGER, dev_BEEP_SPEAKERS,
|
const uint8_t beep[] = {0x00, dev_CD_CHANGER, dev_BEEP_SPEAKERS,
|
||||||
0x60, 0x01};
|
0x60, 0x01};
|
||||||
@@ -154,15 +153,15 @@ int main() {
|
|||||||
case 'P':
|
case 'P':
|
||||||
if (auto out = cache.pop()) {
|
if (auto out = cache.pop()) {
|
||||||
out->is_unicast = true;
|
out->is_unicast = true;
|
||||||
out->controller_addr = cd_changer.address();
|
out->controller_addr = peripheral.address();
|
||||||
out->peripheral_addr = HU_ADDR;
|
out->peripheral_addr = peripheral.controller();
|
||||||
{
|
{
|
||||||
const uint8_t play[] = {0x00, dev_COMM_CTRL, dev_COMM_v1,
|
const uint8_t play[] = {0x00, dev_COMM_CTRL, dev_COMM_v1,
|
||||||
Ejection, dev_CD_CHANGER, 0x01};
|
Ejection, dev_CD_CHANGER, 0x01};
|
||||||
out->length = sizeof(play);
|
out->length = sizeof(play);
|
||||||
memcpy(out->data, play, sizeof(play));
|
memcpy(out->data, play, sizeof(play));
|
||||||
}
|
}
|
||||||
out->reaction = avclan::CDChanger::reaction_t::r_Ejection;
|
out->reaction = CDChanger::reaction_t::r_Ejection;
|
||||||
outgoing.push(std::move(out));
|
outgoing.push(std::move(out));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -224,7 +223,8 @@ int main() {
|
|||||||
if (readBinary) {
|
if (readBinary) {
|
||||||
if (data_tmp[seqIdx] == 0x17) {
|
if (data_tmp[seqIdx] == 0x17) {
|
||||||
if (auto out = cache.pop()) {
|
if (auto out = cache.pop()) {
|
||||||
if (!AVCLAN_parseframe(data_tmp, --seqIdx, out.get())) {
|
if (out->parse(data_tmp, --seqIdx) ==
|
||||||
|
Frame::Error::Parse{0}) {
|
||||||
out->reaction = 1;
|
out->reaction = 1;
|
||||||
outgoing.push(std::move(out));
|
outgoing.push(std::move(out));
|
||||||
}
|
}
|
||||||
@@ -236,8 +236,9 @@ int main() {
|
|||||||
} else {
|
} else {
|
||||||
if (auto out = cache.pop()) {
|
if (auto out = cache.pop()) {
|
||||||
out->is_unicast = seqIsUnicast;
|
out->is_unicast = seqIsUnicast;
|
||||||
out->controller_addr = cd_changer.address();
|
out->controller_addr = peripheral.address();
|
||||||
out->peripheral_addr = seqIsUnicast ? HU_ADDR : 0x1FF;
|
out->peripheral_addr =
|
||||||
|
seqIsUnicast ? peripheral.controller() : 0x1FF;
|
||||||
out->length = seqIdx;
|
out->length = seqIdx;
|
||||||
memcpy(out->data, data_tmp, seqIdx);
|
memcpy(out->data, data_tmp, seqIdx);
|
||||||
out->reaction = 1;
|
out->reaction = 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user