Skip to content

Commit

Permalink
usb_canbus: Detect canbus stalls when in usb to canbus bridge mode
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
KevinOConnor committed Jan 15, 2025
1 parent 778c48c commit 1d298ce
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 2 deletions.
12 changes: 12 additions & 0 deletions klippy/extras/canbus_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Copyright (C) 2025 Kevin O'Connor <[email protected]>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
import logging

class PrinterCANBusStats:
def __init__(self, config):
Expand Down Expand Up @@ -33,9 +34,20 @@ def handle_connect(self):
self.get_canbus_status_cmd = self.mcu.lookup_query_command(
"get_canbus_status",
"canbus_status rx_error=%u tx_error=%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
reactor = self.printer.get_reactor()
reactor.register_timer(self.query_event, 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']
Expand Down
58 changes: 56 additions & 2 deletions src/generic/usb_canbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -118,6 +122,10 @@ static struct usbcan_data {
struct canbus_msg canhw_queue[32];
} UsbCan;

enum {
BSS_READY = 0, BSS_BLOCKING, BSS_DISCARDING
};

enum {
HS_TX_ECHO = 1,
HS_TX_HW = 2,
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand Down

0 comments on commit 1d298ce

Please sign in to comment.