/* * Copyright (c) 2022 Vestas Wind Systems A/S * Copyright (c) 2019 Alexander Wachter * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include LOG_MODULE_REGISTER(can_shell, CONFIG_CAN_LOG_LEVEL); struct can_shell_tx_event { unsigned int frame_no; int error; }; struct can_shell_mode_mapping { const char *name; can_mode_t mode; }; #define CAN_SHELL_MODE_MAPPING(_name, _mode) { .name = _name, .mode = _mode } static const struct can_shell_mode_mapping can_shell_mode_map[] = { /* Array sorted alphabetically based on name */ CAN_SHELL_MODE_MAPPING("fd", CAN_MODE_FD), CAN_SHELL_MODE_MAPPING("listen-only", CAN_MODE_LISTENONLY), CAN_SHELL_MODE_MAPPING("loopback", CAN_MODE_LOOPBACK), CAN_SHELL_MODE_MAPPING("normal", CAN_MODE_NORMAL), CAN_SHELL_MODE_MAPPING("one-shot", CAN_MODE_ONE_SHOT), CAN_SHELL_MODE_MAPPING("triple-sampling", CAN_MODE_3_SAMPLES), }; K_MSGQ_DEFINE(can_shell_tx_msgq, sizeof(struct can_shell_tx_event), CONFIG_CAN_SHELL_TX_QUEUE_SIZE, 4); const struct shell *can_shell_tx_msgq_sh; static struct k_work_poll can_shell_tx_msgq_work; static struct k_poll_event can_shell_tx_msgq_events[] = { K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_MSGQ_DATA_AVAILABLE, K_POLL_MODE_NOTIFY_ONLY, &can_shell_tx_msgq, 0) }; CAN_MSGQ_DEFINE(can_shell_rx_msgq, CONFIG_CAN_SHELL_RX_QUEUE_SIZE); const struct shell *can_shell_rx_msgq_sh; static struct k_work_poll can_shell_rx_msgq_work; static struct k_poll_event can_shell_rx_msgq_events[] = { K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_MSGQ_DATA_AVAILABLE, K_POLL_MODE_NOTIFY_ONLY, &can_shell_rx_msgq, 0) }; /* Forward declarations */ static void can_shell_tx_msgq_triggered_work_handler(struct k_work *work); static void can_shell_rx_msgq_triggered_work_handler(struct k_work *work); static void can_shell_print_frame(const struct shell *sh, const struct can_frame *frame) { uint8_t nbytes = can_dlc_to_bytes(frame->dlc); int i; #ifdef CONFIG_CAN_RX_TIMESTAMP /* Timestamp */ shell_fprintf(sh, SHELL_NORMAL, "(%05d) ", frame->timestamp); #endif /* CONFIG_CAN_RX_TIMESTAMP */ #ifdef CONFIG_CAN_FD_MODE /* Flags */ shell_fprintf(sh, SHELL_NORMAL, "%c%c ", (frame->flags & CAN_FRAME_BRS) == 0 ? '-' : 'B', (frame->flags & CAN_FRAME_ESI) == 0 ? '-' : 'P'); #endif /* CONFIG_CAN_FD_MODE */ /* CAN ID */ shell_fprintf(sh, SHELL_NORMAL, "%*s%0*x ", (frame->flags & CAN_FRAME_IDE) != 0 ? 0 : 5, "", (frame->flags & CAN_FRAME_IDE) != 0 ? 8 : 3, (frame->flags & CAN_FRAME_IDE) != 0 ? frame->id & CAN_EXT_ID_MASK : frame->id & CAN_STD_ID_MASK); /* DLC as number of bytes */ shell_fprintf(sh, SHELL_NORMAL, "%s[%0*d] ", (frame->flags & CAN_FRAME_FDF) != 0 ? "" : " ", (frame->flags & CAN_FRAME_FDF) != 0 ? 2 : 1, nbytes); /* Data payload */ if ((frame->flags & CAN_FRAME_RTR) != 0) { shell_fprintf(sh, SHELL_NORMAL, "remote transmission request"); } else { for (i = 0; i < nbytes; i++) { shell_fprintf(sh, SHELL_NORMAL, "%02x ", frame->data[i]); } } shell_fprintf(sh, SHELL_NORMAL, "\n"); } static int can_shell_tx_msgq_poll_submit(const struct shell *sh) { int err; if (can_shell_tx_msgq_sh == NULL) { can_shell_tx_msgq_sh = sh; k_work_poll_init(&can_shell_tx_msgq_work, can_shell_tx_msgq_triggered_work_handler); } err = k_work_poll_submit(&can_shell_tx_msgq_work, can_shell_tx_msgq_events, ARRAY_SIZE(can_shell_tx_msgq_events), K_FOREVER); if (err != 0) { shell_error(can_shell_tx_msgq_sh, "failed to submit tx msgq polling (err %d)", err); } return err; } static void can_shell_tx_msgq_triggered_work_handler(struct k_work *work) { struct can_shell_tx_event event; while (k_msgq_get(&can_shell_tx_msgq, &event, K_NO_WAIT) == 0) { if (event.error == 0) { shell_print(can_shell_tx_msgq_sh, "CAN frame #%u successfully sent", event.frame_no); } else { shell_error(can_shell_tx_msgq_sh, "failed to send CAN frame #%u (err %d)", event.frame_no, event.error); } } (void)can_shell_tx_msgq_poll_submit(can_shell_tx_msgq_sh); } static void can_shell_tx_callback(const struct device *dev, int error, void *user_data) { struct can_shell_tx_event event; int err; ARG_UNUSED(dev); event.frame_no = POINTER_TO_UINT(user_data); event.error = error; err = k_msgq_put(&can_shell_tx_msgq, &event, K_NO_WAIT); if (err != 0) { LOG_ERR("CAN shell tx event queue full"); } } static int can_shell_rx_msgq_poll_submit(const struct shell *sh) { int err; if (can_shell_rx_msgq_sh == NULL) { can_shell_rx_msgq_sh = sh; k_work_poll_init(&can_shell_rx_msgq_work, can_shell_rx_msgq_triggered_work_handler); } err = k_work_poll_submit(&can_shell_rx_msgq_work, can_shell_rx_msgq_events, ARRAY_SIZE(can_shell_rx_msgq_events), K_FOREVER); if (err != 0) { shell_error(can_shell_rx_msgq_sh, "failed to submit rx msgq polling (err %d)", err); } return err; } static void can_shell_rx_msgq_triggered_work_handler(struct k_work *work) { struct can_frame frame; while (k_msgq_get(&can_shell_rx_msgq, &frame, K_NO_WAIT) == 0) { can_shell_print_frame(can_shell_rx_msgq_sh, &frame); } (void)can_shell_rx_msgq_poll_submit(can_shell_rx_msgq_sh); } static const char *can_shell_state_to_string(enum can_state state) { switch (state) { case CAN_STATE_ERROR_ACTIVE: return "error-active"; case CAN_STATE_ERROR_WARNING: return "error-warning"; case CAN_STATE_ERROR_PASSIVE: return "error-passive"; case CAN_STATE_BUS_OFF: return "bus-off"; case CAN_STATE_STOPPED: return "stopped"; default: return "unknown"; } } static void can_shell_print_capabilities(const struct shell *sh, can_mode_t cap) { int bit; int i; for (bit = 0; bit < sizeof(cap) * 8; bit++) { /* Skip unset bits */ if ((cap & BIT(bit)) == 0) { continue; } /* Lookup symbolic mode name */ for (i = 0; i < ARRAY_SIZE(can_shell_mode_map); i++) { if (BIT(bit) == can_shell_mode_map[i].mode) { shell_fprintf(sh, SHELL_NORMAL, "%s ", can_shell_mode_map[i].name); break; } } if (i == ARRAY_SIZE(can_shell_mode_map)) { /* Symbolic name not found, use raw mode */ shell_fprintf(sh, SHELL_NORMAL, "0x%08x ", (can_mode_t)BIT(bit)); } } } static int cmd_can_start(const struct shell *sh, size_t argc, char **argv) { const struct device *dev = device_get_binding(argv[1]); int err; if (!device_is_ready(dev)) { shell_error(sh, "device %s not ready", argv[1]); return -ENODEV; } shell_print(sh, "starting %s", argv[1]); err = can_start(dev); if (err != 0) { shell_error(sh, "failed to start CAN controller (err %d)", err); return err; } return 0; } static int cmd_can_stop(const struct shell *sh, size_t argc, char **argv) { const struct device *dev = device_get_binding(argv[1]); int err; if (!device_is_ready(dev)) { shell_error(sh, "device %s not ready", argv[1]); return -ENODEV; } shell_print(sh, "stopping %s", argv[1]); err = can_stop(dev); if (err != 0) { shell_error(sh, "failed to stop CAN controller (err %d)", err); return err; } return 0; } static int cmd_can_show(const struct shell *sh, size_t argc, char **argv) { const struct device *dev = device_get_binding(argv[1]); const struct can_timing *timing_min; const struct can_timing *timing_max; struct can_bus_err_cnt err_cnt; enum can_state state; uint32_t max_bitrate = 0; int max_std_filters = 0; int max_ext_filters = 0; uint32_t core_clock; can_mode_t cap; int err; if (!device_is_ready(dev)) { shell_error(sh, "device %s not ready", argv[1]); return -ENODEV; } err = can_get_core_clock(dev, &core_clock); if (err != 0) { shell_error(sh, "failed to get CAN core clock (err %d)", err); return err; } err = can_get_max_bitrate(dev, &max_bitrate); if (err != 0 && err != -ENOSYS) { shell_error(sh, "failed to get maximum bitrate (err %d)", err); return err; } max_std_filters = can_get_max_filters(dev, false); if (max_std_filters < 0 && max_std_filters != -ENOSYS) { shell_error(sh, "failed to get maximum standard (11-bit) filters (err %d)", err); return err; } max_ext_filters = can_get_max_filters(dev, true); if (max_ext_filters < 0 && max_ext_filters != -ENOSYS) { shell_error(sh, "failed to get maximum extended (29-bit) filters (err %d)", err); return err; } err = can_get_capabilities(dev, &cap); if (err != 0) { shell_error(sh, "failed to get CAN controller capabilities (err %d)", err); return err; } err = can_get_state(dev, &state, &err_cnt); if (err != 0) { shell_error(sh, "failed to get CAN controller state (%d)", err); return err; } shell_print(sh, "core clock: %d Hz", core_clock); shell_print(sh, "max bitrate: %d bps", max_bitrate); shell_print(sh, "max std filters: %d", max_std_filters); shell_print(sh, "max ext filters: %d", max_ext_filters); shell_fprintf(sh, SHELL_NORMAL, "capabilities: normal "); can_shell_print_capabilities(sh, cap); shell_fprintf(sh, SHELL_NORMAL, "\n"); shell_print(sh, "state: %s", can_shell_state_to_string(state)); shell_print(sh, "rx errors: %d", err_cnt.rx_err_cnt); shell_print(sh, "tx errors: %d", err_cnt.tx_err_cnt); timing_min = can_get_timing_min(dev); timing_max = can_get_timing_max(dev); shell_print(sh, "timing: sjw %u..%u, prop_seg %u..%u, " "phase_seg1 %u..%u, phase_seg2 %u..%u, prescaler %u..%u", timing_min->sjw, timing_max->sjw, timing_min->prop_seg, timing_max->prop_seg, timing_min->phase_seg1, timing_max->phase_seg1, timing_min->phase_seg2, timing_max->phase_seg2, timing_min->prescaler, timing_max->prescaler); if (IS_ENABLED(CONFIG_CAN_FD_MODE) && (cap & CAN_MODE_FD) != 0) { timing_min = can_get_timing_data_min(dev); timing_max = can_get_timing_data_max(dev); shell_print(sh, "timing data: sjw %u..%u, prop_seg %u..%u, " "phase_seg1 %u..%u, phase_seg2 %u..%u, prescaler %u..%u", timing_min->sjw, timing_max->sjw, timing_min->prop_seg, timing_max->prop_seg, timing_min->phase_seg1, timing_max->phase_seg1, timing_min->phase_seg2, timing_max->phase_seg2, timing_min->prescaler, timing_max->prescaler); } return 0; } static int cmd_can_bitrate_set(const struct shell *sh, size_t argc, char **argv) { const struct device *dev = device_get_binding(argv[1]); struct can_timing timing = { 0 }; uint16_t sample_pnt; uint32_t bitrate; char *endptr; int err; if (!device_is_ready(dev)) { shell_error(sh, "device %s not ready", argv[1]); return -ENODEV; } bitrate = (uint32_t)strtoul(argv[2], &endptr, 10); if (*endptr != '\0') { shell_error(sh, "failed to parse bitrate"); return -EINVAL; } if (argc >= 4) { sample_pnt = (uint32_t)strtoul(argv[3], &endptr, 10); if (*endptr != '\0') { shell_error(sh, "failed to parse sample point"); return -EINVAL; } err = can_calc_timing(dev, &timing, bitrate, sample_pnt); if (err < 0) { shell_error(sh, "failed to calculate timing for " "bitrate %d bps, sample point %d.%d%% (err %d)", bitrate, sample_pnt / 10, sample_pnt % 10, err); return err; } if (argc >= 5) { /* Overwrite calculated default SJW with user-provided value */ timing.sjw = (uint16_t)strtoul(argv[4], &endptr, 10); if (*endptr != '\0') { shell_error(sh, "failed to parse SJW"); return -EINVAL; } } shell_print(sh, "setting bitrate to %d bps, sample point %d.%d%% " "(+/- %d.%d%%), sjw %d", bitrate, sample_pnt / 10, sample_pnt % 10, err / 10, err % 10, timing.sjw); LOG_DBG("sjw %u, prop_seg %u, phase_seg1 %u, phase_seg2 %u, prescaler %u", timing.sjw, timing.prop_seg, timing.phase_seg1, timing.phase_seg2, timing.prescaler); err = can_set_timing(dev, &timing); if (err != 0) { shell_error(sh, "failed to set timing (err %d)", err); return err; } } else { shell_print(sh, "setting bitrate to %d bps", bitrate); err = can_set_bitrate(dev, bitrate); if (err != 0) { shell_error(sh, "failed to set bitrate (err %d)", err); return err; } } return 0; } static int cmd_can_dbitrate_set(const struct shell *sh, size_t argc, char **argv) { const struct device *dev = device_get_binding(argv[1]); struct can_timing timing = { 0 }; uint16_t sample_pnt; uint32_t bitrate; char *endptr; int err; if (!device_is_ready(dev)) { shell_error(sh, "device %s not ready", argv[1]); return -ENODEV; } bitrate = (uint32_t)strtoul(argv[2], &endptr, 10); if (*endptr != '\0') { shell_error(sh, "failed to parse data bitrate"); return -EINVAL; } if (argc >= 4) { sample_pnt = (uint32_t)strtoul(argv[3], &endptr, 10); if (*endptr != '\0') { shell_error(sh, "failed to parse sample point"); return -EINVAL; } err = can_calc_timing_data(dev, &timing, bitrate, sample_pnt); if (err < 0) { shell_error(sh, "failed to calculate timing for " "data bitrate %d bps, sample point %d.%d%% (err %d)", bitrate, sample_pnt / 10, sample_pnt % 10, err); return err; } if (argc >= 5) { /* Overwrite calculated default SJW with user-provided value */ timing.sjw = (uint16_t)strtoul(argv[4], &endptr, 10); if (*endptr != '\0') { shell_error(sh, "failed to parse SJW"); return -EINVAL; } } shell_print(sh, "setting data bitrate to %d bps, sample point %d.%d%% " "(+/- %d.%d%%), sjw %d", bitrate, sample_pnt / 10, sample_pnt % 10, err / 10, err % 10, timing.sjw); LOG_DBG("sjw %u, prop_seg %u, phase_seg1 %u, phase_seg2 %u, prescaler %u", timing.sjw, timing.prop_seg, timing.phase_seg1, timing.phase_seg2, timing.prescaler); err = can_set_timing_data(dev, &timing); if (err != 0) { shell_error(sh, "failed to set data timing (err %d)", err); return err; } } else { shell_print(sh, "setting data bitrate to %d bps", bitrate); err = can_set_bitrate_data(dev, bitrate); if (err != 0) { shell_error(sh, "failed to set data bitrate (err %d)", err); return err; } } return 0; } static int cmd_can_mode_set(const struct shell *sh, size_t argc, char **argv) { const struct device *dev = device_get_binding(argv[1]); can_mode_t mode = CAN_MODE_NORMAL; can_mode_t raw; char *endptr; int err; int i; int j; if (!device_is_ready(dev)) { shell_error(sh, "device %s not ready", argv[1]); return -ENODEV; } for (i = 2; i < argc; i++) { /* Lookup symbolic mode name */ for (j = 0; j < ARRAY_SIZE(can_shell_mode_map); j++) { if (strcmp(argv[i], can_shell_mode_map[j].name) == 0) { mode |= can_shell_mode_map[j].mode; break; } } if (j == ARRAY_SIZE(can_shell_mode_map)) { /* Symbolic name not found, use raw mode if hex number */ raw = (can_mode_t)strtoul(argv[i], &endptr, 16); if (*endptr == '\0') { mode |= raw; continue; } shell_error(sh, "failed to parse mode"); return -EINVAL; } } shell_print(sh, "setting mode 0x%08x", mode); err = can_set_mode(dev, mode); if (err != 0) { shell_error(sh, "failed to set mode 0x%08x (err %d)", mode, err); return err; } return 0; } static int cmd_can_send(const struct shell *sh, size_t argc, char **argv) { const struct device *dev = device_get_binding(argv[1]); static unsigned int frame_counter; unsigned int frame_no; struct can_frame frame; uint32_t max_id; int argidx = 2; uint32_t val; char *endptr; int nbytes; int err; int i; if (!device_is_ready(dev)) { shell_error(sh, "device %s not ready", argv[1]); return -ENODEV; } /* Defaults */ max_id = CAN_MAX_STD_ID; frame.flags = 0; frame.dlc = 0; /* Parse options */ while (argidx < argc && strncmp(argv[argidx], "-", 1) == 0) { if (strcmp(argv[argidx], "--") == 0) { argidx++; break; } else if (strcmp(argv[argidx], "-e") == 0) { frame.flags |= CAN_FRAME_IDE; max_id = CAN_MAX_EXT_ID; argidx++; } else if (strcmp(argv[argidx], "-r") == 0) { frame.flags |= CAN_FRAME_RTR; argidx++; } else if (strcmp(argv[argidx], "-f") == 0) { frame.flags |= CAN_FRAME_FDF; argidx++; } else if (strcmp(argv[argidx], "-b") == 0) { frame.flags |= CAN_FRAME_BRS; argidx++; } else { shell_error(sh, "unsupported option %s", argv[argidx]); shell_help(sh); return SHELL_CMD_HELP_PRINTED; } } /* Parse CAN ID */ if (argidx >= argc) { shell_error(sh, "missing CAN ID parameter"); shell_help(sh); return SHELL_CMD_HELP_PRINTED; } val = (uint32_t)strtoul(argv[argidx++], &endptr, 16); if (*endptr != '\0') { shell_error(sh, "failed to parse CAN ID"); return -EINVAL; } if (val > max_id) { shell_error(sh, "CAN ID 0x%0*x out of range", (frame.flags & CAN_FRAME_IDE) != 0 ? 8 : 3, val); return -EINVAL; } frame.id = val; nbytes = argc - argidx; if (nbytes > ARRAY_SIZE(frame.data)) { shell_error(sh, "excessive amount of data (%d bytes)", nbytes); return -EINVAL; } frame.dlc = can_bytes_to_dlc(nbytes); /* Parse data */ for (i = 0; i < nbytes; i++) { val = (uint32_t)strtoul(argv[argidx++], &endptr, 16); if (*endptr != '\0') { shell_error(sh, "failed to parse data %s", argv[argidx++]); return -EINVAL; } if (val > 0xff) { shell_error(sh, "data 0x%x out of range", val); return -EINVAL; } frame.data[i] = val; } err = can_shell_tx_msgq_poll_submit(sh); if (err != 0) { return err; } frame_no = frame_counter++; shell_print(sh, "enqueuing CAN frame #%u with %s (%d-bit) CAN ID 0x%0*x, " "RTR %d, CAN-FD %d, BRS %d, DLC %d", frame_no, (frame.flags & CAN_FRAME_IDE) != 0 ? "extended" : "standard", (frame.flags & CAN_FRAME_IDE) != 0 ? 29 : 11, (frame.flags & CAN_FRAME_IDE) != 0 ? 8 : 3, frame.id, (frame.flags & CAN_FRAME_RTR) != 0 ? 1 : 0, (frame.flags & CAN_FRAME_FDF) != 0 ? 1 : 0, (frame.flags & CAN_FRAME_BRS) != 0 ? 1 : 0, can_dlc_to_bytes(frame.dlc)); err = can_send(dev, &frame, K_NO_WAIT, can_shell_tx_callback, UINT_TO_POINTER(frame_no)); if (err != 0) { shell_error(sh, "failed to enqueue CAN frame #%u (err %d)", frame_no, err); return err; } return 0; } static int cmd_can_filter_add(const struct shell *sh, size_t argc, char **argv) { const struct device *dev = device_get_binding(argv[1]); struct can_filter filter; uint32_t max_id; int argidx = 2; uint32_t val; char *endptr; int err; if (!device_is_ready(dev)) { shell_error(sh, "device %s not ready", argv[1]); return -ENODEV; } /* Defaults */ max_id = CAN_MAX_STD_ID; filter.flags = CAN_FILTER_DATA; /* Parse options */ while (argidx < argc && strncmp(argv[argidx], "-", 1) == 0) { if (strcmp(argv[argidx], "--") == 0) { argidx++; break; } else if (strcmp(argv[argidx], "-e") == 0) { filter.flags |= CAN_FILTER_IDE; max_id = CAN_MAX_EXT_ID; argidx++; } else if (strcmp(argv[argidx], "-f") == 0) { filter.flags |= CAN_FILTER_FDF; argidx++; } else if (strcmp(argv[argidx], "-r") == 0) { filter.flags |= CAN_FILTER_RTR; argidx++; } else if (strcmp(argv[argidx], "-R") == 0) { filter.flags &= ~(CAN_FILTER_DATA); filter.flags |= CAN_FILTER_RTR; argidx++; } else { shell_error(sh, "unsupported argument %s", argv[argidx]); shell_help(sh); return SHELL_CMD_HELP_PRINTED; } } /* Parse CAN ID */ if (argidx >= argc) { shell_error(sh, "missing CAN ID parameter"); shell_help(sh); return SHELL_CMD_HELP_PRINTED; } val = (uint32_t)strtoul(argv[argidx++], &endptr, 16); if (*endptr != '\0') { shell_error(sh, "failed to parse CAN ID"); return -EINVAL; } if (val > max_id) { shell_error(sh, "CAN ID 0x%0*x out of range", (filter.flags & CAN_FILTER_IDE) != 0 ? 8 : 3, val); return -EINVAL; } filter.id = val; if (argidx < argc) { /* Parse CAN ID mask */ val = (uint32_t)strtoul(argv[argidx++], &endptr, 16); if (*endptr != '\0') { shell_error(sh, "failed to parse CAN ID mask"); return -EINVAL; } if (val > max_id) { shell_error(sh, "CAN ID mask 0x%0*x out of range", (filter.flags & CAN_FILTER_IDE) != 0 ? 8 : 3, val); return -EINVAL; } } else { val = max_id; } filter.mask = val; err = can_shell_rx_msgq_poll_submit(sh); if (err != 0) { return err; } shell_print(sh, "adding filter with %s (%d-bit) CAN ID 0x%0*x, " "CAN ID mask 0x%0*x, data frames %d, RTR frames %d, CAN-FD frames %d", (filter.flags & CAN_FILTER_IDE) != 0 ? "extended" : "standard", (filter.flags & CAN_FILTER_IDE) != 0 ? 29 : 11, (filter.flags & CAN_FILTER_IDE) != 0 ? 8 : 3, filter.id, (filter.flags & CAN_FILTER_IDE) != 0 ? 8 : 3, filter.mask, (filter.flags & CAN_FILTER_DATA) != 0 ? 1 : 0, (filter.flags & CAN_FILTER_RTR) != 0 ? 1 : 0, (filter.flags & CAN_FILTER_FDF) != 0 ? 1 : 0); err = can_add_rx_filter_msgq(dev, &can_shell_rx_msgq, &filter); if (err < 0) { shell_error(sh, "failed to add filter (err %d)", err); return err; } shell_print(sh, "filter ID: %d", err); return 0; } static int cmd_can_filter_remove(const struct shell *sh, size_t argc, char **argv) { const struct device *dev = device_get_binding(argv[1]); int filter_id; char *endptr; if (!device_is_ready(dev)) { shell_error(sh, "device %s not ready", argv[1]); return -ENODEV; } /* Parse filter ID */ filter_id = (int)strtol(argv[2], &endptr, 10); if (*endptr != '\0') { shell_error(sh, "failed to parse filter ID"); return -EINVAL; } shell_print(sh, "removing filter with ID %d", filter_id); can_remove_rx_filter(dev, filter_id); return 0; } static int cmd_can_recover(const struct shell *sh, size_t argc, char **argv) { const struct device *dev = device_get_binding(argv[1]); k_timeout_t timeout = K_FOREVER; int millisec; char *endptr; int err; if (!device_is_ready(dev)) { shell_error(sh, "device %s not ready", argv[1]); return -ENODEV; } if (argc >= 3) { /* Parse timeout */ millisec = (int)strtol(argv[2], &endptr, 10); if (*endptr != '\0') { shell_error(sh, "failed to parse timeout"); return -EINVAL; } timeout = K_MSEC(millisec); shell_print(sh, "recovering, timeout %d ms", millisec); } else { shell_print(sh, "recovering, no timeout"); } err = can_recover(dev, timeout); if (err != 0) { shell_error(sh, "failed to recover CAN controller from bus-off (err %d)", err); return err; } return 0; } static void cmd_can_device_name(size_t idx, struct shell_static_entry *entry) { const struct device *dev = shell_device_lookup(idx, NULL); entry->syntax = (dev != NULL) ? dev->name : NULL; entry->handler = NULL; entry->help = NULL; entry->subcmd = NULL; } SHELL_DYNAMIC_CMD_CREATE(dsub_can_device_name, cmd_can_device_name); static void cmd_can_mode(size_t idx, struct shell_static_entry *entry); SHELL_DYNAMIC_CMD_CREATE(dsub_can_mode, cmd_can_mode); static void cmd_can_mode(size_t idx, struct shell_static_entry *entry) { if (idx < ARRAY_SIZE(can_shell_mode_map)) { entry->syntax = can_shell_mode_map[idx].name; } else { entry->syntax = NULL; } entry->handler = NULL; entry->help = NULL; entry->subcmd = &dsub_can_mode; } static void cmd_can_device_name_mode(size_t idx, struct shell_static_entry *entry) { const struct device *dev = shell_device_lookup(idx, NULL); entry->syntax = (dev != NULL) ? dev->name : NULL; entry->handler = NULL; entry->help = NULL; entry->subcmd = &dsub_can_mode; } SHELL_DYNAMIC_CMD_CREATE(dsub_can_device_name_mode, cmd_can_device_name_mode); SHELL_STATIC_SUBCMD_SET_CREATE(sub_can_filter_cmds, SHELL_CMD_ARG(add, &dsub_can_device_name, "Add rx filter\n" "Usage: can filter add [-e] [-f] [-r] [-R] [CAN ID mask]\n" "-e use extended (29-bit) CAN ID/CAN ID mask\n" "-f match CAN-FD format frames\n" "-r also match Remote Transmission Request (RTR) frames\n" "-R only match Remote Transmission Request (RTR) frames", cmd_can_filter_add, 3, 5), SHELL_CMD_ARG(remove, &dsub_can_device_name, "Remove rx filter\n" "Usage: can filter remove ", cmd_can_filter_remove, 3, 0), SHELL_SUBCMD_SET_END ); SHELL_STATIC_SUBCMD_SET_CREATE(sub_can_cmds, SHELL_CMD_ARG(start, &dsub_can_device_name, "Start CAN controller\n" "Usage: can start ", cmd_can_start, 2, 0), SHELL_CMD_ARG(stop, &dsub_can_device_name, "Stop CAN controller\n" "Usage: can stop ", cmd_can_stop, 2, 0), SHELL_CMD_ARG(show, &dsub_can_device_name, "Show CAN controller information\n" "Usage: can show ", cmd_can_show, 2, 0), SHELL_CMD_ARG(bitrate, &dsub_can_device_name, "Set CAN controller bitrate (sample point and SJW optional)\n" "Usage: can bitrate [sample point] [sjw]", cmd_can_bitrate_set, 3, 2), SHELL_COND_CMD_ARG(CONFIG_CAN_FD_MODE, dbitrate, &dsub_can_device_name, "Set CAN controller data phase bitrate (sample point and SJW optional)\n" "Usage: can dbitrate [sample point] [sjw]", cmd_can_dbitrate_set, 3, 2), SHELL_CMD_ARG(mode, &dsub_can_device_name_mode, "Set CAN controller mode\n" "Usage: can mode [mode] [mode] [...]", cmd_can_mode_set, 3, SHELL_OPT_ARG_CHECK_SKIP), SHELL_CMD_ARG(send, &dsub_can_device_name, "Enqueue a CAN frame for sending\n" "Usage: can send [-e] [-r] [-f] [-b] [data] [...]\n" "-e use extended (29-bit) CAN ID\n" "-r send Remote Transmission Request (RTR) frame\n" "-f use CAN-FD frame format\n" "-b use CAN-FD Bit Rate Switching (BRS)", cmd_can_send, 3, SHELL_OPT_ARG_CHECK_SKIP), SHELL_CMD(filter, &sub_can_filter_cmds, "CAN rx filter commands\n" "Usage: can filter ...", NULL), SHELL_EXPR_CMD_ARG(!IS_ENABLED(CONFIG_CAN_AUTO_BUS_OFF_RECOVERY), recover, &dsub_can_device_name, "Recover CAN controller from bus-off state\n" "Usage: can recover [timeout ms]", cmd_can_recover, 2, 1), SHELL_SUBCMD_SET_END ); SHELL_CMD_REGISTER(can, &sub_can_cmds, "CAN controller commands", NULL);