diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2025-01-14 22:38:08 -0500 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2025-02-02 18:43:34 -0500 |
commit | 2c90c97ccd5ceb9b4071f95866160344f013a86f (patch) | |
tree | 1b7028ae85b0cdd4aa1748cea9f5ba649170a1d7 | |
parent | 2db2ef82f2c0ba08d7e72fbab355de0846272b3a (diff) | |
download | kutter-2c90c97ccd5ceb9b4071f95866160344f013a86f.tar.gz kutter-2c90c97ccd5ceb9b4071f95866160344f013a86f.tar.xz kutter-2c90c97ccd5ceb9b4071f95866160344f013a86f.zip |
usb_canbus: Detect canbus stalls when in usb to canbus bridge mode
If the low-level canbus stops working then it could become impossible
to send messages to and from the canbus bridge node itself. This can
make it difficult to diagnose canbus problems.
Change the canbus bridge code to detect if message transmits become
stalled for 50+ milliseconds and go into a "discarding" state. In
this discarding state, messages destined for the canbus will be
discarded until the canbus becomes active again. In this discarding
state it will therefore be possible to transmit messages to and from
the canbus bridge node.
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
-rw-r--r-- | klippy/extras/canbus_stats.py | 12 | ||||
-rw-r--r-- | src/generic/usb_canbus.c | 60 |
2 files changed, 69 insertions, 3 deletions
diff --git a/klippy/extras/canbus_stats.py b/klippy/extras/canbus_stats.py index b9ee3102..0ddafaf3 100644 --- a/klippy/extras/canbus_stats.py +++ b/klippy/extras/canbus_stats.py @@ -3,6 +3,7 @@ # Copyright (C) 2025 Kevin O'Connor <kevin@koconnor.net> # # This file may be distributed under the terms of the GNU GPLv3 license. +import logging class PrinterCANBusStats: def __init__(self, config): @@ -36,8 +37,19 @@ class PrinterCANBusStats: "get_canbus_status", "canbus_status rx_error=%u tx_error=%u tx_retries=%u" " canbus_bus_state=%u") + # Register usb_canbus_state message handling (for usb to canbus bridge) + self.mcu.register_response(self.handle_usb_canbus_state, + "usb_canbus_state") # Register periodic query timer self.reactor.register_timer(self.query_event, self.reactor.NOW) + def handle_usb_canbus_state(self, params): + discard = params['discard'] + if discard: + logging.warning("USB CANBUS bridge '%s' is discarding!" + % (self.name,)) + else: + logging.warning("USB CANBUS bridge '%s' is no longer discarding." + % (self.name,)) def query_event(self, eventtime): prev_rx = self.status['rx_error'] prev_tx = self.status['tx_error'] diff --git a/src/generic/usb_canbus.c b/src/generic/usb_canbus.c index 9c2893bf..25d41c1b 100644 --- a/src/generic/usb_canbus.c +++ b/src/generic/usb_canbus.c @@ -1,6 +1,6 @@ // Support for Linux "gs_usb" CANbus adapter emulation // -// Copyright (C) 2018-2022 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2018-2025 Kevin O'Connor <kevin@koconnor.net> // // This file may be distributed under the terms of the GNU GPLv3 license. @@ -108,6 +108,10 @@ static struct usbcan_data { uint8_t notify_local, usb_send_busy; uint32_t assigned_id; + // State tracking for messages to be sent from host to canbus + uint32_t bus_send_discard_time; + uint8_t bus_send_state; + // Canbus data from host uint8_t host_status; uint32_t host_pull_pos, host_push_pos; @@ -119,6 +123,10 @@ static struct usbcan_data { } UsbCan; enum { + BSS_READY = 0, BSS_BLOCKING, BSS_DISCARDING +}; + +enum { HS_TX_ECHO = 1, HS_TX_HW = 2, HS_TX_LOCAL = 4, @@ -205,10 +213,56 @@ fill_usb_host_queue(void) } } +// Report bus stall state +static void +note_discard_state(uint32_t discard) +{ + sendf("usb_canbus_state discard=%u", discard); +} + +// Check if canbus queue has gotten stuck +static int +check_need_discard(void) +{ + if (UsbCan.bus_send_state != BSS_BLOCKING) + return 0; + return timer_is_before(UsbCan.bus_send_discard_time, timer_read_time()); +} + +// Attempt to send a message on the canbus +static int +try_canmsg_send(struct canbus_msg *msg) +{ + int ret = canhw_send(msg); + if (ret >= 0) { + // Success + if (UsbCan.bus_send_state == BSS_DISCARDING) + note_discard_state(0); + UsbCan.bus_send_state = BSS_READY; + return ret; + } + + // Unable to send message + if (check_need_discard()) { + // The canbus is stalled - start discarding messages + note_discard_state(1); + UsbCan.bus_send_state = BSS_DISCARDING; + } + if (UsbCan.bus_send_state == BSS_DISCARDING) + // Queue is stalled - just discard the message + return 0; + if (UsbCan.bus_send_state == BSS_READY) { + // Just starting to block - setup stall detection after 50ms + UsbCan.bus_send_state = BSS_BLOCKING; + UsbCan.bus_send_discard_time = timer_read_time() + timer_from_us(50000); + } + return ret; +} + void usbcan_task(void) { - if (!sched_check_wake(&UsbCan.wake)) + if (!sched_check_wake(&UsbCan.wake) && !check_need_discard()) return; // Send any pending hw frames to host @@ -235,7 +289,7 @@ usbcan_task(void) UsbCan.host_status = host_status = host_status & ~HS_TX_LOCAL; } if (host_status & HS_TX_HW) { - int ret = canhw_send(&msg); + int ret = try_canmsg_send(&msg); if (ret < 0) break; UsbCan.host_status = host_status = host_status & ~HS_TX_HW; |