/** * @ingroup can * @defgroup can_msg Message pooling * @brief di_can_msg pooling * @date Oct 29, 2015 * @author jjacobs * @copyright 2015 Dual Inventive Technology Centre B.V. * * CAN message buffer pooling * @{ */ #ifndef LIBDI_INCLUDE_DI_CAN_MSG_H_ #define LIBDI_INCLUDE_DI_CAN_MSG_H_ #include #include #include #include #include #include #include struct di_can_ctx; #ifdef __cplusplus extern "C" { #endif /** Declare di_can_msg array */ #define DI_CAN_MSG_DECL_ARRAY(name, elements, size) \ DI_BUFFER_DECL_ARRAY(name##_buffers, elements, size); \ static struct di_can_msg name[elements] /** Initialize di_can_msg array */ #define DI_CAN_MSG_INIT_ARRAY(ctx, name, elements, size) \ DI_BUFFER_INIT_ARRAY(name##_buffers, elements, size); \ di_can_msg_init_array(ctx, name, elements, name##_buffers, elements) /** Set new message state */ #define DI_CAN_MSG_SET_STATE(msg, msg_state) \ msg->state = msg_state /** * Message state */ enum di_can_msg_state { DI_CAN_MSG_STATE_UNUSED = 0, /**< Message unused */ DI_CAN_MSG_STATE_REASSEMBLE, /**< Message in use by reassemble, as not all frames have been received */ DI_CAN_MSG_STATE_SEND, /**< Message in queue for async send */ DI_CAN_MSG_STATE_READY, /**< Message ready (crc checked) */ DI_CAN_MSG_STATE_APPLICATION, /**< Message in use by the application (WILL NOT GC!) */ DI_CAN_MSG_STATE_DIRTY, /**< Message marked for garbage collection */ DI_CAN_MSG_STATE_ERROR /**< Message in error state, after async send */ }; /** * DI CAN Message */ struct di_can_msg { enum di_can_msg_state state; /**< Message state */ di_errno_t error; /**< Message error (DNOK when good) */ uint32_t canid; /**< DI CAN id */ uint32_t src_id; /**< Source Node ID */ uint32_t dst_id; /**< Destination Node ID */ enum di_can_ptypes ptype; /**< Payload type */ uint8_t emflags; /**< Extended metadata flags */ uint64_t timeout; /**< Garbage collection timeout (di timestamp milliseconds) */ uint8_t *msg; /**< Message (when set, message is fully assembled and valid crc) */ size_t size; /**< Message size */ size_t size_written; /**< Message size already written by di_can_reassemble */ struct di_buffer *buf; /**< Raw buffer */ const struct di_can_ctx *ctx; /**< DI CAN context */ di_bsem_t lock; /**< Message lock (wait) */ }; /** * DI CAN message field byte offsets in msg->buf */ enum di_can_msg_offsets { DI_CAN_MSG_OFFSET_SRC_ID = 0, /**< Source node ID */ DI_CAN_MSG_OFFSET_DST_ID = 4, /**< Destination node ID */ DI_CAN_MSG_OFFSET_PTYPE = 8, /**< Payload type */ DI_CAN_MSG_OFFSET_PSIZE = 9, /**< Payload size */ DI_CAN_MSG_OFFSET_CRC = 11, /**< Payload CRC */ DI_CAN_MSG_OFFSET_EMFLAGS = 13, /**< Extended metadata flags */ DI_CAN_MSG_OFFSET_EMDATA = 14, /**< Extended metadata */ DI_CAN_MSG_OFFSET_DATA = 16 /**< Payload data */ }; /** * DI CAN message Extended Metadata flags */ enum di_can_msg_emflags { DI_CAN_MSG_EMFLAGS_NONE = 0, DI_CAN_MSG_EMFLAG_EM_DISABLE = (1 << 7), /**< Extended Metadata Flags and Extended Metadata disable */ DI_CAN_MSG_EMFLAG_RT = (1 << 6), /**< Realtime message flag (emdata field contains uint16 seqnr) */ }; /** * @name Initialisation and configuration * @{ */ /** * Initialize can message subsystem * @param ctx CAN context * @param list List of can messages * @param size Elements of can messages in list * @note When the subsystem is already initialized next function calls will be ignored */ void di_can_msg_init(struct di_can_ctx *ctx, struct di_can_msg *list, size_t size); /** * Attach di_buffer to di_can_msg * @param ctx CAN context where the messages are attached to * @param msg_list Array of di_can_msg * @param msg_size Element count of msg_list * @param buffer_list Array of di_buffer (which are already initialized) * @param buffer_size Element count of buffer_size * @note msg_size and buffer_size MUST be equal */ void di_can_msg_init_array(const struct di_can_ctx *ctx, struct di_can_msg *msg_list, size_t msg_size, struct di_buffer *buffer_list, size_t buffer_size); /** * Enable time synchronisation (default disabled) * When enable di_time_set_ms is called * * msg['ttype'] = DI_CAN_TRANSFERTYPE_PUBLISH * * msg['dtype'] = DI_CAN_RAW_DTYPE_DINET_TIME * * msg['ptype'] = DI_CAN_PTYPE_U64 * @param ctx CAN context * @param enable Enable or disable */ void di_can_msg_enable_timesync(struct di_can_ctx *ctx, bool enable); /** * Enable invalid dst_id filter, which only allows: * * msg->dst == DI_CAN_NODEID_BROADCAST * * msg->dst == ctx->nodeid * These are filtered when the messages are reassemble and tried to set to the READY state */ void di_can_msg_enable_dst_id_filter(struct di_can_ctx *ctx, bool enable); /** @} */ /** * @name Locking * @{ */ /** Lock the message subsystem */ void di_can_msg_lock(struct di_can_ctx *ctx); /** Unlock the message subsystem */ void di_can_msg_unlock(struct di_can_ctx *ctx); /** @} */ /** * @name Pooling */ /** Allocate message */ struct di_can_msg *di_can_msg_alloc(struct di_can_ctx *ctx); /** Allocate message and initialize with canid * @param ctx CAN context * @param msgtype Message type * @param ttype Transfer type * @param dtype Data type */ struct di_can_msg *di_can_msg_alloc_with_canid(struct di_can_ctx *ctx, const enum di_can_msgtype msgtype, const enum di_can_transfertype ttype, const uint16_t dtype); /** Get unused message for di_can_reassemble */ struct di_can_msg *di_can_msg_alloc_reassemble(struct di_can_ctx *ctx); /** Give message back to the pool */ void di_can_msg_free(struct di_can_msg **msg); /** Search for first message which match canid, msg->state == DI_CAN_MSG_STATE_REASSEMBLY */ struct di_can_msg *di_can_msg_search(struct di_can_ctx *ctx, uint32_t canid); /** * Garbage collect where di_can_msg timeouts are exceeded * This will use the di_time_get_uptime to test against * the message timeout * * When di_can_msg.timeout is set to DI_CAN_TIMEOUT_GC_SKIP it is not garbage collected * * When di_can_msg.timeout is set to DI_CAN_TIMEOUT_GC_IMMEDIATE it is immediate collected * @note This should be run with ~50ms interval from thread with all being freed up */ void di_can_msg_gc(struct di_can_ctx *ctx, enum di_can_gc gc); /** @} */ /** * @name Receive * @{ */ /** * Initialize message receive semaphores * @warning This is called by di_can_msg_init and should not be called directly */ void di_can_msg_recv_init(struct di_can_ctx *ctx); /** * Reset message receive counters */ void di_can_msg_recv_reset(struct di_can_ctx *ctx); /** * Set timeout for di_can_msg_recv_wait */ void di_can_msg_recv_set_timeout(struct di_can_ctx *ctx, uint32_t timeout_ms); /** * Set block/nonblocking mode for di_can_msg_recv_wait * @param ctx CAN context * @param recv_is_blocking Flag to set blocking/non-blocking mode on recv */ void di_can_msg_recv_set_blocking(struct di_can_ctx *ctx, bool recv_is_blocking); /** * Increment message counter for received msgtype */ void di_can_msg_recv_incr(struct di_can_ctx *ctx, enum di_can_msgtype type); /** * Decrement message counter for received msgtype */ void di_can_msg_recv_decr(struct di_can_ctx *ctx, enum di_can_msgtype type); /** * Wait until di_can_msgtype is received the behaviour is controlled with * - di_can_msg_recv_set_timeout * - di_can_msg_recv_set_blocking * @param ctx CAN context * @param type Wait until one message of type is ready */ di_errno_t di_can_msg_recv_wait(struct di_can_ctx *ctx, enum di_can_msgtype type); /** * Signal a di_can_msgtype is ready */ void di_can_msg_recv_post(struct di_can_ctx *ctx, enum di_can_msgtype type); /** * Check if one or more messages are received of type msgtype */ bool di_can_msg_recv_is_ready(struct di_can_ctx *ctx, enum di_can_msgtype type); /** @} */ /** * @name Message ready * @{ */ void di_can_msg_set_ready(struct di_can_msg **msg); /** * Get ready (assembled) message based on msgtype * When DI_CAN_MSGTYPE_ANY any is given, the first ready message in the queue is returned */ struct di_can_msg *di_can_msg_get_ready_type(struct di_can_ctx *ctx, enum di_can_msgtype type); /** * Get ready (assembled) message based on canid * - enum di_can_msgtype * - enum di_can_raw_dtypes, enum di_rpc_types or enum di_can_net_dtypes * - enum di_can_transfertype */ struct di_can_msg *di_can_msg_get_ready_canid(struct di_can_ctx *ctx, uint32_t canid); /** * Synchronize the di_time with the incoming di_can_msg when: * * Transfertype is DI_CAN_TRANSFERTYPE_PUBLISH * * Msgtype is DI_CAN_MSGTYPE_RAW * * Datatype is DI_CAN_RAW_DTYPE_DINET_TIME * * Payload type is DI_CAN_PTYPE_U64 * * Size is 8 bytes * @param ctx CAN context * @param msg CAN message to be checked * @return true when msg time is synced * @note The caller needs to cleanup the message with di_can_msg_free */ bool di_can_msg_timesync(struct di_can_ctx *ctx, const struct di_can_msg *msg); /** @} */ /** * @name Read message fields * @{ */ uint32_t di_can_msg_read_src_id(const struct di_can_msg *msg); uint32_t di_can_msg_read_dst_id(const struct di_can_msg *msg); enum di_can_ptypes di_can_msg_read_ptype(const struct di_can_msg *msg); uint16_t di_can_msg_read_crc(const struct di_can_msg *msg); uint8_t di_can_msg_read_emflags(const struct di_can_msg *msg); uint16_t di_can_msg_read_emdata(const struct di_can_msg *msg); uint16_t di_can_msg_read_psize(const struct di_can_msg *msg); di_errno_t di_can_msg_read_data_bool(const struct di_can_msg *msg, bool *value); di_errno_t di_can_msg_read_data_i8(const struct di_can_msg *msg, int8_t *value); di_errno_t di_can_msg_read_data_i16(const struct di_can_msg *msg, int16_t *value); di_errno_t di_can_msg_read_data_i32(const struct di_can_msg *msg, int32_t *value); di_errno_t di_can_msg_read_data_i64(const struct di_can_msg *msg, int64_t *value); di_errno_t di_can_msg_read_data_u8(const struct di_can_msg *msg, uint8_t *value); di_errno_t di_can_msg_read_data_u16(const struct di_can_msg *msg, uint16_t *value); di_errno_t di_can_msg_read_data_u32(const struct di_can_msg *msg, uint32_t *value); di_errno_t di_can_msg_read_data_u64(const struct di_can_msg *msg, uint64_t *value); di_errno_t di_can_msg_read_data_float(const struct di_can_msg *msg, float *value); di_errno_t di_can_msg_read_data_double(const struct di_can_msg *msg, double *value); di_errno_t di_can_msg_read_data_di_errno(const struct di_can_msg *msg, di_errno_t *err); /** @} */ /** * @name Write message fields * @{ */ di_errno_t di_can_msg_write_src_id(struct di_can_msg *msg, uint32_t src_id); di_errno_t di_can_msg_write_dst_id(struct di_can_msg *msg, uint32_t dst_id); di_errno_t di_can_msg_write_ptype(struct di_can_msg *msg, enum di_can_ptypes ptype); di_errno_t di_can_msg_write_crc(struct di_can_msg *msg, uint16_t crc); di_errno_t di_can_msg_write_psize(struct di_can_msg *msg, uint16_t psize); di_errno_t di_can_msg_write_data_bool(struct di_can_msg *msg, bool value); di_errno_t di_can_msg_write_data_i8(struct di_can_msg *msg, int8_t value); di_errno_t di_can_msg_write_data_i16(struct di_can_msg *msg, int16_t value); di_errno_t di_can_msg_write_data_i32(struct di_can_msg *msg, int32_t value); di_errno_t di_can_msg_write_data_i64(struct di_can_msg *msg, int64_t value); di_errno_t di_can_msg_write_data_u8(struct di_can_msg *msg, uint8_t value); di_errno_t di_can_msg_write_data_u16(struct di_can_msg *msg, uint16_t value); di_errno_t di_can_msg_write_data_u32(struct di_can_msg *msg, uint32_t value); di_errno_t di_can_msg_write_data_u64(struct di_can_msg *msg, uint64_t value); di_errno_t di_can_msg_write_data_float(struct di_can_msg *msg, float value); di_errno_t di_can_msg_write_data_double(struct di_can_msg *msg, double value); di_errno_t di_can_msg_write_data_string(struct di_can_msg *msg, const char *value); di_errno_t di_can_msg_write_data_di_errno(struct di_can_msg *msg, di_errno_t err); /** Copy raw buffer into can message at correct offset */ void di_can_msg_memcpy(struct di_can_msg *msg, const uint8_t *buffer, size_t size); /** @} */ /** * @name CAN identifier * @{ */ /** * Set CAN message CAN identifier * @note Only the msgtype, ttype and type fields are overwritten. The other bits are * untouched */ void di_can_msg_set_canid(struct di_can_msg *msg, const enum di_can_msgtype msgtype, const enum di_can_transfertype ttype, const uint16_t dtype); /** @} */ /** * @name Message checking helpers * @{ */ /** * Check if the message has the request transfertype set * @param msg CAN message * @return true when the can message is a request, false otherwise */ bool di_can_msg_is_request(const struct di_can_msg *msg); /** @} */ /** * @name Extended metadata * @{ */ /** * Set the Extended Metadata RT flag which marks the message a Realtime * @param[in] msg CAN message * @param state State of the flag */ void di_can_msg_emflag_set_rt(struct di_can_msg *msg, const bool state); /** * Set the Extended Metadata EM disable flag which marks the message * the emdata and emflags fields are reserved (compatibility with older can stack messages) * @param[in] msg CAN message * @param state State of the flag */ void di_can_msg_emflag_set_em_disable(struct di_can_msg *msg, const bool state); /** * Get the RT flag from the Extended Metadata flags field * @return true when flag is set, false otherwise */ bool di_can_msg_emflag_rt(const struct di_can_msg *msg); /** * Get the EM disable flag from the Extended Metadata flags field * @return true when flag is set, false otherwise */ bool di_can_msg_emflag_em_disable(const struct di_can_msg *msg); /** @} */ #ifdef __cplusplus } #endif /** @} */ #endif /* LIBDI_INCLUDE_DI_CAN_MSG_H_ */