#ifndef _INCLUDE_FRAME_ #define _INCLUDE_FRAME_ #include #include #include #include #include //#define COM_FRAME_DEBUG #define COM_FRAME_MAGIC_NUMBER 0xEB90 #define COM_FRAME_DEFAULT_PAYLOAD_LENGTH 2 #define COM_FRAME_PADDING_DATA 0xAA #define COM_FRAME_TYPE_COMMAND 0x00C0 #define COM_FRAME_TYPE_SPEECH_INJECTED 0x00D0 #define COM_FRAME_TYPE_SPEECH_TEXT 0x00D0 #define COM_FRAME_TYPE_SPEECH_PCM 0x00D1 #define COM_FRAME_TYPE_AUDIO 0x00D1 #define COM_FRAME_TYPE_REQUEST 0x0090 #define COM_FRAME_TYPE_TELEMETRY_REQUEST 0x00B0 #define COM_FRAME_TYPE_TELEMETRY_ANSWER 0x0070 #define COM_FRAME_TYPE_TELEMETRY_ERROR 0x0071 #define COM_FRAME_ADDRESS_VOIX 0x011A // added by gejp start 2024.10.03 #define COM_FRAME_TYPE_GRANT_TIME 0x0085 #define COM_FRAME_TYPE_VOICE_ANNOUNCEMENT_AND_ALARM_CODE_REQUEST 0x0083 #define COM_FRAME_TYPE_VOICE_ANNOUNCEMENT_AND_ALARM_CODE_RESPONSE 0x0083 #define byteswapl(x) (0xff000000 & x << 24) \ |(0x00ff0000 & x << 8) \ |(0x0000ff00 & x >> 8) \ |(0x000000ff & x >> 24) #define byteswaps(x) (0xff00 & x << 8) \ |(0x00ff & x >> 8) #ifdef __cplusplus extern "C" { #endif struct ComFrameHeader { uint16_t magic_number; uint16_t address; uint16_t type; uint16_t length; } __attribute__((packed)); typedef struct _ComFrame { struct ComFrameHeader header; uint8_t payload[COM_FRAME_DEFAULT_PAYLOAD_LENGTH]; } ComFrame; #define ComFrame_HEADER(frame) ((struct ComFrameHeader*)(frame)) #define ComFrame_ADDRESS(frame) (ComFrame_HEADER(frame)->address) #define ComFrame_TYPE(frame) (ComFrame_HEADER(frame)->type) #define ComFrame_PAYLOAD(frame) (frame->payload) #define ComFrame_PAYLOAD_LENGTH(frame) (ComFrame_HEADER(frame)->length - 1) #define ComFrame_PAYLOAD_ALIGNED_LENGTH(frame) (ComFrame_HEADER(frame)->length & (~1)) #define ComFrame_CHECKSUM(frame) (((uint16_t*)&(frame)->payload)[ComFrame_HEADER(frame)->length >> 1]) #define ComFrame_LENGTH(frame) (sizeof(ComFrame) + ComFrame_PAYLOAD_ALIGNED_LENGTH(frame) - COM_FRAME_DEFAULT_PAYLOAD_LENGTH + sizeof(uint16_t)) /// @brief init frame header struct /// @param header header struct /// @param address device address /// @param type frame type /// @param length frame payload length + 1 static __inline void ComFrameHeader_Init(struct ComFrameHeader* header, uint16_t address, uint16_t type, uint16_t length) { header->magic_number = COM_FRAME_MAGIC_NUMBER; header->address = address; header->type = type; header->length = length; } /// @brief swap haeder structure endianness /// @param header header struct static __inline void ComFrameHeader_SwapEndian(struct ComFrameHeader* header) { header->magic_number = byteswaps(header->magic_number); header->address = byteswaps(header->address); header->type = byteswaps(header->type); header->length = byteswaps(header->length); } /// @brief check magic number of header struct /// @param header header struct /// @param swap_endian whether to switch endianness /// @return structure validity, true means invalid static __inline bool ComFrameHeader_Check(const struct ComFrameHeader* header, const bool swap_endian) { uint16_t number = header->magic_number; if (swap_endian) { number = byteswaps(number); } if (number != COM_FRAME_MAGIC_NUMBER) { return true; } number = header->address; if (swap_endian) { number = byteswaps(number); } if (number != COM_FRAME_ADDRESS_VOIX) { return true; } if (ComFrame_LENGTH(header) < 12) { return true; } number = header->type; if (swap_endian) { number = byteswaps(number); } // if ( // commented by gejp 20201007 // number != COM_FRAME_TYPE_COMMAND && // number != COM_FRAME_TYPE_SPEECH_INJECTED && // number != COM_FRAME_TYPE_SPEECH_TEXT && // number != COM_FRAME_TYPE_SPEECH_PCM && // number != COM_FRAME_TYPE_AUDIO && // number != COM_FRAME_TYPE_REQUEST && // number != COM_FRAME_TYPE_TELEMETRY_REQUEST // ) { // return true; // } return false; } /// @brief create new com frame uninitialized /// @param payload_length length of payload /// @return new uinitialized frame static __inline ComFrame* ComFrame_NewUninited(uint16_t payload_length) { if (payload_length & 1) { payload_length += 1; } return (ComFrame*)malloc(sizeof(ComFrame) + payload_length - COM_FRAME_DEFAULT_PAYLOAD_LENGTH + sizeof(uint16_t)); } /// @brief get frame checksum /// @param frame com frame struct /// @param swap_endian whether to switch endianness /// @return checksum static __inline uint16_t ComFrame_Checksum(const ComFrame* frame, const bool swap_endian) { const uint16_t* buffer = (const uint16_t*)&frame->payload; uint16_t length = ComFrame_HEADER(frame)->length; if (swap_endian) { length = byteswaps(length); } return buffer[length >> 1]; } /// @brief get frame length /// @param frame com frame struct /// @param swap_endian whether to switch endianness /// @return length of the frame static __inline uint32_t ComFrame_Length(const ComFrame* frame, const bool swap_endian) { uint16_t length = ComFrame_HEADER(frame)->length; if (swap_endian) { length = byteswaps(length); } length &= (~1); return sizeof(ComFrame) + length - COM_FRAME_DEFAULT_PAYLOAD_LENGTH + sizeof(uint16_t); } /// @brief destroy the frame /// @param frame the frame to destroy static __inline void ComFrame_Del(ComFrame* frame) { if (frame) free(frame); } /// @brief flush receive buffer /// @param offset the size used, usually the length of the data frame /// @param buffer receive buffer /// @param p_cached_size cache size to reset, not null /// @note please set the offset to 0 after calling the function if used with ComFrame_ReceiveEx static __inline void ComFrame_ReceiveFlushBuffer(size_t offset, uint8_t* buffer, size_t* p_cached_size) { *p_cached_size -= offset; memmove(buffer, buffer + offset, *p_cached_size); } /// @brief calculate the checksum of the specified payload /// @param header header struct (must be little endian) /// @param payload payload data /// @return checksum uint16_t ComFramePayload_EvalChecksum(const struct ComFrameHeader* header, const void* payload); /// @brief verify the checksum of the specified payload /// @param header header struct (must be little endian) /// @param payload payload data /// @param checksum checksum to verify /// @param swap_checksum_endian whether to switch checksum endianness /// @return payload validity, true means invalid bool ComFramePayload_VerifyChecksum(const struct ComFrameHeader* header, const void* payload, uint16_t checksum); /// @brief create new com frame from header and known checksum /// @param header header struct (must be little endian) /// @param payload payload data /// @param checksum payload checksum /// @param swap_endian whether to switch endianness /// @return new frame ComFrame* ComFrame_FromHeaderEx(const struct ComFrameHeader* header, const void* payload, uint16_t checksum, const bool swap_endian); /// @brief create new com frame from header /// @param header header struct (must be little endian) /// @param payload payload data /// @param swap_endian whether to switch endianness /// @return new frame ComFrame* ComFrame_FromHeader(const struct ComFrameHeader* header, const void* payload, const bool swap_endian); /// @brief create new com frame /// @param address device address /// @param type frame type /// @param payload payload data /// @param payload_length payload data real length /// @param swap_endian whether to switch endianness /// @return new frame ComFrame* ComFrame_New(uint16_t address, uint16_t type, const void* payload, uint16_t payload_length, const bool swap_endian); /// @brief update frame payload and checksum /// @param frame com frame struct /// @param payload payload data or NULL, if set to NULL, the payload will not be updated. /// @param payload_length payload data real length /// @param swap_endian whether to switch endianness void ComFrame_UpdatePayload(ComFrame* frame, const void* payload, const uint16_t payload_length, const bool swap_endian); /// @brief verify frame checksum /// @param frame con frame struct /// @param swap_endian whether to switch endianness /// @return structure validity, true means invalid bool ComFrame_Verify(const ComFrame* frame, const bool swap_endian); /// @brief send a frame to com /// @param tty_id com tty id /// @param frame con frame struct /// @param swap_endian whether to switch endianness /// @return error number, 0 means ok ssize_t ComFrame_Send(int tty_id, const ComFrame* frame, const bool swap_endian); /// @brief receive a frame from com /// @param tty_id com tty id /// @param buffer_size receive buffer size /// @param swap_endian whether to switch endianness /// @return new frame (little endian) or NULL ComFrame* ComFrame_Receive(int tty_id, const size_t buffer_size, const bool swap_endian); /// @brief receive a frame from com /// @param tty_id com tty id /// @param buffer buffer pointer, create a new one when it is NULL /// @param buffer_size receive buffer size /// @param p_offset offset will be set if not null /// @param p_cached_size cached size will be set if not null /// @param p_run_flag run flag pointer, if the inner date is set to false, the function will stop and return NULL /// @param swap_endian whether to switch endianness /// @return frame (little endian) in buffer or NULL ComFrame* ComFrame_ReceiveEx(int tty_id, void* buffer, const size_t buffer_size, size_t* p_offset, size_t* p_cached_size, bool* p_run_flag, const bool swap_endian); int get_com_tty_id(const char* path, unsigned int baudRate); void init_tty(int tty_id, unsigned int baudRate); #ifdef __cplusplus } #endif #endif