mirror of
https://github.com/halleysfifthinc/AVCLAN-Mockingboard.git
synced 2026-08-06 17:03:17 +00:00
Shift Device/Action enums to avclan.h
This commit is contained in:
+3
-1
@@ -3,7 +3,9 @@ cmake_minimum_required(VERSION 3.24)
|
||||
project(avclan-mockingboard VERSION 1.1 LANGUAGES C CXX)
|
||||
|
||||
set(CMAKE_C_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
add_compile_options(
|
||||
|
||||
+97
-3
@@ -7,12 +7,105 @@
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define AVCLAN_ENUM_CLASS class
|
||||
namespace avclan::detail {
|
||||
struct Error {
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace avclan {
|
||||
|
||||
#if defined(__cpp_lib_to_underlying) && __cpp_lib_to_underlying >= 202102L
|
||||
using std::to_underlying;
|
||||
#else
|
||||
template <class Enum>
|
||||
requires std::is_enum_v<Enum>
|
||||
constexpr std::underlying_type_t<Enum> to_underlying(Enum e) noexcept {
|
||||
return static_cast<std::underlying_type_t<Enum>>(e);
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define AVCLAN_ENUM_CLASS
|
||||
#endif
|
||||
|
||||
enum AVCLAN_ENUM_CLASS Action : uint8_t {
|
||||
// LAN related
|
||||
List_Functions_Req = 0x00,
|
||||
List_Functions_Resp = 0x10,
|
||||
Restart_Lan = 0x01,
|
||||
// Lan_Startup_Complete = 0x58,
|
||||
Lancheck_End_Req = 0x08,
|
||||
Lancheck_End_Resp = 0x18,
|
||||
Lancheck_Scan_Req = 0x0a,
|
||||
Lancheck_Scan_Resp = 0x1a,
|
||||
Lancheck_Req = 0x0c,
|
||||
Lancheck_Resp = 0x1c,
|
||||
// Lancheck_UNK_Req = 0x0d,
|
||||
// Lancheck_UNK_Resp = 0x1d,
|
||||
Ping_Req = 0x20,
|
||||
Ping_Resp = 0x30,
|
||||
|
||||
// Device switching
|
||||
Enable_Function_Req = 0x42,
|
||||
Enable_Function_Resp = 0x52,
|
||||
Disable_Function_Req = 0x43,
|
||||
Disable_Function_Resp = 0x53,
|
||||
|
||||
Current_Function = 0x45,
|
||||
General_Query = 0x46,
|
||||
|
||||
// Events
|
||||
Insertion = 0x50,
|
||||
Ejection = 0x51,
|
||||
|
||||
// Physical interface
|
||||
Backlight_Adjust = 0x59,
|
||||
Beep = 0x60,
|
||||
Eject = 0x80,
|
||||
Disc_Up = 0x90,
|
||||
Disc_Down = 0x91,
|
||||
Track_Seek_Up = 0x94,
|
||||
Track_Seek_Down = 0x95,
|
||||
Track_Fast_Forward = 0x98,
|
||||
Track_Rewind = 0x99,
|
||||
Pwrvol_Knob_Righthand_Turn = 0x9c,
|
||||
Pwrvol_Knob_Lefthand_Turn = 0x9d,
|
||||
CD_Enable_Repeat = 0xa0,
|
||||
CD_Disable_Repeat = 0xa1,
|
||||
CD_Enable_Disk_Repeat = 0xa3,
|
||||
CD_Disable_Disk_Repeat = 0xa4,
|
||||
CD_Enable_Scan = 0xa6,
|
||||
CD_Disable_Scan = 0xa7,
|
||||
CD_Enable_Disk_Scan = 0xa9,
|
||||
CD_Disable_Disk_Scan = 0xaa,
|
||||
CD_Enable_Random = 0xb0,
|
||||
CD_Disable_Random = 0xb1,
|
||||
CD_Enable_Disk_Random = 0xb3,
|
||||
CD_Disable_Disk_Random = 0xb4,
|
||||
|
||||
// Requests and Response pairs
|
||||
Initial_Report_Request = 0xe0,
|
||||
Initial_Report_Response = 0xf0,
|
||||
|
||||
Playback_Request = 0xe2,
|
||||
Playback_Report = 0xf2,
|
||||
|
||||
Loading_Request2 = 0xe4,
|
||||
Loading_Response2 = 0xf4,
|
||||
|
||||
Request_Track_Name = 0xed,
|
||||
Report_Track_Name = 0xfd,
|
||||
|
||||
// Reports
|
||||
Status_Report = 0xf1, // Typically unprompted, sent to Device::STATUS
|
||||
Loading_Status_Report = 0xf3, // Typically unprompted, sent to Device::STATUS
|
||||
Report_TOC = 0xf9,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace detail {
|
||||
struct Error {
|
||||
#endif
|
||||
|
||||
// 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 AVCLAN_ENUM_CLASS Read : uint8_t {
|
||||
@@ -49,7 +142,8 @@ enum AVCLAN_ENUM_CLASS Bit : uint8_t {
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // namespace avclan::detail
|
||||
} // namespace detail
|
||||
} // namespace avclan
|
||||
#endif
|
||||
|
||||
#undef AVCLAN_ENUM_CLASS
|
||||
|
||||
@@ -1,105 +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
|
||||
|
||||
// Shared cross-cutting AVC-LAN definitions used by two or more layers
|
||||
// (phy / frame / protocol / cdchanger). This is a leaf header: it must not
|
||||
// include any other project header.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum : uint8_t {
|
||||
dev_LAN = 0x00,
|
||||
dev_COMM_CTRL = 0x01,
|
||||
dev_COMM_v1 = 0x11,
|
||||
dev_COMM_v2 = 0x12,
|
||||
dev_SW = 0x21,
|
||||
dev_SW_NAME = 0x23,
|
||||
dev_SW_CONVERTING = 0x24,
|
||||
dev_CMD_SW = 0x25,
|
||||
dev_STATUS = 0x31,
|
||||
dev_BEEP_HU = 0x28,
|
||||
dev_BEEP_SPEAKERS = 0x29,
|
||||
dev_TUNER = 0x60,
|
||||
dev_TAPE_DECK = 0x61,
|
||||
dev_CD = 0x62,
|
||||
dev_CD_CHANGER = 0x63,
|
||||
dev_AUDIO_AMP = 0x74,
|
||||
} devices;
|
||||
|
||||
typedef enum : uint8_t {
|
||||
// LAN related
|
||||
List_Functions_Req = 0x00,
|
||||
List_Functions_Resp = 0x10,
|
||||
Restart_Lan = 0x01,
|
||||
// Lan_Startup_Complete = 0x58,
|
||||
Lancheck_End_Req = 0x08,
|
||||
Lancheck_End_Resp = 0x18,
|
||||
Lancheck_Scan_Req = 0x0a,
|
||||
Lancheck_Scan_Resp = 0x1a,
|
||||
Lancheck_Req = 0x0c,
|
||||
Lancheck_Resp = 0x1c,
|
||||
// Lancheck_UNK_Req = 0x0d,
|
||||
// Lancheck_UNK_Resp = 0x1d,
|
||||
Ping_Req = 0x20,
|
||||
Ping_Resp = 0x30,
|
||||
|
||||
// Device switching
|
||||
Enable_Function_Req = 0x42,
|
||||
Enable_Function_Resp = 0x52,
|
||||
Disable_Function_Req = 0x43,
|
||||
Disable_Function_Resp = 0x53,
|
||||
|
||||
Current_Function = 0x45,
|
||||
General_Query = 0x46,
|
||||
|
||||
// Events
|
||||
Insertion = 0x50,
|
||||
Ejection = 0x51,
|
||||
|
||||
// Physical interface
|
||||
Backlight_Adjust = 0x59,
|
||||
Beep = 0x60,
|
||||
Eject = 0x80,
|
||||
Disc_Up = 0x90,
|
||||
Disc_Down = 0x91,
|
||||
Track_Seek_Up = 0x94,
|
||||
Track_Seek_Down = 0x95,
|
||||
Track_Fast_Forward = 0x98,
|
||||
Track_Rewind = 0x99,
|
||||
Pwrvol_Knob_Righthand_Turn = 0x9c,
|
||||
Pwrvol_Knob_Lefthand_Turn = 0x9d,
|
||||
CD_Enable_Repeat = 0xa0,
|
||||
CD_Disable_Repeat = 0xa1,
|
||||
CD_Enable_Disk_Repeat = 0xa3,
|
||||
CD_Disable_Disk_Repeat = 0xa4,
|
||||
CD_Enable_Scan = 0xa6,
|
||||
CD_Disable_Scan = 0xa7,
|
||||
CD_Enable_Disk_Scan = 0xa9,
|
||||
CD_Disable_Disk_Scan = 0xaa,
|
||||
CD_Enable_Random = 0xb0,
|
||||
CD_Disable_Random = 0xb1,
|
||||
CD_Enable_Disk_Random = 0xb3,
|
||||
CD_Disable_Disk_Random = 0xb4,
|
||||
|
||||
// Requests and Response pairs
|
||||
Initial_Report_Request = 0xe0,
|
||||
Initial_Report_Response = 0xf0,
|
||||
|
||||
Playback_Request = 0xe2,
|
||||
Playback_Report = 0xf2,
|
||||
|
||||
Loading_Request2 = 0xe4,
|
||||
Loading_Response2 = 0xf4,
|
||||
|
||||
Request_Track_Name = 0xed,
|
||||
Report_Track_Name = 0xfd,
|
||||
|
||||
// Reports
|
||||
Status_Report = 0xf1, // Typically unprompted, sent to dev_STATUS
|
||||
Loading_Status_Report = 0xf3, // Typically unprompted, sent to dev_STATUS
|
||||
Report_TOC = 0xf9,
|
||||
} actions;
|
||||
+73
-53
@@ -6,24 +6,27 @@
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include "avclan_defs.h"
|
||||
#include "avclan.h"
|
||||
#include "cdchanger.hpp"
|
||||
#include "device.hpp"
|
||||
#include "frame.hpp"
|
||||
#include "mediacontrol.h"
|
||||
#include "statustimer.h"
|
||||
|
||||
namespace {
|
||||
using namespace avclan;
|
||||
|
||||
constexpr uint8_t cdloading_resp[] = {dev_CD_CHANGER,
|
||||
dev_STATUS,
|
||||
Loading_Status_Report,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x01,
|
||||
0x02};
|
||||
constexpr uint8_t cdloading_resp[] = {
|
||||
to_underlying(Device::CD_CHANGER),
|
||||
to_underlying(Device::STATUS),
|
||||
to_underlying(Action::Loading_Status_Report),
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x01,
|
||||
0x02};
|
||||
|
||||
constexpr int WIRE_SIZE = 8; // cd state report size in bytes
|
||||
constexpr int TIME_SKIP = 15; // seconds
|
||||
@@ -56,20 +59,23 @@ void CDChanger::init() {
|
||||
|
||||
void CDChanger::handle(const Frame *in, Frame *out) {
|
||||
const uint8_t *data = &in->data[1];
|
||||
const auto from = static_cast<devices>(*data++);
|
||||
const auto from = static_cast<Device>(*data++);
|
||||
/* const auto to = */ data++;
|
||||
const auto action = *data++;
|
||||
const auto action = static_cast<Action>(*data++);
|
||||
|
||||
static const uint8_t function_change_resp[] = {0x00, dev_CD_CHANGER, from,
|
||||
0xFF, 0x01};
|
||||
static const uint8_t function_change_resp[] = {
|
||||
0x00, to_underlying(Device::CD_CHANGER), to_underlying(from), 0xFF, 0x01};
|
||||
|
||||
using enum Action;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
// Unicast to CD changer: bytes are (0x00, from, to, action, [extra...]).
|
||||
switch (action) {
|
||||
case Enable_Function_Req:
|
||||
out->is_unicast = true;
|
||||
out->length = sizeof(function_change_resp);
|
||||
memcpy(out->data, function_change_resp, sizeof(function_change_resp));
|
||||
out->data[3] = Enable_Function_Resp;
|
||||
out->data[3] = to_underlying(Enable_Function_Resp);
|
||||
state = 0;
|
||||
flags2 = 0x80;
|
||||
out->reaction = r_StatusReport;
|
||||
@@ -80,7 +86,7 @@ void CDChanger::handle(const Frame *in, Frame *out) {
|
||||
stopPlaying();
|
||||
out->length = sizeof(function_change_resp);
|
||||
memcpy(out->data, function_change_resp, sizeof(function_change_resp));
|
||||
out->data[3] = Disable_Function_Resp;
|
||||
out->data[3] = to_underlying(Disable_Function_Resp);
|
||||
state = 0;
|
||||
flags2 = 0x80;
|
||||
out->is_unicast = true;
|
||||
@@ -90,8 +96,9 @@ void CDChanger::handle(const Frame *in, Frame *out) {
|
||||
case Eject: {
|
||||
// "Eject" label is multiply wrong; proper meaning unclear:
|
||||
// - First observed on initial multiple presses of "CD" button,
|
||||
// triggering (after {0x00, dev_CD_CHANGER, dev_COMM_v1, Insertion,
|
||||
// 0x01} response) proper activation of mockingboard/cd-changer.
|
||||
// triggering (after {0x00, Device::CD_CHANGER, Device::COMM_v1,
|
||||
// Insertion, 0x01} response) proper activation of
|
||||
// mockingboard/cd-changer.
|
||||
// - Subsequently observed when pressing (technically
|
||||
// releasing?) the fast-forward button and rewind
|
||||
if (static_cast<bool>(state & SEEKING)) { // FF/RW button released
|
||||
@@ -99,8 +106,9 @@ void CDChanger::handle(const Frame *in, Frame *out) {
|
||||
} else {
|
||||
out->is_unicast = true;
|
||||
{
|
||||
const uint8_t msg[] = {0x00, dev_CD_CHANGER, dev_CMD_SW, Insertion,
|
||||
0x01};
|
||||
const uint8_t msg[] = {0x00, to_underlying(Device::CD_CHANGER),
|
||||
to_underlying(Device::CMD_SW),
|
||||
to_underlying(Insertion), 0x01};
|
||||
out->length = sizeof(msg);
|
||||
memcpy(out->data, msg, sizeof(msg));
|
||||
}
|
||||
@@ -112,8 +120,15 @@ void CDChanger::handle(const Frame *in, Frame *out) {
|
||||
out->is_unicast = true;
|
||||
// No knowledge/understanding of field meaning/interpretation
|
||||
const uint8_t cdinitreport_resp[] = {
|
||||
0x00, dev_CD_CHANGER, from, Initial_Report_Response, 0x01, 0x31,
|
||||
0x10, 0x01, 0x01};
|
||||
0x00,
|
||||
to_underlying(Device::CD_CHANGER),
|
||||
to_underlying(from),
|
||||
to_underlying(Initial_Report_Response),
|
||||
0x01,
|
||||
0x31,
|
||||
0x10,
|
||||
0x01,
|
||||
0x01};
|
||||
out->length = sizeof(cdinitreport_resp);
|
||||
memcpy(out->data, cdinitreport_resp, sizeof(cdinitreport_resp));
|
||||
out->reaction = r_SendOnly;
|
||||
@@ -121,9 +136,9 @@ void CDChanger::handle(const Frame *in, Frame *out) {
|
||||
}
|
||||
case Playback_Request:
|
||||
out->data[0] = 0x00;
|
||||
out->data[1] = dev_CD_CHANGER;
|
||||
out->data[2] = from;
|
||||
out->data[3] = Playback_Report;
|
||||
out->data[1] = to_underlying(Device::CD_CHANGER);
|
||||
out->data[2] = to_underlying(from);
|
||||
out->data[3] = to_underlying(Playback_Report);
|
||||
out->length = WIRE_SIZE + 4;
|
||||
serialize(&out->data[4]);
|
||||
out->is_unicast = true;
|
||||
@@ -133,8 +148,8 @@ void CDChanger::handle(const Frame *in, Frame *out) {
|
||||
out->data[0] = 0x00;
|
||||
out->length = sizeof(cdloading_resp) + 1;
|
||||
memcpy(&out->data[1], cdloading_resp, sizeof(cdloading_resp));
|
||||
out->data[2] = from;
|
||||
out->data[3] = Loading_Response2;
|
||||
out->data[2] = to_underlying(from);
|
||||
out->data[3] = to_underlying(Loading_Response2);
|
||||
out->is_unicast = true;
|
||||
out->reaction = r_SendOnly;
|
||||
break;
|
||||
@@ -147,7 +162,7 @@ void CDChanger::handle(const Frame *in, Frame *out) {
|
||||
mins = 0xff;
|
||||
secs = 0x7f;
|
||||
flags2 = 0xc0;
|
||||
generateStatus(out, true, dev_CMD_SW);
|
||||
generateStatus(out, true, Device::CMD_SW);
|
||||
AVCLAN_mediaFunction(MEDIA_SKIP_FORWARD);
|
||||
out->reaction = r_TrackChange;
|
||||
break;
|
||||
@@ -163,7 +178,7 @@ void CDChanger::handle(const Frame *in, Frame *out) {
|
||||
mins = 0xff;
|
||||
secs = 0x7f;
|
||||
flags2 = 0xc0;
|
||||
generateStatus(out, true, dev_CMD_SW);
|
||||
generateStatus(out, true, Device::CMD_SW);
|
||||
AVCLAN_mediaFunction(MEDIA_SKIP_BACKWARD);
|
||||
out->reaction = r_TrackChange;
|
||||
break;
|
||||
@@ -174,7 +189,7 @@ void CDChanger::handle(const Frame *in, Frame *out) {
|
||||
secs -= 60;
|
||||
++mins;
|
||||
}
|
||||
generateStatus(out, true, dev_CMD_SW);
|
||||
generateStatus(out, true, Device::CMD_SW);
|
||||
AVCLAN_mediaFunction(MEDIA_SKIP_FORWARD);
|
||||
statustimer_reset(); // Skipped to a whole/round sec; ensure next tick
|
||||
// is ~1 sec from now
|
||||
@@ -194,7 +209,7 @@ void CDChanger::handle(const Frame *in, Frame *out) {
|
||||
}
|
||||
} else
|
||||
secs -= TIME_SKIP;
|
||||
generateStatus(out, true, dev_CMD_SW);
|
||||
generateStatus(out, true, Device::CMD_SW);
|
||||
AVCLAN_mediaFunction(MEDIA_SKIP_BACKWARD);
|
||||
statustimer_reset(); // Skipped to a whole/round sec; ensure next tick
|
||||
// is ~1 sec from now
|
||||
@@ -203,46 +218,47 @@ void CDChanger::handle(const Frame *in, Frame *out) {
|
||||
}
|
||||
case CD_Enable_Random:
|
||||
flags |= RANDOM;
|
||||
generateStatus(out, true, dev_CMD_SW);
|
||||
generateStatus(out, true, Device::CMD_SW);
|
||||
out->reaction = r_StatusReport;
|
||||
break;
|
||||
case CD_Disable_Random:
|
||||
flags &= ~RANDOM;
|
||||
generateStatus(out, true, dev_CMD_SW);
|
||||
generateStatus(out, true, Device::CMD_SW);
|
||||
out->reaction = r_StatusReport;
|
||||
break;
|
||||
case CD_Enable_Repeat:
|
||||
flags |= REPEAT;
|
||||
generateStatus(out, true, dev_CMD_SW);
|
||||
generateStatus(out, true, Device::CMD_SW);
|
||||
out->reaction = r_StatusReport;
|
||||
break;
|
||||
case CD_Disable_Repeat:
|
||||
flags &= ~REPEAT;
|
||||
generateStatus(out, true, dev_CMD_SW);
|
||||
generateStatus(out, true, Device::CMD_SW);
|
||||
out->reaction = r_StatusReport;
|
||||
break;
|
||||
case CD_Enable_Disk_Random:
|
||||
flags |= DISK_RANDOM;
|
||||
generateStatus(out, true, dev_CMD_SW);
|
||||
generateStatus(out, true, Device::CMD_SW);
|
||||
out->reaction = r_StatusReport;
|
||||
break;
|
||||
case CD_Disable_Disk_Random:
|
||||
flags &= ~DISK_RANDOM;
|
||||
generateStatus(out, true, dev_CMD_SW);
|
||||
generateStatus(out, true, Device::CMD_SW);
|
||||
out->reaction = r_StatusReport;
|
||||
break;
|
||||
case CD_Enable_Disk_Repeat:
|
||||
flags |= DISK_REPEAT;
|
||||
generateStatus(out, true, dev_CMD_SW);
|
||||
generateStatus(out, true, Device::CMD_SW);
|
||||
out->reaction = r_StatusReport;
|
||||
break;
|
||||
case CD_Disable_Disk_Repeat:
|
||||
flags &= ~DISK_REPEAT;
|
||||
generateStatus(out, true, dev_CMD_SW);
|
||||
generateStatus(out, true, Device::CMD_SW);
|
||||
out->reaction = r_StatusReport;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
void CDChanger::react(Frame *out, detail::Error::Send err) {
|
||||
@@ -258,8 +274,12 @@ void CDChanger::react(Frame *out, detail::Error::Send err) {
|
||||
}
|
||||
break;
|
||||
case r_Ejection: {
|
||||
const uint8_t play[] = {0x00, dev_COMM_CTRL, dev_COMM_v1,
|
||||
Insertion, dev_CD_CHANGER, 0x01};
|
||||
const uint8_t play[] = {0x00,
|
||||
to_underlying(Device::COMM_CTRL),
|
||||
to_underlying(Device::COMM_v1),
|
||||
to_underlying(Action::Insertion),
|
||||
to_underlying(Device::CD_CHANGER),
|
||||
0x01};
|
||||
out->length = sizeof(play);
|
||||
memcpy(out->data, play, sizeof(play));
|
||||
}
|
||||
@@ -270,8 +290,8 @@ void CDChanger::react(Frame *out, detail::Error::Send err) {
|
||||
out->peripheral_addr = 0x1FF;
|
||||
out->length = sizeof(cdloading_resp) + 1;
|
||||
memcpy(out->data, cdloading_resp, sizeof(cdloading_resp));
|
||||
out->data[1] = dev_STATUS;
|
||||
out->data[2] = Loading_Status_Report;
|
||||
out->data[1] = to_underlying(Device::STATUS);
|
||||
out->data[2] = to_underlying(Action::Loading_Status_Report);
|
||||
out->reaction = r_SendOnly;
|
||||
break;
|
||||
case r_TrackChange:
|
||||
@@ -281,12 +301,12 @@ void CDChanger::react(Frame *out, detail::Error::Send err) {
|
||||
[[fallthrough]];
|
||||
case r_NormalizeState:
|
||||
normalizeState();
|
||||
generateStatus(out, true, dev_STATUS);
|
||||
generateStatus(out, true, Device::STATUS);
|
||||
out->reaction = r_SendOnly;
|
||||
break;
|
||||
case r_StartPlaying:
|
||||
normalizeState();
|
||||
generateStatus(out, true, dev_STATUS);
|
||||
generateStatus(out, true, Device::STATUS);
|
||||
out->reaction = r_BeganPlaying;
|
||||
break;
|
||||
case r_BeganPlaying:
|
||||
@@ -294,7 +314,7 @@ void CDChanger::react(Frame *out, detail::Error::Send err) {
|
||||
out->reaction = r_Nothing;
|
||||
break;
|
||||
case r_StatusReport:
|
||||
generateStatus(out, true, dev_STATUS);
|
||||
generateStatus(out, true, Device::STATUS);
|
||||
out->reaction = r_SendOnly;
|
||||
break;
|
||||
case r_SendOnly: [[fallthrough]];
|
||||
@@ -311,7 +331,7 @@ void CDChanger::enable(Frame *out) {
|
||||
secs = 0;
|
||||
state = SEEKING | SEEKING_TRACK;
|
||||
flags2 = 0xc0;
|
||||
generateStatus(out, true, dev_STATUS);
|
||||
generateStatus(out, true, Device::STATUS);
|
||||
out->reaction = r_StartPlaying;
|
||||
}
|
||||
}
|
||||
@@ -321,7 +341,7 @@ void CDChanger::resolvepending() { statustimer_clearTick(); }
|
||||
|
||||
void CDChanger::emit(Frame *out, uint16_t peripheral) {
|
||||
out->peripheral_addr = peripheral;
|
||||
generateStatus(out, true, dev_STATUS);
|
||||
generateStatus(out, true, Device::STATUS);
|
||||
out->reaction = r_StateReport;
|
||||
}
|
||||
|
||||
@@ -379,7 +399,7 @@ void CDChanger::incrementTime() {
|
||||
|
||||
// Used for changed status messages
|
||||
void CDChanger::generateStatus(Frame *status, bool is_unicast,
|
||||
devices to) const {
|
||||
Device to) const {
|
||||
status->is_unicast = is_unicast;
|
||||
if (!is_unicast)
|
||||
status->peripheral_addr = 0x1FF;
|
||||
@@ -389,9 +409,9 @@ void CDChanger::generateStatus(Frame *status, bool is_unicast,
|
||||
uint8_t *data = status->data;
|
||||
if (is_unicast)
|
||||
*data++ = 0x00;
|
||||
*data++ = dev_CD_CHANGER;
|
||||
*data++ = to;
|
||||
*data++ = Status_Report;
|
||||
*data++ = to_underlying(Device::CD_CHANGER);
|
||||
*data++ = to_underlying(to);
|
||||
*data++ = to_underlying(Action::Status_Report);
|
||||
serialize(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "avclan.h"
|
||||
#include "avclan_defs.h"
|
||||
#include "device.hpp"
|
||||
#include "frame.hpp"
|
||||
|
||||
namespace avclan {
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
r_StateReport, // *IS* a status report (follow-up or unprompted)
|
||||
};
|
||||
|
||||
static constexpr uint8_t id = dev_CD_CHANGER;
|
||||
static constexpr Device id = Device::CD_CHANGER;
|
||||
void init();
|
||||
|
||||
void handle(const Frame *in, Frame *out);
|
||||
@@ -75,7 +75,7 @@ private:
|
||||
void stopPlaying();
|
||||
void serialize(uint8_t *dst) const;
|
||||
void setTime(uint8_t mins, uint8_t secs);
|
||||
void generateStatus(Frame *status, bool is_unicast, devices to) const;
|
||||
void generateStatus(Frame *status, bool is_unicast, Device to) const;
|
||||
void normalizeState();
|
||||
|
||||
bool playing = false;
|
||||
|
||||
+35
-14
@@ -3,23 +3,44 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "avclan.h"
|
||||
#include "frame.hpp"
|
||||
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
|
||||
#include "avclan.h"
|
||||
#include "frame.hpp"
|
||||
|
||||
namespace avclan {
|
||||
|
||||
enum class Device : uint8_t {
|
||||
LAN = 0x00,
|
||||
COMM_CTRL = 0x01,
|
||||
COMM_v1 = 0x11,
|
||||
COMM_v2 = 0x12,
|
||||
SW = 0x21,
|
||||
SW_NAME = 0x23,
|
||||
SW_CONVERTING = 0x24,
|
||||
CMD_SW = 0x25,
|
||||
STATUS = 0x31,
|
||||
BEEP_HU = 0x28,
|
||||
BEEP_SPEAKERS = 0x29,
|
||||
TUNER = 0x60,
|
||||
TAPE_DECK = 0x61,
|
||||
CD = 0x62,
|
||||
CD_CHANGER = 0x63,
|
||||
AUDIO_AMP = 0x74,
|
||||
};
|
||||
|
||||
template <class T>
|
||||
concept Device = requires { std::integral_constant<uint8_t, T::id>{}; } &&
|
||||
requires(T dev, const Frame *in, Frame *out,
|
||||
detail::Error::Send err, uint16_t peripheral) {
|
||||
dev.init();
|
||||
dev.handle(in, out);
|
||||
dev.enable(out);
|
||||
dev.react(out, err);
|
||||
{ dev.pending() } -> std::convertible_to<bool>;
|
||||
dev.resolvepending();
|
||||
dev.emit(out, peripheral);
|
||||
};
|
||||
concept DeviceInterface =
|
||||
requires { std::integral_constant<Device, T::id>{}; } &&
|
||||
requires(T dev, const Frame *in, Frame *out, detail::Error::Send err,
|
||||
uint16_t peripheral) {
|
||||
dev.init();
|
||||
dev.handle(in, out);
|
||||
dev.enable(out);
|
||||
dev.react(out, err);
|
||||
{ dev.pending() } -> std::convertible_to<bool>;
|
||||
dev.resolvepending();
|
||||
dev.emit(out, peripheral);
|
||||
};
|
||||
} // namespace avclan
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <cstdint>
|
||||
|
||||
namespace avclan {
|
||||
enum class Device : uint8_t;
|
||||
|
||||
struct Frame {
|
||||
struct Print {
|
||||
bool print : 1 = false; // print at all
|
||||
@@ -27,7 +29,7 @@ struct Frame {
|
||||
void print(Print print) const;
|
||||
|
||||
uint8_t reaction;
|
||||
uint8_t owning_device;
|
||||
Device owning_device;
|
||||
bool is_unicast;
|
||||
uint16_t controller_addr; // formerly "master"
|
||||
uint16_t peripheral_addr; // formerly "slave"
|
||||
|
||||
+35
-25
@@ -4,7 +4,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#pragma once
|
||||
|
||||
#include "avclan_defs.h"
|
||||
#include "avclan.h"
|
||||
#include "bus.hpp"
|
||||
#include "device.hpp"
|
||||
#include "frame.hpp"
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <tuple>
|
||||
|
||||
namespace avclan {
|
||||
template <Device... Devs> class Peripheral {
|
||||
template <DeviceInterface... Devs> class Peripheral {
|
||||
public:
|
||||
using Error = detail::Error;
|
||||
|
||||
@@ -38,6 +38,8 @@ public:
|
||||
#define PACK3(a, b, c) (((uint32_t)(a) << 16) | ((uint32_t)(b) << 8) | (c))
|
||||
|
||||
void route(const Frame *in, Frame *out) {
|
||||
using enum Device;
|
||||
using enum Action;
|
||||
out->reaction = 0;
|
||||
|
||||
if (AVCLAN_ismuted() || in->length < 3)
|
||||
@@ -45,8 +47,8 @@ public:
|
||||
|
||||
// 0xFF placeholders are variant bytes filled by writing directly to
|
||||
// out->data[N] after memcpy.
|
||||
static const uint8_t lancheck_resp[] = {0x00, dev_COMM_CTRL, dev_LAN, 0xFF,
|
||||
0xFF};
|
||||
static const uint8_t lancheck_resp[] = {0x00, to_underlying(COMM_CTRL),
|
||||
to_underlying(LAN), 0xFF, 0xFF};
|
||||
|
||||
out->peripheral_addr = controller_;
|
||||
|
||||
@@ -62,51 +64,58 @@ public:
|
||||
// Broadcast: bytes are (from, to, action, [extra...]).
|
||||
// peripheral_addr unchecked — always 0xFFF or 0x1FF in known traffic.
|
||||
switch (PACK3(b0, b1, b2)) {
|
||||
case PACK3(dev_LAN, dev_COMM_CTRL, Lancheck_Scan_Req):
|
||||
case PACK3(LAN, COMM_CTRL, to_underlying(Lancheck_Scan_Req)):
|
||||
out->length = sizeof(lancheck_resp);
|
||||
out->is_unicast = true;
|
||||
memcpy(out->data, lancheck_resp, sizeof(lancheck_resp));
|
||||
out->data[3] = Lancheck_Scan_Resp;
|
||||
out->data[3] = to_underlying(Lancheck_Scan_Resp);
|
||||
out->data[4] = 0x01;
|
||||
out->reaction = 1;
|
||||
break;
|
||||
case PACK3(dev_LAN, dev_COMM_CTRL, Lancheck_Req):
|
||||
case PACK3(LAN, COMM_CTRL, to_underlying(Lancheck_Req)):
|
||||
out->length = sizeof(lancheck_resp);
|
||||
out->is_unicast = true;
|
||||
memcpy(out->data, lancheck_resp, sizeof(lancheck_resp));
|
||||
out->data[3] = Lancheck_Resp;
|
||||
out->data[3] = to_underlying(Lancheck_Resp);
|
||||
out->data[4] = 0x00;
|
||||
out->reaction = 1;
|
||||
break;
|
||||
case PACK3(dev_LAN, dev_COMM_CTRL, Lancheck_End_Req):
|
||||
case PACK3(LAN, COMM_CTRL, to_underlying(Lancheck_End_Req)):
|
||||
out->is_unicast = true;
|
||||
out->length = sizeof(lancheck_resp) - 1;
|
||||
memcpy(out->data, lancheck_resp, out->length);
|
||||
out->data[3] = Lancheck_End_Resp;
|
||||
out->data[3] = to_underlying(Lancheck_End_Resp);
|
||||
out->reaction = 1;
|
||||
break;
|
||||
case PACK3(dev_COMM_v1, dev_COMM_CTRL, Current_Function):
|
||||
case PACK3(dev_COMM_v2, dev_COMM_CTRL, Current_Function):
|
||||
((Devs::id == b3 ? std::get<Devs>(devices_).enable(out), 0 : 0), ...);
|
||||
case PACK3(COMM_v1, COMM_CTRL, to_underlying(Current_Function)):
|
||||
case PACK3(COMM_v2, COMM_CTRL, to_underlying(Current_Function)):
|
||||
((Devs::id == static_cast<Device>(b3)
|
||||
? std::get<Devs>(devices_).enable(out),
|
||||
0 : 0),
|
||||
...);
|
||||
break;
|
||||
case PACK3(dev_COMM_v1, dev_COMM_CTRL, Ping_Req):
|
||||
case PACK3(dev_COMM_v2, dev_COMM_CTRL, Ping_Req): {
|
||||
case PACK3(COMM_v1, COMM_CTRL, to_underlying(Ping_Req)):
|
||||
case PACK3(COMM_v2, COMM_CTRL, to_underlying(Ping_Req)): {
|
||||
out->is_unicast = true;
|
||||
const uint8_t ping_resp[] = {0x00, dev_COMM_CTRL, dev_COMM_v1,
|
||||
Ping_Resp, 0xFF, b3};
|
||||
const uint8_t ping_resp[] = {0x00,
|
||||
to_underlying(COMM_CTRL),
|
||||
to_underlying(COMM_v1),
|
||||
to_underlying(Ping_Resp),
|
||||
0xFF,
|
||||
b3};
|
||||
out->length = sizeof(ping_resp);
|
||||
memcpy(out->data, ping_resp, sizeof(ping_resp));
|
||||
out->reaction = 1;
|
||||
break;
|
||||
}
|
||||
case PACK3(dev_COMM_v1, dev_COMM_CTRL, List_Functions_Req):
|
||||
case PACK3(dev_COMM_v2, dev_COMM_CTRL, List_Functions_Req): {
|
||||
case PACK3(COMM_v1, COMM_CTRL, to_underlying(List_Functions_Req)):
|
||||
case PACK3(COMM_v2, COMM_CTRL, to_underlying(List_Functions_Req)): {
|
||||
controller_ = in->controller_addr;
|
||||
out->peripheral_addr = controller_;
|
||||
out->is_unicast = true;
|
||||
const uint8_t list_functions_resp[] = {
|
||||
0x00, dev_COMM_CTRL, dev_COMM_v1, List_Functions_Resp,
|
||||
dev_CD_CHANGER};
|
||||
0x00, to_underlying(COMM_CTRL), to_underlying(COMM_v1),
|
||||
to_underlying(List_Functions_Resp), to_underlying(CD_CHANGER)};
|
||||
out->length = sizeof(list_functions_resp);
|
||||
memcpy(out->data, list_functions_resp, sizeof(list_functions_resp));
|
||||
out->reaction = 1;
|
||||
@@ -115,8 +124,9 @@ public:
|
||||
// case Restart_Lan: not handled
|
||||
}
|
||||
} else if (in->peripheral_addr == address_ && b0 == 0x00) {
|
||||
((Devs::id == b2 ? device_preroute(std::get<Devs>(devices_), in, out),
|
||||
0 : 0),
|
||||
((Devs::id == static_cast<Device>(b2)
|
||||
? device_preroute(std::get<Devs>(devices_), in, out),
|
||||
0 : 0),
|
||||
...);
|
||||
}
|
||||
}
|
||||
@@ -143,11 +153,11 @@ private:
|
||||
out->control = 0xF;
|
||||
}
|
||||
|
||||
template <Device Dev, class F> void poller(Dev &dev, F &&fun) {
|
||||
template <DeviceInterface Dev, class F> void poller(Dev &dev, F &&fun) {
|
||||
if (dev.pending() && fun(dev))
|
||||
dev.resolvepending();
|
||||
}
|
||||
template <Device Dev>
|
||||
template <DeviceInterface Dev>
|
||||
void device_preroute(Dev &dev, const Frame *in, Frame *out) {
|
||||
out->owning_device = Dev::id;
|
||||
dev.handle(in, out);
|
||||
|
||||
+12
-5
@@ -66,6 +66,8 @@ int main() {
|
||||
uint8_t seqIdx = 0; // current index in data_tmp
|
||||
|
||||
Bus phy;
|
||||
using enum Action;
|
||||
using enum Device;
|
||||
Peripheral<CDChanger> peripheral(phy, 0x360);
|
||||
using Error = decltype(peripheral)::Error;
|
||||
using Print = Frame::Print;
|
||||
@@ -139,22 +141,27 @@ int main() {
|
||||
out->is_unicast = true;
|
||||
out->peripheral_addr = peripheral.controller();
|
||||
{
|
||||
const uint8_t beep[] = {0x00, dev_CD_CHANGER, dev_BEEP_SPEAKERS,
|
||||
0x60, 0x01};
|
||||
const uint8_t beep[] = {0x00, to_underlying(CD_CHANGER),
|
||||
to_underlying(BEEP_SPEAKERS), 0x60, 0x01};
|
||||
out->length = sizeof(beep);
|
||||
memcpy(out->data, beep, sizeof(beep));
|
||||
}
|
||||
out->reaction = 1;
|
||||
outgoing.push(std::move(out));
|
||||
}
|
||||
} else
|
||||
RS232_Print("!! Cache empty; unable to queue beep request");
|
||||
break;
|
||||
case 'P':
|
||||
if (auto out = cache.pop()) {
|
||||
out->is_unicast = true;
|
||||
out->peripheral_addr = peripheral.controller();
|
||||
{
|
||||
const uint8_t play[] = {0x00, dev_COMM_CTRL, dev_COMM_v1,
|
||||
Ejection, dev_CD_CHANGER, 0x01};
|
||||
const uint8_t play[] = {0x00,
|
||||
to_underlying(COMM_CTRL),
|
||||
to_underlying(COMM_v1),
|
||||
to_underlying(Ejection),
|
||||
to_underlying(CD_CHANGER),
|
||||
0x01};
|
||||
out->length = sizeof(play);
|
||||
memcpy(out->data, play, sizeof(play));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user