can: isotp: Add ISO-TP library

This commit adds a ISO-TP (ISO15765-2) library.
The library makes use of net buffers and spawns a workqueue thread.
The CAN device that is passed to bind and send can be used concurrently
beside this library.

Signed-off-by: Alexander Wachter <alexander.wachter@student.tugraz.at>
This commit is contained in:
Alexander Wachter 2019-02-11 21:17:26 +01:00 committed by Jukka Rissanen
commit 55baaf0365
7 changed files with 1924 additions and 0 deletions

393
include/canbus/isotp.h Normal file
View file

@ -0,0 +1,393 @@
/*
* Copyright (c) 2019 Alexander Wachter
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Public API for ISO-TP (ISO 15765-2:2016)
*
* ISO-TP is a transport protocol for CAN (Controller Area Network)
*/
#ifndef ZEPHYR_INCLUDE_ISOTP_H_
#define ZEPHYR_INCLUDE_ISOTP_H_
/**
* @brief CAN ISO-TP Interf
* @defgroup can_isotp CAN ISO-TP Interface
* @ingroup CAN
* @{
*/
#include <drivers/can.h>
#include <zephyr/types.h>
#include <net/buf.h>
/*
* Abbreviations
* BS Block Size
* CAN_DL CAN LL data size
* CF Consecutive Frame
* CTS Continue to send
* DLC Data length code
* FC Flow Control
* FF First Frame
* FS Flow Status
* AE Adders Extension
*/
/*
* N_Result according to ISO 15765-2:2016
* ISOTP_ prefix is used to be zephyr conform
*/
/** Completed successfully */
#define ISOTP_N_OK 0
/** Ar/As has timed out */
#define ISOTP_N_TIMEOUT_A -1
/** Reception of next FC has timed out */
#define ISOTP_N_TIMEOUT_BS -2
/** Cr has timed out */
#define ISOTP_N_TIMEOUT_CR -3
/** Unexpected sequence number */
#define ISOTP_N_WRONG_SN -4
/** Invalid flow status received*/
#define ISOTP_N_INVALID_FS -5
/** Unexpected PDU received */
#define ISOTP_N_UNEXP_PDU -6
/** Maximum number of WAIT flowStatus PDUs exceeded */
#define ISOTP_N_WFT_OVRN -7
/** FlowStatus OVFLW PDU was received */
#define ISOTP_N_BUFFER_OVERFLW -8
/** General error */
#define ISOTP_N_ERROR -9
/** Implementation specific errors */
/** Can't bind or send because the CAN device has no filter left*/
#define ISOTP_NO_FREE_FILTER -10
/** No net buffer left to allocate */
#define ISOTP_NO_NET_BUF_LEFT -11
/** Not sufficient space in the buffer left for the data */
#define ISOTP_NO_BUF_DATA_LEFT -12
/** No context buffer left to allocate */
#define ISOTP_NO_CTX_LEFT -13
/** Timeout for recv */
#define ISOTP_RECV_TIMEOUT -14
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief ISO-TP message id struct
*
* Used to pass addresses to the bind and send functions.
*/
struct isotp_msg_id {
/** Message identifier*/
union {
u32_t std_id : 11;
u32_t ext_id : 29;
};
/** extended address */
u8_t ext_addr;
/** Indicates the identifier type (standard or extended) */
u8_t id_type : 1;
/** Indicates if extended addressing is used */
u8_t use_ext_addr : 1;
};
/*
* STmin is split in two valid ranges:
* 0-127: 0ms-127ms
* 128-240: Reserved
* 241-249: 100us-900us (multiples of 100us)
* 250- : Reserved
*/
/**
* @brief ISO-TP frame control options struct
*
* Used to pass the options to the bind and send functions.
*/
struct isotp_fc_opts {
u8_t bs; /**< Block size. Number of CF PDUs before next CF is sent */
u8_t stmin; /**< Minimum separation time. Min time between frames */
};
typedef void (*isotp_tx_callback_t)(int error_nr, void *arg);
struct isotp_send_ctx;
struct isotp_recv_ctx;
/**
* @brief Bind an address to a receiving context.
*
* This function binds an RX and TX address combination to an RX context.
* When data arrives from the specified address, it is buffered and can be read
* by calling isotp_recv.
* When calling this routine, a filter is applied in the CAN device, and the
* context is initialized. The context must be valid until calling unbind.
*
* @param ctx Context to store the internal states.
* @param can_dev The CAN device to be used for sending and receiving.
* @param rx_addr Identifier for incoming data.
* @param tx_addr Identifier for FC frames.
* @param opts Flow control options.
* @param timeout Timeout for FF SF buffer allocation.
*
* @retval ISOTP_N_OK on success
* @retval ISOTP_NO_FREE_FILTER if CAN device has no filters left.
*/
int isotp_bind(struct isotp_recv_ctx *ctx, struct device *can_dev,
const struct isotp_msg_id *rx_addr,
const struct isotp_msg_id *tx_addr,
const struct isotp_fc_opts *opts,
s32_t timeout);
/**
* @brief Unbind a context from the interface
*
* This function removes the binding from isotp_bind.
* The filter is detached from the CAN device, and if a transmission is ongoing,
* buffers are freed.
* The context can be discarded safely after calling this function.
*
* @param ctx Context that should be unbound.
*/
void isotp_unbind(struct isotp_recv_ctx *ctx);
/**
* @brief Read out received data from fifo.
*
* This function reads the data from the receive FIFO of the context.
* It blocks if the FIFO is empty.
* If an error occurs, the function returns a negative number and leaves the
* data buffer unchanged.
*
* @param ctx Context that is already bound.
* @param data Pointer to a buffer where the data is copied to.
* @param len Size of the buffer.
* @param timeout Timeout for incoming data.
*
* @retval Number of bytes copied on success
* @retval ISOTP_WAIT_TIMEOUT when "timeout" timed out
* @retval ISOTP_N_* on error
*/
int isotp_recv(struct isotp_recv_ctx *ctx, u8_t *data, size_t len,
s32_t timeout);
/**
* @brief Get the net buffer on data reception
*
* This function reads incoming data into net-buffers.
* It blocks until the entire packet is received, BS is reached, or an error
* occurred. If BS was zero, the data is in a single net_buf. Otherwise,
* the data is fragmented in chunks of BS size.
* The net-buffers are referenced and must be freed with net_buf_unref after the
* data is processed.
*
* @param ctx Context that is already bound.
* @param buffer Pointer where the net_buf pointer is written to.
* @param timeout Timeout for incoming data.
*
* @retval Remaining data length for this transfer if BS > 0, 0 for BS = 0
* @retval ISOTP_WAIT_TIMEOUT when "timeout" timed out
* @retval ISOTP_N_* on error
*/
int isotp_recv_net(struct isotp_recv_ctx *ctx, struct net_buf **buffer,
s32_t timeout);
/**
* @brief Send data
*
* This function is used to send data to a peer that listens to the tx_addr.
* An internal work-queue is used to transfer the segmented data.
* Data and context must be valid until the transmission has finished.
* If a complete_cb is given, this function is non-blocking, and the callback
* is called on completion with the return value as a parameter.
*
* @param ctx Context to store the internal states.
* @param can_dev The CAN device to be used for sending and receiving.
* @param data Data to be sent.
* @param len Length of the data to be sent.
* @param rx_addr Identifier for FC frames.
* @param tx_addr Identifier for outgoing frames the receiver listens on.
* @param complete_cb Function called on completion or NULL.
* @param cb_arg Argument passed to the complete callback.
*
* @retval ISOTP_N_OK on success
* @retval ISOTP_N_* on error
*/
int isotp_send(struct isotp_send_ctx *ctx, struct device *can_dev,
const u8_t *data, size_t len,
const struct isotp_msg_id *tx_addr,
const struct isotp_msg_id *rx_addr,
isotp_tx_callback_t complete_cb, void *cb_arg);
#ifdef CONFIG_ISOTP_ENABLE_CONTEXT_BUFFERS
/**
* @brief Send data with buffered context
*
* This function is similar to isotp_send, but the context is automatically
* allocated from an internal pool.
*
* @param can_dev The CAN device to be used for sending and receiving.
* @param data Data to be sent.
* @param len Length of the data to be sent.
* @param rx_addr Identifier for FC frames.
* @param tx_addr Identifier for outgoing frames the receiver listens on.
* @param complete_cb Function called on completion or NULL.
* @param cb_arg Argument passed to the complete callback.
* @param timeout Timeout for buffer allocation.
*
* @retval ISOTP_N_OK on success
* @retval ISOTP_N_* on error
*/
int isotp_send_ctx_buf(struct device *can_dev,
const u8_t *data, size_t len,
const struct isotp_msg_id *tx_addr,
const struct isotp_msg_id *rx_addr,
isotp_tx_callback_t complete_cb, void *cb_arg,
s32_t timeout);
/**
* @brief Send data with buffered context
*
* This function is similar to isotp_send_ctx_buf, but the data is carried in
* a net_buf. net_buf_unref is called on the net_buf when sending is completed.
*
* @param can_dev The CAN device to be used for sending and receiving.
* @param data Data to be sent.
* @param len Length of the data to be sent.
* @param rx_addr Identifier for FC frames.
* @param tx_addr Identifier for outgoing frames the receiver listens on.
* @param complete_cb Function called on completion or NULL.
* @param cb_arg Argument passed to the complete callback.
* @param timeout Timeout for buffer allocation.
*
* @retval ISOTP_N_OK on success
* @retval ISOTP_* on error
*/
int isotp_send_net_ctx_buf(struct device *can_dev,
struct net_buf *data,
const struct isotp_msg_id *tx_addr,
const struct isotp_msg_id *rx_addr,
isotp_tx_callback_t complete_cb, void *cb_arg,
s32_t timeout);
#endif /*CONFIG_ISOTP_ENABLE_CONTEXT_BUFFERS*/
#if defined(CONFIG_ISOTP_USE_TX_BUF) && \
defined(CONFIG_ISOTP_ENABLE_CONTEXT_BUFFERS)
/**
* @brief Send data with buffered context
*
* This function is similar to isotp_send, but the context is automatically
* allocated from an internal pool and the data to be send is buffered in an
* internal net_buff.
*
* @param can_dev The CAN device to be used for sending and receiving.
* @param data Data to be sent.
* @param len Length of the data to be sent.
* @param rx_addr Identifier for FC frames.
* @param tx_addr Identifier for outgoing frames the receiver listens on.
* @param complete_cb Function called on completion or NULL.
* @param cb_arg Argument passed to the complete callback.
* @param timeout Timeout for buffer allocation.
*
* @retval ISOTP_N_OK on success
* @retval ISOTP_* on error
*/
int isotp_send_buf(struct device *can_dev,
const u8_t *data, size_t len,
const struct isotp_msg_id *tx_addr,
const struct isotp_msg_id *rx_addr,
isotp_tx_callback_t complete_cb, void *cb_arg,
s32_t timeout);
#endif
/** @cond INTERNAL_HIDDEN */
struct isotp_callback {
isotp_tx_callback_t cb;
void *arg;
};
struct isotp_send_ctx {
int filter_id;
u32_t error_nr;
struct device *can_dev;
union {
struct net_buf *buf;
struct {
const u8_t *data;
size_t len;
};
};
struct k_work work;
struct _timeout timeout;
union {
struct isotp_callback fin_cb;
struct k_sem fin_sem;
};
struct isotp_fc_opts opts;
u8_t state;
u8_t tx_backlog;
struct isotp_msg_id rx_addr;
struct isotp_msg_id tx_addr;
u8_t wft;
u8_t bs;
u8_t sn : 4;
u8_t is_net_buf : 1;
u8_t is_ctx_slab : 1;
u8_t has_callback: 1;
};
struct isotp_recv_ctx {
int filter_id;
struct device *can_dev;
struct net_buf *buf;
struct net_buf *act_frag;
sys_snode_t alloc_node;
u32_t length;
int error_nr;
struct k_work work;
struct _timeout timeout;
struct k_fifo fifo;
struct isotp_msg_id rx_addr;
struct isotp_msg_id tx_addr;
struct isotp_fc_opts opts;
u8_t state;
u8_t bs;
u8_t wft;
u8_t sn_expected : 4;
};
/** @endcond */
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_ISOTP_H_ */

