zephyr/include/canbus/isotp.h
Martin Jäger 0c8cb0586a canbus: isotp: add fixed addressing feature
Fixed addressing as specified in ISO 15765-2 encodes the source and
target address of a device or function inside the CAN ID according to
SAE J1939.

In order to allow to receive incoming requests from different nodes,
the CAN filter mask has to be set such that the source address is
ignored in the receive context. In addition to that, flow control
frames have to be sent back to the actual source of the request, which
requires adjustments to the TX CAN ID.

This commit implements above features and thus allows ISO-TP to be
used in a network based on SAE J1939 or NMEA-2000.

Signed-off-by: Martin Jäger <martin@libre.solar>
2021-04-06 16:44:34 -04:00

435 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;
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_ */