2018-06-12 08:23:20 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018 Karsten Koenig
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
2020-03-25 10:05:28 -05:00
|
|
|
#define DT_DRV_COMPAT microchip_mcp2515
|
|
|
|
|
2018-06-12 08:23:20 +02:00
|
|
|
#include <kernel.h>
|
|
|
|
#include <device.h>
|
2022-03-01 14:45:01 +01:00
|
|
|
#include <drivers/can/transceiver.h>
|
2019-06-25 15:54:01 -04:00
|
|
|
#include <drivers/spi.h>
|
2019-06-25 15:53:52 -04:00
|
|
|
#include <drivers/gpio.h>
|
2018-06-12 08:23:20 +02:00
|
|
|
#include <logging/log.h>
|
2022-02-22 12:06:54 +01:00
|
|
|
|
|
|
|
LOG_MODULE_REGISTER(can_mcp2515, CONFIG_CAN_LOG_LEVEL);
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2019-04-19 18:23:17 +02:00
|
|
|
#include "can_mcp2515.h"
|
2021-05-05 09:31:56 +02:00
|
|
|
#include "can_utils.h"
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2020-11-20 13:39:48 +01:00
|
|
|
#define SP_IS_SET(inst) DT_INST_NODE_HAS_PROP(inst, sample_point) ||
|
|
|
|
|
|
|
|
/* Macro to exclude the sample point algorithm from compilation if not used
|
|
|
|
* Without the macro, the algorithm would always waste ROM
|
|
|
|
*/
|
|
|
|
#define USE_SP_ALGO (DT_INST_FOREACH_STATUS_OKAY(SP_IS_SET) 0)
|
|
|
|
|
|
|
|
#define SP_AND_TIMING_NOT_SET(inst) \
|
|
|
|
(!DT_INST_NODE_HAS_PROP(inst, sample_point) && \
|
|
|
|
!(DT_INST_NODE_HAS_PROP(inst, prop_seg) && \
|
|
|
|
DT_INST_NODE_HAS_PROP(inst, phase_seg1) && \
|
|
|
|
DT_INST_NODE_HAS_PROP(inst, phase_seg2))) ||
|
|
|
|
|
|
|
|
#if DT_INST_FOREACH_STATUS_OKAY(SP_AND_TIMING_NOT_SET) 0
|
|
|
|
#error You must either set a sampling-point or timings (phase-seg* and prop-seg)
|
|
|
|
#endif
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int mcp2515_cmd_soft_reset(const struct device *dev)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
const struct mcp2515_config *dev_cfg = dev->config;
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t cmd_buf[] = { MCP2515_OPCODE_RESET };
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
const struct spi_buf tx_buf = {
|
|
|
|
.buf = cmd_buf, .len = sizeof(cmd_buf),
|
|
|
|
};
|
|
|
|
const struct spi_buf_set tx = {
|
|
|
|
.buffers = &tx_buf, .count = 1U
|
|
|
|
};
|
|
|
|
|
2022-01-18 12:50:32 +01:00
|
|
|
return spi_write_dt(&dev_cfg->bus, &tx);
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int mcp2515_cmd_bit_modify(const struct device *dev, uint8_t reg_addr,
|
|
|
|
uint8_t mask,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t data)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
const struct mcp2515_config *dev_cfg = dev->config;
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t cmd_buf[] = { MCP2515_OPCODE_BIT_MODIFY, reg_addr, mask, data };
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
const struct spi_buf tx_buf = {
|
|
|
|
.buf = cmd_buf, .len = sizeof(cmd_buf),
|
|
|
|
};
|
|
|
|
const struct spi_buf_set tx = {
|
|
|
|
.buffers = &tx_buf, .count = 1U
|
|
|
|
};
|
|
|
|
|
2022-01-18 12:50:32 +01:00
|
|
|
return spi_write_dt(&dev_cfg->bus, &tx);
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int mcp2515_cmd_write_reg(const struct device *dev, uint8_t reg_addr,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t *buf_data, uint8_t buf_len)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
const struct mcp2515_config *dev_cfg = dev->config;
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t cmd_buf[] = { MCP2515_OPCODE_WRITE, reg_addr };
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
struct spi_buf tx_buf[] = {
|
|
|
|
{ .buf = cmd_buf, .len = sizeof(cmd_buf) },
|
|
|
|
{ .buf = buf_data, .len = buf_len }
|
|
|
|
};
|
|
|
|
const struct spi_buf_set tx = {
|
|
|
|
.buffers = tx_buf, .count = ARRAY_SIZE(tx_buf)
|
|
|
|
};
|
|
|
|
|
2022-01-18 12:50:32 +01:00
|
|
|
return spi_write_dt(&dev_cfg->bus, &tx);
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
2019-08-08 00:08:08 +10:00
|
|
|
/*
|
|
|
|
* Load TX buffer instruction
|
|
|
|
*
|
|
|
|
* When loading a transmit buffer, reduces the overhead of a normal WRITE
|
|
|
|
* command by placing the Address Pointer at one of six locations, as
|
|
|
|
* selected by parameter abc.
|
|
|
|
*
|
|
|
|
* 0: TX Buffer 0, Start at TXB0SIDH (0x31)
|
|
|
|
* 1: TX Buffer 0, Start at TXB0D0 (0x36)
|
|
|
|
* 2: TX Buffer 1, Start at TXB1SIDH (0x41)
|
|
|
|
* 3: TX Buffer 1, Start at TXB1D0 (0x46)
|
|
|
|
* 4: TX Buffer 2, Start at TXB2SIDH (0x51)
|
|
|
|
* 5: TX Buffer 2, Start at TXB2D0 (0x56)
|
|
|
|
*/
|
2020-04-30 20:33:38 +02:00
|
|
|
static int mcp2515_cmd_load_tx_buffer(const struct device *dev, uint8_t abc,
|
|
|
|
uint8_t *buf_data, uint8_t buf_len)
|
2019-08-08 00:08:08 +10:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
const struct mcp2515_config *dev_cfg = dev->config;
|
|
|
|
|
2019-08-08 00:08:08 +10:00
|
|
|
__ASSERT(abc <= 5, "abc <= 5");
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t cmd_buf[] = { MCP2515_OPCODE_LOAD_TX_BUFFER | abc };
|
2019-08-08 00:08:08 +10:00
|
|
|
|
|
|
|
struct spi_buf tx_buf[] = {
|
|
|
|
{ .buf = cmd_buf, .len = sizeof(cmd_buf) },
|
|
|
|
{ .buf = buf_data, .len = buf_len }
|
|
|
|
};
|
|
|
|
const struct spi_buf_set tx = {
|
|
|
|
.buffers = tx_buf, .count = ARRAY_SIZE(tx_buf)
|
|
|
|
};
|
|
|
|
|
2022-01-18 12:50:32 +01:00
|
|
|
return spi_write_dt(&dev_cfg->bus, &tx);
|
2019-08-08 00:08:08 +10:00
|
|
|
}
|
|
|
|
|
2019-08-08 00:23:34 +10:00
|
|
|
/*
|
|
|
|
* Request-to-Send Instruction
|
|
|
|
*
|
|
|
|
* Parameter nnn is the combination of bits at positions 0, 1 and 2 in the RTS
|
|
|
|
* opcode that respectively initiate transmission for buffers TXB0, TXB1 and
|
|
|
|
* TXB2.
|
|
|
|
*/
|
2020-04-30 20:33:38 +02:00
|
|
|
static int mcp2515_cmd_rts(const struct device *dev, uint8_t nnn)
|
2019-08-08 00:23:34 +10:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
const struct mcp2515_config *dev_cfg = dev->config;
|
|
|
|
|
2019-08-08 00:23:34 +10:00
|
|
|
__ASSERT(nnn < BIT(MCP2515_TX_CNT), "nnn < BIT(MCP2515_TX_CNT)");
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t cmd_buf[] = { MCP2515_OPCODE_RTS | nnn };
|
2019-08-08 00:23:34 +10:00
|
|
|
|
|
|
|
struct spi_buf tx_buf[] = {
|
|
|
|
{ .buf = cmd_buf, .len = sizeof(cmd_buf) }
|
|
|
|
};
|
|
|
|
const struct spi_buf_set tx = {
|
|
|
|
.buffers = tx_buf, .count = ARRAY_SIZE(tx_buf)
|
|
|
|
};
|
|
|
|
|
2022-01-18 12:50:32 +01:00
|
|
|
return spi_write_dt(&dev_cfg->bus, &tx);
|
2019-08-08 00:23:34 +10:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int mcp2515_cmd_read_reg(const struct device *dev, uint8_t reg_addr,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t *buf_data, uint8_t buf_len)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
const struct mcp2515_config *dev_cfg = dev->config;
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t cmd_buf[] = { MCP2515_OPCODE_READ, reg_addr };
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
struct spi_buf tx_buf[] = {
|
|
|
|
{ .buf = cmd_buf, .len = sizeof(cmd_buf) },
|
|
|
|
{ .buf = NULL, .len = buf_len }
|
|
|
|
};
|
|
|
|
const struct spi_buf_set tx = {
|
|
|
|
.buffers = tx_buf, .count = ARRAY_SIZE(tx_buf)
|
|
|
|
};
|
|
|
|
struct spi_buf rx_buf[] = {
|
|
|
|
{ .buf = NULL, .len = sizeof(cmd_buf) },
|
|
|
|
{ .buf = buf_data, .len = buf_len }
|
|
|
|
};
|
|
|
|
const struct spi_buf_set rx = {
|
|
|
|
.buffers = rx_buf, .count = ARRAY_SIZE(rx_buf)
|
|
|
|
};
|
|
|
|
|
2022-01-18 12:50:32 +01:00
|
|
|
return spi_transceive_dt(&dev_cfg->bus, &tx, &rx);
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
2019-07-21 14:59:53 +10:00
|
|
|
/*
|
|
|
|
* Read RX Buffer instruction
|
|
|
|
*
|
|
|
|
* When reading a receive buffer, reduces the overhead of a normal READ
|
|
|
|
* command by placing the Address Pointer at one of four locations selected by
|
|
|
|
* parameter nm:
|
|
|
|
* 0: Receive Buffer 0, Start at RXB0SIDH (0x61)
|
|
|
|
* 1: Receive Buffer 0, Start at RXB0D0 (0x66)
|
|
|
|
* 2: Receive Buffer 1, Start at RXB1SIDH (0x71)
|
|
|
|
* 3: Receive Buffer 1, Start at RXB1D0 (0x76)
|
|
|
|
*/
|
2020-04-30 20:33:38 +02:00
|
|
|
static int mcp2515_cmd_read_rx_buffer(const struct device *dev, uint8_t nm,
|
|
|
|
uint8_t *buf_data, uint8_t buf_len)
|
2019-07-21 14:59:53 +10:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
const struct mcp2515_config *dev_cfg = dev->config;
|
|
|
|
|
2019-07-21 14:59:53 +10:00
|
|
|
__ASSERT(nm <= 0x03, "nm <= 0x03");
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t cmd_buf[] = { MCP2515_OPCODE_READ_RX_BUFFER | (nm << 1) };
|
2019-07-21 14:59:53 +10:00
|
|
|
|
|
|
|
struct spi_buf tx_buf[] = {
|
|
|
|
{ .buf = cmd_buf, .len = sizeof(cmd_buf) },
|
|
|
|
{ .buf = NULL, .len = buf_len }
|
|
|
|
};
|
|
|
|
const struct spi_buf_set tx = {
|
|
|
|
.buffers = tx_buf, .count = ARRAY_SIZE(tx_buf)
|
|
|
|
};
|
|
|
|
struct spi_buf rx_buf[] = {
|
|
|
|
{ .buf = NULL, .len = sizeof(cmd_buf) },
|
|
|
|
{ .buf = buf_data, .len = buf_len }
|
|
|
|
};
|
|
|
|
const struct spi_buf_set rx = {
|
|
|
|
.buffers = rx_buf, .count = ARRAY_SIZE(rx_buf)
|
|
|
|
};
|
|
|
|
|
2022-01-18 12:50:32 +01:00
|
|
|
return spi_transceive_dt(&dev_cfg->bus, &tx, &rx);
|
2019-07-21 14:59:53 +10:00
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static uint8_t mcp2515_convert_canmode_to_mcp2515mode(enum can_mode mode)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
|
|
|
switch (mode) {
|
|
|
|
case CAN_NORMAL_MODE:
|
|
|
|
return MCP2515_MODE_NORMAL;
|
|
|
|
case CAN_SILENT_MODE:
|
|
|
|
return MCP2515_MODE_SILENT;
|
|
|
|
case CAN_LOOPBACK_MODE:
|
|
|
|
return MCP2515_MODE_LOOPBACK;
|
|
|
|
default:
|
|
|
|
LOG_ERR("Unsupported CAN Mode %u", mode);
|
|
|
|
return MCP2515_MODE_SILENT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mcp2515_convert_zcanframe_to_mcp2515frame(const struct zcan_frame
|
2020-05-27 11:26:57 -05:00
|
|
|
*source, uint8_t *target)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t rtr;
|
|
|
|
uint8_t dlc;
|
|
|
|
uint8_t data_idx = 0U;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
if (source->id_type == CAN_STANDARD_IDENTIFIER) {
|
2020-11-19 20:46:56 +01:00
|
|
|
target[MCP2515_FRAME_OFFSET_SIDH] = source->id >> 3;
|
2018-06-12 08:23:20 +02:00
|
|
|
target[MCP2515_FRAME_OFFSET_SIDL] =
|
2020-11-19 20:46:56 +01:00
|
|
|
(source->id & 0x07) << 5;
|
2018-06-12 08:23:20 +02:00
|
|
|
} else {
|
2020-11-19 20:46:56 +01:00
|
|
|
target[MCP2515_FRAME_OFFSET_SIDH] = source->id >> 21;
|
2018-06-12 08:23:20 +02:00
|
|
|
target[MCP2515_FRAME_OFFSET_SIDL] =
|
2020-11-19 20:46:56 +01:00
|
|
|
(((source->id >> 18) & 0x07) << 5) | (BIT(3)) |
|
|
|
|
((source->id >> 16) & 0x03);
|
|
|
|
target[MCP2515_FRAME_OFFSET_EID8] = source->id >> 8;
|
|
|
|
target[MCP2515_FRAME_OFFSET_EID0] = source->id;
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
rtr = (source->rtr == CAN_REMOTEREQUEST) ? BIT(6) : 0;
|
|
|
|
dlc = (source->dlc) & 0x0F;
|
|
|
|
|
|
|
|
target[MCP2515_FRAME_OFFSET_DLC] = rtr | dlc;
|
|
|
|
|
2019-10-24 22:15:17 +11:00
|
|
|
for (; data_idx < CAN_MAX_DLC; data_idx++) {
|
2018-06-12 08:23:20 +02:00
|
|
|
target[MCP2515_FRAME_OFFSET_D0 + data_idx] =
|
|
|
|
source->data[data_idx];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static void mcp2515_convert_mcp2515frame_to_zcanframe(const uint8_t *source,
|
2019-03-31 13:06:01 +02:00
|
|
|
struct zcan_frame *target)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t data_idx = 0U;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
if (source[MCP2515_FRAME_OFFSET_SIDL] & BIT(3)) {
|
|
|
|
target->id_type = CAN_EXTENDED_IDENTIFIER;
|
2020-11-19 20:46:56 +01:00
|
|
|
target->id =
|
2018-06-12 08:23:20 +02:00
|
|
|
(source[MCP2515_FRAME_OFFSET_SIDH] << 21) |
|
|
|
|
((source[MCP2515_FRAME_OFFSET_SIDL] >> 5) << 18) |
|
|
|
|
((source[MCP2515_FRAME_OFFSET_SIDL] & 0x03) << 16) |
|
|
|
|
(source[MCP2515_FRAME_OFFSET_EID8] << 8) |
|
|
|
|
source[MCP2515_FRAME_OFFSET_EID0];
|
|
|
|
} else {
|
|
|
|
target->id_type = CAN_STANDARD_IDENTIFIER;
|
2020-11-19 20:46:56 +01:00
|
|
|
target->id = (source[MCP2515_FRAME_OFFSET_SIDH] << 3) |
|
2018-06-12 08:23:20 +02:00
|
|
|
(source[MCP2515_FRAME_OFFSET_SIDL] >> 5);
|
|
|
|
}
|
|
|
|
|
|
|
|
target->dlc = source[MCP2515_FRAME_OFFSET_DLC] & 0x0F;
|
|
|
|
target->rtr = source[MCP2515_FRAME_OFFSET_DLC] & BIT(6) ?
|
|
|
|
CAN_REMOTEREQUEST : CAN_DATAFRAME;
|
|
|
|
|
2019-10-24 22:15:17 +11:00
|
|
|
for (; data_idx < CAN_MAX_DLC; data_idx++) {
|
2018-06-12 08:23:20 +02:00
|
|
|
target->data[data_idx] = source[MCP2515_FRAME_OFFSET_D0 +
|
|
|
|
data_idx];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-27 18:58:05 +02:00
|
|
|
const int mcp2515_set_mode_int(const struct device *dev, uint8_t mcp2515_mode)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t canstat;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
mcp2515_cmd_bit_modify(dev, MCP2515_ADDR_CANCTRL,
|
|
|
|
MCP2515_CANCTRL_MODE_MASK,
|
|
|
|
mcp2515_mode << MCP2515_CANCTRL_MODE_POS);
|
|
|
|
mcp2515_cmd_read_reg(dev, MCP2515_ADDR_CANSTAT, &canstat, 1);
|
|
|
|
|
|
|
|
if (((canstat & MCP2515_CANSTAT_MODE_MASK) >> MCP2515_CANSTAT_MODE_POS)
|
|
|
|
!= mcp2515_mode) {
|
|
|
|
LOG_ERR("Failed to set MCP2515 operation mode");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int mcp2515_get_mode(const struct device *dev, uint8_t *mode)
|
2020-08-16 17:38:16 +12:00
|
|
|
{
|
|
|
|
uint8_t canstat;
|
|
|
|
|
|
|
|
if (mode == NULL) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mcp2515_cmd_read_reg(dev, MCP2515_ADDR_CANSTAT, &canstat, 1)) {
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
*mode = (canstat & MCP2515_CANSTAT_MODE_MASK)
|
|
|
|
>> MCP2515_CANSTAT_MODE_POS;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-27 18:58:05 +02:00
|
|
|
static int mcp2515_get_core_clock(const struct device *dev, uint32_t *rate)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
const struct mcp2515_config *dev_cfg = dev->config;
|
2020-04-27 18:58:05 +02:00
|
|
|
|
|
|
|
*rate = dev_cfg->osc_freq / 2;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-03-30 15:09:31 +02:00
|
|
|
static int mcp2515_get_max_filters(const struct device *dev, enum can_ide id_type)
|
2021-10-12 11:46:44 +02:00
|
|
|
{
|
|
|
|
ARG_UNUSED(id_type);
|
|
|
|
|
|
|
|
return CONFIG_CAN_MAX_FILTER;
|
|
|
|
}
|
2020-04-27 18:58:05 +02:00
|
|
|
|
2022-03-30 15:09:31 +02:00
|
|
|
static int mcp2515_get_max_bitrate(const struct device *dev, uint32_t *max_bitrate)
|
2022-03-01 14:45:01 +01:00
|
|
|
{
|
|
|
|
const struct mcp2515_config *dev_cfg = dev->config;
|
|
|
|
|
|
|
|
*max_bitrate = dev_cfg->max_bitrate;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-27 18:58:05 +02:00
|
|
|
static int mcp2515_set_timing(const struct device *dev,
|
|
|
|
const struct can_timing *timing,
|
|
|
|
const struct can_timing *timing_data)
|
|
|
|
{
|
|
|
|
ARG_UNUSED(timing_data);
|
2022-01-18 12:50:32 +01:00
|
|
|
struct mcp2515_data *dev_data = dev->data;
|
2018-06-19 18:26:31 +02:00
|
|
|
int ret;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2020-04-27 18:58:05 +02:00
|
|
|
if (!timing) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-06-12 08:23:20 +02:00
|
|
|
/* CNF3, CNF2, CNF1, CANINTE */
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t config_buf[4];
|
2020-08-16 17:38:16 +12:00
|
|
|
uint8_t reset_mode;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
/* CNF1; SJW<7:6> | BRP<5:0> */
|
2021-04-09 12:55:11 +03:00
|
|
|
__ASSERT(timing->prescaler > 0, "Prescaler should be bigger than zero");
|
|
|
|
uint8_t brp = timing->prescaler - 1;
|
2021-04-25 17:15:56 +02:00
|
|
|
if (timing->sjw != CAN_SJW_NO_CHANGE) {
|
|
|
|
dev_data->sjw = (timing->sjw - 1) << 6;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t cnf1 = dev_data->sjw | brp;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
/* CNF2; BTLMODE<7>|SAM<6>|PHSEG1<5:3>|PRSEG<2:0> */
|
2020-05-27 11:26:57 -05:00
|
|
|
const uint8_t btlmode = 1 << 7;
|
|
|
|
const uint8_t sam = 0 << 6;
|
2020-04-27 18:58:05 +02:00
|
|
|
const uint8_t phseg1 = (timing->phase_seg1 - 1) << 3;
|
|
|
|
const uint8_t prseg = (timing->prop_seg - 1);
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
const uint8_t cnf2 = btlmode | sam | phseg1 | prseg;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
/* CNF3; SOF<7>|WAKFIL<6>|UND<5:3>|PHSEG2<2:0> */
|
2020-05-27 11:26:57 -05:00
|
|
|
const uint8_t sof = 0 << 7;
|
|
|
|
const uint8_t wakfil = 0 << 6;
|
|
|
|
const uint8_t und = 0 << 3;
|
2020-04-27 18:58:05 +02:00
|
|
|
const uint8_t phseg2 = (timing->phase_seg2 - 1);
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
const uint8_t cnf3 = sof | wakfil | und | phseg2;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
const uint8_t caninte = MCP2515_INTE_RX0IE | MCP2515_INTE_RX1IE |
|
2018-06-19 18:26:31 +02:00
|
|
|
MCP2515_INTE_TX0IE | MCP2515_INTE_TX1IE |
|
|
|
|
MCP2515_INTE_TX2IE | MCP2515_INTE_ERRIE;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
/* Receive everything, filtering done in driver, RXB0 roll over into
|
|
|
|
* RXB1 */
|
2020-05-27 11:26:57 -05:00
|
|
|
const uint8_t rx0_ctrl = BIT(6) | BIT(5) | BIT(2);
|
|
|
|
const uint8_t rx1_ctrl = BIT(6) | BIT(5);
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2021-04-25 17:15:56 +02:00
|
|
|
__ASSERT(timing->sjw <= 4, "1 <= SJW <= 4");
|
2020-04-27 18:58:05 +02:00
|
|
|
__ASSERT((timing->prop_seg >= 1) && (timing->prop_seg <= 8),
|
2019-03-31 13:06:01 +02:00
|
|
|
"1 <= PROP <= 8");
|
2020-04-27 18:58:05 +02:00
|
|
|
__ASSERT((timing->phase_seg1 >= 1) && (timing->phase_seg1 <= 8),
|
2019-03-31 13:06:01 +02:00
|
|
|
"1 <= BS1 <= 8");
|
2020-04-27 18:58:05 +02:00
|
|
|
__ASSERT((timing->phase_seg2 >= 2) && (timing->phase_seg2 <= 8),
|
2019-03-31 13:06:01 +02:00
|
|
|
"2 <= BS2 <= 8");
|
2020-04-27 18:58:05 +02:00
|
|
|
__ASSERT(timing->prop_seg + timing->phase_seg1 >= timing->phase_seg2,
|
2018-06-12 08:23:20 +02:00
|
|
|
"PROP + BS1 >= BS2");
|
2020-04-27 18:58:05 +02:00
|
|
|
__ASSERT(timing->phase_seg2 > timing->sjw, "BS2 > SJW");
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
config_buf[0] = cnf3;
|
|
|
|
config_buf[1] = cnf2;
|
|
|
|
config_buf[2] = cnf1;
|
|
|
|
config_buf[3] = caninte;
|
|
|
|
|
2018-06-19 18:26:31 +02:00
|
|
|
k_mutex_lock(&dev_data->mutex, K_FOREVER);
|
2020-08-16 17:38:16 +12:00
|
|
|
|
2020-04-27 18:58:05 +02:00
|
|
|
k_usleep(MCP2515_OSC_STARTUP_US);
|
2020-08-16 17:38:16 +12:00
|
|
|
|
2018-06-12 08:23:20 +02:00
|
|
|
/* will enter configuration mode automatically */
|
2018-06-19 18:26:31 +02:00
|
|
|
ret = mcp2515_cmd_soft_reset(dev);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to reset the device [%d]", ret);
|
|
|
|
goto done;
|
|
|
|
}
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2020-04-27 18:58:05 +02:00
|
|
|
k_usleep(MCP2515_OSC_STARTUP_US);
|
2020-08-16 17:38:16 +12:00
|
|
|
|
|
|
|
ret = mcp2515_get_mode(dev, &reset_mode);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to read device mode [%d]",
|
|
|
|
ret);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reset_mode != MCP2515_MODE_CONFIGURATION) {
|
|
|
|
LOG_ERR("Device did not reset into configuration mode [%d]",
|
|
|
|
reset_mode);
|
|
|
|
ret = -EIO;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2018-06-19 18:26:31 +02:00
|
|
|
ret = mcp2515_cmd_write_reg(dev, MCP2515_ADDR_CNF3, config_buf,
|
|
|
|
sizeof(config_buf));
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to write the configuration [%d]", ret);
|
|
|
|
}
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2018-06-19 18:26:31 +02:00
|
|
|
ret = mcp2515_cmd_bit_modify(dev, MCP2515_ADDR_RXB0CTRL, rx0_ctrl,
|
|
|
|
rx0_ctrl);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to write RXB0CTRL [%d]", ret);
|
|
|
|
}
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2018-06-19 18:26:31 +02:00
|
|
|
ret = mcp2515_cmd_bit_modify(dev, MCP2515_ADDR_RXB1CTRL, rx1_ctrl,
|
|
|
|
rx1_ctrl);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to write RXB1CTRL [%d]", ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2020-04-27 18:58:05 +02:00
|
|
|
k_mutex_unlock(&dev_data->mutex);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mcp2515_set_mode(const struct device *dev, enum can_mode mode)
|
|
|
|
{
|
2022-03-01 14:45:01 +01:00
|
|
|
const struct mcp2515_config *dev_cfg = dev->config;
|
2022-01-18 12:50:32 +01:00
|
|
|
struct mcp2515_data *dev_data = dev->data;
|
2020-04-27 18:58:05 +02:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
k_mutex_lock(&dev_data->mutex, K_FOREVER);
|
2022-03-01 14:45:01 +01:00
|
|
|
|
|
|
|
if (dev_cfg->phy != NULL) {
|
|
|
|
ret = can_transceiver_enable(dev_cfg->phy);
|
|
|
|
if (ret != 0) {
|
|
|
|
LOG_ERR("failed to enable CAN transceiver (err %d)", ret);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-27 18:58:05 +02:00
|
|
|
k_usleep(MCP2515_OSC_STARTUP_US);
|
|
|
|
|
|
|
|
ret = mcp2515_set_mode_int(dev,
|
|
|
|
mcp2515_convert_canmode_to_mcp2515mode(mode));
|
2018-06-19 18:26:31 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to set the mode [%d]", ret);
|
2022-03-01 14:45:01 +01:00
|
|
|
|
|
|
|
if (dev_cfg->phy != NULL) {
|
|
|
|
/* Attempt to disable the CAN transceiver in case of error */
|
|
|
|
(void)can_transceiver_disable(dev_cfg->phy);
|
|
|
|
}
|
2018-06-19 18:26:31 +02:00
|
|
|
}
|
|
|
|
|
2022-03-01 14:45:01 +01:00
|
|
|
done:
|
2018-06-19 18:26:31 +02:00
|
|
|
k_mutex_unlock(&dev_data->mutex);
|
|
|
|
return ret;
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int mcp2515_send(const struct device *dev,
|
2021-12-04 15:21:32 +01:00
|
|
|
const struct zcan_frame *frame,
|
2020-04-30 20:33:38 +02:00
|
|
|
k_timeout_t timeout, can_tx_callback_t callback,
|
2021-12-04 15:21:32 +01:00
|
|
|
void *user_data)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
struct mcp2515_data *dev_data = dev->data;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t tx_idx = 0U;
|
|
|
|
uint8_t abc;
|
|
|
|
uint8_t nnn;
|
|
|
|
uint8_t len;
|
|
|
|
uint8_t tx_frame[MCP2515_FRAME_LEN];
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2021-12-04 15:21:32 +01:00
|
|
|
if (frame->dlc > CAN_MAX_DLC) {
|
2020-05-04 15:11:41 +02:00
|
|
|
LOG_ERR("DLC of %d exceeds maximum (%d)",
|
2021-12-04 15:21:32 +01:00
|
|
|
frame->dlc, CAN_MAX_DLC);
|
2021-12-08 00:01:57 +01:00
|
|
|
return -EINVAL;
|
2019-11-06 10:03:20 +01:00
|
|
|
}
|
|
|
|
|
2018-06-12 08:23:20 +02:00
|
|
|
if (k_sem_take(&dev_data->tx_sem, timeout) != 0) {
|
2021-12-08 00:01:57 +01:00
|
|
|
return -EAGAIN;
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
2018-06-19 18:26:31 +02:00
|
|
|
k_mutex_lock(&dev_data->mutex, K_FOREVER);
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
/* find a free tx slot */
|
|
|
|
for (; tx_idx < MCP2515_TX_CNT; tx_idx++) {
|
|
|
|
if ((BIT(tx_idx) & dev_data->tx_busy_map) == 0) {
|
|
|
|
dev_data->tx_busy_map |= BIT(tx_idx);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-19 18:26:31 +02:00
|
|
|
k_mutex_unlock(&dev_data->mutex);
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
if (tx_idx == MCP2515_TX_CNT) {
|
|
|
|
LOG_WRN("no free tx slot available");
|
2021-12-08 00:01:57 +01:00
|
|
|
return -EIO;
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
dev_data->tx_cb[tx_idx].cb = callback;
|
2021-12-04 15:21:32 +01:00
|
|
|
dev_data->tx_cb[tx_idx].cb_arg = user_data;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2021-12-04 15:21:32 +01:00
|
|
|
mcp2515_convert_zcanframe_to_mcp2515frame(frame, tx_frame);
|
2019-08-08 00:08:08 +10:00
|
|
|
|
|
|
|
/* Address Pointer selection */
|
|
|
|
abc = 2 * tx_idx;
|
|
|
|
|
2019-10-24 22:15:17 +11:00
|
|
|
/* Calculate minimum length to transfer */
|
2021-12-04 15:21:32 +01:00
|
|
|
len = sizeof(tx_frame) - CAN_MAX_DLC + frame->dlc;
|
2019-10-24 22:15:17 +11:00
|
|
|
|
|
|
|
mcp2515_cmd_load_tx_buffer(dev, abc, tx_frame, len);
|
2019-08-08 00:08:08 +10:00
|
|
|
|
2018-06-12 08:23:20 +02:00
|
|
|
/* request tx slot transmission */
|
2019-08-08 00:23:34 +10:00
|
|
|
nnn = BIT(tx_idx);
|
|
|
|
mcp2515_cmd_rts(dev, nnn);
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
if (callback == NULL) {
|
|
|
|
k_sem_take(&dev_data->tx_cb[tx_idx].sem, K_FOREVER);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-12-28 20:00:34 +01:00
|
|
|
static int mcp2515_add_rx_filter(const struct device *dev,
|
|
|
|
can_rx_callback_t rx_cb,
|
|
|
|
void *cb_arg,
|
|
|
|
const struct zcan_filter *filter)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
struct mcp2515_data *dev_data = dev->data;
|
2021-12-28 20:00:34 +01:00
|
|
|
int filter_id = 0;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2019-02-11 14:40:28 +01:00
|
|
|
__ASSERT(rx_cb != NULL, "response_ptr can not be null");
|
|
|
|
|
2018-06-19 18:26:31 +02:00
|
|
|
k_mutex_lock(&dev_data->mutex, K_FOREVER);
|
2019-02-11 14:40:28 +01:00
|
|
|
|
|
|
|
/* find free filter */
|
2021-12-28 20:00:34 +01:00
|
|
|
while ((BIT(filter_id) & dev_data->filter_usage)
|
|
|
|
&& (filter_id < CONFIG_CAN_MAX_FILTER)) {
|
|
|
|
filter_id++;
|
2019-02-11 14:40:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* setup filter */
|
2021-12-28 20:00:34 +01:00
|
|
|
if (filter_id < CONFIG_CAN_MAX_FILTER) {
|
|
|
|
dev_data->filter_usage |= BIT(filter_id);
|
2019-02-11 14:40:28 +01:00
|
|
|
|
2021-12-28 20:00:34 +01:00
|
|
|
dev_data->filter[filter_id] = *filter;
|
|
|
|
dev_data->rx_cb[filter_id] = rx_cb;
|
|
|
|
dev_data->cb_arg[filter_id] = cb_arg;
|
2019-02-11 14:40:28 +01:00
|
|
|
|
|
|
|
} else {
|
2021-12-28 20:00:34 +01:00
|
|
|
filter_id = -ENOSPC;
|
2019-02-11 14:40:28 +01:00
|
|
|
}
|
|
|
|
|
2018-06-19 18:26:31 +02:00
|
|
|
k_mutex_unlock(&dev_data->mutex);
|
2019-02-11 14:40:28 +01:00
|
|
|
|
2021-12-28 20:00:34 +01:00
|
|
|
return filter_id;
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
2021-12-28 20:00:34 +01:00
|
|
|
static void mcp2515_remove_rx_filter(const struct device *dev, int filter_id)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
struct mcp2515_data *dev_data = dev->data;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2018-06-19 18:26:31 +02:00
|
|
|
k_mutex_lock(&dev_data->mutex, K_FOREVER);
|
2021-12-28 20:00:34 +01:00
|
|
|
dev_data->filter_usage &= ~BIT(filter_id);
|
2018-06-19 18:26:31 +02:00
|
|
|
k_mutex_unlock(&dev_data->mutex);
|
|
|
|
}
|
|
|
|
|
2021-12-28 20:00:34 +01:00
|
|
|
static void mcp2515_set_state_change_callback(const struct device *dev,
|
2022-01-10 12:32:19 +01:00
|
|
|
can_state_change_callback_t cb,
|
|
|
|
void *user_data)
|
2018-06-19 18:26:31 +02:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
struct mcp2515_data *dev_data = dev->data;
|
2018-06-19 18:26:31 +02:00
|
|
|
|
2021-12-28 20:00:34 +01:00
|
|
|
dev_data->state_change_cb = cb;
|
2022-01-10 12:32:19 +01:00
|
|
|
dev_data->state_change_cb_data = user_data;
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void mcp2515_rx_filter(const struct device *dev,
|
2021-12-04 15:21:32 +01:00
|
|
|
struct zcan_frame *frame)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
struct mcp2515_data *dev_data = dev->data;
|
2021-12-28 20:00:34 +01:00
|
|
|
uint8_t filter_id = 0U;
|
2019-02-11 14:40:28 +01:00
|
|
|
can_rx_callback_t callback;
|
2021-12-04 15:21:32 +01:00
|
|
|
struct zcan_frame tmp_frame;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2018-06-19 18:26:31 +02:00
|
|
|
k_mutex_lock(&dev_data->mutex, K_FOREVER);
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2021-12-28 20:00:34 +01:00
|
|
|
for (; filter_id < CONFIG_CAN_MAX_FILTER; filter_id++) {
|
|
|
|
if (!(BIT(filter_id) & dev_data->filter_usage)) {
|
2018-06-12 08:23:20 +02:00
|
|
|
continue; /* filter slot empty */
|
|
|
|
}
|
|
|
|
|
2021-12-04 15:21:32 +01:00
|
|
|
if (!can_utils_filter_match(frame,
|
2021-12-28 20:00:34 +01:00
|
|
|
&dev_data->filter[filter_id])) {
|
2018-06-12 08:23:20 +02:00
|
|
|
continue; /* filter did not match */
|
|
|
|
}
|
|
|
|
|
2021-12-28 20:00:34 +01:00
|
|
|
callback = dev_data->rx_cb[filter_id];
|
2019-02-11 14:40:28 +01:00
|
|
|
/*Make a temporary copy in case the user modifies the message*/
|
2021-12-04 15:21:32 +01:00
|
|
|
tmp_frame = *frame;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2022-03-17 22:06:51 +01:00
|
|
|
callback(dev, &tmp_frame, dev_data->cb_arg[filter_id]);
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
2018-06-19 18:26:31 +02:00
|
|
|
k_mutex_unlock(&dev_data->mutex);
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void mcp2515_rx(const struct device *dev, uint8_t rx_idx)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2019-07-21 14:59:53 +10:00
|
|
|
__ASSERT(rx_idx < MCP2515_RX_CNT, "rx_idx < MCP2515_RX_CNT");
|
|
|
|
|
2021-12-04 15:21:32 +01:00
|
|
|
struct zcan_frame frame;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t rx_frame[MCP2515_FRAME_LEN];
|
|
|
|
uint8_t nm;
|
2019-07-21 14:59:53 +10:00
|
|
|
|
|
|
|
/* Address Pointer selection */
|
|
|
|
nm = 2 * rx_idx;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
/* Fetch rx buffer */
|
2019-07-21 14:59:53 +10:00
|
|
|
mcp2515_cmd_read_rx_buffer(dev, nm, rx_frame, sizeof(rx_frame));
|
2021-12-04 15:21:32 +01:00
|
|
|
mcp2515_convert_mcp2515frame_to_zcanframe(rx_frame, &frame);
|
|
|
|
mcp2515_rx_filter(dev, &frame);
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void mcp2515_tx_done(const struct device *dev, uint8_t tx_idx)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
struct mcp2515_data *dev_data = dev->data;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
if (dev_data->tx_cb[tx_idx].cb == NULL) {
|
|
|
|
k_sem_give(&dev_data->tx_cb[tx_idx].sem);
|
|
|
|
} else {
|
2022-03-17 22:06:51 +01:00
|
|
|
dev_data->tx_cb[tx_idx].cb(dev, 0, dev_data->tx_cb[tx_idx].cb_arg);
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
2018-06-19 18:26:31 +02:00
|
|
|
k_mutex_lock(&dev_data->mutex, K_FOREVER);
|
2018-06-12 08:23:20 +02:00
|
|
|
dev_data->tx_busy_map &= ~BIT(tx_idx);
|
2018-06-19 18:26:31 +02:00
|
|
|
k_mutex_unlock(&dev_data->mutex);
|
2018-06-12 08:23:20 +02:00
|
|
|
k_sem_give(&dev_data->tx_sem);
|
|
|
|
}
|
|
|
|
|
2022-01-19 10:21:01 +01:00
|
|
|
static int mcp2515_get_state(const struct device *dev, enum can_state *state,
|
|
|
|
struct can_bus_err_cnt *err_cnt)
|
2018-06-19 18:26:31 +02:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t eflg;
|
|
|
|
uint8_t err_cnt_buf[2];
|
2018-06-19 18:26:31 +02:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = mcp2515_cmd_read_reg(dev, MCP2515_ADDR_EFLG, &eflg, sizeof(eflg));
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to read error register [%d]", ret);
|
2022-01-19 10:21:01 +01:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state != NULL) {
|
|
|
|
if (eflg & MCP2515_EFLG_TXBO) {
|
|
|
|
*state = CAN_BUS_OFF;
|
|
|
|
} else if ((eflg & MCP2515_EFLG_RXEP) || (eflg & MCP2515_EFLG_TXEP)) {
|
|
|
|
*state = CAN_ERROR_PASSIVE;
|
|
|
|
} else if (eflg & MCP2515_EFLG_EWARN) {
|
|
|
|
*state = CAN_ERROR_WARNING;
|
|
|
|
} else {
|
|
|
|
*state = CAN_ERROR_ACTIVE;
|
|
|
|
}
|
2018-06-19 18:26:31 +02:00
|
|
|
}
|
|
|
|
|
2022-01-19 10:21:01 +01:00
|
|
|
if (err_cnt != NULL) {
|
2018-06-19 18:26:31 +02:00
|
|
|
ret = mcp2515_cmd_read_reg(dev, MCP2515_ADDR_TEC, err_cnt_buf,
|
|
|
|
sizeof(err_cnt_buf));
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to read error counters [%d]", ret);
|
2022-01-19 10:21:01 +01:00
|
|
|
return -EIO;
|
2018-06-19 18:26:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
err_cnt->tx_err_cnt = err_cnt_buf[0];
|
|
|
|
err_cnt->rx_err_cnt = err_cnt_buf[1];
|
|
|
|
}
|
|
|
|
|
2022-01-19 10:21:01 +01:00
|
|
|
return 0;
|
2018-06-19 18:26:31 +02:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void mcp2515_handle_errors(const struct device *dev)
|
2018-06-19 18:26:31 +02:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
struct mcp2515_data *dev_data = dev->data;
|
2021-12-28 20:00:34 +01:00
|
|
|
can_state_change_callback_t state_change_cb = dev_data->state_change_cb;
|
2022-01-10 12:32:19 +01:00
|
|
|
void *state_change_cb_data = dev_data->state_change_cb_data;
|
2018-06-19 18:26:31 +02:00
|
|
|
enum can_state state;
|
|
|
|
struct can_bus_err_cnt err_cnt;
|
2022-01-19 10:21:01 +01:00
|
|
|
int err;
|
2018-06-19 18:26:31 +02:00
|
|
|
|
2022-01-19 10:21:01 +01:00
|
|
|
err = mcp2515_get_state(dev, &state, state_change_cb ? &err_cnt : NULL);
|
|
|
|
if (err != 0) {
|
|
|
|
LOG_ERR("Failed to get CAN controller state [%d]", err);
|
|
|
|
return;
|
|
|
|
}
|
2018-06-19 18:26:31 +02:00
|
|
|
|
2021-12-28 20:00:34 +01:00
|
|
|
if (state_change_cb && dev_data->old_state != state) {
|
2018-06-19 18:26:31 +02:00
|
|
|
dev_data->old_state = state;
|
2022-03-17 22:06:51 +01:00
|
|
|
state_change_cb(dev, state, err_cnt, state_change_cb_data);
|
2018-06-19 18:26:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
|
2022-03-30 11:32:38 +02:00
|
|
|
static int mcp2515_recover(const struct device *dev, k_timeout_t timeout)
|
2018-06-19 18:26:31 +02:00
|
|
|
{
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
ARG_UNUSED(timeout);
|
2022-03-30 11:32:38 +02:00
|
|
|
|
|
|
|
return -ENOTSUP;
|
2018-06-19 18:26:31 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void mcp2515_handle_interrupts(const struct device *dev)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
const struct mcp2515_config *dev_cfg = dev->config;
|
2019-07-21 22:41:16 +10:00
|
|
|
int ret;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t canintf;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2019-11-07 20:16:06 +01:00
|
|
|
/* Loop until INT pin is inactive (all interrupt flags handled) */
|
2019-07-21 22:41:16 +10:00
|
|
|
while (1) {
|
|
|
|
ret = mcp2515_cmd_read_reg(dev, MCP2515_ADDR_CANINTF,
|
|
|
|
&canintf, 1);
|
|
|
|
if (ret != 0) {
|
|
|
|
LOG_ERR("Couldn't read INTF register %d", ret);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (canintf == 0) {
|
|
|
|
/* No interrupt flags set */
|
|
|
|
break;
|
|
|
|
}
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
if (canintf & MCP2515_CANINTF_RX0IF) {
|
|
|
|
mcp2515_rx(dev, 0);
|
2019-07-21 14:59:53 +10:00
|
|
|
|
|
|
|
/* RX0IF flag cleared automatically during read */
|
|
|
|
canintf &= ~MCP2515_CANINTF_RX0IF;
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (canintf & MCP2515_CANINTF_RX1IF) {
|
|
|
|
mcp2515_rx(dev, 1);
|
2019-07-21 14:59:53 +10:00
|
|
|
|
|
|
|
/* RX1IF flag cleared automatically during read */
|
|
|
|
canintf &= ~MCP2515_CANINTF_RX1IF;
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (canintf & MCP2515_CANINTF_TX0IF) {
|
|
|
|
mcp2515_tx_done(dev, 0);
|
|
|
|
}
|
|
|
|
|
2019-08-02 00:38:31 +10:00
|
|
|
if (canintf & MCP2515_CANINTF_TX1IF) {
|
2018-06-12 08:23:20 +02:00
|
|
|
mcp2515_tx_done(dev, 1);
|
|
|
|
}
|
|
|
|
|
2019-08-02 00:38:31 +10:00
|
|
|
if (canintf & MCP2515_CANINTF_TX2IF) {
|
2018-06-12 08:23:20 +02:00
|
|
|
mcp2515_tx_done(dev, 2);
|
|
|
|
}
|
|
|
|
|
2018-06-19 18:26:31 +02:00
|
|
|
if (canintf & MCP2515_CANINTF_ERRIF) {
|
|
|
|
mcp2515_handle_errors(dev);
|
|
|
|
}
|
|
|
|
|
2019-07-21 14:59:53 +10:00
|
|
|
if (canintf != 0) {
|
|
|
|
/* Clear remaining flags */
|
|
|
|
mcp2515_cmd_bit_modify(dev, MCP2515_ADDR_CANINTF,
|
|
|
|
canintf, ~canintf);
|
|
|
|
}
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2019-11-07 20:16:06 +01:00
|
|
|
/* Break from loop if INT pin is inactive */
|
2022-01-27 15:42:01 +01:00
|
|
|
ret = gpio_pin_get_dt(&dev_cfg->int_gpio);
|
2019-11-07 20:16:06 +01:00
|
|
|
if (ret < 0) {
|
2019-07-21 22:41:16 +10:00
|
|
|
LOG_ERR("Couldn't read INT pin");
|
2019-11-07 20:16:06 +01:00
|
|
|
} else if (ret == 0) {
|
2019-07-21 22:41:16 +10:00
|
|
|
/* All interrupt flags handled */
|
|
|
|
break;
|
|
|
|
}
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void mcp2515_int_thread(const struct device *dev)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
struct mcp2515_data *dev_data = dev->data;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
|
|
|
while (1) {
|
|
|
|
k_sem_take(&dev_data->int_sem, K_FOREVER);
|
|
|
|
mcp2515_handle_interrupts(dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static void mcp2515_int_gpio_callback(const struct device *dev,
|
2020-05-27 11:26:57 -05:00
|
|
|
struct gpio_callback *cb, uint32_t pins)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
|
|
|
struct mcp2515_data *dev_data =
|
|
|
|
CONTAINER_OF(cb, struct mcp2515_data, int_gpio_cb);
|
|
|
|
|
|
|
|
k_sem_give(&dev_data->int_sem);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct can_driver_api can_api_funcs = {
|
2020-04-27 18:58:05 +02:00
|
|
|
.set_timing = mcp2515_set_timing,
|
|
|
|
.set_mode = mcp2515_set_mode,
|
2018-06-12 08:23:20 +02:00
|
|
|
.send = mcp2515_send,
|
2021-12-28 20:00:34 +01:00
|
|
|
.add_rx_filter = mcp2515_add_rx_filter,
|
|
|
|
.remove_rx_filter = mcp2515_remove_rx_filter,
|
2018-06-19 18:26:31 +02:00
|
|
|
.get_state = mcp2515_get_state,
|
|
|
|
#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY
|
|
|
|
.recover = mcp2515_recover,
|
|
|
|
#endif
|
2021-12-28 20:00:34 +01:00
|
|
|
.set_state_change_callback = mcp2515_set_state_change_callback,
|
2020-04-27 18:58:05 +02:00
|
|
|
.get_core_clock = mcp2515_get_core_clock,
|
2021-10-12 11:46:44 +02:00
|
|
|
.get_max_filters = mcp2515_get_max_filters,
|
2022-03-01 14:45:01 +01:00
|
|
|
.get_max_bitrate = mcp2515_get_max_bitrate,
|
2020-04-27 18:58:05 +02:00
|
|
|
.timing_min = {
|
|
|
|
.sjw = 0x1,
|
|
|
|
.prop_seg = 0x01,
|
|
|
|
.phase_seg1 = 0x01,
|
2022-04-01 21:05:46 +02:00
|
|
|
.phase_seg2 = 0x02,
|
2020-04-27 18:58:05 +02:00
|
|
|
.prescaler = 0x01
|
|
|
|
},
|
|
|
|
.timing_max = {
|
|
|
|
.sjw = 0x04,
|
|
|
|
.prop_seg = 0x08,
|
|
|
|
.phase_seg1 = 0x08,
|
|
|
|
.phase_seg2 = 0x08,
|
2022-04-01 21:05:46 +02:00
|
|
|
.prescaler = 0x40
|
2020-04-27 18:58:05 +02:00
|
|
|
}
|
2018-06-12 08:23:20 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int mcp2515_init(const struct device *dev)
|
2018-06-12 08:23:20 +02:00
|
|
|
{
|
2022-01-18 12:50:32 +01:00
|
|
|
const struct mcp2515_config *dev_cfg = dev->config;
|
|
|
|
struct mcp2515_data *dev_data = dev->data;
|
2020-04-27 18:58:05 +02:00
|
|
|
struct can_timing timing;
|
2022-02-10 17:04:27 +01:00
|
|
|
int ret;
|
|
|
|
int i;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2019-07-21 22:41:16 +10:00
|
|
|
k_sem_init(&dev_data->int_sem, 0, 1);
|
2018-06-19 18:26:31 +02:00
|
|
|
k_mutex_init(&dev_data->mutex);
|
2019-08-08 00:08:08 +10:00
|
|
|
k_sem_init(&dev_data->tx_sem, MCP2515_TX_CNT, MCP2515_TX_CNT);
|
2022-02-10 17:04:27 +01:00
|
|
|
|
|
|
|
for (i = 0; i < MCP2515_TX_CNT; i++) {
|
|
|
|
k_sem_init(&dev_data->tx_cb[i].sem, 0, 1);
|
|
|
|
dev_data->tx_cb[i].cb = NULL;
|
|
|
|
}
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2022-03-01 14:45:01 +01:00
|
|
|
if (dev_cfg->phy != NULL) {
|
|
|
|
if (!device_is_ready(dev_cfg->phy)) {
|
|
|
|
LOG_ERR("CAN transceiver not ready");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-01 16:00:49 +10:00
|
|
|
if (!spi_is_ready(&dev_cfg->bus)) {
|
|
|
|
LOG_ERR("SPI bus %s not ready", dev_cfg->bus.bus->name);
|
2018-06-12 08:23:20 +02:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset MCP2515 */
|
|
|
|
if (mcp2515_cmd_soft_reset(dev)) {
|
|
|
|
LOG_ERR("Soft-reset failed");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize interrupt handling */
|
2022-01-27 15:42:01 +01:00
|
|
|
if (!device_is_ready(dev_cfg->int_gpio.port)) {
|
|
|
|
LOG_ERR("Interrupt GPIO port not ready");
|
|
|
|
return -ENODEV;
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
2022-01-27 15:42:01 +01:00
|
|
|
if (gpio_pin_configure_dt(&dev_cfg->int_gpio, GPIO_INPUT)) {
|
2022-02-24 01:10:04 +00:00
|
|
|
LOG_ERR("Unable to configure interrupt GPIO");
|
2018-06-12 08:23:20 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
gpio_init_callback(&(dev_data->int_gpio_cb), mcp2515_int_gpio_callback,
|
2022-01-27 15:42:01 +01:00
|
|
|
BIT(dev_cfg->int_gpio.pin));
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2022-01-27 15:42:01 +01:00
|
|
|
if (gpio_add_callback(dev_cfg->int_gpio.port,
|
|
|
|
&(dev_data->int_gpio_cb))) {
|
2018-06-12 08:23:20 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-01-27 15:42:01 +01:00
|
|
|
if (gpio_pin_interrupt_configure_dt(&dev_cfg->int_gpio,
|
|
|
|
GPIO_INT_EDGE_TO_ACTIVE)) {
|
2018-06-12 08:23:20 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
k_thread_create(&dev_data->int_thread, dev_data->int_thread_stack,
|
|
|
|
dev_cfg->int_thread_stack_size,
|
|
|
|
(k_thread_entry_t) mcp2515_int_thread, (void *)dev,
|
|
|
|
NULL, NULL, K_PRIO_COOP(dev_cfg->int_thread_priority),
|
|
|
|
0, K_NO_WAIT);
|
|
|
|
|
2019-02-11 14:40:28 +01:00
|
|
|
(void)memset(dev_data->rx_cb, 0, sizeof(dev_data->rx_cb));
|
2018-06-12 08:23:20 +02:00
|
|
|
(void)memset(dev_data->filter, 0, sizeof(dev_data->filter));
|
2018-06-19 18:26:31 +02:00
|
|
|
dev_data->old_state = CAN_ERROR_ACTIVE;
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2020-04-27 18:58:05 +02:00
|
|
|
timing.sjw = dev_cfg->tq_sjw;
|
2020-11-20 13:39:48 +01:00
|
|
|
if (dev_cfg->sample_point && USE_SP_ALGO) {
|
2020-04-27 18:58:05 +02:00
|
|
|
ret = can_calc_timing(dev, &timing, dev_cfg->bus_speed,
|
|
|
|
dev_cfg->sample_point);
|
|
|
|
if (ret == -EINVAL) {
|
|
|
|
LOG_ERR("Can't find timing for given param");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
LOG_DBG("Presc: %d, BS1: %d, BS2: %d",
|
|
|
|
timing.prescaler, timing.phase_seg1, timing.phase_seg2);
|
|
|
|
LOG_DBG("Sample-point err : %d", ret);
|
|
|
|
} else {
|
|
|
|
timing.prop_seg = dev_cfg->tq_prop;
|
|
|
|
timing.phase_seg1 = dev_cfg->tq_bs1;
|
|
|
|
timing.phase_seg2 = dev_cfg->tq_bs2;
|
|
|
|
ret = can_calc_prescaler(dev, &timing, dev_cfg->bus_speed);
|
|
|
|
if (ret) {
|
|
|
|
LOG_WRN("Bitrate error: %d", ret);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-02 13:21:23 +05:30
|
|
|
ret = can_set_timing(dev, &timing, NULL);
|
2020-04-27 18:58:05 +02:00
|
|
|
if (ret) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-02-02 13:21:23 +05:30
|
|
|
ret = can_set_mode(dev, CAN_NORMAL_MODE);
|
2019-03-31 13:06:01 +02:00
|
|
|
|
|
|
|
return ret;
|
2018-06-12 08:23:20 +02:00
|
|
|
}
|
|
|
|
|
2020-05-11 11:56:08 -07:00
|
|
|
#if DT_NODE_HAS_STATUS(DT_DRV_INST(0), okay)
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2020-07-31 12:29:38 -07:00
|
|
|
static K_KERNEL_STACK_DEFINE(mcp2515_int_thread_stack,
|
2018-06-12 08:23:20 +02:00
|
|
|
CONFIG_CAN_MCP2515_INT_THREAD_STACK_SIZE);
|
|
|
|
|
|
|
|
static struct mcp2515_data mcp2515_data_1 = {
|
|
|
|
.int_thread_stack = mcp2515_int_thread_stack,
|
|
|
|
.tx_busy_map = 0U,
|
|
|
|
.filter_usage = 0U,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct mcp2515_config mcp2515_config_1 = {
|
2021-08-01 16:00:49 +10:00
|
|
|
.bus = SPI_DT_SPEC_INST_GET(0, SPI_WORD_SET(8), 0),
|
2022-02-24 01:10:04 +00:00
|
|
|
.int_gpio = GPIO_DT_SPEC_INST_GET(0, int_gpios),
|
2018-06-12 08:23:20 +02:00
|
|
|
.int_thread_stack_size = CONFIG_CAN_MCP2515_INT_THREAD_STACK_SIZE,
|
|
|
|
.int_thread_priority = CONFIG_CAN_MCP2515_INT_THREAD_PRIO,
|
2020-03-25 10:05:28 -05:00
|
|
|
.tq_sjw = DT_INST_PROP(0, sjw),
|
2020-11-20 13:39:48 +01:00
|
|
|
.tq_prop = DT_INST_PROP_OR(0, prop_seg, 0),
|
|
|
|
.tq_bs1 = DT_INST_PROP_OR(0, phase_seg1, 0),
|
|
|
|
.tq_bs2 = DT_INST_PROP_OR(0, phase_seg2, 0),
|
2020-03-25 10:05:28 -05:00
|
|
|
.bus_speed = DT_INST_PROP(0, bus_speed),
|
2020-11-20 13:39:48 +01:00
|
|
|
.osc_freq = DT_INST_PROP(0, osc_freq),
|
2022-03-01 14:45:01 +01:00
|
|
|
.sample_point = DT_INST_PROP_OR(0, sample_point, 0),
|
|
|
|
.phy = DEVICE_DT_GET_OR_NULL(DT_INST_PHANDLE(0, phys)),
|
|
|
|
.max_bitrate = DT_INST_CAN_TRANSCEIVER_MAX_BITRATE(0, 1000000),
|
2018-06-12 08:23:20 +02:00
|
|
|
};
|
|
|
|
|
2021-04-28 10:09:53 +02:00
|
|
|
DEVICE_DT_INST_DEFINE(0, &mcp2515_init, NULL,
|
2018-06-12 08:23:20 +02:00
|
|
|
&mcp2515_data_1, &mcp2515_config_1, POST_KERNEL,
|
2021-10-20 14:36:07 -05:00
|
|
|
CONFIG_CAN_INIT_PRIORITY, &can_api_funcs);
|
2018-06-12 08:23:20 +02:00
|
|
|
|
2020-12-07 16:03:24 +05:30
|
|
|
#if defined(CONFIG_NET_SOCKETS_CAN)
|
|
|
|
|
|
|
|
#include "socket_can_generic.h"
|
|
|
|
|
2021-02-02 12:58:15 -06:00
|
|
|
static struct socket_can_context socket_can_context_1;
|
|
|
|
|
2020-12-07 16:03:24 +05:30
|
|
|
static int socket_can_init(const struct device *dev)
|
|
|
|
{
|
2020-12-17 11:35:11 -06:00
|
|
|
const struct device *can_dev = DEVICE_DT_INST_GET(1);
|
2020-12-07 16:03:24 +05:30
|
|
|
struct socket_can_context *socket_context = dev->data;
|
|
|
|
|
|
|
|
LOG_DBG("Init socket CAN device %p (%s) for dev %p (%s)",
|
|
|
|
dev, dev->name, can_dev, can_dev->name);
|
|
|
|
|
|
|
|
socket_context->can_dev = can_dev;
|
|
|
|
socket_context->msgq = &socket_can_msgq;
|
|
|
|
|
|
|
|
socket_context->rx_tid =
|
|
|
|
k_thread_create(&socket_context->rx_thread_data,
|
|
|
|
rx_thread_stack,
|
|
|
|
K_KERNEL_STACK_SIZEOF(rx_thread_stack),
|
|
|
|
rx_thread, socket_context, NULL, NULL,
|
|
|
|
RX_THREAD_PRIORITY, 0, K_NO_WAIT);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
NET_DEVICE_INIT(socket_can_mcp2515_1, SOCKET_CAN_NAME_1, socket_can_init,
|
2021-04-28 10:09:53 +02:00
|
|
|
NULL, &socket_can_context_1, NULL,
|
2021-10-20 14:36:07 -05:00
|
|
|
CONFIG_CAN_INIT_PRIORITY,
|
2020-12-07 16:03:24 +05:30
|
|
|
&socket_can_api,
|
|
|
|
CANBUS_RAW_L2, NET_L2_GET_CTX_TYPE(CANBUS_RAW_L2), CAN_MTU);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2020-05-11 11:56:08 -07:00
|
|
|
#endif /* DT_NODE_HAS_STATUS(DT_DRV_INST(0), okay) */
|