View file

@ -1,3 +1,4 @@
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
add_subdirectory_if_kconfig(canopen) add_subdirectory_if_kconfig(canopen)
add_subdirectory_if_kconfig(isotp)

View file

@ -6,5 +6,6 @@
menu "Controller Area Network (CAN) bus subsystem" menu "Controller Area Network (CAN) bus subsystem"
source "subsys/canbus/canopen/Kconfig" source "subsys/canbus/canopen/Kconfig"
source "subsys/canbus/isotp/Kconfig"
endmenu endmenu

View file

@ -0,0 +1,4 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
zephyr_library_sources_if_kconfig(isotp.c)

133
subsys/canbus/isotp/Kconfig Normal file
View file

@ -0,0 +1,133 @@
# ISO-TP configuration options
# Copyright (c) 2019 Alexander Wachter
# SPDX-License-Identifier: Apache-2.0
menuconfig ISOTP
bool "ISO-TP Transport [EXPERIMENTAL]"
select NET_BUF
select POLL
help
Enable ISO TP support for CAN
if ISOTP
module = ISOTP
module-str = ISOTP
source "subsys/logging/Kconfig.template.log_config"
config ISOTP_WFTMAX
int "WFTmax (Max WAIT frames before aborting)."
default 10
range 0 254
help
This value defines the maximum number of WAIT frames before the transmission
is aborted.
config ISOTP_BS_TIMEOUT
int "Bs timeout [ms] (timeout for receiving the frame control)"
default 1000
range 200 10000
help
Timeout for the reception of the next FC frame. ISO 15765-2: 1000ms
config ISOTP_A_TIMEOUT
int "Ar and As timeout [ms] (sending and receiving timeout)"
default 1000
range 200 10000
help
As (sender transmit timeout) and Ar (receiver transmit timeout).
ISO 15765-2: 1000ms
config ISOTP_CR_TIMEOUT
int "Cr timeout [ms] (timeout for consecutive frames)"
default 1000
range 200 10000
help
Cr (receiver consecutive frame) timeout.
ISO 15765-2: 1000ms
config ISOTP_WORKQUEUE_PRIO
int "Priority level of the RX and TX work queue"
default 2
help
This value defines the priority level of the work queue thread that
handles flow control, consecutive sending, receiving and callbacks.
config ISOTP_WORKQ_STACK_SIZE
int "Work queue stack size"
default 256
help
This value defines the stack size of the work queue thread that
handles flow control, consecutive sending, receiving and callbacks.
config ISOTP_RX_BUF_COUNT
int "Number of data buffers for receiving data"
default 4
help
Each data buffer will occupy ISOTP_RX_BUF_SIZE + smallish
header (sizeof(struct net_buf)) amount of data.
config ISOTP_RX_BUF_SIZE
int "Size of one buffer data block"
default 56
help
This value defines the size of a single block in the pool. The number of
blocks is given by ISOTP_RX_BUF_COUNT. To be efficient use a multiple of
CAN_DL - 1 (for classic can : 8 - 1 = 7).
config ISOTP_RX_SF_FF_BUF_COUNT
int "Number of SF and FF data buffers for receiving data"
default 4
help
This buffer is used for first and single frames. It is extra because the
buffer has to be ready for the first reception in isr context and therefor
is allocated when binding.
Each buffer will occupy CAN_DL - 1 byte + header (sizeof(struct net_buf))
amount of data.
config ISOTP_USE_TX_BUF
bool "Buffer tx writes"
default n
help
Copy the outgoing data to a net buffer so that the calling function
can discard the data.
if ISOTP_USE_TX_BUF
config ISOTP_TX_BUF_COUNT
int "Number of data buffers for sending data"
default 4
help
Each data buffer will occupy CONFIG_NET_BUF_DATA_SIZE + smallish
header (sizeof(struct net_buf)) amount of data. If context buffers
are used, use the same size here.
config ISOTP_BUF_TX_DATA_POOL_SIZE
int "Size of the memory pool where buffers are allocated from"
default 256
help
This value defines the size of the memory pool where the buffers
for sending are allocated from.
endif # ISOTP_USE_TX_BUF
config ISOTP_ENABLE_CONTEXT_BUFFERS
bool "Enable buffered tx contexts"
default y
help
This option enables buffered sending contexts. This makes send and
forget possible. A memory slab is used to buffer the context.
if ISOTP_ENABLE_CONTEXT_BUFFERS
config ISOTP_TX_CONTEXT_BUF_COUNT
int "Amount of context buffers for sending data"
default 4
help
This defines the size of the memory slab where the buffers are
allocated from.
endif # ISOTP_ENABLE_CONTEXT_BUFFERS
endif # ISOTP

