234 lines
7.2 KiB
C
234 lines
7.2 KiB
C
/**
|
|
* @file include/di/rpc/lowlevel.h
|
|
* @brief Lowlevel RPC
|
|
* @date Aug 27, 2015
|
|
* @author rheijden
|
|
* @copyright 2015 Dual Inventive Technology Centre B.V.
|
|
*
|
|
* @defgroup rpc_ll DI-Net RPC lowlevel
|
|
* @{
|
|
*/
|
|
#ifndef INCLUDE_DI_RPC_LL_H_
|
|
#define INCLUDE_DI_RPC_LL_H_
|
|
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <di/types.h>
|
|
#include <di/buffer.h>
|
|
#include <di/device.h>
|
|
#include <mpack/mpack.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/**< DI-Net lowlevel message header magic */
|
|
#define DI_RPC_LL_MSG_HDR_MAGIC \
|
|
"\x44\x4a\x52"
|
|
#define DI_RPC_LL_MSG_HDR_MAGIC_SIZE \
|
|
(sizeof(DI_RPC_LL_MSG_HDR_MAGIC) - 1)
|
|
|
|
#define DI_RPC_LL_MSG_HDR_TYPE_LEN 1U /** Message type field length in bytes */
|
|
#define DI_RPC_LL_MSG_HDR_TYPE_OFFSET 3U /** Message type field byte offset, after magic */
|
|
|
|
#define DI_RPC_LL_MSG_HDR_SIZE_LEN 2U /** Message size field length in bytes */
|
|
#define DI_RPC_LL_MSG_HDR_SIZE_OFFSET 4U /** Message size field byte offset, after magic */
|
|
|
|
#define DI_RPC_LL_MSG_HANDSHAKE_DEVICE_UID_SIZE 32U /** Message handshake device:uid size */
|
|
|
|
/** DI-Net lowlevel message header size */
|
|
#define DI_RPC_LL_MSG_HDR_SIZE \
|
|
(DI_RPC_LL_MSG_HDR_MAGIC_SIZE + \
|
|
DI_RPC_LL_MSG_HDR_TYPE_LEN + \
|
|
DI_RPC_LL_MSG_HDR_SIZE_LEN)
|
|
|
|
#define DI_RPC_LL_MSG_REPLY_MKAY "MKAY"
|
|
#define DI_RPC_LL_MSG_REPLY_MKAY_SIZE (sizeof(DI_RPC_LL_MSG_REPLY_MKAY) - 1)
|
|
#define DI_RPC_LL_MSG_REPLY_WTF "WTF"
|
|
#define DI_RPC_LL_MSG_REPLY_WTF_SIZE (sizeof(DI_RPC_LL_MSG_REPLY_WTF) - 1)
|
|
|
|
/** Message status */
|
|
enum di_rpc_ll_msg_status {
|
|
DI_RPC_LL_MSG_STAT_ERR_INVALID_TYPE = -3, /**< Message has invalid type */
|
|
DI_RPC_LL_MSG_STAT_ERR_MORE_DATA_NEEDED = -2, /**< Message incomplete, buffer needs more data */
|
|
DI_RPC_LL_MSG_STAT_UNKNOWN = -1, /**< Unknown message, header magic not found */
|
|
DI_RPC_LL_MSG_STAT_OK = 0, /**< Message ok */
|
|
};
|
|
|
|
/** DI-Net available lowlevel message types */
|
|
enum di_rpc_ll_msg_type {
|
|
DI_RPC_LL_MSG_TYPE_UNKNOWN = 0x00, /**< Unknown */
|
|
DI_RPC_LL_MSG_TYPE_HS_REQUEST = 0x01, /**< Handshake request */
|
|
DI_RPC_LL_MSG_TYPE_REPLY = 0x02, /**< Reply @see di_rpc_ll_msg_reply_status */
|
|
DI_RPC_LL_MSG_TYPE_REGISTER = 0x03, /**< Register device:uid on existing connection */
|
|
DI_RPC_LL_MSG_TYPE_UNREGISTER = 0x04, /**< Unregister device:uid on existing connection */
|
|
DI_RPC_LL_MSG_TYPE_PLAIN = 0x10, /**< Plain DI-Net RPC message */
|
|
DI_RPC_LL_MSG_TYPE_ENCRYPTED = 0x20, /**< Encrypted DI-Net RPC message */
|
|
DI_RPC_LL_MSG_TYPE_TIME = 0x40 /**< DI-Net time request/reply message */
|
|
};
|
|
|
|
/** Reply status when message is of type `DI_RPC_LL_MSG_TYPE_REPLY` */
|
|
enum di_rpc_ll_msg_reply {
|
|
DI_RPC_LL_MSG_REPLY_UNKNOWN, /**< Unknown reply status */
|
|
DI_RPC_LL_MSG_REPLY_ERROR, /**< Reply error (`"WTF"`) */
|
|
DI_RPC_LL_MSG_REPLY_OK, /**< Reply ok (`"MKAY"`) */
|
|
DI_RPC_LL_MSG_REPLY_TIME /**< Reply DI-Net timestamp */
|
|
};
|
|
|
|
/** DI-Net RPC lowlevel message */
|
|
struct di_rpc_ll_msg {
|
|
enum di_rpc_ll_msg_status status; /**< Message status */
|
|
enum di_rpc_ll_msg_reply reply; /**< Reply status when status == `DI_RPC_LL_MSG_TYPE_REPLY` */
|
|
enum di_rpc_ll_msg_type type; /**< Message type */
|
|
uint8_t *data; /**< Message data payload */
|
|
uint16_t size; /**< Message data payload size */
|
|
size_t remaining; /**< Bytes remaining in buf after message */
|
|
struct di_buffer *buf; /**< Buffer of current message */
|
|
};
|
|
|
|
/**
|
|
* @name Encoding
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Encode message from src buffer with src->used into dst buffer
|
|
* @param dst Destination buffer
|
|
* @param src Source buffer
|
|
* @param type Lowlevel message type of source buffer
|
|
*/
|
|
di_errno_t di_rpc_ll_encode(struct di_buffer *dst, struct di_buffer *src, enum di_rpc_ll_msg_type type);
|
|
|
|
/**
|
|
* Encode handshake which uses device:uid
|
|
* @param buf The destination buffer to encode the handshake message to
|
|
* @param device_uid The device:uid must be 33 bytes which must include the NULL termination
|
|
* @retval DNOK when device_uid is correct and buf holds the encoded message
|
|
*/
|
|
di_errno_t di_rpc_ll_encode_handshake(struct di_buffer *buf,
|
|
const char device_uid[DI_DEVICE_UID_LEN]);
|
|
|
|
/**
|
|
* Write register which uses device:uid
|
|
* @param buf The destination buffer to encode the register message to
|
|
* @param device_uid The device:uid must be 33 bytes which must include the NULL termination
|
|
* @retval DNOK when device_uid is correct and buf holds the encoded message
|
|
*/
|
|
di_errno_t di_rpc_ll_encode_register(struct di_buffer *buf,
|
|
const char device_uid[DI_DEVICE_UID_LEN]);
|
|
|
|
/**
|
|
* Write time request
|
|
*/
|
|
di_errno_t di_rpc_ll_encode_time_request(struct di_buffer *buf);
|
|
|
|
/**
|
|
* Encode time
|
|
*/
|
|
di_errno_t di_rpc_ll_encode_time(struct di_buffer *buf, uint64_t time);
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/**
|
|
* @name Decoding
|
|
* @{
|
|
*/
|
|
|
|
/** Decode buffer into rpc lowlevel message
|
|
* @note DI_RPC_LL_MSG_STAT_OK -> DNOK, set llmsg->status accordingly ... DNOK is more convenient
|
|
*/
|
|
enum di_rpc_ll_msg_status di_rpc_ll_decode(struct di_buffer *buf, struct di_rpc_ll_msg *llmsg);
|
|
|
|
/**
|
|
* Decode the time from the message
|
|
*/
|
|
di_errno_t di_rpc_ll_decode_time(struct di_rpc_ll_msg *llmsg, uint64_t *time);
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/**
|
|
* @name RPC reader
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Prepare RPC reader from buffer. It decodes the message header.
|
|
*/
|
|
di_errno_t di_rpc_ll_rpc_reader_start(mpack_writer_t *reader, struct di_buffer *buf);
|
|
|
|
/**
|
|
* Finish RPC reader.
|
|
* @todo how to return there is more data in buf, with di_errno?
|
|
*/
|
|
di_errno_t di_rpc_ll_rpc_reader_finish(mpack_writer_t *reader, struct di_buffer *buf);
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/**
|
|
* @name RPC writer
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Prepare RPC writer with buffer. Skips enough bytes to write the lowlevel header.
|
|
*/
|
|
di_errno_t di_rpc_ll_rpc_writer_start(mpack_writer_t *writer, struct di_buffer *buf);
|
|
|
|
/**
|
|
* Finish RPC writer. Writers the lowlevel header.
|
|
*/
|
|
di_errno_t di_rpc_ll_rpc_writer_finish(mpack_writer_t *writer, struct di_buffer *buf);
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
/**
|
|
* @name Lowlevel operations
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* Write lowlevel message header
|
|
* @note The buffer size must be minimal size of DI_RPC_LL_MSG_HDR_SIZE
|
|
* @param buf Target buffer (starts writing at offset 0)
|
|
* @param type Lowlevel message type
|
|
* @param data_len Length of data payload
|
|
* @note The current DI-Net RPC protocol specification has a limit of uint16_t frame length
|
|
*/
|
|
di_errno_t di_rpc_ll_write_header(struct di_buffer *buf, enum di_rpc_ll_msg_type type, size_t data_len);
|
|
|
|
/**
|
|
* Write lowlevel message data payload
|
|
* @note This only writes the payload, the header needs to be written first with di_rpc_ll_write_header.
|
|
* @param buf Target buffer (starts writing at offset after the lowlevel header)
|
|
* @param data Data payload
|
|
* @param len Data payload length
|
|
* @note The current DI-Net RPC protocol specification has a limit of uint16_t frame length
|
|
*/
|
|
di_errno_t di_rpc_ll_write_data(struct di_buffer *buf, const uint8_t *data, size_t len);
|
|
|
|
/**
|
|
* Copy remaining data from lowlevel message into a di_buffer
|
|
* @param dst Destination buffer (starts writing at offset 0)
|
|
* @param src Decoded lowlevel message which holds remaining data
|
|
*/
|
|
di_errno_t di_rpc_ll_memcpy_remaining(struct di_buffer *dst, struct di_rpc_ll_msg *src);
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
/** @} */
|
|
|
|
#endif /* INCLUDE_DI_RPC_LL_H_ */
|