From 6b22f14e6db173efb117bef5f13a31671cfa37fb Mon Sep 17 00:00:00 2001 From: Allen Hill Date: Fri, 18 Sep 2026 13:10:52 -0700 Subject: [PATCH] Fix `deafen` semantics --- src/avclan/bus.cc | 5 +++- src/avclan/bus.hpp | 3 +- src/avclan/hal/phy.h | 6 ++++ src/avclan/target/avr-attiny3216/phy_avr.c | 22 +++++++++++++-- src/avclan/target/pico2/phy.cc | 33 ++++++++++++++++++++-- 5 files changed, 62 insertions(+), 7 deletions(-) diff --git a/src/avclan/bus.cc b/src/avclan/bus.cc index bf9f83d..50b9576 100644 --- a/src/avclan/bus.cc +++ b/src/avclan/bus.cc @@ -120,7 +120,10 @@ void Bus::mute(bool mute) { phy_mute(mute); muted_ = mute; // Only update muted_ *AFTER* hardware has finished muting }; -void Bus::deafen(bool deaf) { deafened_ = deaf; } +void Bus::deafen(bool deaf) { + phy_deafen(deaf); + deafened_ = deaf; // Only update deafened_ *AFTER* the phy has stopped acking +} auto Bus::read(Frame::Print print) -> expected, Error::Read> { diff --git a/src/avclan/bus.hpp b/src/avclan/bus.hpp index bb43981..9e40592 100644 --- a/src/avclan/bus.hpp +++ b/src/avclan/bus.hpp @@ -81,7 +81,8 @@ public: void mute(bool mute); bool is_muted() const { return muted_; }; - // Set the device to be deaf to (ie ignore) bus activity + // Set the device to be deaf to (ie don't react to) bus activity. It is + // orthogonal to mute — a deaf device can still send frames. void deafen(bool deaf); #ifndef NDEBUG diff --git a/src/avclan/hal/phy.h b/src/avclan/hal/phy.h index ee0e016..c67f134 100644 --- a/src/avclan/hal/phy.h +++ b/src/avclan/hal/phy.h @@ -30,6 +30,11 @@ void phy_mute(bool mute); // Non-mutating (e.g. theoretically const qualified/-able) bool phy_is_muted(void); +// Withhold acknowledgement. Unlike mute this leaves TX alone: a deaf device +// can still manipulate the bus/send frames, it just never responds (e.g. ACK, +// etc). An empty implementation is sufficient for synchronous ports. +void phy_deafen(bool deaf); + // True when there is a frame to read. This may reflect current bus state (e.g. // a frame can be synchronously read from the bus) or indicate that a buffered // frame is available to "read". @@ -93,6 +98,7 @@ Send phy_send_done(uint8_t *data_index); #ifndef NDEBUG +// Hold the bus at a level until the matching call. void phy_set_dominant(void); void phy_set_recessive(void); diff --git a/src/avclan/target/avr-attiny3216/phy_avr.c b/src/avclan/target/avr-attiny3216/phy_avr.c index 3a46954..e6db06b 100644 --- a/src/avclan/target/avr-attiny3216/phy_avr.c +++ b/src/avclan/target/avr-attiny3216/phy_avr.c @@ -83,6 +83,10 @@ bool phy_is_muted() { return (((VPORTA_DIR & PIN4_bm) | (VPORTA_DIR & PIN0_bm)) == 0); } +// No-op: acks are driven from inside the synchronous read path, which a deaf +// caller never enters. +void phy_deafen([[maybe_unused]] bool deaf) {} + // True when the bus is being driven (i.e. not idle/floating). bool phy_frame_pending() { return (!BUS_IS_IDLE) != 0; } @@ -621,8 +625,22 @@ void phy_guard_leave() { #ifndef NDEBUG -void phy_set_dominant(void) { AVCLAN_setBusDriven(); } -void phy_set_recessive(void) { AVCLAN_setBusIdle(); } +// Hold the bus at a given level. +// Overrides and restores mute state upon release (i.e. set recessive). +static void phy_set_state(bool dominant) { + static bool saved_mute = false; + if (dominant) { + saved_mute = phy_is_muted(); + AVCLAN_setBusDriven(); + phy_mute(false); + } else { + AVCLAN_setBusIdle(); + phy_mute(saved_mute); + } +} + +void phy_set_dominant(void) { phy_set_state(true); } +void phy_set_recessive(void) { phy_set_state(false); } #ifdef MEASURE_BUS #include // phy_measure() reporting (debug builds only) diff --git a/src/avclan/target/pico2/phy.cc b/src/avclan/target/pico2/phy.cc index b3be62d..2411c31 100644 --- a/src/avclan/target/pico2/phy.cc +++ b/src/avclan/target/pico2/phy.cc @@ -154,6 +154,11 @@ public: sync_ack_arming(); } + void deafen(bool deaf) { + deafened_ = deaf; + sync_ack_arming(); + } + // Disarm RX ACK'ing behavior; called prior to frame TX to prevent // self-ACK'ing. Safe to rearm any time after sending controller addr. void disarm_ack() { @@ -310,7 +315,7 @@ private: // Disarming inverts the Y register (self address + parity), which leaves the // upper 19 bits set and so prevents any read from matching. void sync_ack_arming() { - const bool arm = !muted_ && !transmitting_; + const bool arm = !muted_ && !deafened_ && !transmitting_; if (arm == ack_armed_) return; pio_sm_exec_wait_blocking(pio_, sm_, pio_encode_mov_not(pio_y, pio_y)); @@ -378,6 +383,7 @@ private: bool ack_armed_ = false; // hardware: y holds self_addrp_, or its complement bool muted_ = false; // we don't answer on the bus + bool deafened_ = false; // we don't answer, but may still transmit bool transmitting_ = false; // our own frame is on the wire std::array rxq_ = {}; @@ -431,6 +437,7 @@ public: // Claims and configures both SMs, but doesn't start them. void init(PIO pio, uint pin_rx, uint pin_tx) { pio_ = pio; + pin_tx_ = pin_tx; tx_offset_ = (uint)pio_add_program(pio_, &iebus_tx_program); ack_offset_ = (uint)pio_add_program(pio_, &iebus_ack_program); tx_sm_ = (uint)pio_claim_unused_sm(pio_, true); @@ -474,6 +481,23 @@ public: bool is_muted() const { return muted_; } + // Hold the bus at a given level. + // Overrides and restores mute state upon release (i.e. set recessive). + void set_state(bool dominant) { + static bool saved_mute = false; + if (dominant) { + saved_mute = muted_; + mute(true); + pio_sm_set_pins_with_mask(pio_, tx_sm_, 0U, 1U << pin_tx_); + } else { + pio_sm_set_pins_with_mask(pio_, tx_sm_, 1U << pin_tx_, 1U << pin_tx_); + // The ack SM parks on `wait 1 irq`, which consumes a flag raised while it + // was down and drives the slot immediately -- Phy::mute's ordering. + pio_interrupt_clear(pio_, ack_irq); + mute(saved_mute); + } + } + // Send start and broadcast bits. Send send_header(bool is_unicast) { if (muted_) @@ -624,6 +648,7 @@ private: IEBusRx &rx_; PIO pio_; + uint pin_tx_; uint tx_sm_; uint tx_offset_; uint ack_sm_; @@ -768,6 +793,8 @@ extern "C" void phy_mute(bool mute) { phy.mute(mute); } extern "C" bool phy_is_muted() { return phy.is_muted(); } +extern "C" void phy_deafen(bool deaf) { phy.rx().deafen(deaf); } + extern "C" bool phy_frame_pending() { return phy.rx().frame_pending(); } extern "C" void phy_guard_enter() {} @@ -852,8 +879,8 @@ extern "C" Send phy_send_done(uint8_t *data_index) { #ifndef NDEBUG -void phy_set_dominant() {} -void phy_set_recessive() {} +extern "C" void phy_set_dominant() { phy.tx().set_state(true); } +extern "C" void phy_set_recessive() { phy.tx().set_state(false); } #ifdef MEASURE_BUS // Sample and dump bus bit timing over the serial link (REPL `M`).