aboutsummaryrefslogtreecommitdiffstats
path: root/src/generic/canbus.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/generic/canbus.c')
-rw-r--r--src/generic/canbus.c334
1 files changed, 12 insertions, 322 deletions
diff --git a/src/generic/canbus.c b/src/generic/canbus.c
index 3cc051c8..a4f33aa1 100644
--- a/src/generic/canbus.c
+++ b/src/generic/canbus.c
@@ -1,342 +1,32 @@
-// Generic handling of serial over CAN support
+// Wrapper functions connecting canserial.c to low-level can hardware
//
-// Copyright (C) 2019 Eug Krashtan <eug.krashtan@gmail.com>
-// Copyright (C) 2020 Pontus Borg <glpontus@gmail.com>
-// Copyright (C) 2021 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2022 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
-#include <string.h> // memcpy
-#include "board/armcm_reset.h" // try_request_canboot
-#include "board/io.h" // readb
-#include "board/irq.h" // irq_save
-#include "board/misc.h" // console_sendf
-#include "canbus.h" // canbus_set_uuid
-#include "command.h" // DECL_CONSTANT
-#include "fasthash.h" // fasthash64
-#include "sched.h" // sched_wake_task
+#include "canbus.h" // canbus_send
+#include "canserial.h" // canserial_send
-#define CANBUS_UUID_LEN 6
-
-// Global storage
-static struct canbus_data {
- uint32_t assigned_id;
- uint8_t uuid[CANBUS_UUID_LEN];
-
- // Tx data
- struct task_wake tx_wake;
- uint8_t transmit_pos, transmit_max;
-
- // Rx data
- struct task_wake rx_wake;
- uint8_t receive_pos;
- uint32_t admin_pull_pos, admin_push_pos;
-
- // Transfer buffers
- struct canbus_msg admin_queue[8];
- uint8_t transmit_buf[96];
- uint8_t receive_buf[192];
-} CanData;
-
-
-/****************************************************************
- * Data transmission over CAN
- ****************************************************************/
-
-void
-canbus_notify_tx(void)
+int
+canserial_send(struct canbus_msg *msg)
{
- sched_wake_task(&CanData.tx_wake);
+ return canbus_send(msg);
}
void
-canbus_tx_task(void)
+canserial_set_filter(uint32_t id)
{
- if (!sched_check_wake(&CanData.tx_wake))
- return;
- uint32_t id = CanData.assigned_id;
- if (!id) {
- CanData.transmit_pos = CanData.transmit_max = 0;
- return;
- }
- struct canbus_msg msg;
- msg.id = id + 1;
- uint32_t tpos = CanData.transmit_pos, tmax = CanData.transmit_max;
- for (;;) {
- int avail = tmax - tpos, now = avail > 8 ? 8 : avail;
- if (avail <= 0)
- break;
- msg.dlc = now;
- memcpy(msg.data, &CanData.transmit_buf[tpos], now);
- int ret = canbus_send(&msg);
- if (ret <= 0)
- break;
- tpos += now;
- }
- CanData.transmit_pos = tpos;
+ canbus_set_filter(id);
}
-DECL_TASK(canbus_tx_task);
-// Encode and transmit a "response" message
void
-console_sendf(const struct command_encoder *ce, va_list args)
-{
- // Verify space for message
- uint32_t tpos = CanData.transmit_pos, tmax = CanData.transmit_max;
- if (tpos >= tmax)
- CanData.transmit_pos = CanData.transmit_max = tpos = tmax = 0;
- uint32_t max_size = ce->max_size;
- if (tmax + max_size > sizeof(CanData.transmit_buf)) {
- if (tmax + max_size - tpos > sizeof(CanData.transmit_buf))
- // Not enough space for message
- return;
- // Move buffer
- tmax -= tpos;
- memmove(&CanData.transmit_buf[0], &CanData.transmit_buf[tpos], tmax);
- CanData.transmit_pos = tpos = 0;
- CanData.transmit_max = tmax;
- }
-
- // Generate message
- uint32_t msglen = command_encode_and_frame(&CanData.transmit_buf[tmax]
- , ce, args);
-
- // Start message transmit
- CanData.transmit_max = tmax + msglen;
- canbus_notify_tx();
-}
-
-
-/****************************************************************
- * CAN "admin" command handling
- ****************************************************************/
-
-// Available commands and responses
-#define CANBUS_CMD_QUERY_UNASSIGNED 0x00
-#define CANBUS_CMD_SET_KLIPPER_NODEID 0x01
-#define CANBUS_CMD_REQUEST_BOOTLOADER 0x02
-#define CANBUS_RESP_NEED_NODEID 0x20
-
-// Helper to verify a UUID in a command matches this chip's UUID
-static int
-can_check_uuid(struct canbus_msg *msg)
-{
- return (msg->dlc >= 7
- && memcmp(&msg->data[1], CanData.uuid, sizeof(CanData.uuid)) == 0);
-}
-
-// Helpers to encode/decode a CAN identifier to a 1-byte "nodeid"
-static int
-can_get_nodeid(void)
-{
- if (!CanData.assigned_id)
- return 0;
- return (CanData.assigned_id - 0x100) >> 1;
-}
-static uint32_t
-can_decode_nodeid(int nodeid)
-{
- return (nodeid << 1) + 0x100;
-}
-
-static void
-can_process_query_unassigned(struct canbus_msg *msg)
-{
- if (CanData.assigned_id)
- return;
- struct canbus_msg send;
- send.id = CANBUS_ID_ADMIN_RESP;
- send.dlc = 8;
- send.data[0] = CANBUS_RESP_NEED_NODEID;
- memcpy(&send.data[1], CanData.uuid, sizeof(CanData.uuid));
- send.data[7] = CANBUS_CMD_SET_KLIPPER_NODEID;
- // Send with retry
- for (;;) {
- int ret = canbus_send(&send);
- if (ret >= 0)
- return;
- }
-}
-
-static void
-can_id_conflict(void)
-{
- CanData.assigned_id = 0;
- canbus_set_filter(CanData.assigned_id);
- shutdown("Another CAN node assigned this ID");
-}
-
-static void
-can_process_set_klipper_nodeid(struct canbus_msg *msg)
-{
- if (msg->dlc < 8)
- return;
- uint32_t newid = can_decode_nodeid(msg->data[7]);
- if (can_check_uuid(msg)) {
- if (newid != CanData.assigned_id) {
- CanData.assigned_id = newid;
- canbus_set_filter(CanData.assigned_id);
- }
- } else if (newid == CanData.assigned_id) {
- can_id_conflict();
- }
-}
-
-static void
-can_process_request_bootloader(struct canbus_msg *msg)
-{
- if (!can_check_uuid(msg))
- return;
- try_request_canboot();
-}
-
-// Handle an "admin" command
-static void
-can_process_admin(struct canbus_msg *msg)
-{
- if (!msg->dlc)
- return;
- switch (msg->data[0]) {
- case CANBUS_CMD_QUERY_UNASSIGNED:
- can_process_query_unassigned(msg);
- break;
- case CANBUS_CMD_SET_KLIPPER_NODEID:
- can_process_set_klipper_nodeid(msg);
- break;
- case CANBUS_CMD_REQUEST_BOOTLOADER:
- can_process_request_bootloader(msg);
- break;
- }
-}
-
-
-/****************************************************************
- * CAN packet reading
- ****************************************************************/
-
-static void
-canbus_notify_rx(void)
+canbus_notify_tx(void)
{
- sched_wake_task(&CanData.rx_wake);
+ canserial_notify_tx();
}
-DECL_CONSTANT("RECEIVE_WINDOW", ARRAY_SIZE(CanData.receive_buf));
-
-// Handle incoming data (called from IRQ handler)
void
canbus_process_data(struct canbus_msg *msg)
{
- uint32_t id = msg->id;
- if (CanData.assigned_id && id == CanData.assigned_id) {
- // Add to incoming data buffer
- int rpos = CanData.receive_pos;
- uint32_t len = CANMSG_DATA_LEN(msg);
- if (len > sizeof(CanData.receive_buf) - rpos)
- len = sizeof(CanData.receive_buf) - rpos;
- memcpy(&CanData.receive_buf[rpos], msg->data, len);
- CanData.receive_pos = rpos + len;
- canbus_notify_rx();
- } else if (id == CANBUS_ID_ADMIN
- || (CanData.assigned_id && id == CanData.assigned_id + 1)) {
- // Add to admin command queue
- uint32_t pushp = CanData.admin_push_pos;
- if (pushp >= CanData.admin_pull_pos + ARRAY_SIZE(CanData.admin_queue))
- // No space - drop message
- return;
- uint32_t pos = pushp % ARRAY_SIZE(CanData.admin_queue);
- memcpy(&CanData.admin_queue[pos], msg, sizeof(*msg));
- CanData.admin_push_pos = pushp + 1;
- canbus_notify_rx();
- }
-}
-
-// Remove from the receive buffer the given number of bytes
-static void
-console_pop_input(int len)
-{
- int copied = 0;
- for (;;) {
- int rpos = readb(&CanData.receive_pos);
- int needcopy = rpos - len;
- if (needcopy) {
- memmove(&CanData.receive_buf[copied]
- , &CanData.receive_buf[copied + len], needcopy - copied);
- copied = needcopy;
- canbus_notify_rx();
- }
- irqstatus_t flag = irq_save();
- if (rpos != readb(&CanData.receive_pos)) {
- // Raced with irq handler - retry
- irq_restore(flag);
- continue;
- }
- CanData.receive_pos = needcopy;
- irq_restore(flag);
- break;
- }
-}
-
-// Task to process incoming commands and admin messages
-void
-canbus_rx_task(void)
-{
- if (!sched_check_wake(&CanData.rx_wake))
- return;
-
- // Process pending admin messages
- for (;;) {
- uint32_t pushp = readl(&CanData.admin_push_pos);
- uint32_t pullp = CanData.admin_pull_pos;
- if (pushp == pullp)
- break;
- uint32_t pos = pullp % ARRAY_SIZE(CanData.admin_queue);
- struct canbus_msg *msg = &CanData.admin_queue[pos];
- uint32_t id = msg->id;
- if (CanData.assigned_id && id == CanData.assigned_id + 1)
- can_id_conflict();
- else if (id == CANBUS_ID_ADMIN)
- can_process_admin(msg);
- CanData.admin_pull_pos = pullp + 1;
- }
-
- // Check for a complete message block and process it
- uint_fast8_t rpos = readb(&CanData.receive_pos), pop_count;
- int ret = command_find_block(CanData.receive_buf, rpos, &pop_count);
- if (ret > 0)
- command_dispatch(CanData.receive_buf, pop_count);
- if (ret) {
- console_pop_input(pop_count);
- if (ret > 0)
- command_send_ack();
- }
-}
-DECL_TASK(canbus_rx_task);
-
-
-/****************************************************************
- * Setup and shutdown
- ****************************************************************/
-
-void
-command_get_canbus_id(uint32_t *args)
-{
- sendf("canbus_id canbus_uuid=%.*s canbus_nodeid=%u"
- , sizeof(CanData.uuid), CanData.uuid, can_get_nodeid());
-}
-DECL_COMMAND_FLAGS(command_get_canbus_id, HF_IN_SHUTDOWN, "get_canbus_id");
-
-void
-canbus_set_uuid(uint8_t *raw_uuid, uint32_t raw_uuid_len)
-{
- uint64_t hash = fasthash64(raw_uuid, raw_uuid_len, 0xA16231A7);
- memcpy(CanData.uuid, &hash, sizeof(CanData.uuid));
- canbus_notify_rx();
-}
-
-void
-canbus_shutdown(void)
-{
- canbus_notify_tx();
- canbus_notify_rx();
+ canserial_process_data(msg);
}
-DECL_SHUTDOWN(canbus_shutdown);