drivers: can: rework support for manual bus-off recovery

Since all CAN controllers drivers seem to support automatic recovery (for
any future drivers for hardware without this hardware capability this can
easily be implemented in the driver), change the Zephyr CAN controller API
policy to:

- Always enable automatic bus recovery upon driver initialization,
  regardless of Kconfig options. Since CAN controllers are initialized in
  "stopped" state, no unwanted bus-off recovery will be started at this
  point.

- Invert and rename the Kconfig CONFIG_CAN_AUTO_BUS_OFF_RECOVERY, which is
  enabled by default, to CONFIG_CAN_MANUAL_RECOVERY_MODE, which is disabled
  by default. Enabling CONFIG_CAN_MANUAL_RECOVERY_MODE=y enables support
  for the can_recover() API function and a new manual recovery mode (see
  next bullet). Keeping this guarded by Kconfig allows keeping the flash
  footprint down for applications not using manual bus-off recovery.

- Introduce a new CAN controller operational mode
  CAN_MODE_MANUAL_RECOVERY. Support for this is only enabled if
  CONFIG_CAN_MANUAL_RECOVERY_MODE=y. Having this as a mode allows
  applications to inquire whether the CAN controller supports manual
  recovery mode via the can_get_capabilities() API function and either fail
  or rely on automatic recovery - and it allows CAN controller drivers not
  supporting manual recovery mode to fail early in can_set_mode() during
  application startup instead of failing when can_recover() is called at a
  later point in time.

Signed-off-by: Henrik Brix Andersen <hebad@vestas.com>
This commit is contained in:
Henrik Brix Andersen 2024-02-14 22:38:34 +01:00 committed by Alberto Escolar
parent 118f1592ff
commit a57db0ddcb
34 changed files with 294 additions and 238 deletions

View file

@ -33,8 +33,7 @@ The following :ref:`Kconfig <kconfig>` options enable additional subcommands and
* :kconfig:option:`CONFIG_CAN_STATS` enables printing of various statistics for the CAN controller
in the ``can show`` subcommand. This depends on :kconfig:option:`CONFIG_STATS` being enabled as
well.
* :kconfig:option:`CONFIG_CAN_AUTO_BUS_OFF_RECOVERY` enables the ``can recover`` subcommand when
disabled.
* :kconfig:option:`CONFIG_CAN_MANUAL_RECOVERY_MODE` enables the ``can recover`` subcommand.
For example, building the :ref:`hello_world` sample for the :ref:`frdm_k64f` with the CAN shell and
CAN statistics enabled:
@ -253,8 +252,8 @@ details on the supported arguments.
Bus Recovery
************
The ``can recover`` subcommand can be used for initiating recovery from a CAN bus-off event as shown
below:
The ``can recover`` subcommand can be used for initiating manual recovery from a CAN bus-off event
as shown below:
.. code-block:: console
@ -265,5 +264,5 @@ The subcommand accepts an optional bus recovery timeout in milliseconds. If no t
the command will wait indefinitely for the bus recovery to succeed.
.. note::
The ``recover`` subcommand is only available if
:kconfig:option:`CONFIG_CAN_AUTO_BUS_OFF_RECOVERY` is disabled.
The ``recover`` subcommand is only available if :kconfig:option:`CONFIG_CAN_MANUAL_RECOVERY_MODE`
is enabled.

View file

@ -62,10 +62,16 @@ config CAN_ACCEPT_RTR
level.
config CAN_FD_MODE
bool "CAN FD"
bool "CAN FD support"
help
Enable CAN FD support. Not all CAN controllers support CAN FD.
config CAN_MANUAL_RECOVERY_MODE
bool "Manual bus-off recovery support"
help
Enable support for manual (non-automatic) recovery from bus-off state. Not all CAN
controllers support manual recovery mode.
config CAN_RX_TIMESTAMP
bool "Receiving timestamps"
help
@ -73,15 +79,6 @@ config CAN_RX_TIMESTAMP
The value is incremented every bit time and starts when the controller
is initialized. Not all CAN controllers support timestamps.
config CAN_AUTO_BUS_OFF_RECOVERY
bool "Automatic recovery from bus-off"
default y
help
This option enables the automatic bus-off recovery according to
ISO 11898-1 (recovery after 128 occurrences of 11 consecutive
recessive bits). When this option is enabled, the recovery API is not
available.
config CAN_QEMU_IFACE_NAME
string "SocketCAN interface name for QEMU"
default ""

View file