1268
subsys/canbus/isotp/isotp.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,124 @@
/*
* Copyright (c) 2019 Alexander Wachter
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_SUBSYS_NET_CAN_ISOTP_INTERNAL_H_
#define ZEPHYR_SUBSYS_NET_CAN_ISOTP_INTERNAL_H_
#include <canbus/isotp.h>
#include <sys/slist.h>
/*
* Abbreviations
* BS Block Size
* CAN_DL CAN LL data size
* CF Consecutive Frame
* CTS Continue to send
* DLC Data length code
* FC Flow Control
* FF First Frame
* SF Single Frame
* FS Flow Status
* AE Adders Extension
* SN Sequence Number
* ST Separation time
* PCI Process Control Information
*/
/* This is for future use when we have CAN-FD */
#ifdef ISOTP_USE_CAN_FD
/* #define ISOTP_CAN_DL CONFIG_ISOTP_TX_DL* */
#define ISOTP_CAN_DL 8
#else
#define ISOTP_CAN_DL 8
#endif/*ISOTP_USE_CAN_FD*/
/* Protocol control information*/
#define ISOTP_PCI_SF 0x00 /* Single frame*/
#define ISOTP_PCI_FF 0x01 /* First frame */
#define ISOTP_PCI_CF 0x02 /* Consecutive frame */
#define ISOTP_PCI_FC 0x03 /* Flow control frame */
#define ISOTP_PCI_TYPE_BYTE 0
#define ISOTP_PCI_TYPE_POS 4
#define ISOTP_PCI_TYPE_MASK 0xF0
#define ISOTP_PCI_TYPE_SF (ISOTP_PCI_SF << ISOTP_PCI_TYPE_POS)
#define ISOTP_PCI_TYPE_FF (ISOTP_PCI_FF << ISOTP_PCI_TYPE_POS)
#define ISOTP_PCI_TYPE_CF (ISOTP_PCI_CF << ISOTP_PCI_TYPE_POS)
#define ISOTP_PCI_TYPE_FC (ISOTP_PCI_FC << ISOTP_PCI_TYPE_POS)
#define ISOTP_PCI_SF_DL_MASK 0x0F
#define ISOTP_PCI_FF_DL_UPPER_BYTE 0
#define ISOTP_PCI_FF_DL_UPPER_MASK 0x0F
#define ISOTP_PCI_FF_DL_LOWER_BYTE 1
#define ISOTP_PCI_FS_BYTE 0
#define ISOTP_PCI_FS_MASK 0x0F
#define ISOTP_PCI_BS_BYTE 1
#define ISOTP_PCI_ST_MIN_BYTE 2
#define ISOTP_PCI_FS_CTS 0x0
#define ISOTP_PCI_FS_WAIT 0x1
#define ISOTP_PCI_FS_OVFLW 0x2
#define ISOTP_PCI_SN_MASK 0x0F
#define ISOTP_FF_DL_MIN (ISOTP_CAN_DL)
#define ISOTP_STMIN_MAX 0xFA
#define ISOTP_STMIN_MS_MAX 0x7F
#define ISOTP_STMIN_US_BEGIN 0xF1
#define ISOTP_STMIN_US_END 0xF9
#define ISOTP_WFT_FIRST 0xFF
#define ISOTP_BS K_MSEC(CONFIG_ISOTP_BS_TIMEOUT)
#define ISOTP_A K_MSEC(CONFIG_ISOTP_A_TIMEOUT)
#define ISOTP_CR K_MSEC(CONFIG_ISOTP_CR_TIMEOUT)
/* Just before the sender would time out*/
#define ISOTP_ALLOC_TIMEOUT K_MSEC(CONFIG_ISOTP_A_TIMEOUT - 100)
#ifdef __cplusplus
extern "C" {
#endif
enum isotp_rx_state {
ISOTP_RX_STATE_WAIT_FF_SF,
ISOTP_RX_STATE_PROCESS_SF,
ISOTP_RX_STATE_PROCESS_FF,
ISOTP_RX_STATE_TRY_ALLOC,
ISOTP_RX_STATE_SEND_FC,
ISOTP_RX_STATE_WAIT_CF,
ISOTP_RX_STATE_SEND_WAIT,
ISOTP_RX_STATE_ERR,
ISOTP_RX_STATE_RECYCLE,
ISOTP_RX_STATE_UNBOUND
};
enum isotp_tx_state {
ISOTP_TX_STATE_RESET,
ISOTP_TX_SEND_SF,
ISOTP_TX_SEND_FF,
ISOTP_TX_WAIT_FC,
ISOTP_TX_SEND_CF,
ISOTP_TX_WAIT_ST,
ISOTP_TX_WAIT_BACKLOG,
ISOTP_TX_WAIT_FIN,
ISOTP_TX_ERR
};
struct isotp_global_ctx {
sys_slist_t alloc_list;
sys_slist_t ff_sf_alloc_list;
};
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_SUBSYS_NET_CAN_ISOTP_INTERNAL_H_ */