diff --git a/src/avclan/bus.cc b/src/avclan/bus.cc index 3996d1e..ac4b209 100644 --- a/src/avclan/bus.cc +++ b/src/avclan/bus.cc @@ -29,6 +29,7 @@ No acknowledge bits are sent for broadcast frames. */ +#include #include #include "avclan.h" @@ -54,7 +55,12 @@ inline constexpr with_ack_t with_ack{}; namespace avclan { class Bus::Handle { - Handle() { phy_guard_enter(); }; + // A live handle exclusively borrows the Bus for the duration of a + // transaction; holding the reference is what forces `get()` (and thus + // `read`/`send`) to require a mutable Bus, even though the bus hardware + // itself is reached through free `phy_*` functions. + Bus &bus_; + explicit Handle(Bus &bus) : bus_{bus} { phy_guard_enter(); }; friend Bus; public: @@ -62,17 +68,19 @@ public: Handle(const Handle &) = delete; Handle(Handle &&) = delete; + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) Send sendstartbit() { return phy_send_startbit(); }; + // NOLINTNEXTLINE(readability-convert-member-functions-to-static) Read readstartbit() { return phy_read_startbit(); }; template Trailer> requires(sizeof(T) < 3 && N < 16 && !std::same_as) Send send(T bits, Trailer /*tag*/) { - const auto parity = sendbits(bits); + const Bit parity = sendbits(bits); if constexpr (std::is_same_v) - sendbits<1>(static_cast(parity)); + sendbits<1>(to_underlying(parity)); return Send{0}; }; @@ -93,11 +101,11 @@ public: std::derived_from Trailer> requires(sizeof(T) < 3 && N < 16 && !std::same_as) Read read(T *bits, Trailer /*tag*/) { - const auto calc_parity = readbits(bits); + const Bit calc_parity = readbits(bits); if constexpr (std::is_same_v) { uint8_t read_parity; readbits<1>(&read_parity); - if (static_cast(calc_parity) != read_parity) + if (to_underlying(calc_parity) != read_parity) return Read::BAD_PARITY; } return Read{0}; @@ -165,10 +173,23 @@ template <> inline Bit Bus::Handle::readbits<8>(uint8_t *bits) { return phy_read_byte(bits); }; -void Bus::init() { phy_init(); }; +void Bus::init() { + // Idempotent: the single Bus is shared by reference, so every Peripheral's + // ctor calls init() on it — but the hardware must be brought up exactly once + // (phy_init is not assumed re-entrant/idempotent). + if (inited_) + return; + phy_init(); + muted_ = false; // phy_init leaves the bus TX unmuted + inited_ = true; +}; + +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) bool Bus::is_active() const { return phy_active(); }; -void Bus::mute(bool mute) { phy_mute(mute); }; -bool Bus::is_muted() const { return phy_is_muted(); }; +void Bus::mute(bool mute) { + phy_mute(mute); + muted_ = mute; // Only update muted_ *AFTER* hardware has finished muting +}; auto Bus::read(uint16_t address, Frame *in, Frame::Print print) -> Read { struct errtype { @@ -383,9 +404,12 @@ auto Bus::send(const Frame *out, Frame::Print print) -> Send { return err.errno; } -Bus::Handle Bus::get() { return {}; }; +Bus::Handle Bus::get() { return Handle{*this}; }; #ifndef NDEBUG +// Debug bit-timing measurement on the one physical bus; instance-scoped for the +// same reason as is_active(). +// NOLINTNEXTLINE(readability-convert-member-functions-to-static) void Bus::measure() { phy_measure(); } #endif diff --git a/src/avclan/bus.hpp b/src/avclan/bus.hpp index 07c93eb..487d26d 100644 --- a/src/avclan/bus.hpp +++ b/src/avclan/bus.hpp @@ -47,9 +47,7 @@ #pragma once -#include #include -#include #include "avclan.h" #include "frame.hpp" @@ -60,11 +58,20 @@ class Bus { public: using Error = detail::Error; + // There is exactly one physical bus (the HAL `phy_*` layer is a singleton). + // `Bus` models that single hardware instance: it is owned once and shared by + // reference (e.g. multiple `Peripheral`s hold a `Bus &`), never copied — a + // copy would fork `muted_`, which must stay coherent with the one hardware + // TX state. + Bus() = default; + Bus(const Bus &) = delete; + Bus &operator=(const Bus &) = delete; + void init(); bool is_active() const; void mute(bool mute); - bool is_muted() const; + bool is_muted() const { return muted_; }; #ifndef NDEBUG void measure(); @@ -75,7 +82,11 @@ public: private: class Handle; - static Handle get(); + Handle get(); + + // Assume mute after default ctor; only viable after init call + bool muted_ = true; + bool inited_ = false; }; } // namespace avclan diff --git a/src/avclan/peripheral.hpp b/src/avclan/peripheral.hpp index e564569..e26bbcb 100644 --- a/src/avclan/peripheral.hpp +++ b/src/avclan/peripheral.hpp @@ -18,7 +18,7 @@ template class Peripheral { public: using Error = detail::Error; - Peripheral(Bus bus, uint16_t address) : bus{bus}, address_{address} { + Peripheral(Bus &bus, uint16_t address) : bus{bus}, address_{address} { bus.init(); (std::get(devices_).init(), ...); } @@ -181,7 +181,7 @@ private: dev.handle(in, out); } - Bus bus; + Bus &bus; uint16_t controller_ = 0; const uint16_t address_; std::tuple devices_;