diff --git a/CMakeLists.txt b/CMakeLists.txt index 029bd2f..9eb037a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,16 @@ cmake_minimum_required(VERSION 3.24) -project(avclan-mockingboard VERSION 1 LANGUAGES C CXX ASM) +project(avclan-mockingboard VERSION 1.1 LANGUAGES C CXX) set(CMAKE_C_STANDARD 23) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) add_compile_options( -Wall -Wswitch-enum -Werror - "$<$:-fanalyzer>") + $<$:-fno-exceptions> + $<$:-fno-rtti> + $<$:-fanalyzer>) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "13") @@ -33,10 +36,9 @@ add_library(avclan STATIC src/avclan/cdchanger.c) add_executable(mockingboard - src/sniffer.c - src/com232.c - src/queue.c) - + 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. diff --git a/src/avclan/avclan_defs.h b/src/avclan/avclan_defs.h index 029993a..71c7803 100644 --- a/src/avclan/avclan_defs.h +++ b/src/avclan/avclan_defs.h @@ -116,12 +116,13 @@ typedef struct print_struct { } log_t; typedef struct AVCLAN_frame_struct { + uint8_t reaction; bool is_unicast; uint16_t controller_addr; // formerly "master" uint16_t peripheral_addr; // formerly "slave" uint8_t control; uint8_t length; - uint8_t *data; + uint8_t data[MAXMSGLEN]; } AVCLAN_frame_t; // A single bus symbol. bit_zero/bit_one carry data (and double as parity diff --git a/src/avclan/avclan_protocol.c b/src/avclan/avclan_protocol.c index f94d41d..6d0f4e7 100644 --- a/src/avclan/avclan_protocol.c +++ b/src/avclan/avclan_protocol.c @@ -25,11 +25,11 @@ static const uint8_t cdloading_resp[] = {dev_CD_CHANGER, 0x01, 0x02}; -response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { - response_t respond = r_Nothing; +void AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { + out->reaction = r_Nothing; if (AVCLAN_ismuted() || in->length < 3) - return respond; + return; // 0xFF placeholders are variant bytes filled by writing directly to // out->data[N] after memcpy. @@ -60,7 +60,7 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { memcpy(out->data, lancheck_resp, sizeof(lancheck_resp)); out->data[3] = Lancheck_Scan_Resp; out->data[4] = 0x01; - respond = r_Handled; + out->reaction = r_SendOnly; break; case PACK3(dev_LAN, dev_COMM_CTRL, Lancheck_Req): out->length = sizeof(lancheck_resp); @@ -69,7 +69,7 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { memcpy(out->data, lancheck_resp, sizeof(lancheck_resp)); out->data[3] = Lancheck_Resp; out->data[4] = 0x00; - respond = r_Handled; + out->reaction = r_SendOnly; break; case PACK3(dev_LAN, dev_COMM_CTRL, Lancheck_End_Req): out->is_unicast = true; @@ -77,7 +77,7 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { out->length = sizeof(lancheck_resp) - 1; memcpy(out->data, lancheck_resp, out->length); out->data[3] = Lancheck_End_Resp; - respond = r_Handled; + out->reaction = r_SendOnly; break; case PACK3(dev_COMM_v1, dev_COMM_CTRL, Current_Function): case PACK3(dev_COMM_v2, dev_COMM_CTRL, Current_Function): @@ -89,7 +89,7 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { cd_status.state = cd_SEEKING | cd_SEEKING_TRACK; cd_status.flags2 = 0xc0; AVCLAN_generateStatus(out, true, dev_STATUS); - respond = r_StartPlaying; + out->reaction = r_StartPlaying; } break; case PACK3(dev_COMM_v1, dev_COMM_CTRL, Ping_Req): @@ -100,7 +100,7 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { Ping_Resp, 0xFF, b3}; out->length = sizeof(ping_resp); memcpy(out->data, ping_resp, sizeof(ping_resp)); - respond = r_Handled; + out->reaction = r_SendOnly; break; } case PACK3(dev_COMM_v1, dev_COMM_CTRL, List_Functions_Req): @@ -112,7 +112,7 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { dev_CD_CHANGER}; out->length = sizeof(list_functions_resp); memcpy(out->data, list_functions_resp, sizeof(list_functions_resp)); - respond = r_Handled; + out->reaction = r_SendOnly; break; } // case Restart_Lan: not handled @@ -130,7 +130,7 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { out->data[3] = Enable_Function_Resp; cd_status.state = 0; cd_status.flags2 = 0x80; - respond = r_StatusReport; + out->reaction = r_StatusReport; break; case PACK3(dev_COMM_v1, dev_CD_CHANGER, Disable_Function_Req): [[fallthrough]]; @@ -145,7 +145,7 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { cd_status.flags2 = 0x80; out->is_unicast = true; out->peripheral_addr = HU_ADDR; - respond = r_StatusReport; + out->reaction = r_StatusReport; } break; case PACK3(dev_CMD_SW, dev_CD_CHANGER, Eject): { @@ -166,7 +166,7 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { out->length = sizeof(msg); memcpy(out->data, msg, sizeof(msg)); } - respond = r_Handled; + out->reaction = r_SendOnly; } break; } @@ -182,7 +182,7 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { 0x10, 0x01, 0x01}; out->length = sizeof(cdinitreport_resp); memcpy(&out->data[1], cdinitreport_resp, sizeof(cdinitreport_resp)); - respond = r_Handled; + out->reaction = r_SendOnly; break; } case PACK3(dev_CMD_SW, dev_CD_CHANGER, Playback_Request): [[fallthrough]]; @@ -195,7 +195,7 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { serializeCDStatus(&out->data[4]); out->is_unicast = true; out->peripheral_addr = HU_ADDR; - respond = r_Handled; + out->reaction = r_SendOnly; break; case PACK3(dev_CMD_SW, dev_CD_CHANGER, Loading_Request2): [[fallthrough]]; case PACK3(dev_STATUS, dev_CD_CHANGER, Loading_Request2): @@ -206,7 +206,7 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { out->data[3] = Loading_Response2; out->is_unicast = true; out->peripheral_addr = HU_ADDR; - respond = r_Handled; + out->reaction = r_SendOnly; break; case PACK3(dev_CMD_SW, dev_CD_CHANGER, Track_Seek_Up): cd_status.state = cd_SEEKING_TRACK; @@ -219,7 +219,7 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { cd_status.flags2 = 0xc0; AVCLAN_generateStatus(out, true, dev_CMD_SW); AVCLAN_mediaFunction(MEDIA_SKIP_FORWARD); - respond = r_TrackChange; + out->reaction = r_TrackChange; break; case PACK3(dev_CMD_SW, dev_CD_CHANGER, Track_Seek_Down): cd_status.state = cd_SEEKING_TRACK; @@ -235,7 +235,7 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { cd_status.flags2 = 0xc0; AVCLAN_generateStatus(out, true, dev_CMD_SW); AVCLAN_mediaFunction(MEDIA_SKIP_BACKWARD); - respond = r_TrackChange; + out->reaction = r_TrackChange; break; case PACK3(dev_CMD_SW, dev_CD_CHANGER, Track_Fast_Forward): { cd_status.state |= cd_SEEKING; @@ -248,7 +248,7 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { AVCLAN_mediaFunction(MEDIA_SKIP_FORWARD); statustimer_reset(); // Skipped to a whole/round sec; ensure next tick // is ~1 sec from now - respond = r_Handled; + out->reaction = r_SendOnly; break; } case PACK3(dev_CMD_SW, dev_CD_CHANGER, Track_Rewind): { @@ -268,67 +268,66 @@ response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out) { AVCLAN_mediaFunction(MEDIA_SKIP_BACKWARD); statustimer_reset(); // Skipped to a whole/round sec; ensure next tick // is ~1 sec from now - respond = r_Handled; + out->reaction = r_SendOnly; break; } case PACK3(dev_CMD_SW, dev_CD_CHANGER, CD_Enable_Random): cd_status.flags |= cd_RANDOM; AVCLAN_generateStatus(out, true, dev_CMD_SW); - respond = r_StatusReport; + out->reaction = r_StatusReport; break; case PACK3(dev_CMD_SW, dev_CD_CHANGER, CD_Disable_Random): cd_status.flags &= ~cd_RANDOM; AVCLAN_generateStatus(out, true, dev_CMD_SW); - respond = r_StatusReport; + out->reaction = r_StatusReport; break; case PACK3(dev_CMD_SW, dev_CD_CHANGER, CD_Enable_Repeat): cd_status.flags |= cd_REPEAT; AVCLAN_generateStatus(out, true, dev_CMD_SW); - respond = r_StatusReport; + out->reaction = r_StatusReport; break; case PACK3(dev_CMD_SW, dev_CD_CHANGER, CD_Disable_Repeat): cd_status.flags &= ~cd_REPEAT; AVCLAN_generateStatus(out, true, dev_CMD_SW); - respond = r_StatusReport; + out->reaction = r_StatusReport; break; case PACK3(dev_CMD_SW, dev_CD_CHANGER, CD_Enable_Disk_Random): cd_status.flags |= cd_DISK_RANDOM; AVCLAN_generateStatus(out, true, dev_CMD_SW); - respond = r_StatusReport; + out->reaction = r_StatusReport; break; case PACK3(dev_CMD_SW, dev_CD_CHANGER, CD_Disable_Disk_Random): cd_status.flags &= ~cd_DISK_RANDOM; AVCLAN_generateStatus(out, true, dev_CMD_SW); - respond = r_StatusReport; + out->reaction = r_StatusReport; break; case PACK3(dev_CMD_SW, dev_CD_CHANGER, CD_Enable_Disk_Repeat): cd_status.flags |= cd_DISK_REPEAT; AVCLAN_generateStatus(out, true, dev_CMD_SW); - respond = r_StatusReport; + out->reaction = r_StatusReport; break; case PACK3(dev_CMD_SW, dev_CD_CHANGER, CD_Disable_Disk_Repeat): cd_status.flags &= ~cd_DISK_REPEAT; AVCLAN_generateStatus(out, true, dev_CMD_SW); - respond = r_StatusReport; + out->reaction = r_StatusReport; break; } } - - return respond; } #undef PACK3 -RFrame_t *AVCLAN_statemachine(RFrame_t *resp) { - AVCLAN_frame_t *out = resp->frame; - switch (resp->r) { +void AVCLAN_statemachine(AVCLAN_frame_t *out) { + reaction_t r = out->reaction; + out->reaction = r_Nothing; + switch (r) { case r_Ejection: { const uint8_t play[] = {0x00, dev_COMM_CTRL, dev_COMM_v1, Insertion, dev_CD_CHANGER, 0x01}; out->length = sizeof(play); memcpy(out->data, play, sizeof(play)); } - resp->r = r_Report_Load; + out->reaction = r_Report_Load; break; case r_Report_Load: out->is_unicast = false; @@ -337,7 +336,7 @@ RFrame_t *AVCLAN_statemachine(RFrame_t *resp) { memcpy(out->data, cdloading_resp, sizeof(cdloading_resp)); out->data[1] = dev_STATUS; out->data[2] = Loading_Status_Report; - resp->r = r_Handled; + out->reaction = r_SendOnly; break; case r_TrackChange: AVCLAN_setTime(0x00, 0x00); @@ -347,24 +346,23 @@ RFrame_t *AVCLAN_statemachine(RFrame_t *resp) { case r_NormalizeState: AVCLAN_normalizeState(); AVCLAN_generateStatus(out, true, dev_STATUS); - resp->r = r_Handled; + out->reaction = r_SendOnly; break; case r_StartPlaying: AVCLAN_normalizeState(); AVCLAN_generateStatus(out, true, dev_STATUS); - resp->r = r_BeganPlaying; + out->reaction = r_BeganPlaying; break; case r_BeganPlaying: AVCLAN_startPlaying(); // only start PIT after normalizing state - resp->r = r_Nothing; + out->reaction = r_Nothing; break; case r_StatusReport: AVCLAN_generateStatus(out, true, dev_STATUS); - resp->r = r_Handled; + out->reaction = r_SendOnly; break; - case r_Handled: [[fallthrough]]; + case r_SendOnly: [[fallthrough]]; case r_Nothing: [[fallthrough]]; - default: resp->r = r_Nothing; + default: out->reaction = r_Nothing; } - return resp; } diff --git a/src/avclan/avclan_protocol.h b/src/avclan/avclan_protocol.h index f8fc066..aa7aa8c 100644 --- a/src/avclan/avclan_protocol.h +++ b/src/avclan/avclan_protocol.h @@ -17,12 +17,11 @@ extern "C" { /// Message state machine // - r_Nothing (0x00) means don't send current message -// - r_Handled means send current message and stop/finished state machine // - All other instances mean send current message and imply the presence of // follow-up messages within state machine typedef enum : uint8_t { r_Nothing = 0x00, - r_Handled, // No follow-up needed + r_SendOnly, // No further follow-up needed (beyond sending current) r_StatusReport = 0x02, // Needs follow-up status report r_NormalizeState, // cd_status needs normalized and resent r_StartPlaying, // ~equivalent to normalizeState, but cycles to BeganPlaying @@ -30,15 +29,10 @@ typedef enum : uint8_t { r_TrackChange, // Time needs reset r_Ejection, r_Report_Load, -} response_t; +} reaction_t; -typedef struct RFrame_struct { - response_t r; - AVCLAN_frame_t *frame; -} RFrame_t; - -response_t AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out); -RFrame_t *AVCLAN_statemachine(RFrame_t *resp); +void AVCLAN_handleframe(const AVCLAN_frame_t *in, AVCLAN_frame_t *out); +void AVCLAN_statemachine(AVCLAN_frame_t *out); #ifdef __cplusplus } diff --git a/src/avclan/cdchanger.c b/src/avclan/cdchanger.c index 1595a08..1585999 100644 --- a/src/avclan/cdchanger.c +++ b/src/avclan/cdchanger.c @@ -75,30 +75,14 @@ void AVCLAN_setTime(uint8_t mins, uint8_t secs) { cd_status.secs = secs; } -// Only used for regularly scheduled periodic updates -AVCLAN_frame_t *AVCLAN_getStatusFrame() { - static uint8_t status_data[sizeof(AVCLAN_CD_Status_t) + 3] = {0}; - static AVCLAN_frame_t status = {.is_unicast = false, - .controller_addr = DEVICE_ADDR, - .peripheral_addr = 0x1FF, - .control = 0xF, - .length = sizeof(status_data), - .data = status_data}; - - return &status; -} - // Used for changed status messages void AVCLAN_generateStatus(AVCLAN_frame_t *status, bool is_unicast, devices to) { - *status = (AVCLAN_frame_t){ - .is_unicast = is_unicast, - .controller_addr = DEVICE_ADDR, - .peripheral_addr = (is_unicast) ? HU_ADDR : 0x1FF, - .control = 0xF, - .length = sizeof(AVCLAN_CD_Status_t) + ((is_unicast) ? 4 : 3), - .data = status->data, // don't overwrite data pointer - }; + status->is_unicast = is_unicast; + status->controller_addr = DEVICE_ADDR; + status->peripheral_addr = (is_unicast) ? HU_ADDR : 0x1FF; + status->control = 0xF; + status->length = sizeof(AVCLAN_CD_Status_t) + ((is_unicast) ? 4 : 3); uint8_t *data = status->data; if (is_unicast) diff --git a/src/avclan/target/avr-attiny3216/CMakeLists.txt b/src/avclan/target/avr-attiny3216/CMakeLists.txt index ab5a98c..dd5dfbc 100644 --- a/src/avclan/target/avr-attiny3216/CMakeLists.txt +++ b/src/avclan/target/avr-attiny3216/CMakeLists.txt @@ -1,6 +1,6 @@ # AVR / ATtiny3216 hardware target (port). - include(CMakeDependentOption) +include(FetchContent) # --- Port implementation sources ------------------------------------------- target_sources(avclan PRIVATE @@ -79,8 +79,21 @@ try_compile(LIBC_VERSION_TEST COMPILE_DEFINITIONS -mmcu=${AVR_MCU} ) +if (AVCLAN_TARGET STREQUAL "avr-attiny3216") +FetchContent_Declare( + avr_libstdcpp + GIT_REPOSITORY https://github.com/modm-io/avr-libstdcpp.git + GIT_TAG 5354296040a2289c911062daa82336762231e897 +) +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) +endif() + + if(NOT LIBC_VERSION_TEST) - include(FetchContent) FetchContent_Declare( attiny_atpack URL http://packs.download.atmel.com/Atmel.ATtiny_DFP.2.0.368.atpack diff --git a/src/queue.c b/src/queue.c deleted file mode 100644 index bcd7968..0000000 --- a/src/queue.c +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (C) 2026 Allen Hill -// SPDX-License-Identifier: GPL-3.0-or-later - -#include - -#include "avclandrv.h" -#include "queue.h" - -void constructQueue(Queue_t *q, void **slots, void *items, uint8_t itemSize, - uint8_t len, bool constructFull) { - q->read = 0; - q->size = len; - q->buf = slots; - if (constructFull) { - q->write = len; - - for (uint8_t i = 0; i < len; ++i) { - q->buf[i] = items; - items = (char *)items + itemSize; - } - } else { - q->write = 0; - } -} - -void constructEmptyQueue(Queue_t *q, void **slots, uint8_t len) { - constructQueue(q, slots, nullptr, 0, len, false); -} - -bool isEmpty(const Queue_t *q) { return (q->write == q->read); } - -static inline bool isFull(const Queue_t *q) { - return ((q->write - q->read) == q->size); -} - -static inline uint8_t qMask(const Queue_t *q, uint8_t pos) { - return pos & (q->size - 1); -} - -uint8_t pushQueue(Queue_t *q, void *x) { - if (x == nullptr || isFull(q)) - return 1; - - q->buf[qMask(q, q->write++)] = x; - - return 0; -} - -void *peekQueue(const Queue_t *q) { - if (isEmpty(q)) - return nullptr; - - return q->buf[qMask(q, q->read)]; -} - -void *popQueue(Queue_t *q) { - if (isEmpty(q)) - return nullptr; - - return q->buf[qMask(q, q->read++)]; -} diff --git a/src/queue.h b/src/queue.h deleted file mode 100644 index afe04c3..0000000 --- a/src/queue.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2026 Allen Hill -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct Queue_struct { - uint8_t write; - uint8_t read; - uint8_t size; // MUST be a power of 2 (qMask depends on it) - void **buf; // size entries, owned by caller -} Queue_t; - -void constructQueue(Queue_t *q, void **slots, void *items, uint8_t itemSize, - uint8_t len, bool constructFull); -void constructEmptyQueue(Queue_t *q, void **slots, uint8_t len); -bool isEmpty(const Queue_t *q); -static inline void incrementRead(Queue_t *q) { q->read++; } -uint8_t pushQueue(Queue_t *q, void *x); -void *peekQueue(const Queue_t *q); -void *popQueue(Queue_t *q); - -#ifdef __cplusplus -} -#endif diff --git a/src/queue.hpp b/src/queue.hpp new file mode 100644 index 0000000..aef6cff --- /dev/null +++ b/src/queue.hpp @@ -0,0 +1,103 @@ +// Copyright (C) 2026 Allen Hill +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include +#include + +namespace detail { +template struct Deleter; +} + +template + requires((N & (N - 1)) == 0 && N <= std::numeric_limits::max()) +class Queue { + using Deleter = detail::template Deleter; + friend Deleter; + +public: + // Only full and copy-convert-from-full construction is allowed + Queue() = delete; + Queue(const Queue &) = delete; + + // Moving is unsupported due to being self-referential + Queue(Queue &&) = delete; + Queue &operator=(Queue &&) = delete; + + constexpr Queue(T (&items)[N]) + requires(Owning) + : owner{this}, write{N} { + for (uint8_t i = 0; i < N; ++i) + buf[i] = &items[i]; + } + constexpr Queue(Queue &queue) + requires(!Owning) + : owner{&queue} {} + + bool isEmpty() const { return write == read; } + uint8_t size() const { return write - read; } + uint8_t capacity() const { return N; } + bool isFull() const { return size() == capacity(); } + + uint8_t push(std::unique_ptr x) + requires(!Owning) + { + // structurally unnecessary; empty construction from same size parent + // guarantees + // !isFull assert(!isFull()); + + if (!x || !x.get_deleter().is_owned_by(owner)) + return 1; + + reclaim(x.release()); + + return 0; + } + + const T *peek() const { + if (isEmpty()) + return nullptr; + + return buf[mask(read)]; + } + + std::unique_ptr pop() { + if (isEmpty()) + return nullptr; + + if constexpr (Owning) + return {buf[mask(read++)], {this}}; + else + return {buf[mask(read++)], {owner}}; + } + +private: + uint8_t mask(uint8_t pos) const { return pos & (N - 1); } + void reclaim(T *x) { buf[mask(write++)] = x; } + + std::array buf = {}; + Queue *const owner; + uint8_t read = 0; + uint8_t write = 0; +}; + +template Queue(Queue &) -> Queue; +template Queue(T (&items)[N]) -> Queue; + +namespace detail { +template struct Deleter { + Deleter() = default; + constexpr Deleter(Queue *owner) : owner{owner} {} + void operator()(T *x) const { owner->reclaim(x); } + constexpr bool is_owned_by(const Queue *parent) const { + return owner == parent; + } + +private: + Queue *const owner = nullptr; +}; +} // namespace detail diff --git a/src/sniffer.c b/src/sniffer.cc similarity index 54% rename from src/sniffer.c rename to src/sniffer.cc index bffc54d..dc4b7e1 100644 --- a/src/sniffer.c +++ b/src/sniffer.cc @@ -3,55 +3,28 @@ // Copyright (C) 2015 Allen Hill // SPDX-License-Identifier: GPL-3.0-or-later -#include -#include -#include -#include -#include +#include +#include +#include #include "avclandrv.h" #include "board.h" #include "com232.h" -#include "queue.h" +#include "queue.hpp" const char *const offon[] = {"OFF", "ON"}; -#define CACHE_SIZE 16 -static_assert((CACHE_SIZE & (CACHE_SIZE - 1)) == 0, - "CACHE_SIZE must be a power of two (qMask depends on it)"); +static constexpr uint8_t CACHE_SIZE = 32; static AVCLAN_frame_t frames[CACHE_SIZE]; -static RFrame_t responses[CACHE_SIZE]; -static uint8_t framesdata[CACHE_SIZE][MAXMSGLEN]; -static void *cacheSlots[CACHE_SIZE]; -static void *rcacheSlots[CACHE_SIZE]; -static void *incomingSlots[CACHE_SIZE]; -static void *outgoingSlots[CACHE_SIZE]; - -static Queue_t cache, rcache, incoming, outgoing; +constinit static Queue cache(frames); +constinit static Queue incoming = cache; +constinit static Queue outgoing = cache; void Setup(); void print_help(); -static uint8_t return_resp(RFrame_t *resp) { - uint8_t err; - AVCLAN_frame_t *out = resp->frame; - if ((out >= frames) && (out < &frames[CACHE_SIZE])) - // only return cache-owned frames (e.g. not status, etc) - err = pushQueue(&cache, out); - err = pushQueue(&rcache, resp); - return err; -} - -static void push_or_return_resp(RFrame_t *resp) { - // r_Nothing == don't respond/send; should never be added to outgoing - if (resp->r != r_Nothing && !pushQueue(&outgoing, resp)) - return; - - return_resp(resp); -} - static void toggle_flag(bool *flag, const char *msg) { *flag = !*flag; RS232_Print(msg); @@ -89,87 +62,65 @@ int main() { uint8_t err = 0; uint8_t failedStatusReports = 0; + // Temporary, direct access is questionable since cache has ownership for (uint8_t i = 0; i < CACHE_SIZE; ++i) { frames[i].control = 0x0f; - frames[i].data = framesdata[i]; } - constructQueue(&cache, cacheSlots, frames, sizeof(AVCLAN_frame_t), CACHE_SIZE, - true); - constructEmptyQueue(&incoming, incomingSlots, CACHE_SIZE); - - constructQueue(&rcache, rcacheSlots, responses, sizeof(RFrame_t), CACHE_SIZE, - true); - constructEmptyQueue(&outgoing, outgoingSlots, CACHE_SIZE); + const AVCLAN_frame_t *lastStatus = nullptr; Setup(); print_help(); while (true) { if (AVCLAN_busActive()) { - if (AVCLAN_frame_t *msg = popQueue(&cache)) { - err = AVCLAN_readframe(msg, (log_t){.print = printAllFrames, - .binary = printBinary, - .verbose = verbose}); + if (auto msg = cache.pop()) { + err = AVCLAN_readframe(msg.get(), (log_t){.print = printAllFrames, + .binary = printBinary, + .verbose = verbose}); if (!err) - err = pushQueue(&incoming, msg); - - if (err) - pushQueue(&cache, msg); + incoming.push(std::move(msg)); } else { RS232_Print("!! Dropping an incoming message; cache is empty !!\n"); } } - if (AVCLAN_frame_t *in = peekQueue(&incoming)) { - if (AVCLAN_frame_t *out = popQueue(&cache)) { - incrementRead(&incoming); // successful out = pop cache; claim the - // peeked incoming - response_t respond = AVCLAN_handleframe(in, out); - pushQueue(&cache, in); // return in after use + if (auto in = incoming.peek()) { + if (auto out = cache.pop()) { + AVCLAN_handleframe(in, out.get()); + incoming.pop(); - if (respond) { - if (RFrame_t *resp = popQueue(&rcache)) { - *resp = (RFrame_t){.r = respond, .frame = out}; - push_or_return_resp(resp); - } else - pushQueue(&cache, out); // rcache exhausted — don't leak the frame - } else // no response needed; return to circulation - pushQueue(&cache, out); + if (out->reaction) + outgoing.push(std::move(out)); } else { RS232_Print("!! Unable to respond; cache is empty !!\n"); } } - if (RFrame_t *resp = popQueue(&outgoing)) { - AVCLAN_frame_t *out = resp->frame; + if (statustimer_tickPending()) { + if (auto status = cache.pop()) { + lastStatus = status.get(); + AVCLAN_generateStatus(status.get(), true, dev_STATUS); + status->reaction = r_SendOnly; + outgoing.push(std::move(status)); + statustimer_clearTick(); + } + } + + if (auto out = outgoing.pop()) { err = AVCLAN_sendframe( - out, (log_t){.print = printAllFrames, .binary = printBinary}); - if (err || resp->r == r_Handled) { - if (err && out == AVCLAN_getStatusFrame() && - ++failedStatusReports > 1) { + 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?) } - return_resp(resp); } else { - resp = AVCLAN_statemachine(resp); - push_or_return_resp(resp); + AVCLAN_statemachine(out.get()); + if (out->reaction) + outgoing.push(std::move(out)); } - } else if (statustimer_tickPending()) { - AVCLAN_frame_t *status = AVCLAN_getStatusFrame(); - AVCLAN_generateStatus(status, true, dev_STATUS); - if (RFrame_t *resp = (RFrame_t *)popQueue(&rcache)) { - *resp = (RFrame_t){.r = r_Handled, .frame = status}; - err = pushQueue(&outgoing, resp); - if (err) { - RS232_Print("Outgoing queue full; unable to send status update\n"); - pushQueue(&rcache, resp); - } else - statustimer_clearTick(); // Only clear if successful - } - // no further error handling needed; status isn't part of the cache } // Key handler @@ -192,39 +143,33 @@ int main() { case 'x': set_flag(&printBinary, false, "Binary: "); break; case 'E': // Beep - if (AVCLAN_frame_t *out = (AVCLAN_frame_t *)popQueue(&cache)) { - if (RFrame_t *resp = popQueue(&rcache)) { - out->is_unicast = true; - out->controller_addr = DEVICE_ADDR; - out->peripheral_addr = HU_ADDR; - { - const uint8_t beep[] = {0x00, dev_CD_CHANGER, dev_BEEP_SPEAKERS, - 0x60, 0x01}; - out->length = sizeof(beep); - memcpy(out->data, beep, sizeof(beep)); - } - *resp = (RFrame_t){.r = r_Handled, .frame = out}; - push_or_return_resp(resp); - } else - pushQueue(&cache, out); + if (auto out = cache.pop()) { + out->is_unicast = true; + out->controller_addr = DEVICE_ADDR; + out->peripheral_addr = HU_ADDR; + { + const uint8_t beep[] = {0x00, dev_CD_CHANGER, dev_BEEP_SPEAKERS, + 0x60, 0x01}; + out->length = sizeof(beep); + memcpy(out->data, beep, sizeof(beep)); + } + out->reaction = r_SendOnly; + outgoing.push(std::move(out)); } break; case 'P': - if (AVCLAN_frame_t *out = (AVCLAN_frame_t *)popQueue(&cache)) { - if (RFrame_t *resp = popQueue(&rcache)) { - out->is_unicast = true; - out->controller_addr = DEVICE_ADDR; - out->peripheral_addr = HU_ADDR; - { - const uint8_t play[] = {0x00, dev_COMM_CTRL, dev_COMM_v1, - Ejection, dev_CD_CHANGER, 0x01}; - out->length = sizeof(play); - memcpy(out->data, play, sizeof(play)); - } - *resp = (RFrame_t){.r = r_Ejection, .frame = out}; - push_or_return_resp(resp); - } else - pushQueue(&cache, out); + if (auto out = cache.pop()) { + out->is_unicast = true; + out->controller_addr = DEVICE_ADDR; + out->peripheral_addr = HU_ADDR; + { + const uint8_t play[] = {0x00, dev_COMM_CTRL, dev_COMM_v1, + Ejection, dev_CD_CHANGER, 0x01}; + out->length = sizeof(play); + memcpy(out->data, play, sizeof(play)); + } + out->reaction = r_Ejection; + outgoing.push(std::move(out)); } break; @@ -284,32 +229,25 @@ int main() { if (readSeq) { if (readBinary) { if (data_tmp[seqIdx] == 0x17) { - if (AVCLAN_frame_t *out = (AVCLAN_frame_t *)popQueue(&cache)) { - if (!AVCLAN_parseframe(data_tmp, --seqIdx, out)) { - if (RFrame_t *resp = popQueue(&rcache)) { - *resp = (RFrame_t){.r = r_Handled, .frame = out}; - push_or_return_resp(resp); - } else - pushQueue(&cache, out); - } else - pushQueue(&cache, out); + if (auto out = cache.pop()) { + if (!AVCLAN_parseframe(data_tmp, --seqIdx, out.get())) { + out->reaction = r_SendOnly; + outgoing.push(std::move(out)); + } } readSeq = readBinary = false; } else goto DEFAULT; // reading binary and this is a real data byte; // fall through to default } else { - if (AVCLAN_frame_t *out = (AVCLAN_frame_t *)popQueue(&cache)) { - if (RFrame_t *resp = popQueue(&rcache)) { - out->is_unicast = seqIsUnicast; - out->controller_addr = DEVICE_ADDR; - out->peripheral_addr = seqIsUnicast ? HU_ADDR : 0x1FF; - out->length = seqIdx; - memcpy(out->data, data_tmp, seqIdx); - *resp = (RFrame_t){.r = r_Handled, .frame = out}; - push_or_return_resp(resp); - } else - pushQueue(&cache, out); + if (auto out = cache.pop()) { + out->is_unicast = seqIsUnicast; + out->controller_addr = DEVICE_ADDR; + out->peripheral_addr = seqIsUnicast ? HU_ADDR : 0x1FF; + out->length = seqIdx; + memcpy(out->data, data_tmp, seqIdx); + out->reaction = r_SendOnly; + outgoing.push(std::move(out)); } printAllFrames = lastPrintAllFrames; }