drivers: can: add start and stop CAN controller API functions
Up until now, the Zephyr CAN controller drivers set a default bitrate (or timing) specified via devicetree and start the CAN controller in their respective driver initialization functions. This is fine for CAN nodes using only one fixed bitrate, but if the bitrate is set by the user (e.g. via a DIP-switch or other HMI which is very common), the CAN driver will still initialise with the default bitrate/timing at boot and use this until the application has determined the requested bitrate/timing and set it using can_set_bitrate()/can_set_timing(). During this period, the CAN node will potentially destroy valid CAN frames on the CAN bus (which is using the soon-to-be-set-by-the-application bitrate) by sending error frames. This causes interruptions to the ongoing CAN bus traffic when a Zephyr-based CAN node connected to the bus is (re-)booted. Instead, require all configuration (setting bitrate, timing, or mode) to take place when the CAN controller is stopped. This maps nicely to entering "reset mode" (called "configuration mode" or "freeze mode" for some CAN controller implementations) when stopping and exiting this mode when starting the CAN controller. Fixes: #45304 Signed-off-by: Henrik Brix Andersen <hebad@vestas.com>
This commit is contained in:
parent
6d094d0ad5
commit
180cdc105e
30 changed files with 930 additions and 319 deletions
|
@ -39,6 +39,7 @@ struct can_npl_data {
|
|||
bool mode_fd;
|
||||
int dev_fd; /* Linux socket file descriptor */
|
||||
struct k_thread rx_thread;
|
||||
bool started;
|
||||
|
||||
K_KERNEL_STACK_MEMBER(rx_thread_stack, CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE);
|
||||
};
|
||||
|
@ -102,7 +103,7 @@ static void rx_thread(void *arg1, void *arg2, void *arg3)
|
|||
|
||||
k_sem_give(&data->tx_idle);
|
||||
|
||||
if (!data->loopback) {
|
||||
if (!data->loopback || !data->started) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -158,6 +159,10 @@ static int can_npl_send(const struct device *dev, const struct can_frame *frame,
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
if (!data->started) {
|
||||
return -ENETDOWN;
|
||||
}
|
||||
|
||||
socketcan_from_can_frame(frame, &sframe);
|
||||
|
||||
if (k_sem_take(&data->tx_idle, timeout) != 0) {
|
||||
|
@ -250,6 +255,32 @@ static int can_npl_get_capabilities(const struct device *dev, can_mode_t *cap)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int can_npl_start(const struct device *dev)
|
||||
{
|
||||
struct can_npl_data *data = dev->data;
|
||||
|
||||
if (data->started) {
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
data->started = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_npl_stop(const struct device *dev)
|
||||
{
|
||||
struct can_npl_data *data = dev->data;
|
||||
|
||||
if (!data->started) {
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
data->started = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int can_npl_set_mode(const struct device *dev, can_mode_t mode)
|
||||
{
|
||||
struct can_npl_data *data = dev->data;
|
||||
|
@ -266,6 +297,10 @@ static int can_npl_set_mode(const struct device *dev, can_mode_t mode)
|
|||
}
|
||||
#endif /* CONFIG_CAN_FD_MODE */
|
||||
|
||||
if (data->started) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* loopback is handled internally in rx_thread */
|
||||
data->loopback = (mode & CAN_MODE_LOOPBACK) != 0;
|
||||
|
||||
|
@ -277,18 +312,28 @@ static int can_npl_set_mode(const struct device *dev, can_mode_t mode)
|
|||
|
||||
static int can_npl_set_timing(const struct device *dev, const struct can_timing *timing)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
struct can_npl_data *data = dev->data;
|
||||
|
||||
ARG_UNUSED(timing);
|
||||
|
||||
if (data->started) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CAN_FD_MODE
|
||||
static int can_npl_set_timing_data(const struct device *dev, const struct can_timing *timing)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
struct can_npl_data *data = dev->data;
|
||||
|
||||
ARG_UNUSED(timing);
|
||||
|
||||
if (data->started) {
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_CAN_FD_MODE */
|
||||
|
@ -296,11 +341,15 @@ static int can_npl_set_timing_data(const struct device *dev, const struct can_ti
|
|||
static int can_npl_get_state(const struct device *dev, enum can_state *state,
|
||||
struct can_bus_err_cnt *err_cnt)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
struct can_npl_data *data = dev->data;
|
||||
|
||||
if (state != NULL) {
|
||||
/* SocketCAN does not forward error frames by default */
|
||||
*state = CAN_STATE_ERROR_ACTIVE;
|
||||
if (!data->started) {
|
||||
*state = CAN_STATE_STOPPED;
|
||||
} else {
|
||||
/* SocketCAN does not forward error frames by default */
|
||||
*state = CAN_STATE_ERROR_ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
if (err_cnt) {
|
||||
|
@ -314,9 +363,14 @@ static int can_npl_get_state(const struct device *dev, enum can_state *state,
|
|||
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
|
||||
static int can_npl_recover(const struct device *dev, k_timeout_t timeout)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
struct can_npl_data *data = dev->data;
|
||||
|
||||
ARG_UNUSED(timeout);
|
||||
|
||||
if (!data->started) {
|
||||
return -ENETDOWN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
|
||||
|
@ -346,6 +400,8 @@ static int can_npl_get_max_filters(const struct device *dev, enum can_ide id_typ
|
|||
}
|
||||
|
||||
static const struct can_driver_api can_npl_driver_api = {
|
||||
.start = can_npl_start,
|
||||
.stop = can_npl_stop,
|
||||
.get_capabilities = can_npl_get_capabilities,
|
||||
.set_mode = can_npl_set_mode,
|
||||
.set_timing = can_npl_set_timing,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue