├── benchmark ├── images │ └── logger.png ├── peer_a.c └── peer_b.c ├── utils ├── uni_crc16.h ├── uni_interruptable.h ├── uni_log.h ├── uni_crc16.c ├── uni_interruptable.c └── uni_log.c ├── README.md ├── inc └── uni_communication.h └── src └── uni_communication.c /benchmark/images/logger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/junlon2006/libUartCommProtocol/HEAD/benchmark/images/logger.png -------------------------------------------------------------------------------- /utils/uni_crc16.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2018-2019 Junlon2006 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | ************************************************************************** 18 | * 19 | * Description : uni_crc16.h 20 | * Author : junlon2006@163.com 21 | * Date : 2019.11.11 22 | * 23 | **************************************************************************/ 24 | 25 | #ifndef CRC16_INC_UNI_CRC16_H_ 26 | #define CRC16_INC_UNI_CRC16_H_ 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | #include 33 | 34 | uint16_t crc16(const char *buf, int len); 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | #endif // CRC16_INC_UNI_CRC16_H_ 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libUartCommProtocol [Linux posix platform avaliable] 2 | 跨平台协议栈版本:https://github.com/junlon2006/libUartCommProtocolV2 3 | UART Communication Protocol provides uart transmission like TCP/UDP feature 4 | RWND = 1【low memory usage】easy way to make reliable transmission, support ACK/NACK only 5 | protocol stack like TFTP 6 | cover both big-endian and little-endian arch 7 | ``` 8 | 1:TCP like: uart in 921600bps mode [symbol error rate:1bit/1000000bit, one bit error every 122KB, payload_len 512 byte] can reach 64KB/s reliable transmission【full-duplex】total memory usage about 1KB. 9 | 10 | 2:UDP like: uart in 921600bps mode, payload_len 512 byte can reach 94KB/s unreliable transmission【full-duplex】. 11 | 12 | 3:recommend payload_len max len 512 byte 13 | int CommProtocolPacketAssembleAndSend(CommCmd cmd, char *payload, 14 | CommPayloadLen payload_len, 15 | CommAttribute *attribute); 16 | ``` 17 | ``` 18 | Linux x86 benchmark demo, ubuntu 16.04 recommend 19 | ./build.sh 20 | ./peera 21 | ./peerb 22 | ``` 23 | benchmark 24 | RT-Thread rtos下UDP模式84KB/s UART传输能力,同软硬件条件下TCP模式极限性能71KB/s,BW RATIO≈84.5%(需考虑串口连接线长度,目前几毫米级别) 25 | 协议栈性能瓶颈:(不考虑RWND=1吞吐问题,设计如此)checksum约占30%性能开销,crc16性能远低于TCP checksum算法,性能差2~3倍 26 | tcp checksum https://github.com/junlon2006/linux-c/issues/96 27 | Uart通道下忽略该瓶颈 28 | ![image](https://github.com/junlon2006/libUartCommProtocol/blob/master/benchmark/images/logger.png) 29 | 30 | -------------------------------------------------------------------------------- /utils/uni_interruptable.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2018-2019 Junlon2006 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | ************************************************************************** 18 | * 19 | * Description : uni_interruptable.h 20 | * Author : junlon2006@163.com 21 | * Date : 2019.03.17 22 | * 23 | **************************************************************************/ 24 | #ifndef INTERRUPTABLE_SLEEP_INC_UNI_INTERRUPTABLE_H_ 25 | #define INTERRUPTABLE_SLEEP_INC_UNI_INTERRUPTABLE_H_ 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | typedef void* InterruptHandle; 32 | 33 | InterruptHandle InterruptCreate(); 34 | int InterruptDestroy(InterruptHandle handle); 35 | int InterruptableSleep(InterruptHandle handle, int sleep_msec); 36 | int InterruptableBreak(InterruptHandle handle); 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | #endif 42 | -------------------------------------------------------------------------------- /utils/uni_log.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2018-2019 Junlon2006 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | ************************************************************************** 18 | * 19 | * Description : uni_log.h 20 | * Author : junlon2006@163.com 21 | * Date : 2019.03.17 22 | * 23 | **************************************************************************/ 24 | #ifndef LOGGER_INC_UNI_LOG_H_ 25 | #define LOGGER_INC_UNI_LOG_H_ 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | typedef enum { 32 | N_LOG_NONE = -1, 33 | N_LOG_ERROR, 34 | N_LOG_WARN, 35 | N_LOG_TRACK, 36 | N_LOG_DEBUG, 37 | N_LOG_RAW, 38 | N_LOG_ALL 39 | } LogLevel; 40 | 41 | typedef struct { 42 | int enable_time; 43 | int enable_thread_id; 44 | int enable_function_line; 45 | int enable_color; 46 | int enable_file; 47 | LogLevel set_level; 48 | } LogConfig; 49 | 50 | int LogInitialize(LogConfig logConfig); 51 | int LogFinalize(void); 52 | int LogLevelSet(LogLevel level); 53 | 54 | int LogLevelValid(LogLevel level); 55 | int LogWrite(LogLevel level, const char *tags, const char *function, 56 | int line, char *fmt, ...); 57 | 58 | #define LOG(level, tag, fmt, ...) do { \ 59 | if (LogLevelValid(level)) { \ 60 | LogWrite(level, tag, __FUNCTION__, __LINE__, (char *)fmt, ##__VA_ARGS__); \ 61 | } \ 62 | } while (0) 63 | 64 | #define LOGD(tag, fmt, ...) LOG(N_LOG_DEBUG, tag, fmt, ##__VA_ARGS__) 65 | #define LOGT(tag, fmt, ...) LOG(N_LOG_TRACK, tag, fmt, ##__VA_ARGS__) 66 | #define LOGW(tag, fmt, ...) LOG(N_LOG_WARN, tag, fmt, ##__VA_ARGS__) 67 | #define LOGE(tag, fmt, ...) LOG(N_LOG_ERROR, tag, fmt, ##__VA_ARGS__) 68 | #define LOGR(tag, fmt, ...) LOG(N_LOG_RAW, tag, fmt, ##__VA_ARGS__) 69 | 70 | #ifdef __cplusplus 71 | } /* __cplusplus */ 72 | #endif 73 | #endif /* LOGGER_INC_UNI_LOG_H_ */ 74 | -------------------------------------------------------------------------------- /utils/uni_crc16.c: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2018-2019 Junlon2006 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | ************************************************************************** 18 | * 19 | * Description : uni_crc16.c 20 | * Author : junlon2006@163.com 21 | * Date : 2019.11.11 22 | * 23 | **************************************************************************/ 24 | 25 | #include "uni_crc16.h" 26 | 27 | static const uint16_t crc16tab[256]= { 28 | 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 29 | 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 30 | 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 31 | 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 32 | 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 33 | 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 34 | 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 35 | 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 36 | 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 37 | 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 38 | 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 39 | 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 40 | 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 41 | 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 42 | 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 43 | 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 44 | 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 45 | 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 46 | 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 47 | 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 48 | 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 49 | 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 50 | 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 51 | 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 52 | 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 53 | 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 54 | 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 55 | 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 56 | 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 57 | 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 58 | 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 59 | 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 60 | }; 61 | 62 | uint16_t crc16(const char *buf, int len) { 63 | int counter; 64 | uint16_t crc = 0; 65 | for (counter = 0; counter < len; counter++) { 66 | crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++) & 0x00FF]; 67 | } 68 | return crc; 69 | } 70 | -------------------------------------------------------------------------------- /inc/uni_communication.h: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2017-2017 Junlon2006 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | ************************************************************************** 18 | * 19 | * Description : uni_communication.h 20 | * Author : junlon2006@163.com 21 | * Date : 2020.03.04 22 | * 23 | **************************************************************************/ 24 | #ifndef UTILS_UART_INC_UNI_COMMUNICATION_H_ 25 | #define UTILS_UART_INC_UNI_COMMUNICATION_H_ 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | #define PACKED __attribute__ ((packed)) 32 | 33 | #ifndef uni_bool 34 | #define uni_bool int 35 | #endif 36 | 37 | typedef unsigned short CommCmd; 38 | typedef unsigned short CommPayloadLen; 39 | typedef int (*CommWriteHandler)(char *buf, int len); 40 | 41 | typedef struct { 42 | CommCmd cmd; /* air condition ctrl cmd such as power_on, power_off */ 43 | CommPayloadLen payload_len; /* parameter length of command */ 44 | char* payload; /* parameter of command */ 45 | } PACKED CommPacket; 46 | 47 | typedef struct { 48 | uni_bool reliable; /* true means this packet need acked, reliable transmission */ 49 | } CommAttribute; 50 | 51 | typedef enum { 52 | E_UNI_COMM_ALLOC_FAILED = -10001, 53 | E_UNI_COMM_BUFFER_PTR_NULL, 54 | E_UNI_COMM_PAYLOAD_TOO_LONG, 55 | E_UNI_COMM_PAYLOAD_ACK_TIMEOUT, 56 | } CommProtocolErrorCode; 57 | 58 | typedef void (*CommRecvPacketHandler)(CommPacket *packet); 59 | 60 | /** 61 | * @brief communication protocol init 62 | * @param write_handler the write handler, such as UartWrite int uni_uart.h 63 | * @param recv_handler when uart data disassemble as communication protocol frame, 64 | the frame will be translate to struct CommPacket, 65 | then the CommPacket will callback to user 66 | * @return 0 means success, -1 means failed 67 | */ 68 | int CommProtocolInit(CommWriteHandler write_handler, CommRecvPacketHandler recv_handler); 69 | 70 | /** 71 | * @brief communication protocol finalize 72 | * @param void 73 | * @return void 74 | */ 75 | void CommProtocolFinal(void); 76 | 77 | /** 78 | * @brief send one packet(communication protocol frame format) 79 | * @param cmd command type, should define as enum (such as power_on、power_off) 80 | * @param payload the payload of cmd, can set as NULL 81 | * @param payload_len the payload length 82 | * @param attribute the attribute for this packet, such as packet need ACK 83 | * @return 0 means success, other means failed 84 | */ 85 | int CommProtocolPacketAssembleAndSend(CommCmd cmd, char *payload, 86 | CommPayloadLen payload_len, 87 | CommAttribute *attribute); 88 | 89 | /** 90 | * @brief receive orignial uart data 91 | * @param buf the uart data buffer pointer 92 | * @param len the uart data length 93 | * @return void 94 | */ 95 | void CommProtocolReceiveUartData(unsigned char *buf, int len); 96 | 97 | #ifdef __cplusplus 98 | } 99 | #endif 100 | #endif // UTILS_UART_INC_UNI_COMMUNICATION_H_ 101 | -------------------------------------------------------------------------------- /utils/uni_interruptable.c: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2018-2019 Junlon2006 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | ************************************************************************** 18 | * 19 | * Description : uni_interruptable.c 20 | * Author : junlon2006@163.com 21 | * Date : 2019.03.17 22 | * 23 | **************************************************************************/ 24 | #include "uni_interruptable.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define uni_min(x,y) (x > y ? y : x) 33 | #define uni_max(x,y) (x > y ? x : y) 34 | 35 | typedef struct { 36 | int fd[2]; 37 | int flag; 38 | } Interruptable; 39 | 40 | static int _socket_set_block_mode(int sockfd, int block) { 41 | int flags = fcntl(sockfd, F_GETFL); 42 | if (flags < 0) { 43 | return -1; 44 | } 45 | if (block) { 46 | if ((flags & O_NONBLOCK) != 0) { 47 | flags ^= O_NONBLOCK; 48 | } 49 | } else { 50 | flags |= O_NONBLOCK; 51 | } 52 | if (fcntl(sockfd, F_SETFL, flags) < 0) { 53 | return -1; 54 | } 55 | return 0; 56 | } 57 | 58 | static int _socket_set_nonblocking(int sockfd) { 59 | return _socket_set_block_mode(sockfd, 0); 60 | } 61 | 62 | static int _async_pipe(int fildes[2]) { 63 | if (0 != pipe(fildes)) { 64 | return -1; 65 | } 66 | if (0 != _socket_set_nonblocking(fildes[0])) { 67 | return -1; 68 | } 69 | if (0 != _socket_set_nonblocking(fildes[1])) { 70 | return -1; 71 | } 72 | return 0; 73 | } 74 | 75 | static int _select(int maxfd, fd_set *readfds, fd_set *writefds, 76 | fd_set *errorfds, int timeout_ms) { 77 | struct timeval tv; 78 | tv.tv_sec = timeout_ms / 1000; 79 | tv.tv_usec = (timeout_ms % 1000) * 1000; 80 | return select(maxfd + 1, readfds, writefds, errorfds, 81 | timeout_ms < 0 ? NULL : &tv); 82 | } 83 | 84 | static int _interruptable_select(Interruptable *interrupter, int maxfd, 85 | fd_set *readfds, fd_set *writefds, 86 | fd_set *errorfds, int timeout_ms) { 87 | unsigned char t[64]; 88 | interrupter->flag = 0; 89 | maxfd = uni_max(maxfd, interrupter->fd[0]); 90 | FD_SET(interrupter->fd[0], readfds); 91 | if (_select(maxfd, readfds, writefds, errorfds, timeout_ms) < 0) { 92 | return -1; 93 | } 94 | if (FD_ISSET(interrupter->fd[0], readfds)) { 95 | if (0 > read(interrupter->fd[0], t, sizeof(t))) { 96 | return -1; 97 | } 98 | interrupter->flag = 1; 99 | } 100 | return 0; 101 | } 102 | 103 | InterruptHandle InterruptCreate() { 104 | Interruptable *interrupter = NULL; 105 | interrupter = (Interruptable *)malloc(sizeof(Interruptable)); 106 | if (NULL == interrupter) { 107 | return NULL; 108 | } 109 | if (0 != _async_pipe(interrupter->fd)) { 110 | free(interrupter); 111 | return NULL; 112 | } 113 | interrupter->flag = 0; 114 | return (InterruptHandle)interrupter; 115 | } 116 | 117 | int InterruptDestroy(InterruptHandle handle) { 118 | Interruptable *interrupter = (Interruptable *)handle; 119 | close(interrupter->fd[0]); 120 | close(interrupter->fd[1]); 121 | free(interrupter); 122 | return 0; 123 | } 124 | 125 | int InterruptableSleep(InterruptHandle handle, int sleep_msec) { 126 | Interruptable *interrupter = (Interruptable *)handle; 127 | fd_set readfds; 128 | if (sleep_msec < 0) { 129 | printf("%s%d: invalid input %d", __FUNCTION__, __LINE__, sleep_msec); 130 | return -1; 131 | } 132 | FD_ZERO(&readfds); 133 | _interruptable_select(interrupter, 0, &readfds, NULL, NULL, sleep_msec); 134 | return interrupter->flag; 135 | } 136 | 137 | int InterruptableBreak(InterruptHandle handle) { 138 | Interruptable *interrupter = (Interruptable *)handle; 139 | char c[1] = {0x5A}; 140 | return (write(interrupter->fd[1], c, sizeof(c)) == sizeof(c) ? 0 : -1); 141 | } 142 | -------------------------------------------------------------------------------- /benchmark/peer_a.c: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2020-2020 Junlon2006 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | ************************************************************************** 18 | * 19 | * Description : peer_a.c 20 | * Author : junlon2006@163.com 21 | * Date : 2020.03.04 22 | * 23 | **************************************************************************/ 24 | 25 | #include "uni_communication.h" 26 | #include "uni_log.h" 27 | #include "uni_interruptable.h" 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #define TAG "peer-a" 45 | #define FIFO_UART_MOCK_READ "/tmp/uart-mock-a" 46 | #define FIFO_UART_MOCK_WRITE "/tmp/uart-mock-b" 47 | #define TRANSMISSION_ERROR_PER_BITS (1000000) 48 | #define BAUD_RATE (921600) 49 | 50 | typedef struct { 51 | int seq; 52 | char buf[512]; 53 | } UserData; 54 | 55 | static int fd_R = -1; 56 | static int fd_T = -1; 57 | 58 | static InterruptHandle interrupt_handle; 59 | 60 | static int _uart_write_mock_api(char *buf, int len) { 61 | static int total_len = 0; 62 | static char clone[8192]; 63 | unsigned int byte_idx; 64 | unsigned int bit_idx; 65 | int sleep_msec; 66 | 67 | memcpy(clone, buf, len); 68 | 69 | for (int i = 0; i < len; i++) { 70 | LOGR(TAG, "0x%0x, ", (unsigned char)clone[i]); 71 | } 72 | LOGR(TAG, "\n"); 73 | 74 | total_len += (len << 3); 75 | if (total_len >= TRANSMISSION_ERROR_PER_BITS) { 76 | total_len = 0; 77 | LOGT(TAG, "random reverse one bit"); 78 | 79 | byte_idx = rand() % len; 80 | bit_idx = rand() & 7; 81 | 82 | clone[byte_idx] ^= (1 << bit_idx); 83 | } 84 | 85 | if (len != write(fd_T, clone, len)) { 86 | LOGE(TAG, "write failed"); 87 | } 88 | 89 | sleep_msec = len * 8 * 1000 / BAUD_RATE; 90 | sleep_msec += 1; 91 | InterruptableSleep(interrupt_handle, sleep_msec); 92 | 93 | return len; 94 | } 95 | 96 | static int64_t _get_now_msec(void) { 97 | struct timeval t1; 98 | gettimeofday(&t1, NULL); 99 | return ((int64_t)t1.tv_sec * 1000 + t1.tv_usec / 1000); 100 | } 101 | 102 | static void _recv_comm_packet(CommPacket *packet) { 103 | static int64_t start_time = -1; 104 | static int64_t start = -1; 105 | int64_t now, cost; 106 | float avg_speed; 107 | static int64_t total_len = 0; 108 | static int seq = 0; 109 | static int err_cnt = 0; 110 | 111 | LOGT(TAG, "recv frame... cmd=%d, len=%d", packet->cmd, packet->payload_len); 112 | 113 | if (-1 == start_time) { 114 | start_time = _get_now_msec(); 115 | start = start_time; 116 | } 117 | 118 | UserData *user_data = (UserData*)packet->payload; 119 | if (user_data->seq != ++seq) { 120 | err_cnt++; 121 | seq = user_data->seq; 122 | } 123 | total_len += packet->payload_len; 124 | now = _get_now_msec(); 125 | if (now - start > 1000) { 126 | cost = (now - start_time) / 1000; 127 | avg_speed = total_len / (float)(now - start_time) * 1000 / 1024; 128 | printf("[%d:ER%d] total=%ldKB, cost=%ld-%02ld:%02ld:%02ld, speed=%.2fKB/s, BW RATIO=%.2f%%\n", 129 | BAUD_RATE, 130 | err_cnt, 131 | (long)total_len >> 10, 132 | (long)cost / (3600 * 24), 133 | (long)cost % (3600 * 24) / 3600, 134 | (long)cost % (3600 * 24) % 3600 / 60, 135 | (long)cost % (3600 * 24) % 3600 % 60, 136 | avg_speed, avg_speed / (BAUD_RATE >> 13) * 100); 137 | start = now; 138 | } 139 | } 140 | 141 | static void* __recv_task(void *args) { 142 | int read_len; 143 | unsigned char buf[1024]; 144 | 145 | while (1) { 146 | read_len = read(fd_R, buf, sizeof(buf)); 147 | CommProtocolReceiveUartData(buf, read_len); 148 | } 149 | 150 | return NULL; 151 | } 152 | 153 | int main() { 154 | LogLevelSet(N_LOG_NONE); 155 | 156 | interrupt_handle = InterruptCreate(); 157 | 158 | mkfifo(FIFO_UART_MOCK_READ, 0644); 159 | mkfifo(FIFO_UART_MOCK_WRITE, 0644); 160 | 161 | fd_R = open(FIFO_UART_MOCK_READ, O_RDONLY); 162 | fd_T = open(FIFO_UART_MOCK_WRITE, O_WRONLY); 163 | 164 | signal(SIGPIPE, SIG_IGN); 165 | 166 | CommProtocolInit(_uart_write_mock_api, _recv_comm_packet); 167 | 168 | UserData user_data = {0}; 169 | 170 | CommAttribute attr; 171 | attr.reliable = 1; 172 | 173 | pthread_t pid; 174 | pthread_create(&pid, NULL, __recv_task, NULL); 175 | 176 | LOGT(TAG, "peer a start..."); 177 | 178 | while (1) { 179 | user_data.seq++; 180 | CommProtocolPacketAssembleAndSend(1, (char *)&user_data, sizeof(user_data), &attr); 181 | } 182 | 183 | return 0; 184 | } 185 | -------------------------------------------------------------------------------- /benchmark/peer_b.c: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2020-2020 Junlon2006 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | ************************************************************************** 18 | * 19 | * Description : peer_b.c 20 | * Author : junlon2006@163.com 21 | * Date : 2020.03.04 22 | * 23 | **************************************************************************/ 24 | 25 | #include "uni_communication.h" 26 | #include "uni_log.h" 27 | #include "uni_interruptable.h" 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #define TAG "peer-b" 45 | #define FIFO_UART_MOCK_READ "/tmp/uart-mock-b" 46 | #define FIFO_UART_MOCK_WRITE "/tmp/uart-mock-a" 47 | #define TRANSMISSION_ERROR_PER_BITS (1000000) 48 | #define BAUD_RATE (921600) 49 | 50 | typedef struct { 51 | int seq; 52 | char buf[512]; 53 | } UserData; 54 | 55 | static int fd_R = -1; 56 | static int fd_T = -1; 57 | 58 | static InterruptHandle interrupt_handle; 59 | 60 | static int _uart_write_mock_api(char *buf, int len) { 61 | static int total_len = 0; 62 | static char clone[8192]; 63 | unsigned int byte_idx; 64 | unsigned int bit_idx; 65 | int sleep_msec; 66 | 67 | memcpy(clone, buf, len); 68 | 69 | for (int i = 0; i < len; i++) { 70 | LOGR(TAG, "0x%0x, ", (unsigned char)clone[i]); 71 | } 72 | LOGR(TAG, "\n"); 73 | 74 | total_len += (len << 3); 75 | if (total_len >= TRANSMISSION_ERROR_PER_BITS) { 76 | total_len = 0; 77 | 78 | LOGT(TAG, "random reverse one bit"); 79 | 80 | byte_idx = rand() % len; 81 | bit_idx = rand() & 7; 82 | 83 | clone[byte_idx] ^= (1 << bit_idx); 84 | } 85 | 86 | if (len != write(fd_T, clone, len)) { 87 | LOGE(TAG, "write failed"); 88 | } 89 | 90 | sleep_msec = len * 8 * 1000 / BAUD_RATE; 91 | sleep_msec += 1; 92 | InterruptableSleep(interrupt_handle, sleep_msec); 93 | 94 | return len; 95 | } 96 | 97 | static int64_t _get_now_msec(void) { 98 | struct timeval t1; 99 | gettimeofday(&t1, NULL); 100 | return ((int64_t)t1.tv_sec * 1000 + t1.tv_usec / 1000); 101 | } 102 | 103 | static void _recv_comm_packet(CommPacket *packet) { 104 | static int64_t start_time = -1; 105 | static int64_t start = -1; 106 | int64_t now, cost; 107 | float avg_speed; 108 | static int64_t total_len = 0; 109 | static int seq = 0; 110 | static int err_cnt = 0; 111 | 112 | LOGT(TAG, "recv frame... cmd=%d, len=%d", packet->cmd, packet->payload_len); 113 | 114 | if (-1 == start_time) { 115 | start_time = _get_now_msec(); 116 | start = start_time; 117 | } 118 | 119 | UserData *user_data = (UserData*)packet->payload; 120 | if (user_data->seq != ++seq) { 121 | err_cnt++; 122 | seq = user_data->seq; 123 | } 124 | total_len += packet->payload_len; 125 | now = _get_now_msec(); 126 | if (now - start > 1000) { 127 | cost = (now - start_time) / 1000; 128 | avg_speed = total_len / (float)(now - start_time) * 1000 / 1024; 129 | printf("[%d:ER%d] total=%ldKB, cost=%ld-%02ld:%02ld:%02ld, speed=%.2fKB/s, BW RATIO=%.2f%%\n", 130 | BAUD_RATE, 131 | err_cnt, 132 | (long)total_len >> 10, 133 | (long)cost / (3600 * 24), 134 | (long)cost % (3600 * 24) / 3600, 135 | (long)cost % (3600 * 24) % 3600 / 60, 136 | (long)cost % (3600 * 24) % 3600 % 60, 137 | avg_speed, avg_speed / (BAUD_RATE >> 13) * 100); 138 | start = now; 139 | } 140 | } 141 | 142 | static void* __recv_task(void *args) { 143 | int read_len; 144 | unsigned char buf[1024]; 145 | 146 | while (1) { 147 | read_len = read(fd_R, buf, sizeof(buf)); 148 | CommProtocolReceiveUartData(buf, read_len); 149 | } 150 | 151 | return NULL; 152 | } 153 | 154 | int main() { 155 | LogLevelSet(N_LOG_NONE); 156 | 157 | interrupt_handle = InterruptCreate(); 158 | 159 | mkfifo(FIFO_UART_MOCK_READ, 0644); 160 | mkfifo(FIFO_UART_MOCK_WRITE, 0644); 161 | 162 | fd_T = open(FIFO_UART_MOCK_WRITE, O_WRONLY); 163 | fd_R = open(FIFO_UART_MOCK_READ, O_RDONLY); 164 | 165 | signal(SIGPIPE, SIG_IGN); 166 | 167 | CommProtocolInit(_uart_write_mock_api, _recv_comm_packet); 168 | 169 | UserData user_data = {0}; 170 | 171 | CommAttribute attr; 172 | attr.reliable = 1; 173 | 174 | pthread_t pid; 175 | pthread_create(&pid, NULL, __recv_task, NULL); 176 | 177 | LOGT(TAG, "peer b start..."); 178 | 179 | while (1) { 180 | user_data.seq++; 181 | CommProtocolPacketAssembleAndSend(1, (char *)&user_data, sizeof(user_data), &attr); 182 | } 183 | 184 | return 0; 185 | } 186 | -------------------------------------------------------------------------------- /utils/uni_log.c: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2018-2019 Junlon2006 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | ************************************************************************** 18 | * 19 | * Description : uni_log.c 20 | * Author : junlon2006@163.com 21 | * Date : 2019.03.17 22 | * 23 | **************************************************************************/ 24 | #include "uni_log.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #define LOG_BUFFER_LEN (1024) 37 | #define LOG_FILE_NAME "app.log" 38 | #define uni_min(x,y) ((x) > (y) ? (y) : (x)) 39 | #define uni_max(x,y) ((x) > (y) ? (x) : (y)) 40 | 41 | typedef struct { 42 | int fd; 43 | pthread_mutex_t mutex; 44 | } LogFile; 45 | 46 | static LogConfig g_log_config = {1, 1, 1, 1, 0, N_LOG_ALL}; 47 | static LogFile g_log_file; 48 | 49 | static const char* _level_tostring(LogLevel level) { 50 | switch (level) { 51 | case N_LOG_ERROR: return g_log_config.enable_color ? 52 | "\033[0m\033[41;33m[E]\033[0m" : "[E]"; 53 | case N_LOG_DEBUG: return g_log_config.enable_color ? 54 | "\033[0m\033[47;33m[D]\033[0m" : "[D]"; 55 | case N_LOG_TRACK: return g_log_config.enable_color ? 56 | "\033[0m\033[42;33m[T]\033[0m" : "[T]"; 57 | case N_LOG_WARN: return g_log_config.enable_color ? 58 | "\033[0m\033[41;33m[W]\033[0m" : "[W]"; 59 | default: return "[N/A]"; 60 | } 61 | } 62 | 63 | static void _get_now_str(char *buf, int len) { 64 | struct timeval tv; 65 | time_t s; 66 | struct tm local; 67 | gettimeofday(&tv, NULL); 68 | s = tv.tv_sec; 69 | localtime_r(&s, &local); 70 | snprintf(buf, len, "%02d:%02d:%02d.%06ld ", local.tm_hour, 71 | local.tm_min, local.tm_sec, (long)tv.tv_usec); 72 | } 73 | 74 | static void _get_thread_id_str(char *buf, int len) { 75 | pthread_t thread_id = pthread_self(); 76 | snprintf(buf, len, "%x", (unsigned int)thread_id); 77 | } 78 | 79 | static int _fill_log_level(LogLevel level, char *buf, int len) { 80 | int write_len = 0; 81 | switch (level) { 82 | case N_LOG_DEBUG: 83 | write_len = snprintf(buf, len, "%s ", _level_tostring(N_LOG_DEBUG)); 84 | break; 85 | case N_LOG_TRACK: 86 | write_len = snprintf(buf, len, "%s ", _level_tostring(N_LOG_TRACK)); 87 | break; 88 | case N_LOG_WARN: 89 | write_len = snprintf(buf, len, "%s ", _level_tostring(N_LOG_WARN)); 90 | break; 91 | case N_LOG_ERROR: 92 | write_len = snprintf(buf, len, "%s ", _level_tostring(N_LOG_ERROR)); 93 | break; 94 | default: 95 | break; 96 | } 97 | return uni_max(0, write_len); 98 | } 99 | 100 | static int _fill_tag(char *buf, int len, const char *tag) { 101 | return uni_max(0, snprintf(buf, len, "<%s>", tag)); 102 | } 103 | 104 | static int _fill_time(char *buf, int len) { 105 | char now[64]; 106 | if (!g_log_config.enable_time) { 107 | return 0; 108 | } 109 | _get_now_str(now, sizeof(now)); 110 | return uni_max(0, snprintf(buf, len, "%s", now)); 111 | } 112 | 113 | static int _fill_function_line(char *buf, int len, const char *function, 114 | int line) { 115 | return (g_log_config.enable_function_line ? 116 | uni_max(0, snprintf(buf, len, "%s:%d->", function, line)) : 0); 117 | } 118 | 119 | static int _fill_thread_id(char *buf, int len) { 120 | char thread_id[32]; 121 | if (!g_log_config.enable_thread_id) { 122 | return 0; 123 | } 124 | _get_thread_id_str(thread_id, sizeof(thread_id)); 125 | return uni_max(0, snprintf(buf, len, "%s", thread_id)); 126 | } 127 | 128 | static void _fill_customer_info(char *buf, int len, char *fmt, va_list args, 129 | int append_feed_line) { 130 | int length, remain_len; 131 | length = vsnprintf(buf, len, fmt, args); 132 | length = uni_max(length, 0); 133 | length = uni_min(length, len); 134 | remain_len = len - length; 135 | if (0 == remain_len) { 136 | if (append_feed_line) { 137 | buf[len - 2] = '\n'; 138 | } 139 | buf[len - 1] = '\0'; 140 | return; 141 | } 142 | if (1 == remain_len) { 143 | if (append_feed_line) { 144 | buf[len - 2] = '\n'; 145 | } 146 | return; 147 | } 148 | if (append_feed_line) { 149 | strncat(buf, "\n", remain_len); 150 | } 151 | return; 152 | } 153 | 154 | static void _save_log_2_file(char *buf, int len) { 155 | if (0 < g_log_file.fd && 0 < len) { 156 | pthread_mutex_lock(&g_log_file.mutex); 157 | if (len != write(g_log_file.fd, buf, len)) { 158 | printf("write log failed\n"); 159 | } 160 | pthread_mutex_unlock(&g_log_file.mutex); 161 | } 162 | } 163 | 164 | int LogLevelValid(LogLevel level) { 165 | return level <= g_log_config.set_level ? 1 : 0; 166 | } 167 | 168 | #define _log_assemble(buf, level, tags, function, line, fmt, args) do { \ 169 | int len = 0; \ 170 | if (level != N_LOG_RAW) { \ 171 | len += _fill_log_level(level, buf + len, LOG_BUFFER_LEN - len); \ 172 | len += _fill_time(buf + len, LOG_BUFFER_LEN - len); \ 173 | len += _fill_thread_id(buf + len, LOG_BUFFER_LEN - len); \ 174 | len += _fill_tag(buf + len, LOG_BUFFER_LEN - len, tags); \ 175 | len += _fill_function_line(buf + len, LOG_BUFFER_LEN - len, \ 176 | function, line); \ 177 | } \ 178 | _fill_customer_info(buf + len, LOG_BUFFER_LEN - len, fmt, args, \ 179 | level != N_LOG_RAW); \ 180 | } while (0) 181 | 182 | static int _sync_write_process(LogLevel level, const char *tags, 183 | const char *function, int line, 184 | char *fmt, va_list args) { 185 | char buf[LOG_BUFFER_LEN]; 186 | _log_assemble(buf, level, tags, function, line, fmt, args); 187 | printf("%s", buf); 188 | _save_log_2_file(buf, strlen(buf) + 1); 189 | return 0; 190 | } 191 | 192 | int LogWrite(LogLevel level, const char *tags, const char *function, int line, 193 | char *fmt, ...) { 194 | va_list args; 195 | va_start(args, fmt); 196 | _sync_write_process(level, tags, function, line, fmt, args); 197 | va_end(args); 198 | return 0; 199 | } 200 | 201 | static void _destroy_all() { 202 | if (g_log_config.enable_file) { 203 | pthread_mutex_destroy(&g_log_file.mutex); 204 | } 205 | } 206 | 207 | int LogLevelSet(LogLevel level) { 208 | g_log_config.set_level = level; 209 | return 0; 210 | } 211 | 212 | static void _open_save_fd() { 213 | g_log_file.fd = open(LOG_FILE_NAME, O_WRONLY | O_CREAT, 0664); 214 | if (g_log_file.fd <= 0) { 215 | printf("%s%d: open save fd[%d] failed.\n", __FUNCTION__, __LINE__, 216 | g_log_file.fd); 217 | } 218 | } 219 | 220 | int LogInitialize(LogConfig logConfig) { 221 | g_log_config.enable_time = logConfig.enable_time; 222 | g_log_config.enable_thread_id = logConfig.enable_thread_id; 223 | g_log_config.enable_function_line = logConfig.enable_function_line; 224 | g_log_config.enable_color = logConfig.enable_color; 225 | g_log_config.enable_file = logConfig.enable_file; 226 | g_log_config.set_level = logConfig.set_level; 227 | if (g_log_config.enable_file) { 228 | pthread_mutex_init(&g_log_file.mutex, NULL); 229 | _open_save_fd(); 230 | } 231 | return 0; 232 | } 233 | 234 | int LogFinalize(void) { 235 | _destroy_all(); 236 | return 0; 237 | } 238 | -------------------------------------------------------------------------------- /src/uni_communication.c: -------------------------------------------------------------------------------- 1 | /************************************************************************** 2 | * Copyright (C) 2020-2020 Junlon2006 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License along 15 | * with this program; if not, write to the Free Software Foundation, Inc., 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | ************************************************************************** 18 | * 19 | * Description : uni_communication.c 20 | * Author : junlon2006@163.com 21 | * Date : 2020.03.04 22 | * 23 | **************************************************************************/ 24 | #include "uni_communication.h" 25 | 26 | #include "uni_log.h" 27 | #include "uni_crc16.h" 28 | #include "uni_interruptable.h" 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #define TAG "uart_comm" 35 | 36 | #define DEFAULT_PROTOCOL_BUF_SIZE (sizeof(struct header)) 37 | #define PROTOCOL_BUF_GC_TRIGGER_SIZE (1024 + sizeof(struct header)) 38 | #define PROTOCOL_BUF_SUPPORT_MAX_SIZE (8192) 39 | 40 | //TODO need refactor, calculate by baud rate 41 | #define WAIT_ACK_TIMEOUT_MSEC (200) 42 | #define TRY_RESEND_TIMES (5) 43 | 44 | #define uni_min(x, y) (x < y ? x : y) 45 | #define uni_max(x, y) (x > y ? x : y) 46 | #define uni_malloc malloc 47 | #define uni_realloc realloc 48 | #define uni_free free 49 | #define false 0 50 | #define true 1 51 | 52 | /*-----------------------------------------------------------------*/ 53 | /* layout of uart communication app protocol */ 54 | /*-----------------------------------------------------------------*/ 55 | /*--6byte-|-1byte-|-1byte-|-2byte-|-2byte-|-2byte-|-2byte-|-N byte-*/ 56 | /*"uArTcP"| seq | ctrl | cmd | crc16 | len |cs(len)|payload */ 57 | /*-----------------------------------------------------------------*/ 58 | 59 | /*---------------------------ack frame-----------------------------*/ 60 | /*"uArTcP"| seq | 0x0 | 0x0 | crc16 | 0x0 | 0x0 | NULL */ 61 | /*-----------------------------------------------------------------*/ 62 | 63 | /*------------------------------------*/ 64 | /*--------------control---------------*/ 65 | /*| 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |*/ 66 | /*|RES|RES|RES|RES|RES|NACK|ACKED|ACK|*/ 67 | /*------------------------------------*/ 68 | 69 | typedef unsigned short CommChecksum; 70 | typedef unsigned char CommSequence; 71 | typedef unsigned char CommControl; 72 | 73 | typedef enum { 74 | ACK = 0, /* need ack */ 75 | ACKED = 1, /* ack packet */ 76 | NACK = 2, /* nack packet */ 77 | } Control; 78 | 79 | typedef enum { 80 | LAYOUT_SYNC_IDX = 0, 81 | LAYOUT_PAYLOAD_LEN_HIGH_IDX = 12, 82 | LAYOUT_PAYLOAD_LEN_LOW_IDX = 13, 83 | LAYOUT_PAYLOAD_LEN_CRC_HIGH_IDX = 14, 84 | LAYOUT_PAYLOAD_LEN_CRC_LOW_IDX = 15, 85 | } CommLayoutIndex; 86 | 87 | typedef struct header { 88 | unsigned char sync[6]; /* must be "uArTcP" */ 89 | CommSequence sequence; /* sequence number */ 90 | CommControl control; /* header ctrl */ 91 | uint8_t cmd[2]; /* command type, such as power on, power off etc */ 92 | uint8_t checksum[2]; /* checksum of packet, use crc16 */ 93 | uint8_t payload_len[2]; /* the length of payload */ 94 | uint8_t payload_len_crc16[2];/* the crc16 of payload_len */ 95 | char payload[0]; /* the payload */ 96 | } PACKED CommProtocolPacket; 97 | 98 | typedef struct { 99 | CommWriteHandler on_write; 100 | CommRecvPacketHandler on_recv_frame; 101 | pthread_mutex_t write_sync_lock; /* avoid uart device write concurrency */ 102 | pthread_mutex_t app_send_sync_lock; /* avoid app send concurrency, out of sequence */ 103 | uni_bool acked; 104 | CommSequence sequence; 105 | short current_acked_seq; /* current received sequence */ 106 | char *protocol_buffer; 107 | InterruptHandle interrupt_handle; 108 | uni_bool inited; 109 | } CommProtocolBusiness; 110 | 111 | static unsigned char g_sync[6] = {'u', 'A', 'r', 'T', 'c', 'P'}; 112 | static CommProtocolBusiness g_comm_protocol_business; 113 | 114 | static uint16_t _byte2_big_endian_2_u16(unsigned char *buf) { 115 | return ((uint16_t)buf[0] << 8) + (uint16_t)buf[1]; 116 | } 117 | 118 | static void _u16_2_byte2_big_endian(uint16_t value, unsigned char *buf) { 119 | buf[0] = (uint8_t)(value >> 8); 120 | buf[1] = (uint8_t)(value & 0xFF); 121 | } 122 | 123 | static void _register_write_handler(CommWriteHandler handler) { 124 | g_comm_protocol_business.on_write = handler; 125 | } 126 | 127 | static void _unregister_write_handler() { 128 | g_comm_protocol_business.on_write = NULL; 129 | } 130 | 131 | static void _set_current_acked_seq(short seq) { 132 | g_comm_protocol_business.current_acked_seq = seq; 133 | } 134 | 135 | static short _get_current_acked_seq() { 136 | return g_comm_protocol_business.current_acked_seq; 137 | } 138 | 139 | static void _sync_set(CommProtocolPacket *packet) { 140 | unsigned int i; 141 | for (i = 0; i < sizeof(g_sync); i++) { 142 | packet->sync[i] = g_sync[i]; 143 | } 144 | } 145 | 146 | static void _sequence_set(CommProtocolPacket *packet, CommSequence seq, 147 | uni_bool is_ack_packet, uni_bool is_nack_packet) { 148 | if (is_ack_packet || is_nack_packet) { 149 | packet->sequence = seq; 150 | } else { 151 | packet->sequence = g_comm_protocol_business.sequence++; 152 | } 153 | } 154 | 155 | static CommSequence _current_sequence_get() { 156 | return g_comm_protocol_business.sequence - 1; 157 | } 158 | 159 | static void _bit_set(CommControl *control, int index) { 160 | *control |= (1 << index); 161 | } 162 | 163 | static uni_bool _is_bit_setted(CommControl control, int index) { 164 | return (control >> index) & 0x1; 165 | } 166 | 167 | static void _set_ack(CommProtocolPacket *packet) { 168 | _bit_set(&packet->control, ACK); 169 | } 170 | 171 | static void _set_acked(CommProtocolPacket *packet) { 172 | _bit_set(&packet->control, ACKED); 173 | } 174 | 175 | static void _set_nack(CommProtocolPacket *packet) { 176 | _bit_set(&packet->control, NACK); 177 | } 178 | 179 | static uni_bool _is_ack_set(CommControl control) { 180 | return _is_bit_setted(control, ACK); 181 | } 182 | 183 | static uni_bool _is_acked_set(CommControl control) { 184 | return _is_bit_setted(control, ACKED); 185 | } 186 | 187 | static uni_bool _is_nacked_set(CommControl control) { 188 | return _is_bit_setted(control, NACK); 189 | } 190 | 191 | static void _control_set(CommProtocolPacket *packet, uni_bool reliable, 192 | uni_bool is_ack_packet, uni_bool is_nack_packet) { 193 | if (reliable) { 194 | _set_ack(packet); 195 | } 196 | 197 | if (is_ack_packet) { 198 | _set_acked(packet); 199 | } 200 | 201 | if (is_nack_packet) { 202 | _set_nack(packet); 203 | } 204 | } 205 | 206 | static void _cmd_set(CommProtocolPacket *packet, CommCmd cmd) { 207 | _u16_2_byte2_big_endian(cmd, packet->cmd); 208 | } 209 | 210 | static void _payload_len_set(CommProtocolPacket *packet, CommPayloadLen payload_len) { 211 | _u16_2_byte2_big_endian(payload_len, packet->payload_len); 212 | } 213 | 214 | static void _payload_len_crc16_set(CommProtocolPacket *packet) { 215 | uint16_t checksum = crc16((const char *)packet->payload_len, sizeof(CommPayloadLen)); 216 | _u16_2_byte2_big_endian(checksum, packet->payload_len_crc16); 217 | } 218 | 219 | static CommPayloadLen _payload_len_get(CommProtocolPacket *packet) { 220 | return _byte2_big_endian_2_u16(packet->payload_len); 221 | } 222 | 223 | static void _payload_set(CommProtocolPacket *packet, char *buf, CommPayloadLen len) { 224 | if (NULL != buf && 0 < len) { 225 | memcpy(packet->payload, buf, len); 226 | } 227 | } 228 | 229 | static char* _payload_get(CommProtocolPacket *packet) { 230 | return packet->payload; 231 | } 232 | 233 | static CommPayloadLen _packet_len_get(CommProtocolPacket *packet) { 234 | return _byte2_big_endian_2_u16(packet->payload_len) + sizeof(CommProtocolPacket) ; 235 | } 236 | 237 | static void _checksum_calc(CommProtocolPacket *packet) { 238 | packet->checksum[0] = 0; /* make sure the checksum be zero before calculate */ 239 | packet->checksum[1] = 0; 240 | uint16_t checksum = crc16((const char*)packet, _packet_len_get(packet)); 241 | _u16_2_byte2_big_endian(checksum, packet->checksum); 242 | } 243 | 244 | static int _checksum_valid(CommProtocolPacket *packet) { 245 | CommChecksum checksum = _byte2_big_endian_2_u16(packet->checksum); /* get the checksum from packet */ 246 | _checksum_calc(packet); /* calc checksum again */ 247 | int valid = (checksum == _byte2_big_endian_2_u16(packet->checksum)); /* check whether checksum valid or not */ 248 | if (!valid) { 249 | LOGW(TAG, "check crc failed, [%04x, %04x]", checksum, 250 | _byte2_big_endian_2_u16(packet->checksum)); 251 | } 252 | return valid; 253 | } 254 | 255 | static void _unset_acked_sync_flag() { 256 | g_comm_protocol_business.acked = false; 257 | } 258 | 259 | static void _set_acked_sync_flag() { 260 | g_comm_protocol_business.acked = true; 261 | } 262 | 263 | static uni_bool _is_acked_packet(CommProtocolPacket *protocol_packet) { 264 | return (_byte2_big_endian_2_u16(protocol_packet->cmd) == 0 && 265 | _byte2_big_endian_2_u16(protocol_packet->payload_len) == 0 && 266 | _is_acked_set(protocol_packet->control)); 267 | } 268 | 269 | static uni_bool _is_nacked_packet(CommProtocolPacket *protocol_packet) { 270 | return (_byte2_big_endian_2_u16(protocol_packet->cmd) == 0 && 271 | _byte2_big_endian_2_u16(protocol_packet->payload_len) == 0 && 272 | _is_nacked_set(protocol_packet->control)); 273 | } 274 | 275 | static int _wait_ack(CommAttribute *attribute, CommProtocolPacket *packet) { 276 | /* acked process */ 277 | if (NULL == attribute || !attribute->reliable) { 278 | return 0; 279 | } 280 | 281 | InterruptableSleep(g_comm_protocol_business.interrupt_handle, WAIT_ACK_TIMEOUT_MSEC); 282 | 283 | if (!g_comm_protocol_business.acked) { 284 | LOGW(TAG, "wait uart ack timeout. seq=%d, cmd=%d, ctrl=%d, len=%d", 285 | packet->sequence, _byte2_big_endian_2_u16(packet->cmd), 286 | packet->control, _byte2_big_endian_2_u16(packet->payload_len)); 287 | } 288 | 289 | return g_comm_protocol_business.acked ? 0 : E_UNI_COMM_PAYLOAD_ACK_TIMEOUT; 290 | } 291 | 292 | static CommProtocolPacket* _packet_alloc(int payload_len) { 293 | CommProtocolPacket *packet = (CommProtocolPacket *)uni_malloc(sizeof(CommProtocolPacket) + payload_len); 294 | if (packet) { 295 | /* only bzero struct header */ 296 | memset(packet, 0, sizeof(CommProtocolPacket)); 297 | } 298 | return packet; 299 | } 300 | 301 | static void _packet_free(CommProtocolPacket *packet) { 302 | uni_free(packet); 303 | } 304 | 305 | #define RESENDING (1) 306 | static int _resend_status(CommAttribute *attribute, int *resend_times, 307 | CommProtocolPacket *packet) { 308 | int ret = _wait_ack(attribute, packet); 309 | if (0 == ret) { 310 | return 0; 311 | } 312 | 313 | if (*resend_times > 0) { 314 | *resend_times = *resend_times - 1; 315 | return RESENDING; 316 | } 317 | 318 | return ret; 319 | } 320 | 321 | /** 322 | * RWND always 1, in 921600bps, 512 byte payload can use 80% bandwidth 90KB/s 323 | * easy way to make reliable transmission, can meet current requirement 324 | */ 325 | static int _write_uart(CommProtocolPacket *packet, CommAttribute *attribute) { 326 | int ret = 0; 327 | int resend_times = TRY_RESEND_TIMES; 328 | 329 | if (NULL != g_comm_protocol_business.on_write) { 330 | if (NULL != attribute && attribute->reliable) { 331 | _unset_acked_sync_flag(); 332 | } 333 | 334 | do { 335 | //TODO 336 | /* sync uart write, we use mutex lock, 337 | but in high concurrency, mutex perf bad, 338 | can sleep 0 when unlock, CAS is better, use CAS insteads */ 339 | pthread_mutex_lock(&g_comm_protocol_business.write_sync_lock); 340 | g_comm_protocol_business.on_write((char *)packet, 341 | (int)_packet_len_get(packet)); 342 | pthread_mutex_unlock(&g_comm_protocol_business.write_sync_lock); 343 | 344 | ret = _resend_status(attribute, &resend_times, packet); 345 | } while (RESENDING == ret); 346 | } 347 | 348 | return ret; 349 | } 350 | 351 | static void _assmeble_packet(CommProtocolPacket *packet, 352 | CommCmd cmd, 353 | char *payload, 354 | CommPayloadLen payload_len, 355 | uni_bool reliable, 356 | CommSequence seq, 357 | uni_bool is_ack_packet, 358 | uni_bool is_nack_packet) { 359 | _sync_set(packet); 360 | _sequence_set(packet, seq, is_ack_packet, is_nack_packet); 361 | _control_set(packet, reliable, is_ack_packet, is_nack_packet); 362 | _cmd_set(packet, cmd); 363 | _payload_set(packet, payload, payload_len); 364 | _payload_len_set(packet, payload_len); 365 | _payload_len_crc16_set(packet); 366 | _checksum_calc(packet); 367 | } 368 | 369 | static uni_bool _is_protocol_buffer_overflow(CommPayloadLen length) { 370 | return length >= PROTOCOL_BUF_SUPPORT_MAX_SIZE; 371 | } 372 | 373 | static int _assemble_and_send_frame(CommCmd cmd, 374 | char *payload, 375 | CommPayloadLen payload_len, 376 | CommAttribute *attribute, 377 | CommSequence seq, 378 | uni_bool is_ack_packet, 379 | uni_bool is_nack_packet) { 380 | int ret = 0; 381 | if (_is_protocol_buffer_overflow(sizeof(CommProtocolPacket) + 382 | payload_len)) { 383 | return E_UNI_COMM_PAYLOAD_TOO_LONG; 384 | } 385 | 386 | CommProtocolPacket *packet = _packet_alloc(payload_len); 387 | if (NULL == packet) { 388 | return E_UNI_COMM_ALLOC_FAILED; 389 | } 390 | 391 | _assmeble_packet(packet, cmd, payload, payload_len, 392 | attribute && attribute->reliable, 393 | seq, is_ack_packet, is_nack_packet); 394 | 395 | ret = _write_uart(packet, attribute); 396 | _packet_free(packet); 397 | return ret; 398 | } 399 | 400 | int CommProtocolPacketAssembleAndSend(CommCmd cmd, char *payload, 401 | CommPayloadLen payload_len, 402 | CommAttribute *attribute) { 403 | int ret; 404 | pthread_mutex_lock(&g_comm_protocol_business.app_send_sync_lock); 405 | ret = _assemble_and_send_frame(cmd, payload, payload_len, 406 | attribute, 0, false, false); 407 | pthread_mutex_unlock(&g_comm_protocol_business.app_send_sync_lock); 408 | return ret; 409 | } 410 | 411 | static int _packet_disassemble(CommProtocolPacket *protocol_packet, 412 | CommPacket *packet) { 413 | if (!_checksum_valid(protocol_packet)) { 414 | LOGD(TAG, "checksum failed"); 415 | return -1; 416 | } 417 | 418 | packet->cmd = _byte2_big_endian_2_u16(protocol_packet->cmd); 419 | packet->payload_len = _payload_len_get(protocol_packet); 420 | packet->payload = _payload_get(protocol_packet); 421 | return 0; 422 | } 423 | 424 | static void _enlarge_protocol_buffer(char **orginal, CommPayloadLen *orginal_len) { 425 | CommPayloadLen new_size = *orginal_len * 2 + sizeof(struct header); /* cover header */ 426 | *orginal = (char *)uni_realloc(*orginal, new_size); 427 | *orginal_len = new_size; 428 | } 429 | 430 | /* small heap memory stays alway, only garbage collection big bins */ 431 | static void _try_garbage_collection_protocol_buffer(char **buffer, 432 | CommPayloadLen *length) { 433 | if (*length > PROTOCOL_BUF_GC_TRIGGER_SIZE) { 434 | uni_free(*buffer); 435 | *buffer = NULL; 436 | *length = DEFAULT_PROTOCOL_BUF_SIZE; 437 | LOGD(TAG, "free buffer=%p, len=%u", *buffer, *length); 438 | } 439 | } 440 | 441 | static void _reset_protocol_buffer_status(unsigned int *index, 442 | CommPayloadLen *length, 443 | uint16_t *crc) { 444 | *index = 0; 445 | *length = 0; 446 | *crc = 0; 447 | } 448 | 449 | static void _protocol_buffer_alloc(char **buffer, 450 | CommPayloadLen *length, 451 | unsigned int index) { 452 | if (NULL == *buffer) { 453 | *buffer = (char *)uni_malloc(*length); 454 | LOGD(TAG, "init buffer=%p, len=%u", *buffer, *length); 455 | return; 456 | } 457 | 458 | if (*length <= index) { 459 | _enlarge_protocol_buffer(buffer, length); 460 | LOGD(TAG, "protocol buffer enlarge. p=%p, new len=%u", *buffer, *length); 461 | return; 462 | } 463 | } 464 | 465 | static void _send_nack_frame(CommSequence seq) { 466 | _assemble_and_send_frame(0, NULL, 0, NULL, seq, false, true); 467 | LOGW(TAG, "send nack seq=%d", seq); 468 | } 469 | 470 | static void _send_ack_frame(CommSequence seq) { 471 | _assemble_and_send_frame(0, NULL, 0, NULL, seq, true, false); 472 | LOGD(TAG, "send ack seq=%d", seq); 473 | } 474 | 475 | static void _do_ack(CommProtocolPacket *protocol_packet) { 476 | if (_is_ack_set(protocol_packet->control)) { 477 | _send_ack_frame(protocol_packet->sequence); 478 | } 479 | } 480 | 481 | static uni_bool _is_duplicate_frame(CommProtocolPacket *protocol_packet) { 482 | static int last_recv_packet_seq = -1; 483 | uni_bool duplicate; 484 | duplicate = (last_recv_packet_seq == (int)protocol_packet->sequence); 485 | last_recv_packet_seq = protocol_packet->sequence; 486 | if (duplicate) { 487 | LOGW(TAG, "duplicate frame seq=%d, cmd=%d", protocol_packet->sequence, 488 | _byte2_big_endian_2_u16(protocol_packet->cmd)); 489 | } 490 | return duplicate; 491 | } 492 | 493 | static uni_bool _is_udp_packet(CommProtocolPacket *protocol_packet) { 494 | return (_byte2_big_endian_2_u16(protocol_packet->cmd) != 0 && 495 | !_is_ack_set(protocol_packet->control)); 496 | } 497 | 498 | static void _one_protocol_frame_process(char *protocol_buffer) { 499 | CommProtocolPacket *protocol_packet = (CommProtocolPacket *)protocol_buffer; 500 | 501 | /* when application not register hook, ignore all */ 502 | if (NULL == g_comm_protocol_business.on_recv_frame) { 503 | LOGW(TAG, "donot register recv_frame hook"); 504 | return; 505 | } 506 | 507 | /* ack frame donnot notify application, ignore it now */ 508 | if (_is_acked_packet(protocol_packet)) { 509 | LOGD(TAG, "recv ack frame"); 510 | /* one sequence can only break once */ 511 | if (protocol_packet->sequence == _current_sequence_get() && 512 | (short)protocol_packet->sequence != _get_current_acked_seq()) { 513 | _set_acked_sync_flag(); 514 | _set_current_acked_seq(protocol_packet->sequence); 515 | InterruptableBreak(g_comm_protocol_business.interrupt_handle); 516 | } 517 | return; 518 | } 519 | 520 | /* nack frame. resend immediately, donot notify application */ 521 | if (_is_nacked_packet(protocol_packet)) { 522 | /* use select can cover payload_len_crc16 error case, sem sometimes not */ 523 | if (protocol_packet->sequence == _current_sequence_get()) { 524 | LOGW(TAG, "recv useful nack frame, seq=%d", protocol_packet->sequence); 525 | InterruptableBreak(g_comm_protocol_business.interrupt_handle); 526 | } else { 527 | LOGW(TAG, "recv outdated nack frame, seq=%d, cur_seq=%d", 528 | protocol_packet->sequence, _current_sequence_get()); 529 | } 530 | return; 531 | } 532 | 533 | /* disassemble protocol buffer */ 534 | CommPacket packet; 535 | if (0 != _packet_disassemble(protocol_packet, &packet)) { 536 | _send_nack_frame(protocol_packet->sequence); 537 | LOGW(TAG, "disassemble packet failed"); 538 | return; 539 | } 540 | 541 | /* ack automatically when ack attribute set */ 542 | _do_ack(protocol_packet); 543 | 544 | /* udp frame reset current acked seq -1 */ 545 | if (_get_current_acked_seq() != -1 && _is_udp_packet(protocol_packet)) { 546 | _set_current_acked_seq(-1); 547 | } 548 | 549 | /* notify application when not ack frame nor duplicate frame */ 550 | if (!_is_duplicate_frame(protocol_packet)) { 551 | g_comm_protocol_business.on_recv_frame(&packet); 552 | } 553 | } 554 | 555 | static uni_bool _is_payload_len_crc16_valid(CommPayloadLen length, CommChecksum crc) { 556 | unsigned char len[2]; 557 | _u16_2_byte2_big_endian(length, len); 558 | uint16_t length_crc = crc16((const char *)len, sizeof(CommPayloadLen)); 559 | if (crc != length_crc) { 560 | LOGW(TAG, "crc_recv=%d, crc_calc=%d", crc, length_crc); 561 | } 562 | return crc == length_crc; 563 | } 564 | 565 | static void _protocol_buffer_generate_byte_by_byte(unsigned char recv_c) { 566 | static unsigned int index = 0; 567 | static CommPayloadLen length = 0; 568 | static uint16_t length_crc16 = 0; 569 | static CommPayloadLen protocol_buffer_length = DEFAULT_PROTOCOL_BUF_SIZE; 570 | CommProtocolPacket *packet; 571 | 572 | /* protect heap use, cannot alloc large than 8K now */ 573 | if (_is_protocol_buffer_overflow(protocol_buffer_length)) { 574 | /* drop remain bytes of this frame */ 575 | if (length > 1) { 576 | length--; 577 | return; 578 | } 579 | 580 | _reset_protocol_buffer_status(&index, &length, &length_crc16); 581 | _try_garbage_collection_protocol_buffer(&g_comm_protocol_business.protocol_buffer, 582 | &protocol_buffer_length); 583 | LOGW(TAG, "recv invalid frame, payload too long"); 584 | return; 585 | } 586 | 587 | _protocol_buffer_alloc(&g_comm_protocol_business.protocol_buffer, 588 | &protocol_buffer_length, index); 589 | 590 | /* get frame header sync byte */ 591 | if (index < LAYOUT_SYNC_IDX + sizeof(g_sync)) { 592 | if (recv_c == g_sync[index]) { 593 | g_comm_protocol_business.protocol_buffer[index++] = recv_c; 594 | } else { 595 | _reset_protocol_buffer_status(&index, &length, &length_crc16); 596 | LOGD(TAG, "nonstandord sync byte, please check"); 597 | } 598 | 599 | return; 600 | } 601 | 602 | /* get payload length (high 8 bit) */ 603 | if (LAYOUT_PAYLOAD_LEN_HIGH_IDX == index) { 604 | length = (((uint16_t)recv_c) << 8); 605 | LOGD(TAG, "length=%d", length); 606 | goto L_HEADER; 607 | } 608 | 609 | /* get payload length (low 8 bit) */ 610 | if (LAYOUT_PAYLOAD_LEN_LOW_IDX == index) { 611 | length += recv_c; 612 | LOGD(TAG, "length=%d", length); 613 | goto L_HEADER; 614 | } 615 | 616 | /* get payload length src16 (high 8 bit) */ 617 | if (LAYOUT_PAYLOAD_LEN_CRC_HIGH_IDX == index) { 618 | length_crc16 = (((uint16_t)recv_c) << 8); 619 | LOGD(TAG, "len crc=%d", length_crc16); 620 | goto L_HEADER; 621 | } 622 | 623 | if (LAYOUT_PAYLOAD_LEN_CRC_LOW_IDX == index) { 624 | length_crc16 += recv_c; 625 | LOGD(TAG, "length_crc16=%d", length_crc16); 626 | if (!_is_payload_len_crc16_valid(length, length_crc16)) { 627 | LOGE(TAG, "length crc check failed"); 628 | _reset_protocol_buffer_status(&index, &length, &length_crc16); 629 | packet = (CommProtocolPacket *)g_comm_protocol_business.protocol_buffer; 630 | _send_nack_frame(packet->sequence); 631 | return; 632 | } 633 | } 634 | 635 | L_HEADER: 636 | /* set protocol header */ 637 | if (index < sizeof(CommProtocolPacket)) { 638 | g_comm_protocol_business.protocol_buffer[index++] = recv_c; 639 | goto L_END; 640 | } 641 | 642 | /* set protocol payload */ 643 | if (sizeof(CommProtocolPacket) <= index && 0 < length) { 644 | g_comm_protocol_business.protocol_buffer[index++] = recv_c; 645 | length--; 646 | } 647 | 648 | L_END: 649 | /* callback protocol buffer */ 650 | if (sizeof(CommProtocolPacket) <= index && 0 == length) { 651 | LOGD(TAG, "assemble new frame, now callback"); 652 | _one_protocol_frame_process(g_comm_protocol_business.protocol_buffer); 653 | _reset_protocol_buffer_status(&index, &length, &length_crc16); 654 | _try_garbage_collection_protocol_buffer(&g_comm_protocol_business.protocol_buffer, 655 | &protocol_buffer_length); 656 | } 657 | } 658 | 659 | void CommProtocolReceiveUartData(unsigned char *buf, int len) { 660 | int i; 661 | if (!g_comm_protocol_business.inited) { 662 | return; 663 | } 664 | 665 | for (i = 0; i < len; i++) { 666 | _protocol_buffer_generate_byte_by_byte(buf[i]); 667 | } 668 | } 669 | 670 | static void _register_packet_receive_handler(CommRecvPacketHandler handler) { 671 | g_comm_protocol_business.on_recv_frame = handler; 672 | } 673 | 674 | static void _unregister_packet_receive_handler() { 675 | g_comm_protocol_business.on_recv_frame = NULL; 676 | } 677 | 678 | static void _protocol_business_init() { 679 | memset(&g_comm_protocol_business, 0, sizeof(g_comm_protocol_business)); 680 | pthread_mutex_init(&g_comm_protocol_business.write_sync_lock, NULL); 681 | pthread_mutex_init(&g_comm_protocol_business.app_send_sync_lock, NULL); 682 | g_comm_protocol_business.interrupt_handle = InterruptCreate(); 683 | _set_current_acked_seq(((CommSequence)-1) >> 1); 684 | g_comm_protocol_business.inited = 1; 685 | } 686 | 687 | static void _try_free_protocol_buffer() { 688 | if (NULL != g_comm_protocol_business.protocol_buffer) { 689 | uni_free(g_comm_protocol_business.protocol_buffer); 690 | g_comm_protocol_business.protocol_buffer = NULL; 691 | } 692 | } 693 | 694 | static void _protocol_business_final() { 695 | pthread_mutex_destroy(&g_comm_protocol_business.write_sync_lock); 696 | pthread_mutex_destroy(&g_comm_protocol_business.app_send_sync_lock); 697 | _try_free_protocol_buffer(); 698 | InterruptDestroy(g_comm_protocol_business.interrupt_handle); 699 | memset(&g_comm_protocol_business, 0, sizeof(g_comm_protocol_business)); 700 | } 701 | 702 | int CommProtocolInit(CommWriteHandler write_handler, 703 | CommRecvPacketHandler recv_handler) { 704 | _protocol_business_init(); 705 | _register_write_handler(write_handler); 706 | _register_packet_receive_handler(recv_handler); 707 | return 0; 708 | } 709 | 710 | void CommProtocolFinal() { 711 | _unregister_packet_receive_handler(); 712 | _unregister_write_handler(); 713 | _protocol_business_final(); 714 | } 715 | --------------------------------------------------------------------------------