437 lines
13 KiB
C
437 lines
13 KiB
C
/*
|
|
* 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 Address Extension
|
|
* SA Source Address
|
|
* TA Target Address
|
|
*/
|
|
|
|
/*
|
|
* 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
|
|
|
|
/*
|
|
* CAN ID filtering for ISO-TP fixed addressing according to SAE J1939
|
|
*
|
|
* Format of 29-bit CAN identifier:
|
|
* ------------------------------------------------------
|
|
* | 28 .. 26 | 25 | 24 | 23 .. 16 | 15 .. 8 | 7 .. 0 |
|
|
* ------------------------------------------------------
|
|
* | Priority | EDP | DP | N_TAtype | N_TA | N_SA |
|
|
* ------------------------------------------------------
|
|
*/
|
|
|
|
/** Position of fixed source address (SA) */
|
|
#define ISOTP_FIXED_ADDR_SA_POS (0U)
|
|
|
|
/** Mask to obtain fixed source address (SA) */
|
|
#define ISOTP_FIXED_ADDR_SA_MASK (0xFF << ISOTP_FIXED_ADDR_SA_POS)
|
|
|
|
/** Position of fixed target address (TA) */
|
|
#define ISOTP_FIXED_ADDR_TA_POS (8U)
|
|
|
|
/** Mask to obtain fixed target address (TA) */
|
|
#define ISOTP_FIXED_ADDR_TA_MASK (0xFF << ISOTP_FIXED_ADDR_TA_POS)
|
|
|
|
/** Position of priority in fixed addressing mode */
|
|
#define ISOTP_FIXED_ADDR_PRIO_POS (26U)
|
|
|
|
/** Mask for priority in fixed addressing mode */
|
|
#define ISOTP_FIXED_ADDR_PRIO_MASK (0x7 << ISOTP_FIXED_ADDR_PRIO_POS)
|
|
|
|
/* CAN filter RX mask to match any priority and source address (SA) */
|
|
#define ISOTP_FIXED_ADDR_RX_MASK (0x03FFFF00)
|
|
|
|
#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 {
|
|
/**
|
|
* CAN identifier
|
|
*
|
|
* If ISO-TP fixed addressing is used, isotp_bind ignores SA and
|
|
* priority sections and modifies TA section in flow control frames.
|
|
*/
|
|
union {
|
|
uint32_t std_id : 11;
|
|
uint32_t ext_id : 29;
|
|
};
|
|
/** ISO-TP extended address (if used) */
|
|
uint8_t ext_addr;
|
|
/** Indicates the CAN identifier type (standard or extended) */
|
|
uint8_t id_type : 1;
|
|
/** Indicates if ISO-TP extended addressing is used */
|
|
uint8_t use_ext_addr : 1;
|
|
/** Indicates if ISO-TP fixed addressing (acc. to SAE J1939) is used */
|
|
uint8_t use_fixed_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 {
|
|
uint8_t bs; /**< Block size. Number of CF PDUs before next CF is sent */
|
|
uint8_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, const struct device *can_dev,
|
|
const struct isotp_msg_id *rx_addr,
|
|
const struct isotp_msg_id *tx_addr,
|
|
const struct isotp_fc_opts *opts,
|
|
k_timeout_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, uint8_t *data, size_t len,
|
|
k_timeout_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,
|
|
k_timeout_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, const struct device *can_dev,
|
|
const uint8_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(const struct device *can_dev,
|
|
const uint8_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,
|
|
k_timeout_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(const 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,
|
|
k_timeout_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(const struct device *can_dev,
|
|
const uint8_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,
|
|
k_timeout_t timeout);
|
|
#endif
|
|
|
|
/** @cond INTERNAL_HIDDEN */
|
|
|
|
struct isotp_callback {
|
|
isotp_tx_callback_t cb;
|
|
void *arg;
|
|
};
|
|
|
|
struct isotp_send_ctx {
|
|
int filter_id;
|
|
uint32_t error_nr;
|
|
const struct device *can_dev;
|
|
union {
|
|
struct net_buf *buf;
|
|
struct {
|
|
const uint8_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;
|
|
uint8_t state;
|
|
uint8_t tx_backlog;
|
|
struct isotp_msg_id rx_addr;
|
|
struct isotp_msg_id tx_addr;
|
|
uint8_t wft;
|
|
uint8_t bs;
|
|
uint8_t sn : 4;
|
|
uint8_t is_net_buf : 1;
|
|
uint8_t is_ctx_slab : 1;
|
|
uint8_t has_callback: 1;
|
|
};
|
|
|
|
struct isotp_recv_ctx {
|
|
int filter_id;
|
|
const struct device *can_dev;
|
|
struct net_buf *buf;
|
|
struct net_buf *act_frag;
|
|
/* buffer currently processed in isotp_recv */
|
|
struct net_buf *recv_buf;
|
|
sys_snode_t alloc_node;
|
|
uint32_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;
|
|
uint8_t state;
|
|
uint8_t bs;
|
|
uint8_t wft;
|
|
uint8_t sn_expected : 4;
|
|
};
|
|
|
|
/** @endcond */
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* ZEPHYR_INCLUDE_ISOTP_H_ */
|