@ -224,9 +224,9 @@ const struct can_driver_api can_esp32_twai_driver_api = {
.set_state_change_callback = can_sja1000_set_state_change_callback,
.get_core_clock = can_esp32_twai_get_core_clock,
.get_max_filters = can_sja1000_get_max_filters,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = can_sja1000_recover,
#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
.timing_min = CAN_SJA1000_TIMING_MIN_INITIALIZER,
#ifdef CONFIG_SOC_SERIES_ESP32
.timing_max = CAN_SJA1000_TIMING_MAX_INITIALIZER,

View file

@ -103,9 +103,9 @@ static const struct can_driver_api fake_can_driver_api = {
.add_rx_filter = fake_can_add_rx_filter,
.remove_rx_filter = fake_can_remove_rx_filter,
.get_state = fake_can_get_state,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = fake_can_recover,
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
.set_state_change_callback = fake_can_set_state_change_callback,
.get_core_clock = fake_can_get_core_clock,
.get_max_filters = fake_can_get_max_filters,

View file

@ -249,15 +249,16 @@ static inline int z_vrfy_can_get_state(const struct device *dev, enum can_state
}
#include <syscalls/can_get_state_mrsh.c>
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
static inline int z_vrfy_can_recover(const struct device *dev, k_timeout_t timeout)
{
K_OOPS(K_SYSCALL_DRIVER_CAN(dev, recover));
/* Optional API function */
K_OOPS(K_SYSCALL_OBJ(dev, K_OBJ_DRIVER_CAN));
return z_impl_can_recover(dev, timeout);
}
#include <syscalls/can_recover_mrsh.c>
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
#ifdef CONFIG_CAN_STATS

View file

@ -143,9 +143,9 @@ const struct can_driver_api can_kvaser_pci_driver_api = {
.set_state_change_callback = can_sja1000_set_state_change_callback,
.get_core_clock = can_kvaser_pci_get_core_clock,
.get_max_filters = can_sja1000_get_max_filters,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = can_sja1000_recover,
#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
.timing_min = CAN_SJA1000_TIMING_MIN_INITIALIZER,
.timing_max = CAN_SJA1000_TIMING_MAX_INITIALIZER,
};

View file

@ -340,21 +340,6 @@ static int can_loopback_get_state(const struct device *dev, enum can_state *stat
return 0;
}
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
static int can_loopback_recover(const struct device *dev, k_timeout_t timeout)
{
struct can_loopback_data *data = dev->data;
ARG_UNUSED(timeout);
if (!data->common.started) {
return -ENETDOWN;
}
return 0;
}
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
static void can_loopback_set_state_change_callback(const struct device *dev,
can_state_change_callback_t cb,
void *user_data)
@ -388,9 +373,6 @@ static const struct can_driver_api can_loopback_driver_api = {
.add_rx_filter = can_loopback_add_rx_filter,
.remove_rx_filter = can_loopback_remove_rx_filter,
.get_state = can_loopback_get_state,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
.recover = can_loopback_recover,
#endif
.set_state_change_callback = can_loopback_set_state_change_callback,
.get_core_clock = can_loopback_get_core_clock,
.get_max_filters = can_loopback_get_max_filters,

View file

@ -258,9 +258,13 @@ int can_mcan_get_capabilities(const struct device *dev, can_mode_t *cap)
*cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY;
#if CONFIG_CAN_FD_MODE
*cap |= CAN_MODE_FD;
#endif /* CONFIG_CAN_FD_MODE */
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
*cap |= CAN_MODE_MANUAL_RECOVERY;
}
if (IS_ENABLED(CONFIG_CAN_FD_MODE)) {
*cap |= CAN_MODE_FD;
}
return 0;
}
@ -350,22 +354,24 @@ int can_mcan_stop(const struct device *dev)
int can_mcan_set_mode(const struct device *dev, can_mode_t mode)
{
can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY;
struct can_mcan_data *data = dev->data;
uint32_t cccr;
uint32_t test;
int err;
#ifdef CONFIG_CAN_FD_MODE
if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_FD)) != 0U) {
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
supported |= CAN_MODE_MANUAL_RECOVERY;
}
if (IS_ENABLED(CONFIG_CAN_FD_MODE)) {
supported |= CAN_MODE_FD;
}
if ((mode & ~(supported)) != 0U) {
LOG_ERR("unsupported mode: 0x%08x", mode);
return -ENOTSUP;
}
#else /* CONFIG_CAN_FD_MODE */
if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY)) != 0U) {
LOG_ERR("unsupported mode: 0x%08x", mode);
return -ENOTSUP;
}
#endif /* !CONFIG_CAN_FD_MODE */
if (data->common.started) {
return -EBUSY;
@ -462,7 +468,8 @@ static void can_mcan_state_change_handler(const struct device *dev)
}
}
if (IS_ENABLED(CONFIG_CAN_AUTO_BUS_OFF_RECOVERY)) {
if (!IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE) ||
(data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) {
/*
* Request leaving init mode, but do not take the lock (as we are in ISR
* context), nor wait for the result.
@ -847,7 +854,7 @@ int can_mcan_get_state(const struct device *dev, enum can_state *state,
return 0;
}
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
int can_mcan_recover(const struct device *dev, k_timeout_t timeout)
{
struct can_mcan_data *data = dev->data;
@ -856,9 +863,13 @@ int can_mcan_recover(const struct device *dev, k_timeout_t timeout)
return -ENETDOWN;
}
if ((data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) {
return -ENOTSUP;
}
return can_mcan_leave_init_mode(dev, timeout);
}
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
int can_mcan_send(const struct device *dev, const struct can_frame *frame, k_timeout_t timeout,
can_tx_callback_t callback, void *user_data)

View file

@ -789,21 +789,6 @@ static void mcp2515_handle_errors(const struct device *dev)
}
}
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
static int mcp2515_recover(const struct device *dev, k_timeout_t timeout)
{
struct mcp2515_data *dev_data = dev->data;
ARG_UNUSED(timeout);
if (!dev_data->common.started) {
return -ENETDOWN;
}
return -ENOTSUP;
}
#endif
static void mcp2515_handle_interrupts(const struct device *dev)
{
const struct mcp2515_config *dev_cfg = dev->config;
@ -904,9 +889,6 @@ static const struct can_driver_api can_api_funcs = {
.add_rx_filter = mcp2515_add_rx_filter,
.remove_rx_filter = mcp2515_remove_rx_filter,
.get_state = mcp2515_get_state,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
.recover = mcp2515_recover,
#endif
.set_state_change_callback = mcp2515_set_state_change_callback,
.get_core_clock = mcp2515_get_core_clock,
.get_max_filters = mcp2515_get_max_filters,

View file

@ -740,21 +740,6 @@ static int mcp251xfd_get_max_filters(const struct device *dev, bool ide)
return CONFIG_CAN_MAX_FILTER;
}
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
static int mcp251xfd_recover(const struct device *dev, k_timeout_t timeout)
{
struct mcp251xfd_data *dev_data = dev->data;
ARG_UNUSED(timeout);
if (!dev_data->common.started) {
return -ENETDOWN;
}
return -ENOTSUP;
}
#endif
static int mcp251xfd_handle_fifo_read(const struct device *dev, const struct mcp251xfd_fifo *fifo,
uint8_t fifo_type)
{
@ -1646,9 +1631,6 @@ static const struct can_driver_api mcp251xfd_api_funcs = {
.send = mcp251xfd_send,
.add_rx_filter = mcp251xfd_add_rx_filter,
.remove_rx_filter = mcp251xfd_remove_rx_filter,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
.recover = mcp251xfd_recover,
#endif
.get_state = mcp251xfd_get_state,
.set_state_change_callback = mcp251xfd_set_state_change_callback,
.get_core_clock = mcp251xfd_get_core_clock,

View file

@ -177,6 +177,10 @@ static int mcux_flexcan_get_capabilities(const struct device *dev, can_mode_t *c
*cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_3_SAMPLES;
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
*cap |= CAN_MODE_MANUAL_RECOVERY;
}
if (UTIL_AND(IS_ENABLED(CONFIG_CAN_MCUX_FLEXCAN_FD), config->flexcan_fd)) {
*cap |= CAN_MODE_FD;
}
@ -388,6 +392,10 @@ static int mcux_flexcan_set_mode(const struct device *dev, can_mode_t mode)
return -EBUSY;
}
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
supported |= CAN_MODE_MANUAL_RECOVERY;
}
if (UTIL_AND(IS_ENABLED(CONFIG_CAN_MCUX_FLEXCAN_FD), config->flexcan_fd)) {
supported |= CAN_MODE_FD;
}
@ -431,6 +439,16 @@ static int mcux_flexcan_set_mode(const struct device *dev, can_mode_t mode)
ctrl1 &= ~(CAN_CTRL1_SMP_MASK);
}
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
if ((mode & CAN_MODE_MANUAL_RECOVERY) != 0) {
/* Disable auto-recovery from bus-off */
ctrl1 |= CAN_CTRL1_BOFFREC_MASK;
} else {
/* Enable auto-recovery from bus-off */
ctrl1 &= ~(CAN_CTRL1_BOFFREC_MASK);
}
}
#ifdef CONFIG_CAN_MCUX_FLEXCAN_FD
if (config->flexcan_fd) {
if ((mode & CAN_MODE_FD) != 0) {
@ -819,7 +837,7 @@ static void mcux_flexcan_set_state_change_callback(const struct device *dev,
data->common.state_change_cb_user_data = user_data;
}
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
static int mcux_flexcan_recover(const struct device *dev, k_timeout_t timeout)
{
const struct mcux_flexcan_config *config = dev->config;
@ -832,6 +850,10 @@ static int mcux_flexcan_recover(const struct device *dev, k_timeout_t timeout)
return -ENETDOWN;
}
if ((data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) {
return -ENOTSUP;
}
(void)mcux_flexcan_get_state(dev, &state, NULL);
if (state != CAN_STATE_BUS_OFF) {
return 0;
@ -857,7 +879,7 @@ static int mcux_flexcan_recover(const struct device *dev, k_timeout_t timeout)
return ret;
}
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
static void mcux_flexcan_remove_rx_filter(const struct device *dev, int filter_id)
{
@ -1225,9 +1247,8 @@ static int mcux_flexcan_init(const struct device *dev)
config->irq_config_func(dev);
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
config->base->CTRL1 |= CAN_CTRL1_BOFFREC_MASK;
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
/* Enable auto-recovery from bus-off */
config->base->CTRL1 &= ~(CAN_CTRL1_BOFFREC_MASK);
(void)mcux_flexcan_get_state(dev, &data->state, NULL);
@ -1244,9 +1265,9 @@ __maybe_unused static const struct can_driver_api mcux_flexcan_driver_api = {
.add_rx_filter = mcux_flexcan_add_rx_filter,
.remove_rx_filter = mcux_flexcan_remove_rx_filter,
.get_state = mcux_flexcan_get_state,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = mcux_flexcan_recover,
#endif
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
.set_state_change_callback = mcux_flexcan_set_state_change_callback,
.get_core_clock = mcux_flexcan_get_core_clock,
.get_max_filters = mcux_flexcan_get_max_filters,
@ -1287,9 +1308,9 @@ static const struct can_driver_api mcux_flexcan_fd_driver_api = {
.add_rx_filter = mcux_flexcan_add_rx_filter,
.remove_rx_filter = mcux_flexcan_remove_rx_filter,
.get_state = mcux_flexcan_get_state,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = mcux_flexcan_recover,
#endif
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
.set_state_change_callback = mcux_flexcan_set_state_change_callback,
.get_core_clock = mcux_flexcan_get_core_clock,
.get_max_filters = mcux_flexcan_get_max_filters,

View file

@ -132,9 +132,9 @@ static const struct can_driver_api mcux_mcan_driver_api = {
.send = can_mcan_send,
.add_rx_filter = can_mcan_add_rx_filter,
.remove_rx_filter = can_mcan_remove_rx_filter,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = can_mcan_recover,
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
.get_state = can_mcan_get_state,
.set_state_change_callback = can_mcan_set_state_change_callback,
.get_core_clock = mcux_mcan_get_core_clock,

View file

@ -373,21 +373,6 @@ static int can_native_linux_get_state(const struct device *dev, enum can_state *
return 0;
}
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
static int can_native_linux_recover(const struct device *dev, k_timeout_t timeout)
{
struct can_native_linux_data *data = dev->data;
ARG_UNUSED(timeout);
if (!data->common.started) {
return -ENETDOWN;
}
return 0;
}
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
static void can_native_linux_set_state_change_callback(const struct device *dev,
can_state_change_callback_t cb,
void *user_data)
@ -422,9 +407,6 @@ static const struct can_driver_api can_native_linux_driver_api = {
.add_rx_filter = can_native_linux_add_rx_filter,
.remove_rx_filter = can_native_linux_remove_rx_filter,
.get_state = can_native_linux_get_state,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
.recover = can_native_linux_recover,
#endif
.set_state_change_callback = can_native_linux_set_state_change_callback,
.get_core_clock = can_native_linux_get_core_clock,
.get_max_filters = can_native_linux_get_max_filters,

View file

@ -169,9 +169,9 @@ static const struct can_driver_api can_numaker_driver_api = {
.send = can_mcan_send,
.add_rx_filter = can_mcan_add_rx_filter,
.remove_rx_filter = can_mcan_remove_rx_filter,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = can_mcan_recover,
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
.get_state = can_mcan_get_state,
.set_state_change_callback = can_mcan_set_state_change_callback,
.get_core_clock = can_numaker_get_core_clock,

View file

@ -123,9 +123,13 @@ static int can_nxp_s32_get_capabilities(const struct device *dev, can_mode_t *ca
*cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY;
#ifdef CAN_NXP_S32_FD_MODE
*cap |= CAN_MODE_FD;
#endif
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
*cap |= CAN_MODE_MANUAL_RECOVERY;
}
if (IS_ENABLED(CAN_NXP_S32_FD_MODE)) {
*cap |= CAN_MODE_FD;
}
return 0;
}
@ -271,6 +275,7 @@ static int can_nxp_s32_stop(const struct device *dev)
static int can_nxp_s32_set_mode(const struct device *dev, can_mode_t mode)
{
can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY;
const struct can_nxp_s32_config *config = dev->config;
struct can_nxp_s32_data *data = dev->data;
Canexcel_Ip_ModesType can_nxp_s32_mode = CAN_MODE_NORMAL;
@ -280,11 +285,16 @@ static int can_nxp_s32_set_mode(const struct device *dev, can_mode_t mode)
if (data->common.started) {
return -EBUSY;
}
#ifdef CAN_NXP_S32_FD_MODE
if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_FD)) != 0) {
#else
if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY)) != 0) {
#endif
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
supported |= CAN_MODE_MANUAL_RECOVERY;
}
if (IS_ENABLED(CAN_NXP_S32_FD_MODE)) {
supported |= CAN_MODE_FD;
}
if ((mode & ~(supported)) != 0) {
LOG_ERR("unsupported mode: 0x%08x", mode);
return -ENOTSUP;
}
@ -309,6 +319,20 @@ static int can_nxp_s32_set_mode(const struct device *dev, can_mode_t mode)
CanXL_SetFDEnabled(config->base_sic, canfd, brs);
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
Canexcel_Ip_StatusType status;
uint32_t options = 0U;
if ((mode & CAN_MODE_MANUAL_RECOVERY) == 0U) {
options = CANXL_IP_BUSOFF_RECOVERY_U32;
}
status = CanXL_ConfigCtrlOptions(config->base_sic, options);
if (status != CANEXCEL_STATUS_SUCCESS) {
return -EIO;
}
}
CanXL_SetOperationMode(config->base_sic, can_nxp_s32_mode);
Canexcel_Ip_ExitFreezeMode(config->instance);
@ -377,7 +401,7 @@ static void can_nxp_s32_set_state_change_callback(const struct device *dev,
data->common.state_change_cb_user_data = user_data;
}
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
static int can_nxp_s32_recover(const struct device *dev, k_timeout_t timeout)
{
const struct can_nxp_s32_config *config = dev->config;
@ -390,6 +414,10 @@ static int can_nxp_s32_recover(const struct device *dev, k_timeout_t timeout)
return -ENETDOWN;
}
if ((data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) {
return -ENOTSUP;
}
can_nxp_s32_get_state(dev, &state, NULL);
if (state != CAN_STATE_BUS_OFF) {
return 0;
@ -415,7 +443,7 @@ static int can_nxp_s32_recover(const struct device *dev, k_timeout_t timeout)
return ret;
}
#endif
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
static void can_nxp_s32_remove_rx_filter(const struct device *dev, int filter_id)
{
@ -1024,9 +1052,9 @@ static const struct can_driver_api can_nxp_s32_driver_api = {
.add_rx_filter = can_nxp_s32_add_rx_filter,
.remove_rx_filter = can_nxp_s32_remove_rx_filter,
.get_state = can_nxp_s32_get_state,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = can_nxp_s32_recover,
#endif
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
.set_state_change_callback = can_nxp_s32_set_state_change_callback,
.get_core_clock = can_nxp_s32_get_core_clock,
.get_max_filters = can_nxp_s32_get_max_filters,
@ -1102,12 +1130,6 @@ static const struct can_driver_api can_nxp_s32_driver_api = {
#define CAN_NXP_S32_BRS 0
#endif
#ifdef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#define CAN_NXP_S32_CTRL_OPTIONS CANXL_IP_BUSOFF_RECOVERY_U32
#else
#define CAN_NXP_S32_CTRL_OPTIONS 0
#endif
#define CAN_NXP_S32_HW_INSTANCE_CHECK(i, n) \
((DT_INST_REG_ADDR(n) == IP_CANXL_##i##__SIC_BASE) ? i : 0)
@ -1135,7 +1157,7 @@ static const struct can_driver_api can_nxp_s32_driver_api = {
.CanxlMode = CANEXCEL_LISTEN_ONLY_MODE, \
.fd_enable = (boolean)IS_ENABLED(CAN_NXP_S32_FD_MODE), \
.bitRateSwitch = (boolean)CAN_NXP_S32_BRS, \
.ctrlOptions = (uint32)CAN_NXP_S32_CTRL_OPTIONS, \
.ctrlOptions = CANXL_IP_BUSOFF_RECOVERY_U32, \
.Callback = nxp_s32_can_##n##_ctrl_callback, \
.ErrorCallback = nxp_s32_can_##n##_err_callback, \
IF_ENABLED(CONFIG_CAN_NXP_S32_RX_FIFO, \

View file

@ -653,12 +653,17 @@ static int can_rcar_stop(const struct device *dev)
static int can_rcar_set_mode(const struct device *dev, can_mode_t mode)
{
can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY;
const struct can_rcar_cfg *config = dev->config;
struct can_rcar_data *data = dev->data;
uint8_t tcr = 0;
int ret = 0;
if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY)) != 0) {
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
supported |= CAN_MODE_MANUAL_RECOVERY;
}
if ((mode & ~(supported)) != 0) {
LOG_ERR("Unsupported mode: 0x%08x", mode);
return -ENOTSUP;
}
@ -687,6 +692,20 @@ static int can_rcar_set_mode(const struct device *dev, can_mode_t mode)
sys_write8(tcr, config->reg_addr + RCAR_CAN_TCR);
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
uint16_t ctlr = can_rcar_read16(config, RCAR_CAN_CTLR);
if ((mode & CAN_MODE_MANUAL_RECOVERY) != 0U) {
/* Set entry to halt automatically at bus-off */
ctlr |= RCAR_CAN_CTLR_BOM_ENT;
} else {
/* Clear entry to halt automatically at bus-off */
ctlr &= ~RCAR_CAN_CTLR_BOM_ENT;
}
can_rcar_write16(config, RCAR_CAN_CTLR, ctlr);
}
data->common.mode = mode;
unlock:
@ -805,7 +824,7 @@ static int can_rcar_get_state(const struct device *dev, enum can_state *state,
return 0;
}
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
static int can_rcar_recover(const struct device *dev, k_timeout_t timeout)
{
const struct can_rcar_cfg *config = dev->config;
@ -817,6 +836,10 @@ static int can_rcar_recover(const struct device *dev, k_timeout_t timeout)
return -ENETDOWN;
}
if ((data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) {
return -ENOTSUP;
}
if (data->state != CAN_STATE_BUS_OFF) {
return 0;
}
@ -843,7 +866,7 @@ done:
k_mutex_unlock(&data->inst_mutex);
return ret;
}
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
static int can_rcar_send(const struct device *dev, const struct can_frame *frame,
k_timeout_t timeout, can_tx_callback_t callback,
@ -1078,9 +1101,7 @@ static int can_rcar_init(const struct device *dev)
ctlr = can_rcar_read16(config, RCAR_CAN_CTLR);
ctlr |= RCAR_CAN_CTLR_IDFM_MIXED; /* Select mixed ID mode */
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
ctlr |= RCAR_CAN_CTLR_BOM_ENT; /* Entry to halt mode automatically at bus-off */
#endif
ctlr &= ~RCAR_CAN_CTLR_BOM_ENT; /* Clear entry to halt automatically at bus-off */
ctlr |= RCAR_CAN_CTLR_MBM; /* Select FIFO mailbox mode */
ctlr |= RCAR_CAN_CTLR_MLM; /* Overrun mode */
ctlr &= ~RCAR_CAN_CTLR_SLPM; /* Clear CAN Sleep mode */
@ -1142,9 +1163,9 @@ static const struct can_driver_api can_rcar_driver_api = {
.add_rx_filter = can_rcar_add_rx_filter,
.remove_rx_filter = can_rcar_remove_rx_filter,
.get_state = can_rcar_get_state,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = can_rcar_recover,
#endif
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
.set_state_change_callback = can_rcar_set_state_change_callback,
.get_core_clock = can_rcar_get_core_clock,
.get_max_filters = can_rcar_get_max_filters,

View file

@ -126,9 +126,9 @@ static const struct can_driver_api can_sam_driver_api = {
.add_rx_filter = can_mcan_add_rx_filter,
.remove_rx_filter = can_mcan_remove_rx_filter,
.get_state = can_mcan_get_state,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = can_mcan_recover,
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
.get_core_clock = can_sam_get_core_clock,
.get_max_filters = can_mcan_get_max_filters,
.set_state_change_callback = can_mcan_set_state_change_callback,

View file

@ -171,9 +171,9 @@ static const struct can_driver_api can_sam0_driver_api = {
.add_rx_filter = can_mcan_add_rx_filter,
.remove_rx_filter = can_mcan_remove_rx_filter,
.get_state = can_mcan_get_state,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = can_mcan_recover,
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
.get_core_clock = can_sam0_get_core_clock,
.get_max_filters = can_mcan_get_max_filters,
.set_state_change_callback = can_mcan_set_state_change_callback,

View file

@ -35,6 +35,7 @@ static const struct can_shell_mode_mapping can_shell_mode_map[] = {
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),
CAN_SHELL_MODE_MAPPING("manual-recovery", CAN_MODE_MANUAL_RECOVERY),
};
K_MSGQ_DEFINE(can_shell_tx_msgq, sizeof(struct can_shell_tx_event),
@ -1054,9 +1055,9 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_can_cmds,
"CAN rx filter commands\n"
"Usage: can filter <add|remove> <device> ...",
NULL),
SHELL_EXPR_CMD_ARG(!IS_ENABLED(CONFIG_CAN_AUTO_BUS_OFF_RECOVERY),
SHELL_COND_CMD_ARG(CONFIG_CAN_MANUAL_RECOVERY_MODE,
recover, &dsub_can_device_name,
"Recover CAN controller from bus-off state\n"
"Manually recover CAN controller from bus-off state\n"
"Usage: can recover <device> [timeout ms]",
cmd_can_recover, 2, 1),
SHELL_SUBCMD_SET_END

View file

@ -141,6 +141,10 @@ int can_sja1000_get_capabilities(const struct device *dev, can_mode_t *cap)
*cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY |
CAN_MODE_ONE_SHOT | CAN_MODE_3_SAMPLES;
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
*cap |= CAN_MODE_MANUAL_RECOVERY;
}
return 0;
}
@ -213,12 +217,17 @@ int can_sja1000_stop(const struct device *dev)
int can_sja1000_set_mode(const struct device *dev, can_mode_t mode)
{
can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT |
CAN_MODE_3_SAMPLES;
struct can_sja1000_data *data = dev->data;
uint8_t btr1;
uint8_t mod;
if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT |
CAN_MODE_3_SAMPLES)) != 0) {
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
supported |= CAN_MODE_MANUAL_RECOVERY;
}
if ((mode & ~(supported)) != 0) {
LOG_ERR("unsupported mode: 0x%08x", mode);
return -ENOTSUP;
}
@ -464,7 +473,7 @@ void can_sja1000_remove_rx_filter(const struct device *dev, int filter_id)
}
}
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
int can_sja1000_recover(const struct device *dev, k_timeout_t timeout)
{
struct can_sja1000_data *data = dev->data;
@ -476,6 +485,10 @@ int can_sja1000_recover(const struct device *dev, k_timeout_t timeout)
return -ENETDOWN;
}
if ((data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) {
return -ENOTSUP;
}
sr = can_sja1000_read_reg(dev, CAN_SJA1000_SR);
if ((sr & CAN_SJA1000_SR_BS) == 0) {
return 0;
@ -509,7 +522,7 @@ int can_sja1000_recover(const struct device *dev, k_timeout_t timeout)
return 0;
}
#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
int can_sja1000_get_state(const struct device *dev, enum can_state *state,
struct can_bus_err_cnt *err_cnt)
@ -659,11 +672,11 @@ static void can_sja1000_handle_error_warning_irq(const struct device *dev)
if ((sr & CAN_SJA1000_SR_BS) != 0) {
data->state = CAN_STATE_BUS_OFF;
can_sja1000_tx_done(dev, -ENETUNREACH);
#ifdef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
if (data->common.started) {
if (data->common.started &&
(data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) {
can_sja1000_leave_reset_mode_nowait(dev);
}
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
} else if ((sr & CAN_SJA1000_SR_ES) != 0) {
data->state = CAN_STATE_ERROR_WARNING;
} else {

View file

@ -380,6 +380,10 @@ static int can_stm32_get_capabilities(const struct device *dev, can_mode_t *cap)
*cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT;
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
*cap |= CAN_MODE_MANUAL_RECOVERY;
}
return 0;
}
@ -473,13 +477,18 @@ unlock:
static int can_stm32_set_mode(const struct device *dev, can_mode_t mode)
{
can_mode_t supported = CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT;
const struct can_stm32_config *cfg = dev->config;
CAN_TypeDef *can = cfg->can;
struct can_stm32_data *data = dev->data;
LOG_DBG("Set mode %d", mode);
if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT)) != 0) {
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
supported |= CAN_MODE_MANUAL_RECOVERY;
}
if ((mode & ~(supported)) != 0) {
LOG_ERR("unsupported mode: 0x%08x", mode);
return -ENOTSUP;
}
@ -511,6 +520,15 @@ static int can_stm32_set_mode(const struct device *dev, can_mode_t mode)
can->MCR &= ~CAN_MCR_NART;
}
if (IS_ENABLED(CONFIG_CAN_MANUAL_RECOVERY_MODE)) {
if ((mode & CAN_MODE_MANUAL_RECOVERY) != 0) {
/* No automatic recovery from bus-off */
can->MCR &= ~CAN_MCR_ABOM;
} else {
can->MCR |= CAN_MCR_ABOM;
}
}
data->common.mode = mode;
k_mutex_unlock(&data->inst_mutex);
@ -637,9 +655,10 @@ static int can_stm32_init(const struct device *dev)
#ifdef CONFIG_CAN_RX_TIMESTAMP
can->MCR |= CAN_MCR_TTCM;
#endif
#ifdef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
/* Enable automatic bus-off recovery */
can->MCR |= CAN_MCR_ABOM;
#endif
ret = can_calc_timing(dev, &timing, cfg->common.bus_speed,
cfg->common.sample_point);
if (ret == -EINVAL) {
@ -686,7 +705,7 @@ static void can_stm32_set_state_change_callback(const struct device *dev,
}
}
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
static int can_stm32_recover(const struct device *dev, k_timeout_t timeout)
{
const struct can_stm32_config *cfg = dev->config;
@ -699,6 +718,10 @@ static int can_stm32_recover(const struct device *dev, k_timeout_t timeout)
return -ENETDOWN;
}
if ((data->common.mode & CAN_MODE_MANUAL_RECOVERY) == 0U) {
return -ENOTSUP;
}
if (!(can->ESR & CAN_ESR_BOFF)) {
return 0;
}
@ -729,8 +752,7 @@ done:
k_mutex_unlock(&data->inst_mutex);
return ret;
}
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
static int can_stm32_send(const struct device *dev, const struct can_frame *frame,
k_timeout_t timeout, can_tx_callback_t callback,
@ -1053,9 +1075,9 @@ static const struct can_driver_api can_api_funcs = {
.add_rx_filter = can_stm32_add_rx_filter,
.remove_rx_filter = can_stm32_remove_rx_filter,
.get_state = can_stm32_get_state,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = can_stm32_recover,
#endif
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
.set_state_change_callback = can_stm32_set_state_change_callback,
.get_core_clock = can_stm32_get_core_clock,
.get_max_filters = can_stm32_get_max_filters,

View file

@ -586,9 +586,9 @@ static const struct can_driver_api can_stm32fd_driver_api = {
.add_rx_filter = can_mcan_add_rx_filter,
.remove_rx_filter = can_mcan_remove_rx_filter,
.get_state = can_mcan_get_state,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = can_mcan_recover,
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
.get_core_clock = can_stm32fd_get_core_clock,
.get_max_filters = can_mcan_get_max_filters,
.set_state_change_callback = can_mcan_set_state_change_callback,

View file

@ -204,9 +204,9 @@ static const struct can_driver_api can_stm32h7_driver_api = {
.add_rx_filter = can_mcan_add_rx_filter,
.remove_rx_filter = can_mcan_remove_rx_filter,
.get_state = can_mcan_get_state,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = can_mcan_recover,
#endif
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE*/
.get_core_clock = can_stm32h7_get_core_clock,
.get_max_filters = can_mcan_get_max_filters,
.set_state_change_callback = can_mcan_set_state_change_callback,

View file

@ -722,9 +722,9 @@ static const struct can_driver_api tcan4x5x_driver_api = {
.send = can_mcan_send,
.add_rx_filter = can_mcan_add_rx_filter,
.remove_rx_filter = can_mcan_remove_rx_filter,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
.recover = can_mcan_recover,
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
.get_state = can_mcan_get_state,
.set_state_change_callback = can_mcan_set_state_change_callback,
.get_core_clock = tcan4x5x_get_core_clock,

View file

@ -514,21 +514,6 @@ static int can_xmc4xxx_get_max_filters(const struct device *dev, bool ide)
return CONFIG_CAN_MAX_FILTER;
}
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
static int can_xmc4xxx_recover(const struct device *dev, k_timeout_t timeout)
{
struct can_xmc4xxx_data *dev_data = dev->data;
ARG_UNUSED(timeout);
if (!dev_data->common.started) {
return -ENETDOWN;
}
return -ENOTSUP;
}
#endif
static void can_xmc4xxx_reset_tx_fifos(const struct device *dev, int status)
{
struct can_xmc4xxx_data *dev_data = dev->data;
@ -908,9 +893,6 @@ static const struct can_driver_api can_xmc4xxx_api_funcs = {
.send = can_xmc4xxx_send,
.add_rx_filter = can_xmc4xxx_add_rx_filter,
.remove_rx_filter = can_xmc4xxx_remove_rx_filter,
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
.recover = can_xmc4xxx_recover,
#endif
.get_state = can_xmc4xxx_get_state,
.set_state_change_callback = can_xmc4xxx_set_state_change_callback,
.get_core_clock = can_xmc4xxx_get_core_clock,

View file

@ -89,19 +89,22 @@ extern "C" {
#define CAN_MODE_NORMAL 0
/** Controller is in loopback mode (receives own frames). */
#define CAN_MODE_LOOPBACK BIT(0)
#define CAN_MODE_LOOPBACK BIT(0)
/** Controller is not allowed to send dominant bits. */
#define CAN_MODE_LISTENONLY BIT(1)
#define CAN_MODE_LISTENONLY BIT(1)
/** Controller allows transmitting/receiving CAN FD frames. */
#define CAN_MODE_FD BIT(2)
#define CAN_MODE_FD BIT(2)
/** Controller does not retransmit in case of lost arbitration or missing ACK */
#define CAN_MODE_ONE_SHOT BIT(3)
#define CAN_MODE_ONE_SHOT BIT(3)
/** Controller uses triple sampling mode */
#define CAN_MODE_3_SAMPLES BIT(4)
#define CAN_MODE_3_SAMPLES BIT(4)
/** Controller requires manual recovery after entering bus-off state */
#define CAN_MODE_MANUAL_RECOVERY BIT(5)
/** @} */
@ -450,7 +453,7 @@ typedef int (*can_add_rx_filter_t)(const struct device *dev,
typedef void (*can_remove_rx_filter_t)(const struct device *dev, int filter_id);
/**
* @brief Callback API upon recovering the CAN bus
* @brief Optional callback API upon manually recovering the CAN controller from bus-off state
* See @a can_recover() for argument description
*/
typedef int (*can_recover_t)(const struct device *dev, k_timeout_t timeout);
@ -491,9 +494,9 @@ __subsystem struct can_driver_api {
can_send_t send;
can_add_rx_filter_t add_rx_filter;
can_remove_rx_filter_t remove_rx_filter;
#if !defined(CONFIG_CAN_AUTO_BUS_OFF_RECOVERY) || defined(__DOXYGEN__)
#if defined(CONFIG_CAN_MANUAL_RECOVERY_MODE) || defined(__DOXYGEN__)
can_recover_t recover;
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
can_get_state_t get_state;
can_set_state_change_callback_t set_state_change_callback;
can_get_core_clock_t get_core_clock;
@ -1413,34 +1416,32 @@ static inline int z_impl_can_get_state(const struct device *dev, enum can_state
*
* Recover the CAN controller from bus-off state to error-active state.
*
* @note @kconfig{CONFIG_CAN_AUTO_BUS_OFF_RECOVERY} must be deselected for this
* @note @kconfig{CONFIG_CAN_MANUAL_RECOVERY_MODE} must be enabled for this
* function to be available.
*
* @param dev Pointer to the device structure for the driver instance.
* @param timeout Timeout for waiting for the recovery or ``K_FOREVER``.
*
* @retval 0 on success.
* @retval -ENOTSUP if the CAN controller is not in manual recovery mode.
* @retval -ENETDOWN if the CAN controller is in stopped state.
* @retval -EAGAIN on timeout.
* @retval -ENOSYS If this function is not implemented by the driver.
*/
#if !defined(CONFIG_CAN_AUTO_BUS_OFF_RECOVERY) || defined(__DOXYGEN__)
__syscall int can_recover(const struct device *dev, k_timeout_t timeout);
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
static inline int z_impl_can_recover(const struct device *dev, k_timeout_t timeout)
{
const struct can_driver_api *api = (const struct can_driver_api *)dev->api;
if (api->recover == NULL) {
return -ENOSYS;
}
return api->recover(dev, timeout);
}
#else /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
/* This implementation prevents inking errors for auto recovery */
static inline int z_impl_can_recover(const struct device *dev, k_timeout_t timeout)
{
ARG_UNUSED(dev);
ARG_UNUSED(timeout);
return 0;
}
#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
/**
* @brief Set a callback for CAN controller state change events

View file

@ -1653,13 +1653,13 @@ int can_mcan_set_timing(const struct device *dev, const struct can_timing *timin
*/
int can_mcan_set_timing_data(const struct device *dev, const struct can_timing *timing_data);
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
/**
* @brief Bosch M_CAN driver callback API upon recovering the CAN bus
* See @a can_recover() for argument description
*/
int can_mcan_recover(const struct device *dev, k_timeout_t timeout);
#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
int can_mcan_send(const struct device *dev, const struct can_frame *frame, k_timeout_t timeout,
can_tx_callback_t callback, void *user_data);

View file

@ -227,13 +227,13 @@ int can_sja1000_add_rx_filter(const struct device *dev, can_rx_callback_t callba
*/
void can_sja1000_remove_rx_filter(const struct device *dev, int filter_id);
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
#ifdef CONFIG_CAN_MANUAL_RECOVERY_MODE
/**
* @brief SJA1000 callback API upon recovering the CAN bus
* See @a can_recover() for argument description
*/
int can_sja1000_recover(const struct device *dev, k_timeout_t timeout);
#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
#endif /* CONFIG_CAN_MANUAL_RECOVERY_MODE */
/**
* @brief SJA1000 callback API upon getting the CAN controller state

View file

@ -173,16 +173,6 @@ void state_change_work_handler(struct k_work *work)
"tx error count: %d\n",
state_to_str(current_state),
current_err_cnt.rx_err_cnt, current_err_cnt.tx_err_cnt);
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
if (current_state == CAN_STATE_BUS_OFF) {
printf("Recover from bus-off\n");
if (can_recover(can_dev, K_MSEC(100)) != 0) {
printf("Recovery timed out\n");
}
}
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
}
void state_change_callback(const struct device *dev, enum can_state state,

View file

@ -3,4 +3,4 @@ CONFIG_TEST_USERSPACE=y
CONFIG_GPIO=y
CONFIG_CAN=y
CONFIG_CAN_FD_MODE=y
CONFIG_CAN_AUTO_BUS_OFF_RECOVERY=n
CONFIG_CAN_MANUAL_RECOVERY_MODE=y

View file

@ -1,6 +1,6 @@
CONFIG_CAN=y
CONFIG_CAN_FD_MODE=y
CONFIG_CAN_AUTO_BUS_OFF_RECOVERY=n
CONFIG_CAN_MANUAL_RECOVERY_MODE=y
CONFIG_STATS=y
CONFIG_CAN_STATS=y
CONFIG_TEST_USERSPACE=y

View file

@ -853,18 +853,55 @@ ZTEST_USER(can_classic, test_send_fd_format)
/**
* @brief Test CAN controller bus recovery.
*
* It is not possible to provoke a bus off state, but verify the API call return codes.
*/
ZTEST_USER(can_classic, test_recover)
{
can_mode_t cap;
int err;
/* It is not possible to provoke a bus off state, but test the API call */
err = can_recover(can_dev, TEST_RECOVER_TIMEOUT);
if (err == -ENOTSUP) {
ztest_test_skip();
Z_TEST_SKIP_IFNDEF(CONFIG_CAN_MANUAL_RECOVERY_MODE);
err = can_get_capabilities(can_dev, &cap);
zassert_equal(err, 0, "failed to get CAN capabilities (err %d)", err);
if ((cap & CAN_MODE_MANUAL_RECOVERY) != 0U) {
/* Check that manual recovery fails when not in manual recovery mode */
err = can_recover(can_dev, TEST_RECOVER_TIMEOUT);
zassert_equal(err, -ENOTSUP, "wrong error return code (err %d)", err);
err = can_stop(can_dev);
zassert_equal(err, 0, "failed to stop CAN controller (err %d)", err);
/* Enter manual recovery mode */
err = can_set_mode(can_dev, CAN_MODE_NORMAL | CAN_MODE_MANUAL_RECOVERY);
zassert_equal(err, 0, "failed to set manual recovery mode (err %d)", err);
zassert_equal(CAN_MODE_NORMAL | CAN_MODE_MANUAL_RECOVERY, can_get_mode(can_dev));
err = can_start(can_dev);
zassert_equal(err, 0, "failed to start CAN controller (err %d)", err);
}
zassert_equal(err, 0, "failed to recover (err %d)", err);
err = can_recover(can_dev, TEST_RECOVER_TIMEOUT);
if ((cap & CAN_MODE_MANUAL_RECOVERY) != 0U) {
zassert_equal(err, 0, "failed to recover (err %d)", err);
err = can_stop(can_dev);
zassert_equal(err, 0, "failed to stop CAN controller (err %d)", err);
/* Restore loopback mode */
err = can_set_mode(can_dev, CAN_MODE_LOOPBACK);
zassert_equal(err, 0, "failed to set loopback-mode (err %d)", err);
zassert_equal(CAN_MODE_LOOPBACK, can_get_mode(can_dev));
err = can_start(can_dev);
zassert_equal(err, 0, "failed to start CAN controller (err %d)", err);
} else {
/* Check that manual recovery fails when not supported */
zassert_equal(err, -ENOSYS, "wrong error return code (err %d)", err);
}
}
/**
@ -1036,8 +1073,18 @@ ZTEST_USER(can_classic, test_start_while_started)
*/
ZTEST_USER(can_classic, test_recover_while_stopped)
{
can_mode_t cap;
int err;
Z_TEST_SKIP_IFNDEF(CONFIG_CAN_MANUAL_RECOVERY_MODE);
err = can_get_capabilities(can_dev, &cap);
zassert_equal(err, 0, "failed to get CAN capabilities (err %d)", err);
if ((cap & CAN_MODE_MANUAL_RECOVERY) == 0U) {
ztest_test_skip();
}
err = can_stop(can_dev);
zassert_equal(err, 0, "failed to stop CAN controller (err %d)", err);

View file

@ -2,7 +2,7 @@ CONFIG_SHELL=y
CONFIG_SHELL_BACKEND_SERIAL=n
CONFIG_SHELL_BACKEND_DUMMY=y
CONFIG_CAN=y
CONFIG_CAN_AUTO_BUS_OFF_RECOVERY=n
CONFIG_CAN_MANUAL_RECOVERY_MODE=y
CONFIG_CAN_FD_MODE=y
CONFIG_CAN_SHELL=y
CONFIG_ZTEST=y

View file

@ -572,7 +572,7 @@ static void can_shell_test_recover(const char *cmd, k_timeout_t expected)
const struct shell *sh = shell_backend_dummy_get_ptr();
int err;
Z_TEST_SKIP_IFDEF(CONFIG_CAN_AUTO_BUS_OFF_RECOVERY);
Z_TEST_SKIP_IFNDEF(CONFIG_CAN_MANUAL_RECOVERY_MODE);
err = shell_execute_cmd(sh, cmd);
zassert_ok(err, "failed to execute shell command (err %d)", err);