aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--klippy/extras/canbus_stats.py12
-rw-r--r--src/generic/usb_canbus.c60
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;