diff --git a/APP_Framework/Applications/main.c b/APP_Framework/Applications/main.c index b10557a97..3216586d0 100644 --- a/APP_Framework/Applications/main.c +++ b/APP_Framework/Applications/main.c @@ -17,12 +17,21 @@ extern int FrameworkInit(); extern void ApplicationOtaTaskInit(void); + +#ifdef OTA_BY_PLATFORM +extern int OtaTask(void); +#endif + int main(void) { - printf("Hello, world! \n"); - FrameworkInit(); + printf("Hello, world! \n"); + FrameworkInit(); #ifdef APPLICATION_OTA - ApplicationOtaTaskInit(); + ApplicationOtaTaskInit(); +#endif + +#ifdef OTA_BY_PLATFORM + OtaTask(); #endif return 0; } diff --git a/APP_Framework/Framework/connection/4g/ec200t/Kconfig b/APP_Framework/Framework/connection/4g/ec200t/Kconfig index 7d3640739..c84c4b9a6 100644 --- a/APP_Framework/Framework/connection/4g/ec200t/Kconfig +++ b/APP_Framework/Framework/connection/4g/ec200t/Kconfig @@ -23,7 +23,7 @@ if ADD_XIZI_FEATURES config ADAPTER_EC200T_DRIVER string "EC200T device uart driver path" - default "/dev/usart2_dev2" + default "/dev/uart8_dev8" depends on !ADAPTER_EC200T_DRIVER_EXTUART if ADAPTER_EC200T_DRIVER_EXTUART diff --git a/APP_Framework/Framework/connection/4g/ec200t/ec200t.c b/APP_Framework/Framework/connection/4g/ec200t/ec200t.c index 67076fe44..92f687303 100644 --- a/APP_Framework/Framework/connection/4g/ec200t/ec200t.c +++ b/APP_Framework/Framework/connection/4g/ec200t/ec200t.c @@ -163,8 +163,12 @@ static int Ec200tIoctl(struct Adapter *adapter, int cmd, void *args) serial_cfg.serial_parity_mode = PARITY_NONE; serial_cfg.serial_bit_order = STOP_BITS_1; serial_cfg.serial_invert_mode = NRZ_NORMAL; +#ifdef TOOL_USING_OTA + serial_cfg.serial_timeout = OTA_RX_TIMEOUT; +#else //serial receive timeout 10s - serial_cfg.serial_timeout = 10000; + serial_cfg.serial_timeout = 100000; +#endif serial_cfg.is_ext_uart = 0; #ifdef ADAPTER_EC200T_DRIVER_EXT_PORT serial_cfg.is_ext_uart = 1; diff --git a/APP_Framework/Framework/connection/Kconfig b/APP_Framework/Framework/connection/Kconfig index b6613f2a3..6e0fd4dd0 100644 --- a/APP_Framework/Framework/connection/Kconfig +++ b/APP_Framework/Framework/connection/Kconfig @@ -6,7 +6,7 @@ menuconfig SUPPORT_CONNECTION_FRAMEWORK if SUPPORT_CONNECTION_FRAMEWORK config CONNECTION_FRAMEWORK_DEBUG bool "Using connection framework debug log function" - default y + default n menuconfig CONNECTION_INDUSTRIAL_NETWORK bool "Using industrial network" diff --git a/APP_Framework/Framework/connection/adapter_agent.c b/APP_Framework/Framework/connection/adapter_agent.c index 445d83ed5..e9c4dcb98 100755 --- a/APP_Framework/Framework/connection/adapter_agent.c +++ b/APP_Framework/Framework/connection/adapter_agent.c @@ -124,7 +124,9 @@ int ParseATReply(char *str, const char *format, ...) void ATSprintf(int fd, const char *format, va_list params) { last_cmd_len = vsnprintf(send_buf, sizeof(send_buf), format, params); +#ifdef CONNECTION_FRAMEWORK_DEBUG printf("AT send %s len %u\n",send_buf, last_cmd_len); +#endif PrivWrite(fd, send_buf, last_cmd_len); } @@ -264,29 +266,34 @@ int AtSetReplyCharNum(ATAgentType agent, unsigned int num) int EntmSend(ATAgentType agent, const char *data, int len) { - char send_buf[128]; - if(len > 128){ - printf("send length %d more then max 128 Bytes.\n",len); + if(len > 256){ + printf("send length %d more then max 256 Bytes.\n",len); return -1; } + char *send_buff = (char *)PrivMalloc(256); PrivMutexObtain(&agent->lock); - memset(send_buf, 0, 128); + memset(send_buff, 0, 256); agent->receive_mode = ENTM_MODE; - memcpy(send_buf, data, len); - // memcpy(send_buf + len, "!@", 2); + memcpy(send_buff, data, len); - PrivWrite(agent->fd, send_buf, len); + PrivWrite(agent->fd, send_buff, len); PrivMutexAbandon(&agent->lock); - printf("entm send %s length %d\n",send_buf, len); +#ifdef CONNECTION_FRAMEWORK_DEBUG + printf("entm send length %d\n", len); +#endif + + PrivFree(send_buff); + return 0; } int EntmRecv(ATAgentType agent, char *rev_buffer, int buffer_len, int timeout_s) { struct timespec abstime; + uint32 real_recv_len = 0; abstime.tv_sec = timeout_s; if(buffer_len > ENTM_RECV_MAX){ @@ -299,21 +306,25 @@ int EntmRecv(ATAgentType agent, char *rev_buffer, int buffer_len, int timeout_s) PrivMutexAbandon(&agent->lock); //PrivTaskDelay(1000); if (PrivSemaphoreObtainWait(&agent->entm_rx_notice, &abstime)) { +#ifdef CONNECTION_FRAMEWORK_DEBUG printf("wait sem[%d] timeout\n",agent->entm_rx_notice); +#endif + agent->entm_recv_len = 0; return -1; } PrivMutexObtain(&agent->lock); - +#ifdef CONNECTION_FRAMEWORK_DEBUG printf("EntmRecv once len %d.\n", agent->entm_recv_len); - +#endif memcpy(rev_buffer, agent->entm_recv_buf, agent->entm_recv_len); - memset(agent->entm_recv_buf, 0, ENTM_RECV_MAX); + + real_recv_len = agent->entm_recv_len; agent->entm_recv_len = 0; agent->read_len = 0; PrivMutexAbandon(&agent->lock); - return buffer_len; + return real_recv_len; } static int GetCompleteATReply(ATAgentType agent) @@ -321,21 +332,22 @@ static int GetCompleteATReply(ATAgentType agent) uint32_t read_len = 0; char ch = 0, last_ch = 0; bool is_full = false; + int res; PrivMutexObtain(&agent->lock); memset(agent->maintain_buffer, 0x00, agent->maintain_max); agent->maintain_len = 0; - memset(agent->entm_recv_buf, 0x00, 256); + memset(agent->entm_recv_buf, 0x00, ENTM_RECV_MAX); agent->entm_recv_len = 0; PrivMutexAbandon(&agent->lock); while (1) { - PrivRead(agent->fd, &ch, 1); + res = PrivRead(agent->fd, &ch, 1); #ifdef CONNECTION_FRAMEWORK_DEBUG - if(ch != 0) { + if((res == 1) && (ch != 0)) { printf(" %c (0x%x)\n", ch, ch); } #endif @@ -343,14 +355,28 @@ static int GetCompleteATReply(ATAgentType agent) PrivMutexObtain(&agent->lock); if (agent->receive_mode == ENTM_MODE) { if (agent->entm_recv_len < ENTM_RECV_MAX) { - agent->entm_recv_buf[agent->entm_recv_len] = ch; - agent->entm_recv_len++; - - if(agent->entm_recv_len < agent->read_len) { +#ifdef TOOL_USING_MQTT + if((res == 1) && (agent->entm_recv_len < agent->read_len)) + { + agent->entm_recv_buf[agent->entm_recv_len] = ch; + agent->entm_recv_len++; PrivMutexAbandon(&agent->lock); continue; - } else { + } +#else + agent->entm_recv_buf[agent->entm_recv_len] = ch; + agent->entm_recv_len++; + if(agent->entm_recv_len < agent->read_len) + { + PrivMutexAbandon(&agent->lock); + continue; + } +#endif + else + { +#ifdef CONNECTION_FRAMEWORK_DEBUG printf("ENTM_MODE recv %d Bytes done.\n",agent->entm_recv_len); +#endif agent->receive_mode = DEFAULT_MODE; PrivSemaphoreAbandon(&agent->entm_rx_notice); } diff --git a/APP_Framework/Framework/connection/at_agent.h b/APP_Framework/Framework/connection/at_agent.h index d3efd5edb..81ed948cf 100755 --- a/APP_Framework/Framework/connection/at_agent.h +++ b/APP_Framework/Framework/connection/at_agent.h @@ -28,6 +28,12 @@ #define REPLY_TIME_OUT 10 +#ifdef TOOL_USING_OTA +#define ENTM_RECV_MAX OTA_RX_BUFFERSIZE +#else +#define ENTM_RECV_MAX 256 +#endif + enum ReceiveMode { DEFAULT_MODE = 0, @@ -70,7 +76,6 @@ struct ATAgent #endif pthread_t at_handler; - #define ENTM_RECV_MAX 256 char entm_recv_buf[ENTM_RECV_MAX]; uint32 entm_recv_len; enum ReceiveMode receive_mode; diff --git a/APP_Framework/lib/Kconfig b/APP_Framework/lib/Kconfig index c13ad7a4c..8f9b35eda 100755 --- a/APP_Framework/lib/Kconfig +++ b/APP_Framework/lib/Kconfig @@ -14,4 +14,5 @@ menu "app lib" source "$APP_DIR/lib/lvgl/Kconfig" source "$APP_DIR/lib/embedded_database/Kconfig" source "$APP_DIR/lib/lorawan/Kconfig" + source "$APP_DIR/lib/mqtt/Kconfig" endmenu diff --git a/APP_Framework/lib/Makefile b/APP_Framework/lib/Makefile index 327c69ef2..5877462e4 100644 --- a/APP_Framework/lib/Makefile +++ b/APP_Framework/lib/Makefile @@ -18,4 +18,8 @@ ifeq ($(CONFIG_LIB_USING_LORAWAN),y) SRC_DIR += lorawan endif +ifeq ($(CONFIG_TOOL_USING_MQTT),y) + SRC_DIR += mqtt +endif + include $(KERNEL_ROOT)/compiler.mk diff --git a/APP_Framework/lib/mqtt/Kconfig b/APP_Framework/lib/mqtt/Kconfig new file mode 100644 index 000000000..1084f4f4a --- /dev/null +++ b/APP_Framework/lib/mqtt/Kconfig @@ -0,0 +1,33 @@ +menu "lib using MQTT" + + menuconfig TOOL_USING_MQTT + bool "Enable support MQTT function" + default n + select SUPPORT_CONNECTION_FRAMEWORK + select CONNECTION_ADAPTER_4G + + if TOOL_USING_MQTT + menu "MQTT connection parameter configuration." + config PLATFORM_PRODUCTKEY + string "Product Key, used to identify a product." + default "iv74vebCdJC" + + config CLIENT_DEVICENAME + string "Device name, used to identify a client device." + default "D001" + + config CLIENT_DEVICESECRET + string "Device secret, used for device authentication and data encryption." + default "d2e613c4f714b6b0774bd7b68eeceae3" + + config PLATFORM_SERVERIP + string "mqtt platform server ip." + default "101.133.196.127" + + config PLATFORM_SERVERPORT + string "mqtt platform server port." + default "1883" + endmenu + endif + +endmenu diff --git a/APP_Framework/lib/mqtt/Makefile b/APP_Framework/lib/mqtt/Makefile new file mode 100644 index 000000000..b924f47d1 --- /dev/null +++ b/APP_Framework/lib/mqtt/Makefile @@ -0,0 +1,3 @@ +SRC_FILES := platform_mqtt.c utils_hmacsha1.c + +include $(KERNEL_ROOT)/compiler.mk \ No newline at end of file diff --git a/APP_Framework/lib/mqtt/platform_mqtt.c b/APP_Framework/lib/mqtt/platform_mqtt.c new file mode 100644 index 000000000..91689d280 --- /dev/null +++ b/APP_Framework/lib/mqtt/platform_mqtt.c @@ -0,0 +1,438 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file: platform_mqtt.c +* @brief: platform_mqtt.c file +* @version: 1.0 +* @author: AIIT XUOS Lab +* @date: 2023/7/27 +* +*/ + +#include +#include +#include +#include +#include "platform_mqtt.h" + +MQTT_TCB Platform_mqtt; //创建一个用于连接云平台mqtt的结构体 +static struct Adapter *adapter; +static const uint8_t parket_connetAck[] = {0x20,0x02,0x00,0x00}; //连接成功服务器回应报文 +static const uint8_t parket_disconnet[] = {0xE0,0x00}; //客户端主动断开连接发送报文 +static const uint8_t parket_heart[] = {0xC0,0x00}; //客户端发送保活心跳包 +static const uint8_t parket_subAck[] = {0x90,0x03,0x00,0x0A,0x01}; //订阅成功服务器回应报文 +static const uint8_t parket_unsubAck[] = {0xB0,0x02,0x00,0x0A}; //取消订阅成功服务器回应报文 +static uint8_t mqtt_rxbuf[16]; + + +/******************************************************************************* +* 函 数 名: AdapterNetActive +* 功能描述: 使能设备的网络模块连接,连接TCP服务器并进入透传模式,当前使用4G方式 +* 形 参: 无 +* 返 回 值: 0表示成功,其他值表示失败 +*******************************************************************************/ +int AdapterNetActive(void) +{ + int ret = 0; + uint32_t baud_rate = BAUD_RATE_115200; + adapter = AdapterDeviceFindByName(ADAPTER_4G_NAME); + adapter->socket.socket_id = 0; + + ret = AdapterDeviceOpen(adapter); + if (ret < 0) + { + goto out; + } + + ret = AdapterDeviceControl(adapter, OPE_INT, &baud_rate); + if (ret < 0) + { + goto out; + } + + ret = AdapterDeviceConnect(adapter, CLIENT, PLATFORM_SERVERIP, PLATFORM_SERVERPORT, IPV4); + if (ret < 0) + { + goto out; + } + +out: + if (ret < 0) + { + AdapterDeviceClose(adapter); + } + + return ret; +} + + + +/******************************************************************************* +* 函 数 名: MQTT_Send +* 功能描述: MQTT client数据发送函数 +* 形 参: buf:要发送的数据,buflen:要发送的数据长度 +* 返 回 值: 发送成功为0,发送失败为-1 +*******************************************************************************/ +int MQTT_Send(const uint8_t* buf, int buflen) +{ + return AdapterDeviceSend(adapter, buf, buflen) ; +} + + +/******************************************************************************* +* 函 数 名: MQTT_Recv +* 功能描述: MQTT client数据接收函数 +* 形 参: buf:数据缓冲区,buflen:期望接收的数据长度 +* 返 回 值: 实际接收到的数据长度,接收失败为-1 +*******************************************************************************/ +int MQTT_Recv(uint8_t* buf, int buflen) +{ + return AdapterDeviceRecv(adapter, buf, buflen) ; +} + + +/******************************************************************************* +* 函 数 名: MQTT_Connect +* 功能描述: 登录MQTT服务器 +* 形 参: 无 +* 返 回 值: 0表示成功,1表示失败 +*******************************************************************************/ +int MQTT_Connect(void) +{ + uint8_t TryConnect_time = 10; //尝试登录次数 + uint8_t passwdtemp[PASSWARD_SIZE]; + + memset(&Platform_mqtt,0,sizeof(Platform_mqtt)); + sprintf(Platform_mqtt.ClientID,"%s|securemode=3,signmethod=hmacsha1|",CLIENT_DEVICENAME); //构建客户端ID并存入缓冲区 + sprintf(Platform_mqtt.Username,"%s&%s",CLIENT_DEVICENAME,PLATFORM_PRODUCTKEY); //构建用户名并存入缓冲区 + memset(passwdtemp,0,sizeof(passwdtemp)); + sprintf(passwdtemp,"clientId%sdeviceName%sproductKey%s",CLIENT_DEVICENAME,CLIENT_DEVICENAME,PLATFORM_PRODUCTKEY); //构建加密时的明文 + utils_hmac_sha1(passwdtemp,strlen(passwdtemp),Platform_mqtt.Passward,(char *)CLIENT_DEVICESECRET,strlen(CLIENT_DEVICESECRET)); //以DeviceSecret为秘钥对temp中的明文进行hmacsha1加密即为密码 + + Platform_mqtt.MessageID = 0; //报文标识符清零,CONNECT报文虽然不需要添加报文标识符,但是CONNECT报文是第一个发送的报文,在此清零报文标识符为后续报文做准备 + Platform_mqtt.Fixed_len = 1; //CONNECT报文固定报头长度暂定为1 + Platform_mqtt.Variable_len = 10; //CONNECT报文可变报头长度为10 + Platform_mqtt.Payload_len = (2+strlen(Platform_mqtt.ClientID)) + (2+strlen(Platform_mqtt.Username)) + (2+strlen(Platform_mqtt.Passward)); //CONNECT报文中负载长度 + Platform_mqtt.Remaining_len = Platform_mqtt.Variable_len + Platform_mqtt.Payload_len; //剩余长度=可变报头长度+负载长度 + memset(Platform_mqtt.Pack_buff,0,sizeof(Platform_mqtt.Pack_buff)); + + Platform_mqtt.Pack_buff[0] = 0x10; //CONNECT报文 固定报头第1个字节0x10 + do{ + if((Platform_mqtt.Remaining_len/128) == 0) + { + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len] = Platform_mqtt.Remaining_len; + } + else + { + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len] = (Platform_mqtt.Remaining_len%128)|0x80; + } + Platform_mqtt.Fixed_len++; + Platform_mqtt.Remaining_len = Platform_mqtt.Remaining_len/128; + }while(Platform_mqtt.Remaining_len); + + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+0] = 0x00; //CONNECT报文,可变报头第1个字节:固定0x00 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+1] = 0x04; //CONNECT报文,可变报头第2个字节:固定0x04 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+2] = 0x4D; //CONNECT报文,可变报头第3个字节:固定0x4D,大写字母M + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+3] = 0x51; //CONNECT报文,可变报头第4个字节:固定0x51,大写字母Q + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+4] = 0x54; //CONNECT报文,可变报头第5个字节:固定0x54,大写字母T + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+5] = 0x54; //CONNECT报文,可变报头第6个字节:固定0x54,大写字母T + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+6] = 0x04; //CONNECT报文,可变报头第7个字节:固定0x04 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+7] = 0xC2; //CONNECT报文,可变报头第8个字节:使能用户名和密码校验,不使用遗嘱功能,不保留会话功能 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+8] = KEEPALIVE_TIME/256; //CONNECT报文,可变报头第9个字节:保活时间高字节 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+9] = KEEPALIVE_TIME%256; //CONNECT报文,可变报头第10个字节:保活时间低字节,单位s + + /* CLIENT_ID */ + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+10] = strlen(Platform_mqtt.ClientID)/256; //客户端ID长度高字节 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+11] = strlen(Platform_mqtt.ClientID)%256; //客户端ID长度低字节 + memcpy(&Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+12],Platform_mqtt.ClientID,strlen(Platform_mqtt.ClientID)); //复制过来客户端ID字串 + /* USER_NAME */ + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+12+strlen(Platform_mqtt.ClientID)] = strlen(Platform_mqtt.Username)/256; //用户名长度高字节 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+13+strlen(Platform_mqtt.ClientID)] = strlen(Platform_mqtt.Username)%256; //用户名长度低字节 + memcpy(&Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+14+strlen(Platform_mqtt.ClientID)],Platform_mqtt.Username,strlen(Platform_mqtt.Username)); //复制过来用户名字串 + /* PASSWARD */ + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+14+strlen(Platform_mqtt.ClientID)+strlen(Platform_mqtt.Username)] = strlen(Platform_mqtt.Passward)/256; //密码长度高字节 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+15+strlen(Platform_mqtt.ClientID)+strlen(Platform_mqtt.Username)] = strlen(Platform_mqtt.Passward)%256; //密码长度低字节 + memcpy(&Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+16+strlen(Platform_mqtt.ClientID)+strlen(Platform_mqtt.Username)],Platform_mqtt.Passward,strlen(Platform_mqtt.Passward)); //复制过来密码字串 + + while(TryConnect_time > 0) + { + memset(mqtt_rxbuf,0,sizeof(mqtt_rxbuf)); + MQTT_Send(Platform_mqtt.Pack_buff,Platform_mqtt.Fixed_len + Platform_mqtt.Variable_len + Platform_mqtt.Payload_len); + MdelayKTask(50); + MQTT_Recv(mqtt_rxbuf, 4); + if(mqtt_rxbuf[0] == parket_connetAck[0] && mqtt_rxbuf[1] == parket_connetAck[1]) //连接成功 + { + return 0; + } + TryConnect_time--; + } + return 1; +} + + +/******************************************************************************* +* 函 数 名: MQTT_Disconnect +* 功能描述: 断开与MQTT服务器的连接 +* 形 参: 无 +* 返 回 值: 无 +*******************************************************************************/ +void MQTT_Disconnect(void) +{ + while(MQTT_Send(parket_disconnet,sizeof(parket_disconnet)) < 0); +} + + +/******************************************************************************* +* 函 数 名: MQTT_SubscribeTopic +* 功能描述: MQTT订阅单个主题 +* 形 参: topic_name:要订阅的主题 +* 返 回 值: 0表示订阅成功,1表示订阅失败 +*******************************************************************************/ +int MQTT_SubscribeTopic(uint8_t *topic_name) +{ + uint8_t TrySub_time = 10; //尝试订阅次数 + + Platform_mqtt.Fixed_len = 1; //SUBSCRIBE报文,固定报头长度暂定为1 + Platform_mqtt.Variable_len = 2;//SUBSCRIBE报文,可变报头长度=2,2为字节报文标识符 + Platform_mqtt.Payload_len = 0; //SUBSCRIBE报文,负载数据长度暂定为0 + + Platform_mqtt.Payload_len = strlen(topic_name) + 2 + 1; //每个需要订阅的topic除了本身的字符串长度,还包含表示topic字符串长度的2字节,以及订阅等级1字节 + Platform_mqtt.Remaining_len = Platform_mqtt.Variable_len + Platform_mqtt.Payload_len; //计算剩余长度=可变报头长度+负载长度 + memset(Platform_mqtt.Pack_buff,0,sizeof(Platform_mqtt.Pack_buff)); + + Platform_mqtt.Pack_buff[0]=0x82; //SUBSCRIBE报文,固定报头第1个字节0x82 + do{ + if((Platform_mqtt.Remaining_len/128) == 0) + { + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len] = Platform_mqtt.Remaining_len; + } + else + { + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len] = (Platform_mqtt.Remaining_len%128)|0x80; + } + Platform_mqtt.Fixed_len++; + Platform_mqtt.Remaining_len = Platform_mqtt.Remaining_len/128; + }while(Platform_mqtt.Remaining_len); + + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+0] = Platform_mqtt.MessageID/256; //报文标识符高字节 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+1] = Platform_mqtt.MessageID%256; //报文标识符低字节 + Platform_mqtt.MessageID++; //每用一次MessageID加1 + + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+2] = strlen(topic_name)/256; //主题长度高字节 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+3] = strlen(topic_name)%256; //主题长度低字节 + memcpy(&Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+4],topic_name,strlen(topic_name)); //复制主题字串 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+4+strlen(topic_name)] = 0; //QOS等级设置为0 + + while(TrySub_time > 0) + { + memset(mqtt_rxbuf,0,sizeof(mqtt_rxbuf)); + MQTT_Send(Platform_mqtt.Pack_buff,Platform_mqtt.Fixed_len + Platform_mqtt.Variable_len + Platform_mqtt.Payload_len); + MdelayKTask(50); + MQTT_Recv(mqtt_rxbuf, 5); + if(mqtt_rxbuf[0] == parket_subAck[0] && mqtt_rxbuf[1] == parket_subAck[1]) //订阅成功 + { + return 0; + } + TrySub_time--; + } + return 1; +} + + +/******************************************************************************* +* 函 数 名: MQTT_UnSubscribeTopic +* 功能描述: MQTT取消订阅单个主题 +* 形 参: topic_name:要取消订阅的主题 +* 返 回 值: 0表示订阅成功,1表示订阅失败 +*******************************************************************************/ +int MQTT_UnSubscribeTopic(uint8_t *topic_name) +{ + uint8_t TryUnSub_time = 10; //尝试取消订阅次数 + + Platform_mqtt.Fixed_len = 1; //UNSUBSCRIBE报文,固定报头长度暂定为1 + Platform_mqtt.Variable_len = 2; //UNSUBSCRIBE报文,可变报头长度=2,2为字节报文标识符 + Platform_mqtt.Payload_len = strlen(topic_name) + 2; //每个需要取消的订阅topic除了本身的字符串长度,还包含表示topic字符串长度的2字节 + Platform_mqtt.Remaining_len = Platform_mqtt.Variable_len + Platform_mqtt.Payload_len; //计算剩余长度=可变报头长度+负载长度 + memset(Platform_mqtt.Pack_buff,0,sizeof(Platform_mqtt.Pack_buff)); + + Platform_mqtt.Pack_buff[0]=0xA0; //UNSUBSCRIBE报文,固定报头第1个字节0xA0 + do{ + if((Platform_mqtt.Remaining_len/128) == 0) + { + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len] = Platform_mqtt.Remaining_len; + } + else + { + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len] = (Platform_mqtt.Remaining_len%128)|0x80; + } + Platform_mqtt.Fixed_len++; + Platform_mqtt.Remaining_len = Platform_mqtt.Remaining_len/128; + }while(Platform_mqtt.Remaining_len); + + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+0] = Platform_mqtt.MessageID/256; //报文标识符高字节 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+1] = Platform_mqtt.MessageID%256; //报文标识符低字节 + Platform_mqtt.MessageID++; //每用一次MessageID加1 + + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+2] = strlen(topic_name)/256; //主题长度高字节 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+3] = strlen(topic_name)%256; //主题长度低字节 + memcpy(&Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+4],topic_name,strlen(topic_name)); //复制主题字串 + + while(TryUnSub_time > 0) + { + memset(mqtt_rxbuf,0,sizeof(mqtt_rxbuf)); + MQTT_Send(Platform_mqtt.Pack_buff,Platform_mqtt.Fixed_len + Platform_mqtt.Variable_len + Platform_mqtt.Payload_len); + MdelayKTask(50); + MQTT_Recv(mqtt_rxbuf, 4); + if(mqtt_rxbuf[0] == parket_unsubAck[0] && mqtt_rxbuf[1] == parket_unsubAck[1]) //取消订阅成功 + { + return 0; + } + TryUnSub_time--; + } + return 1; +} + + +/******************************************************************************* +* 函 数 名: MQTT_PublishDataQs0 +* 功能描述: 向服务器发送等级0的Publish报文 +* 形 参: topic_name:主题名称 + data:数据缓存 + data_len:数据长度 +* 返 回 值: 发布Qs=0的消息服务器不返回确认消息 +*******************************************************************************/ +void MQTT_PublishDataQs0(uint8_t *topic_name,uint8_t *data, uint16_t data_len) +{ + Platform_mqtt.Fixed_len = 1; //PUBLISH等级0报文固定报头长度暂定为1 + Platform_mqtt.Variable_len = 2 + strlen(topic_name); //PUBLISH等级0报文,可变报头长度=2字节topic长度标识字节+topic字符串的长度 + Platform_mqtt.Payload_len = data_len; //PUBLISH等级0报文,负载数据长度=data_len + Platform_mqtt.Remaining_len = Platform_mqtt.Variable_len + Platform_mqtt.Payload_len; //计算剩余长度=可变报头长度+负载长度 + memset(Platform_mqtt.Pack_buff,0,sizeof(Platform_mqtt.Pack_buff)); + + Platform_mqtt.Pack_buff[0]=0x30; //PUBLISH等级0报文固定报头第1个字节0x30 + do{ + if((Platform_mqtt.Remaining_len/128) == 0) + { + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len] = Platform_mqtt.Remaining_len; + } + else + { + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len] = (Platform_mqtt.Remaining_len%128)|0x80; + } + Platform_mqtt.Fixed_len++; + Platform_mqtt.Remaining_len = Platform_mqtt.Remaining_len/128; + }while(Platform_mqtt.Remaining_len); + + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+0]=strlen(topic_name)/256; //主题长度高字节 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+1]=strlen(topic_name)%256; //主题长度低字节 + memcpy(&Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+2],topic_name,strlen(topic_name)); //复制主题字串 + memcpy(&Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+2+strlen(topic_name)],data,data_len); //复制data数据 + + MQTT_Send(Platform_mqtt.Pack_buff, Platform_mqtt.Fixed_len + Platform_mqtt.Variable_len + Platform_mqtt.Payload_len); +} + + +/******************************************************************************* +* 函 数 名: MQTT_PublishDataQs1 +* 功能描述: 向服务器发送等级1的Publish报文 +* 形 参: topic_name:主题名称 + data:数据缓存 + data_len:数据长度 +* 返 回 值: 无 +*******************************************************************************/ +void MQTT_PublishDataQs1(uint8_t *topic_name,uint8_t *data, uint16_t data_len) +{ + Platform_mqtt.Fixed_len = 1; //PUBLISH等级1报文固定报头长度暂定为1 + Platform_mqtt.Variable_len = 2 + 2 + strlen(topic_name); //PUBLISH等级1报文,可变报头长度=2字节消息标识符+2字节topic长度标识字节+topic字符串的长度 + Platform_mqtt.Payload_len = data_len; //PUBLISH等级1报文,负载数据长度=data_len + Platform_mqtt.Remaining_len = Platform_mqtt.Variable_len + Platform_mqtt.Payload_len; //计算剩余长度=可变报头长度+负载长度 + + Platform_mqtt.Pack_buff[0] = 0x32; //等级1的Publish报文固定报头第1个字节,0x32 + do{ + if(Platform_mqtt.Remaining_len/128 == 0) + { + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len] = Platform_mqtt.Remaining_len; + } + else + { + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len] = (Platform_mqtt.Remaining_len%128)|0x80; + } + Platform_mqtt.Fixed_len++; + Platform_mqtt.Remaining_len = Platform_mqtt.Remaining_len/128; + }while(Platform_mqtt.Remaining_len); + + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+0] = strlen(topic_name)/256; //主题长度高字节 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+1] = strlen(topic_name)%256; //主题长度低字节 + memcpy(&Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+2],topic_name,strlen(topic_name)); //复制主题字串 + + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+2+strlen(topic_name)] = Platform_mqtt.MessageID/256; //报文标识符高字节 + Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+3+strlen(topic_name)] = Platform_mqtt.MessageID%256; //报文标识符低字节 + Platform_mqtt.MessageID++; //每用一次MessageID加1 + + memcpy(&Platform_mqtt.Pack_buff[Platform_mqtt.Fixed_len+4+strlen(topic_name)],data,strlen(data)); //复制data数据 + + MQTT_Send(Platform_mqtt.Pack_buff,Platform_mqtt.Fixed_len + Platform_mqtt.Variable_len + Platform_mqtt.Payload_len); +} + + +/******************************************************************************* +* 函 数 名: MQTT_SendHeart +* 功能描述: 发送心跳保活包 +* 形 参: 无 +* 返 回 值: 0表示发送成功,其他值表示发送失败 +*******************************************************************************/ +int MQTT_SendHeart(void) +{ + uint8_t TrySentHeart_time = 10; //尝试发送心跳保活次数 + while(TrySentHeart_time > 0) + { + memset(mqtt_rxbuf,0,sizeof(mqtt_rxbuf)); + MQTT_Send(parket_heart,sizeof(parket_heart)); + MdelayKTask(50); + MQTT_Recv(mqtt_rxbuf, 2); + if(mqtt_rxbuf[0] == 0xD0 && mqtt_rxbuf[1] == 0x00) + { + return 0; + } + TrySentHeart_time--; + } + return 1; +} + + +/******************************************************************************* +* 函 数 名: MQTT_DealPublishData +* 功能描述: 处理服务器发来的等级0的推送数据,附带topic信息 +* 形 参: redata:接收的数据,data_len:要处理的数据长度 +* 返 回 值: 无 +*******************************************************************************/ +void MQTT_DealPublishData(uint8_t *data, uint16_t data_len) +{ + uint8_t i; + uint16_t cmdpos,cmdlen; + + for(i = 1;i < 5;i++) + { + if((data[i] & 0x80) == 0) + break; + } + + cmdpos = 1+i+2; + cmdlen = data_len-(1+i+2); + + if(data_len <= CMD_SIZE) + { + memset(Platform_mqtt.cmdbuff, 0, CMD_SIZE); + memcpy(Platform_mqtt.cmdbuff, &data[cmdpos], cmdlen); + } +} diff --git a/APP_Framework/lib/mqtt/platform_mqtt.h b/APP_Framework/lib/mqtt/platform_mqtt.h new file mode 100644 index 000000000..92333e9b1 --- /dev/null +++ b/APP_Framework/lib/mqtt/platform_mqtt.h @@ -0,0 +1,62 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file: platform_mqtt.h +* @brief: platform_mqtt.h file +* @version: 1.0 +* @author: AIIT XUOS Lab +* @date: 2023/7/27 +* +*/ + +#ifndef _PLATFORM_MQTT_H_ +#define _PLATFORM_MQTT_H_ + +#include +#include "utils_hmacsha1.h" + +#define KEEPALIVE_TIME 300 //保活时间(单位s),300s +#define HEART_TIME 120000 //空闲时发送心跳包的时间间隔(单位ms),120s +#define PACK_SIZE 512 //存放报文数据缓冲区大小 +#define CMD_SIZE 3072 //保存推送的PUBLISH报文中的数据缓冲区大小 +#define CLIENTID_SIZE 64 //存放客户端ID的缓冲区大小 +#define USERNAME_SIZE 64 //存放用户名的缓冲区大小 +#define PASSWARD_SIZE 64 //存放密码的缓冲区大小 + +typedef struct{ + uint8_t ClientID[CLIENTID_SIZE]; //存放客户端ID的缓冲区 + uint8_t Username[USERNAME_SIZE]; //存放用户名的缓冲区 + uint8_t Passward[PASSWARD_SIZE]; //存放密码的缓冲区 + uint8_t Pack_buff[PACK_SIZE]; //存放发送报文数据缓冲区 + uint16_t MessageID; //记录报文标识符 + uint16_t Fixed_len; //固定报头长度 + uint16_t Variable_len; //可变报头长度 + uint16_t Payload_len; //有效负荷长度 + uint16_t Remaining_len; //保存报文剩余长度字节 + uint8_t cmdbuff[CMD_SIZE]; //保存推送的PUBLISH报文中的数据缓冲区 +}MQTT_TCB; + +extern MQTT_TCB Platform_mqtt; //外部变量声明 + +int AdapterNetActive(void); +int MQTT_Send(const uint8_t* buf, int buflen); +int MQTT_Recv(uint8_t* buf, int buflen); +int MQTT_Connect(void); +void MQTT_Disconnect(void); +int MQTT_SubscribeTopic(uint8_t *topic_name); +int MQTT_UnSubscribeTopic(uint8_t *topic_name); +void MQTT_PublishDataQs0(uint8_t *topic_name,uint8_t *data, uint16_t data_len); +void MQTT_PublishDataQs1(uint8_t *topic_name,uint8_t *data, uint16_t data_len); +int MQTT_SendHeart(void); +void MQTT_DealPublishData(uint8_t *data, uint16_t data_len); +#endif \ No newline at end of file diff --git a/APP_Framework/lib/mqtt/utils_hmacsha1.c b/APP_Framework/lib/mqtt/utils_hmacsha1.c new file mode 100644 index 000000000..8ed6670ed --- /dev/null +++ b/APP_Framework/lib/mqtt/utils_hmacsha1.c @@ -0,0 +1,399 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file: utils_hmacsha1.c +* @brief: utils_hmacsha1.c file +* @version: 1.0 +* @author: AIIT XUOS Lab +* @date: 2023/7/27 +* +*/ + +#include "utils_hmacsha1.h" + +#define KEY_IOPAD_SIZE 64 +#define SHA1_DIGEST_SIZE 20 + +static void utils_sha1_zeroize(void *v, size_t n); +static void utils_sha1_init(iot_sha1_context *ctx); +static void utils_sha1_free(iot_sha1_context *ctx); +static void utils_sha1_clone(iot_sha1_context *dst, const iot_sha1_context *src); +static void utils_sha1_starts(iot_sha1_context *ctx); +static void utils_sha1_process(iot_sha1_context *ctx, const unsigned char data[64]); +static void utils_sha1_update(iot_sha1_context *ctx, const unsigned char *input, size_t ilen); +static void utils_sha1_finish(iot_sha1_context *ctx, unsigned char output[20]); +static void utils_sha1(const unsigned char *input, size_t ilen, unsigned char output[20]); +static int8_t utils_hb2hex(uint8_t hb); + +const char * base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* Implementation that should never be optimized out by the compiler */ +static void utils_sha1_zeroize(void *v, size_t n) +{ + volatile unsigned char *p = v; + while(n--) { + *p++ = 0; + } +} + +/* 32-bit integer manipulation macros (big endian) */ +#ifndef IOT_SHA1_GET_UINT32_BE +#define IOT_SHA1_GET_UINT32_BE(n,b,i) \ + { \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ + | ( (uint32_t) (b)[(i) + 1] << 16 ) \ + | ( (uint32_t) (b)[(i) + 2] << 8 ) \ + | ( (uint32_t) (b)[(i) + 3] ); \ + } +#endif + +#ifndef IOT_SHA1_PUT_UINT32_BE +#define IOT_SHA1_PUT_UINT32_BE(n,b,i) \ + { \ + (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ + (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ + (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ + (b)[(i) + 3] = (unsigned char) ( (n) ); \ + } +#endif + +void utils_sha1_init(iot_sha1_context *ctx) +{ + memset(ctx, 0, sizeof(iot_sha1_context)); +} + +void utils_sha1_free(iot_sha1_context *ctx) +{ + if(ctx == NULL) { + return; + } + + utils_sha1_zeroize(ctx, sizeof(iot_sha1_context)); +} + +void utils_sha1_clone(iot_sha1_context *dst, + const iot_sha1_context *src) +{ + *dst = *src; +} + +/* SHA-1 context setup */ +void utils_sha1_starts(iot_sha1_context *ctx) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +void utils_sha1_process(iot_sha1_context *ctx, const unsigned char data[64]) +{ + uint32_t temp, W[16], A, B, C, D, E; + + IOT_SHA1_GET_UINT32_BE(W[ 0], data, 0); + IOT_SHA1_GET_UINT32_BE(W[ 1], data, 4); + IOT_SHA1_GET_UINT32_BE(W[ 2], data, 8); + IOT_SHA1_GET_UINT32_BE(W[ 3], data, 12); + IOT_SHA1_GET_UINT32_BE(W[ 4], data, 16); + IOT_SHA1_GET_UINT32_BE(W[ 5], data, 20); + IOT_SHA1_GET_UINT32_BE(W[ 6], data, 24); + IOT_SHA1_GET_UINT32_BE(W[ 7], data, 28); + IOT_SHA1_GET_UINT32_BE(W[ 8], data, 32); + IOT_SHA1_GET_UINT32_BE(W[ 9], data, 36); + IOT_SHA1_GET_UINT32_BE(W[10], data, 40); + IOT_SHA1_GET_UINT32_BE(W[11], data, 44); + IOT_SHA1_GET_UINT32_BE(W[12], data, 48); + IOT_SHA1_GET_UINT32_BE(W[13], data, 52); + IOT_SHA1_GET_UINT32_BE(W[14], data, 56); + IOT_SHA1_GET_UINT32_BE(W[15], data, 60); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define R(t) \ + ( \ + temp = W[( t - 3 ) & 0x0F] ^ W[( t - 8 ) & 0x0F] ^ \ + W[( t - 14 ) & 0x0F] ^ W[ t & 0x0F], \ + ( W[t & 0x0F] = S(temp,1) ) \ + ) + +#define P(a,b,c,d,e,x) \ + { \ + e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \ + } + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) +#define K 0x5A827999 + + P(A, B, C, D, E, W[0]); + P(E, A, B, C, D, W[1]); + P(D, E, A, B, C, W[2]); + P(C, D, E, A, B, W[3]); + P(B, C, D, E, A, W[4]); + P(A, B, C, D, E, W[5]); + P(E, A, B, C, D, W[6]); + P(D, E, A, B, C, W[7]); + P(C, D, E, A, B, W[8]); + P(B, C, D, E, A, W[9]); + P(A, B, C, D, E, W[10]); + P(E, A, B, C, D, W[11]); + P(D, E, A, B, C, W[12]); + P(C, D, E, A, B, W[13]); + P(B, C, D, E, A, W[14]); + P(A, B, C, D, E, W[15]); + P(E, A, B, C, D, R(16)); + P(D, E, A, B, C, R(17)); + P(C, D, E, A, B, R(18)); + P(B, C, D, E, A, R(19)); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0x6ED9EBA1 + + P(A, B, C, D, E, R(20)); + P(E, A, B, C, D, R(21)); + P(D, E, A, B, C, R(22)); + P(C, D, E, A, B, R(23)); + P(B, C, D, E, A, R(24)); + P(A, B, C, D, E, R(25)); + P(E, A, B, C, D, R(26)); + P(D, E, A, B, C, R(27)); + P(C, D, E, A, B, R(28)); + P(B, C, D, E, A, R(29)); + P(A, B, C, D, E, R(30)); + P(E, A, B, C, D, R(31)); + P(D, E, A, B, C, R(32)); + P(C, D, E, A, B, R(33)); + P(B, C, D, E, A, R(34)); + P(A, B, C, D, E, R(35)); + P(E, A, B, C, D, R(36)); + P(D, E, A, B, C, R(37)); + P(C, D, E, A, B, R(38)); + P(B, C, D, E, A, R(39)); + +#undef K +#undef F + +#define F(x,y,z) ((x & y) | (z & (x | y))) +#define K 0x8F1BBCDC + + P(A, B, C, D, E, R(40)); + P(E, A, B, C, D, R(41)); + P(D, E, A, B, C, R(42)); + P(C, D, E, A, B, R(43)); + P(B, C, D, E, A, R(44)); + P(A, B, C, D, E, R(45)); + P(E, A, B, C, D, R(46)); + P(D, E, A, B, C, R(47)); + P(C, D, E, A, B, R(48)); + P(B, C, D, E, A, R(49)); + P(A, B, C, D, E, R(50)); + P(E, A, B, C, D, R(51)); + P(D, E, A, B, C, R(52)); + P(C, D, E, A, B, R(53)); + P(B, C, D, E, A, R(54)); + P(A, B, C, D, E, R(55)); + P(E, A, B, C, D, R(56)); + P(D, E, A, B, C, R(57)); + P(C, D, E, A, B, R(58)); + P(B, C, D, E, A, R(59)); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0xCA62C1D6 + + P(A, B, C, D, E, R(60)); + P(E, A, B, C, D, R(61)); + P(D, E, A, B, C, R(62)); + P(C, D, E, A, B, R(63)); + P(B, C, D, E, A, R(64)); + P(A, B, C, D, E, R(65)); + P(E, A, B, C, D, R(66)); + P(D, E, A, B, C, R(67)); + P(C, D, E, A, B, R(68)); + P(B, C, D, E, A, R(69)); + P(A, B, C, D, E, R(70)); + P(E, A, B, C, D, R(71)); + P(D, E, A, B, C, R(72)); + P(C, D, E, A, B, R(73)); + P(B, C, D, E, A, R(74)); + P(A, B, C, D, E, R(75)); + P(E, A, B, C, D, R(76)); + P(D, E, A, B, C, R(77)); + P(C, D, E, A, B, R(78)); + P(B, C, D, E, A, R(79)); + +#undef K +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +/* SHA-1 process buffer */ +void utils_sha1_update(iot_sha1_context *ctx, const unsigned char *input, size_t ilen) +{ + size_t fill; + uint32_t left; + + if(ilen == 0) { + return; + } + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += (uint32_t) ilen; + ctx->total[0] &= 0xFFFFFFFF; + + if(ctx->total[0] < (uint32_t) ilen) { + ctx->total[1]++; + } + + if(left && ilen >= fill) { + memcpy((void *)(ctx->buffer + left), input, fill); + utils_sha1_process(ctx, ctx->buffer); + input += fill; + ilen -= fill; + left = 0; + } + + while(ilen >= 64) { + utils_sha1_process(ctx, input); + input += 64; + ilen -= 64; + } + + if(ilen > 0) { + memcpy((void *)(ctx->buffer + left), input, ilen); + } +} + +static const unsigned char iot_sha1_padding[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* SHA-1 final digest */ +void utils_sha1_finish(iot_sha1_context *ctx, unsigned char output[20]) +{ + uint32_t last, padn; + uint32_t high, low; + unsigned char msglen[8]; + + high = (ctx->total[0] >> 29) + | (ctx->total[1] << 3); + low = (ctx->total[0] << 3); + + IOT_SHA1_PUT_UINT32_BE(high, msglen, 0); + IOT_SHA1_PUT_UINT32_BE(low, msglen, 4); + + last = ctx->total[0] & 0x3F; + padn = (last < 56) ? (56 - last) : (120 - last); + + utils_sha1_update(ctx, iot_sha1_padding, padn); + utils_sha1_update(ctx, msglen, 8); + + IOT_SHA1_PUT_UINT32_BE(ctx->state[0], output, 0); + IOT_SHA1_PUT_UINT32_BE(ctx->state[1], output, 4); + IOT_SHA1_PUT_UINT32_BE(ctx->state[2], output, 8); + IOT_SHA1_PUT_UINT32_BE(ctx->state[3], output, 12); + IOT_SHA1_PUT_UINT32_BE(ctx->state[4], output, 16); +} + + +/* output = SHA-1(input buffer) */ +void utils_sha1(const unsigned char *input, size_t ilen, unsigned char output[20]) +{ + iot_sha1_context ctx; + + utils_sha1_init(&ctx); + utils_sha1_starts(&ctx); + utils_sha1_update(&ctx, input, ilen); + utils_sha1_finish(&ctx, output); + utils_sha1_free(&ctx); +} + + +inline int8_t utils_hb2hex(uint8_t hb) +{ + hb = hb & 0xF; + return (int8_t)(hb < 10 ? '0' + hb : hb - 10 + 'a'); +} + + +void utils_hmac_sha1(const char *msg, int msg_len, char *digest, const char *key, int key_len) +{ + iot_sha1_context context; + unsigned char k_ipad[KEY_IOPAD_SIZE]; /* inner padding - key XORd with ipad */ + unsigned char k_opad[KEY_IOPAD_SIZE]; /* outer padding - key XORd with opad */ + unsigned char out[SHA1_DIGEST_SIZE]; + int i; + + if((NULL == msg) || (NULL == digest) || (NULL == key)) { + return; + } + + if(key_len > KEY_IOPAD_SIZE) { + return; + } + + /* start out by storing key in pads */ + memset(k_ipad, 0, sizeof(k_ipad)); + memset(k_opad, 0, sizeof(k_opad)); + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); + + /* XOR key with ipad and opad values */ + for(i = 0; i < KEY_IOPAD_SIZE; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + /* perform inner SHA */ + utils_sha1_init(&context); /* init context for 1st pass */ + utils_sha1_starts(&context); /* setup context for 1st pass */ + utils_sha1_update(&context, k_ipad, KEY_IOPAD_SIZE); /* start with inner pad */ + utils_sha1_update(&context, (unsigned char *) msg, msg_len); /* then text of datagram */ + utils_sha1_finish(&context, out); /* finish up 1st pass */ + + /* perform outer SHA */ + utils_sha1_init(&context); /* init context for 2nd pass */ + utils_sha1_starts(&context); /* setup context for 2nd pass */ + utils_sha1_update(&context, k_opad, KEY_IOPAD_SIZE); /* start with outer pad */ + utils_sha1_update(&context, out, SHA1_DIGEST_SIZE); /* then results of 1st hash */ + utils_sha1_finish(&context, out); /* finish up 2nd pass */ + + for(i = 0; i < SHA1_DIGEST_SIZE; ++i) { + digest[i * 2] = utils_hb2hex(out[i] >> 4); + digest[i * 2 + 1] = utils_hb2hex(out[i]); + } +} \ No newline at end of file diff --git a/APP_Framework/lib/mqtt/utils_hmacsha1.h b/APP_Framework/lib/mqtt/utils_hmacsha1.h new file mode 100644 index 000000000..0ddbe377b --- /dev/null +++ b/APP_Framework/lib/mqtt/utils_hmacsha1.h @@ -0,0 +1,41 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file: utils_hmacsha1.h +* @brief: utils_hmacsha1.h file +* @version: 1.0 +* @author: AIIT XUOS Lab +* @date: 2023/7/27 +* +*/ + +#ifndef UTILS_HMACSHA1_H_ +#define UTILS_HMACSHA1_H_ + +#include "stdio.h" +#include "stdint.h" +#include "stdlib.h" +#include "string.h" + +/* SHA-1 context structure */ +typedef struct { + uint32_t total[2]; /* number of bytes processed */ + uint32_t state[5]; /* intermediate digest state */ + unsigned char buffer[64]; /* data block being processed */ +} iot_sha1_context; + + +void utils_hmac_sha1(const char *msg, int msg_len, char *digest, const char *key, int key_len); + +#endif + diff --git a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/.defconfig b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/.defconfig index fd991b36b..5e2b9c4fd 100644 --- a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/.defconfig +++ b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/.defconfig @@ -21,8 +21,12 @@ CONFIG_BSP_USING_LPUART3=y CONFIG_SERIAL_BUS_NAME_3="uart3" CONFIG_SERIAL_DRV_NAME_3="uart3_drv" CONFIG_SERIAL_3_DEVICE_NAME_0="uart3_dev3" + # CONFIG_BSP_USING_LPUART4 is not set -# CONFIG_BSP_USING_LPUART8 is not set +CONFIG_BSP_USING_LPUART8=y +CONFIG_SERIAL_BUS_NAME_8="uart8" +CONFIG_SERIAL_DRV_NAME_8="uart8_drv" +CONFIG_SERIAL_8_DEVICE_NAME_0="uart8_dev8" # CONFIG_BSP_USING_CH438 is not set CONFIG_BSP_USING_GPIO=y CONFIG_PIN_BUS_NAME="pin" @@ -63,7 +67,7 @@ CONFIG___STACKSIZE__=4096 # CONFIG_RESOURCES_SERIAL=y CONFIG_SERIAL_USING_DMA=y -CONFIG_SERIAL_RB_BUFSZ=128 +CONFIG_SERIAL_RB_BUFSZ=256 CONFIG_RESOURCES_PIN=y # @@ -125,7 +129,7 @@ CONFIG_ZOMBIE_KTASK_STACKSIZE=2048 # CONFIG_KERNEL_CONSOLE=y CONFIG_KERNEL_BANNER=y -CONFIG_KERNEL_CONSOLEBUF_SIZE=128 +CONFIG_KERNEL_CONSOLEBUF_SIZE=256 # # Kernel Hook @@ -231,6 +235,7 @@ CONFIG_ADD_XIZI_FEATURES=y # CONFIG_SUPPORT_KNOWING_FRAMEWORK is not set # CONFIG_SUPPORT_CONTROL_FRAMEWORK is not set + # # Security # diff --git a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/common/flash.c b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/common/flash.c index b10baee6b..a1d77d816 100644 --- a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/common/flash.c +++ b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/common/flash.c @@ -507,7 +507,7 @@ static status_t flexspi_config_mcr1(uint32_t instance, flexspi_mem_config_t *con // Configure MCR1 FLEXSPI->MCR1 = FLEXSPI_MCR1_SEQWAIT(seqWaitTicks) | FLEXSPI_MCR1_AHBBUSWAIT(ahbBusWaitTicks); - return kStatus_Success; + return (status_t)kStatus_Success; } @@ -647,14 +647,14 @@ uint8_t FLASH_WritePage(uint32_t addr, const uint32_t *buf, uint32_t len) /******************************************************************************* -* 函 数 名: FLASH_Read +* 函 数 名: FLASH_ReadBuf * 功能描述: 读Flash内容 * 形 参: addr:读取区域起始地址 buf:数据存储区 len:要读取的字节数 * 返 回 值: 如果函数执行成功,状态值为 kStatus_Success,否则状态值为其他错误码 *******************************************************************************/ -status_t FLASH_Read(uint32_t addr, uint32_t *buf, uint32_t len) +status_t FLASH_ReadBuf(uint32_t addr, uint32_t *buf, uint32_t len) { status_t status; flexspi_xfer_t flashXfer; @@ -678,206 +678,28 @@ status_t FLASH_Read(uint32_t addr, uint32_t *buf, uint32_t len) } -/******************************************************************************* -* 函 数 名: flash_erase -* 功能描述: 擦除Flash指定长度的空间 -* 形 参: addr:擦除区域起始地址 - byte_cnt:要擦除的字节数,以4k字节为最小擦除单位 -* 返 回 值: 如果函数执行成功,状态值为 kStatus_Success,否则状态值为其他错误码 -* 注 释: 不满4k字节的,也需要擦除掉4k字节 -*******************************************************************************/ -status_t flash_erase(uint32_t start_addr, uint32_t byte_cnt) -{ - uint32_t addr; - status_t status; - - addr = start_addr; - while(addr < (byte_cnt + start_addr)) - { - status = FLASH_EraseSector(addr); - if(status != kStatus_Success) - { - return status; - } - addr += FLASH_GetSectorSize(); - } - return status; -} - - -/******************************************************************************* -* 函 数 名: flash_write -* 功能描述: 在指定的flash起始地址写入指定长度的数据 -* 形 参: addr:写入区域起始地址 - buf:数据存储区 - byte_cnt:要写入的字节数 -* 返 回 值: 如果函数执行成功,状态值为 kStatus_Success,否则状态值为其他错误码 -*******************************************************************************/ -status_t flash_write(uint32_t start_addr, uint8_t *buf, uint32_t byte_cnt) -{ - uint32_t size; - status_t status; - while(byte_cnt > 0) - { - size = byte_cnt > FLASH_PAGE_SIZE ? FLASH_PAGE_SIZE : byte_cnt; - status = FLASH_WritePage(start_addr, (void *)buf, size); - if(status != kStatus_Success) - { - return status; - } - start_addr += size; - buf += size; - byte_cnt -= size; - } - - return kStatus_Success; -} - - -/******************************************************************************* -* 函 数 名: flash_read -* 功能描述: 读Flash内容 -* 形 参: addr:读取区域起始地址 - buf:数据存储区 - len:要读取的字节数 -* 返 回 值: 如果函数执行成功,状态值为 kStatus_Success,否则状态值为其他错误码 -*******************************************************************************/ -status_t flash_read(uint32_t addr, uint8_t *buf, uint32_t len) -{ - /* For FlexSPI Memory ReadBack, use IP Command instead of AXI command for security */ - if((addr >= 0x60000000) && (addr < 0x61000000)) - { - return FLASH_Read(addr, (void *)buf, len); - } - - else - { - void* result = memcpy(buf, (void*)addr, len); - if(result == NULL) - { - return (status_t)kStatus_Fail; - } - else - { - return (status_t)kStatus_Success; - } - - } -} - - -/******************************************************************************* -* 函 数 名: flash_copy -* 功能描述: 实现flash数据在分区之间的拷贝 -* 形 参: srcAddr:源flash的起始地址 - dstAddr:目标flash的起始地址; - imageSize:要拷贝的flash空间大小,单位为字节 -* 返 回 值: 如果函数执行成功,状态值为 kStatus_Success,否则状态值为其他错误码 -*******************************************************************************/ -status_t flash_copy(uint32_t srcAddr,uint32_t dstAddr, uint32_t imageSize) -{ - uint32_t PageNum, Remain, i; - status_t status; - - if((srcAddr == dstAddr) || imageSize > APP_FLASH_SIZE) - { - return (status_t)kStatus_Fail; - } - - status = flash_erase(dstAddr,imageSize); - if(status != kStatus_Success) - { - KPrintf("Erase flash 0x%08x failure !\r\n",dstAddr); - return status; - } - - PageNum = imageSize/FLASH_PAGE_SIZE; - Remain = imageSize%FLASH_PAGE_SIZE; - - for(i=0;i= 0x60000000) && (addr < 0x61000000)) + { + return FLASH_ReadBuf(addr, (void *)buf, len); + } + + else + { + void* result = memcpy(buf, (void*)addr, len); + if(result == NULL) + { + return (status_t)kStatus_Fail; + } + else + { + return (status_t)kStatus_Success; + } + + } +} + + +/******************************************************************************* +* 函 数 名: Flash_Copy +* 功能描述: 实现flash数据在分区之间的拷贝 +* 形 参: srcAddr:源flash的起始地址 + dstAddr:目标flash的起始地址; + imageSize:要拷贝的flash空间大小,单位为字节 +* 返 回 值: 如果函数执行成功,状态值为 kStatus_Success,否则状态值为其他错误码 +*******************************************************************************/ +status_t Flash_Copy(uint32_t srcAddr,uint32_t dstAddr, uint32_t imageSize) +{ + uint32_t PageNum, Remain, i; + status_t status; + + if((srcAddr == dstAddr) || imageSize > APP_FLASH_SIZE) + { + return (status_t)kStatus_Fail; + } + + status = Flash_Erase(dstAddr,imageSize); + if(status != kStatus_Success) + { + KPrintf("Erase flash 0x%08x failure !\r\n",dstAddr); + return status; + } + + PageNum = imageSize/FLASH_PAGE_SIZE; + Remain = imageSize%FLASH_PAGE_SIZE; + + for(i=0;i=SECTOR_SIZE) { - NorFlash_Write(dataBuff,WriteAddr,dataLen); + status = Flash_Write(WriteAddr,dataBuff,dataLen); + if (status != kStatus_Success) + { + return status; + } packetNum = 0; dataLen = 0; } @@ -1050,10 +1032,14 @@ uint32_t NOR_FLASH_Write(uint32_t* FlashAddress, uint8_t* Data ,uint16_t DataLen } else { - NorFlash_Write(dataBuff,WriteAddr,dataLen); + status = Flash_Write(WriteAddr,dataBuff,dataLen); + if (status != kStatus_Success) + { + return status; + } packetNum = 0; dataLen = 0; } - return (0); + return (status_t)kStatus_Success;; } #endif diff --git a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/common/ymodem.c b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/common/ymodem.c index 661f6e81c..5eb8b728a 100644 --- a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/common/ymodem.c +++ b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/common/ymodem.c @@ -280,7 +280,7 @@ int32_t Ymodem_Receive(uint8_t *buf, const uint32_t addr) } /* erase user application area */ - NOR_FLASH_Erase(addr,size); + Flash_Erase(addr,size); Send_Byte(ACK); Send_Byte(CRC16); } @@ -300,9 +300,9 @@ int32_t Ymodem_Receive(uint8_t *buf, const uint32_t addr) /* Write received data in Flash */ #ifndef USE_HIGHT_SPEED_TRANS - if(NOR_FLASH_Write(&flashdestination, buf, (uint16_t)packet_length) == 0) + if(NOR_FLASH_Write(&flashdestination, buf, (uint16_t)packet_length) == kStatus_Success) #else - if(NOR_FLASH_Write(&flashdestination, buf, (uint16_t)packet_length, 0) == 0) + if(NOR_FLASH_Write(&flashdestination, buf, (uint16_t)packet_length, 0) == kStatus_Success) #endif { Send_Byte(ACK); @@ -349,7 +349,10 @@ int32_t Ymodem_Receive(uint8_t *buf, const uint32_t addr) } } #ifdef USE_HIGHT_SPEED_TRANS - NOR_FLASH_Write(&flashdestination, buf, (uint16_t) packet_length,1); + if(NOR_FLASH_Write(&flashdestination, buf, (uint16_t) packet_length,1) != kStatus_Success) + { + return -4; + } #endif return (int32_t)size; } @@ -370,13 +373,12 @@ int32_t SerialDownload(const uint32_t addr) Size = Ymodem_Receive(&tab_1024[0], addr); if(Size > 0) { - Serial_PutString("\n\n\r Programming Completed Successfully!\n\r--------------------------------\r\n Name: "); + Serial_PutString("\n\n\rProgramming Completed Successfully!\n\r\r\nName: "); Serial_PutString(FileName); Int2Str(Number, Size); - Serial_PutString("\n\r Size: "); + Serial_PutString("\n\rSize: "); Serial_PutString(Number); Serial_PutString(" Bytes\r\n"); - Serial_PutString("-------------------\n"); } else if(Size == -1) { diff --git a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/include/flash.h b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/include/flash.h index fd59738cb..a71feeceb 100644 --- a/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/include/flash.h +++ b/Ubiquitous/XiZi_IIoT/board/xidatong-arm32/third_party_driver/include/flash.h @@ -63,21 +63,19 @@ void FLASH_Init(void); void FLASH_DeInit(void); uint8_t FLASH_EraseSector(uint32_t addr); uint8_t FLASH_WritePage(uint32_t addr, const uint32_t *buf, uint32_t len); -status_t FLASH_Read(uint32_t addr, uint32_t *buf, uint32_t len); -status_t flash_erase(uint32_t start_addr, uint32_t byte_cnt); -status_t flash_write(uint32_t start_addr, uint8_t *buf, uint32_t byte_cnt); -status_t flash_read(uint32_t addr, uint8_t *buf, uint32_t len); -status_t flash_copy(uint32_t srcAddr,uint32_t dstAddr, uint32_t imageSize); +status_t FLASH_ReadBuf(uint32_t addr, uint32_t *buf, uint32_t len); +status_t NorFlash_Write_PageProgram(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite); +status_t NorFlash_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite); -status_t NOR_FLASH_Erase(uint32_t app_base_addr,uint32_t imageSize); -void NorFlash_Write_PageProgram(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite); -void NorFlash_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite); -void NorFlash_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite); +status_t Flash_Erase(uint32_t start_addr, uint32_t imageSize); +status_t Flash_Write(uint32_t WriteAddr, uint8_t *pBuffer, uint32_t NumByteToWrite); +status_t Flash_Read(uint32_t addr, uint8_t *buf, uint32_t len); +status_t Flash_Copy(uint32_t srcAddr,uint32_t dstAddr, uint32_t imageSize); #ifndef USE_HIGHT_SPEED_TRANS -uint32_t NOR_FLASH_Write(uint32_t* FlashAddress, uint8_t* Data ,uint16_t DataLength); +status_t NOR_FLASH_Write(uint32_t* FlashAddress, uint8_t* Data ,uint16_t DataLength); #else -uint32_t NOR_FLASH_Write(uint32_t* FlashAddress, uint8_t* Data ,uint16_t DataLength,uint8_t doneFlag); +status_t NOR_FLASH_Write(uint32_t* FlashAddress, uint8_t* Data ,uint16_t DataLength,uint8_t doneFlag); #endif #endif diff --git a/Ubiquitous/XiZi_IIoT/path_kernel.mk b/Ubiquitous/XiZi_IIoT/path_kernel.mk index f8f5c5f00..7ef04658d 100755 --- a/Ubiquitous/XiZi_IIoT/path_kernel.mk +++ b/Ubiquitous/XiZi_IIoT/path_kernel.mk @@ -596,6 +596,10 @@ KERNELPATHS +=-I$(KERNEL_ROOT)/tool/bootloader/flash \ -I$(KERNEL_ROOT)/tool/bootloader/ota # endif +ifeq ($(CONFIG_TOOL_USING_MQTT), y) +KERNELPATHS +=-I$(KERNEL_ROOT)/../../APP_Framework/lib/mqtt +endif + ifeq ($(CONFIG_FS_LWEXT4),y) KERNELPATHS += -I$(KERNEL_ROOT)/fs/lwext4/lwext4_submodule/blockdev/xiuos # KERNELPATHS += -I$(KERNEL_ROOT)/fs/lwext4/lwext4_submodule/include # diff --git a/Ubiquitous/XiZi_IIoT/tool/bootloader/Kconfig b/Ubiquitous/XiZi_IIoT/tool/bootloader/Kconfig index f5213d718..c826295d4 100644 --- a/Ubiquitous/XiZi_IIoT/tool/bootloader/Kconfig +++ b/Ubiquitous/XiZi_IIoT/tool/bootloader/Kconfig @@ -16,6 +16,22 @@ menu "OTA function" bool "Config as application." endchoice + if MCUBOOT_APPLICATION + choice + prompt "The way of OTA firmware upgrade." + default OTA_BY_PLATFORM + + config OTA_BY_PLATFORM + bool "Through IoT management platform." + select TOOL_USING_MQTT + + config OTA_BY_TCPSERVER + bool "Through the public network TCP server." + select SUPPORT_CONNECTION_FRAMEWORK + select CONNECTION_ADAPTER_4G + endchoice + endif + menu "Flash area address and size configuration." config CHIP_FLAH_BASE @@ -42,7 +58,18 @@ menu "OTA function" hex "Application package size,the default size is limited to 1M." default 0x00100000 endmenu - + + config OTA_RX_TIMEOUT + int "OTA receive data timeout(ms)." + default 600 if OTA_BY_PLATFORM + default 10000 if OTA_BY_TCPSERVER + default 10000 if MCUBOOT_BOOTLOADER + + config OTA_RX_BUFFERSIZE + int "OTA receive data buffer size." + default 3072 if OTA_BY_PLATFORM + default 2048 if OTA_BY_TCPSERVER + default 256 if MCUBOOT_BOOTLOADER endif endmenu diff --git a/Ubiquitous/XiZi_IIoT/tool/bootloader/flash/flash_ops.h b/Ubiquitous/XiZi_IIoT/tool/bootloader/flash/flash_ops.h index b0ff530f0..e75abcd2e 100644 --- a/Ubiquitous/XiZi_IIoT/tool/bootloader/flash/flash_ops.h +++ b/Ubiquitous/XiZi_IIoT/tool/bootloader/flash/flash_ops.h @@ -30,8 +30,8 @@ typedef struct void (*flash_deinit)(void); /* flash operation */ - status_t (*op_flash_erase)(uint32_t start_addr, uint32_t byte_cnt); - status_t (*op_flash_write)(uint32_t start_addr, uint8_t *buf, uint32_t byte_cnt); + status_t (*op_flash_erase)(uint32_t start_addr, uint32_t imageSize); + status_t (*op_flash_write)(uint32_t WriteAddr, uint8_t *pBuffer, uint32_t NumByteToWrite); status_t (*op_flash_read)(uint32_t addr, uint8_t *buf, uint32_t len); status_t (*op_flash_copy)(uint32_t srcAddr,uint32_t dstAddr, uint32_t imageSize); diff --git a/Ubiquitous/XiZi_IIoT/tool/bootloader/ota/ota.c b/Ubiquitous/XiZi_IIoT/tool/bootloader/ota/ota.c index 4e0416e19..e132346e3 100644 --- a/Ubiquitous/XiZi_IIoT/tool/bootloader/ota/ota.c +++ b/Ubiquitous/XiZi_IIoT/tool/bootloader/ota/ota.c @@ -1,37 +1,50 @@ /* - * Copyright 2018-2020 NXP - * All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -/** -* @file ota.c -* @brief file ota.c -* @version 2.0 -* @author AIIT XUOS Lab -* @date 2023-04-03 +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. */ + +/** +* @file: ota.c +* @brief: file ota.c +* @version: 1.0 +* @author: AIIT XUOS Lab +* @date: 2023/4/23 +* +*/ +#include +#include #include "shell.h" #include "xsconfig.h" #include "mcuboot.h" #include "ymodem.h" #include "ota.h" +#ifdef CONNECTION_ADAPTER_4G +#include +#endif + +#ifdef OTA_BY_PLATFORM +#include "platform_mqtt.h" +#endif + /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static uint32_t calculate_crc32(uint32_t addr, uint32_t len); -static void UpdateApplication(void); +static int create_version(uint8_t* cur_version, uint8_t* new_version); +static status_t UpdateOTAFlag(ota_info_t *ptr); static void InitialVersion(void); static void BackupVersion(void); +static void UpdateNewApplication(void); +static void Update(void); static void BootLoaderJumpApp(void); -static status_t UpdateOTAFlag(ota_info_t *ptr); - -#ifdef MCUBOOT_APPLICATION -static void app_ota(void); -#endif - /**************************************************************************** * Private Data @@ -43,16 +56,15 @@ static const mcuboot_t mcuboot = Serial_PutString, FLASH_Init, FLASH_DeInit, - flash_erase, - flash_write, - flash_read, - flash_copy, + Flash_Erase, + Flash_Write, + Flash_Read, + Flash_Copy, SerialDownload, mcuboot_reset, mcuboot_jump, mcuboot_delay }; - static const uint32_t crc32tab[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, @@ -111,176 +123,43 @@ static uint32_t calculate_crc32(uint32_t addr, uint32_t len) /******************************************************************************* -* 函 数 名: UpdateApplication -* 功能描述: 在bootloader里进行调用,根据Flash中Flag分区中的信息决定是否进行版本更新 -* 形 参: 无 -* 返 回 值: 无 -* 注 释: 该函数调用后无论结果如何都将跳转到app分区 +* 函 数 名: create_version +* 功能描述: 根据当前版本号生成新的三段式版本号,适用于iap方式和TCPSERVER +* 形 参: cur_version:当前版本号,new_version:生成的新版本号 +* 返 回 值: 0:生成成功,-1:生成失败 +* 说 明: 为保持一致,平台通过OTA传输而来的版本号也要保持这样三段式的形式 + 版本形式为major.minor.patch,如01.02.03 *******************************************************************************/ -static void UpdateApplication(void) +static int create_version(uint8_t* cur_version, uint8_t* new_version) { - status_t status; - ota_info_t ota_info; // 定义OTA信息结构体 + int major, minor, patch; //三段式版本号的各个部分 - // 从Flash中读取OTA信息 - mcuboot.op_flash_read(FLAG_FLAH_ADDRESS, (void*)&ota_info, sizeof(ota_info_t)); + //解析当前版本号,版本号格式不对直接返回 + if (sscanf(cur_version, "%03d.%03d.%03d", &major, &minor, &patch) != 3) { + return -1; + } - // 如果OTA升级状态为准备状态,且APP分区与download分区版本不同,才可以进行升级 - if((ota_info.status == OTA_STATUS_READY) && (ota_info.os.crc32 != ota_info.down.crc32)) + //将当前版本号加1 + patch++; + if(patch > 999) { - mcuboot.print_string("\r\n------Start to update the app!------\r\n"); - // 校验downlad分区固件CRC - if(calculate_crc32(DOWN_FLAH_ADDRESS, ota_info.down.size) == ota_info.down.crc32) + minor++; + patch = 0; + if(minor > 999) { - ota_info.status = OTA_STATUS_UPDATING; - UpdateOTAFlag(&ota_info); - - // 1.如果CRC校验通过,开始升级,逐字节拷贝Flash,先备份当前XiUOS System分区内容 - status = mcuboot.op_flash_copy(XIUOS_FLAH_ADDRESS, BAKUP_FLAH_ADDRESS, ota_info.os.size); - if((status == kStatus_Success) &&(calculate_crc32(BAKUP_FLAH_ADDRESS, ota_info.os.size) == ota_info.os.crc32)) + major++; + minor = 0; + patch = 0; + if(major > 999) { - mcuboot.print_string("\r\n------Backup app success!------\r\n"); - ota_info.bak.size = ota_info.os.size; - ota_info.bak.crc32 = ota_info.os.crc32; - ota_info.bak.version = ota_info.os.version; - strncpy(ota_info.bak.description, ota_info.os.description, sizeof(ota_info.os.description)); - UpdateOTAFlag(&ota_info);; + return -1; } - else - { - mcuboot.print_string("\r\n------Backup app failed!------\r\n"); - ota_info.status = OTA_STATUS_ERROR; - strncpy(ota_info.error_message, "Backup app failed!",sizeof(ota_info.error_message)); - UpdateOTAFlag(&ota_info);; - goto finish; - } - - // 2.拷贝download分区到XiUOS System分区 - status = mcuboot.op_flash_copy(DOWN_FLAH_ADDRESS, XIUOS_FLAH_ADDRESS, ota_info.down.size); - if((status == kStatus_Success) &&(calculate_crc32(XIUOS_FLAH_ADDRESS, ota_info.down.size) == ota_info.down.crc32)) - { - mcuboot.print_string("\r\n------The download partition is copied successfully!------\r\n"); - - ota_info.os.size = ota_info.down.size; - ota_info.os.crc32 = ota_info.down.crc32; - ota_info.os.version = ota_info.down.version; - strncpy(ota_info.os.description, ota_info.down.description, sizeof(ota_info.down.description)); - ota_info.status == OTA_STATUS_IDLE; // 拷贝download分区到XiUOS System分区成功,将OTA升级状态设置为IDLE - UpdateOTAFlag(&ota_info);; - } - else - { - mcuboot.print_string("\r\n------The download partition copy failed!------\r\n"); - ota_info.status = OTA_STATUS_ERROR; - strncpy(ota_info.error_message, "The download partition copy failed!",sizeof(ota_info.error_message)); - UpdateOTAFlag(&ota_info);; - goto finish; - } - - mcuboot.print_string("\r\n------Update completed!------\r\n"); - goto finish; } - else - { - // 如果download分区CRC校验失败,升级失败 - mcuboot.print_string("\r\n------Download Firmware CRC check failed!------\r\n"); - ota_info.status = OTA_STATUS_ERROR; - strncpy(ota_info.error_message, "Download Firmware CRC check failed!",sizeof(ota_info.error_message)); - UpdateOTAFlag(&ota_info);; - goto finish; - } } - else - { - mcuboot.print_string("\r\n------No need to update the app!------\r\n"); - goto finish; - } -finish: - return; -} - -/******************************************************************************* -* 函 数 名: InitialVersion -* 功能描述: 该函数可以烧写APP分区的初始化版本,初始化版本的版本号为0x1 -* 形 参: 无 -* 返 回 值: 无 -*******************************************************************************/ -static void InitialVersion(void) -{ - int32_t size; - ota_info_t ota_info; - - memset(&ota_info, 0, sizeof(ota_info_t)); - size = mcuboot.download_by_serial(XIUOS_FLAH_ADDRESS); - if(size > 0) - { - ota_info.os.size = size; - ota_info.os.crc32 = calculate_crc32(XIUOS_FLAH_ADDRESS, size); - ota_info.os.version = 0x1; - strncpy(ota_info.os.description, "This is the initial firmware for the device!", sizeof(ota_info.os.description)); - UpdateOTAFlag(&ota_info); - } -} - -/******************************************************************************* -* 函 数 名: BackupVersion -* 功能描述: 版本回退函数,如果升级的APP存在bug导致无法跳转需调用此函数进行版本回退 -* 形 参: 无 -* 返 回 值: 无 -*******************************************************************************/ -static void BackupVersion(void) -{ - status_t status; - ota_info_t ota_info; - mcuboot.op_flash_read(FLAG_FLAH_ADDRESS, (void*)&ota_info, sizeof(ota_info_t)); - - ota_info.status = OTA_STATUS_BACKUP; - UpdateOTAFlag(&ota_info); - status = mcuboot.op_flash_copy(BAKUP_FLAH_ADDRESS, XIUOS_FLAH_ADDRESS, ota_info.bak.size); - if((status == kStatus_Success) &&(calculate_crc32(XIUOS_FLAH_ADDRESS, ota_info.bak.size) == ota_info.bak.crc32)) - { - mcuboot.print_string("\r\n------Backup app version success!------\r\n"); - ota_info.os.size = ota_info.bak.size; - ota_info.os.crc32 = ota_info.bak.crc32; - ota_info.os.version = ota_info.bak.version; - strncpy(ota_info.os.description, ota_info.bak.description, sizeof(ota_info.bak.description)); - UpdateOTAFlag(&ota_info); - } - else - { - mcuboot.print_string("\r\n------Backup app version failed!------\r\n"); - ota_info.status = OTA_STATUS_ERROR; - strncpy(ota_info.error_message, "Backup app version failed!",sizeof(ota_info.error_message)); - UpdateOTAFlag(&ota_info); - } -} - - -/******************************************************************************* -* 函 数 名: BootLoaderJumpApp -* 功能描述: 上次跳转若是失败的,先从BAKUP分区进行恢复,然后再进行跳转 -* 形 参: 无 -* 返 回 值: 无 -*******************************************************************************/ -static void BootLoaderJumpApp(void) -{ - ota_info_t ota_info; - mcuboot.flash_init(); - mcuboot.op_flash_read(FLAG_FLAH_ADDRESS, (void*)&ota_info, sizeof(ota_info_t)); - - if(ota_info.lastjumpflag == JUMP_FAILED_FLAG) - { - mcuboot.print_string("\r\n------Bootloader false, begin backup!------\r\n"); - BackupVersion(); - } - else - { - ota_info.lastjumpflag = JUMP_FAILED_FLAG; - UpdateOTAFlag(&ota_info); - } - mcuboot.flash_deinit(); - mcuboot.op_jump(); + //更新版本号 + sprintf(new_version, "%03d.%03d.%03d", major, minor, patch); + return 0; } @@ -306,19 +185,254 @@ static status_t UpdateOTAFlag(ota_info_t *ptr) /******************************************************************************* -* 函 数 名: app_ota -* 功能描述: 在app中通过命令来进行ota升级,该函数与升级的命令关联 +* 函 数 名: InitialVersion +* 功能描述: 该函数可以烧写APP分区的初始化版本,初始化版本的版本号为0x1 * 形 参: 无 * 返 回 值: 无 *******************************************************************************/ -static void app_ota(void) +static void InitialVersion(void) +{ + int32_t size; + ota_info_t ota_info; + + memset(&ota_info, 0, sizeof(ota_info_t)); + size = mcuboot.download_by_serial(XIUOS_FLAH_ADDRESS); + if(size > 0) + { + ota_info.os.size = size; + ota_info.os.crc32 = calculate_crc32(XIUOS_FLAH_ADDRESS, size); + + strncpy(ota_info.os.version,"001.000.000",sizeof(ota_info.os.version)); + strncpy(ota_info.os.description, "The initial firmware.", sizeof(ota_info.os.description)); + + UpdateOTAFlag(&ota_info); + } +} + + +/******************************************************************************* +* 函 数 名: BackupVersion +* 功能描述: 版本回退函数,如果升级的APP存在bug导致无法跳转需调用此函数进行版本回退 +* 形 参: 无 +* 返 回 值: 无 +*******************************************************************************/ +static void BackupVersion(void) +{ + status_t status; + + ota_info_t ota_info; + memset(&ota_info, 0, sizeof(ota_info_t)); + mcuboot.op_flash_read(FLAG_FLAH_ADDRESS, (void*)&ota_info, sizeof(ota_info_t)); + + ota_info.status = OTA_STATUS_BACKUP; + UpdateOTAFlag(&ota_info); + status = mcuboot.op_flash_copy(BAKUP_FLAH_ADDRESS, XIUOS_FLAH_ADDRESS, ota_info.bak.size); + if((status == kStatus_Success) &&(calculate_crc32(XIUOS_FLAH_ADDRESS, ota_info.bak.size) == ota_info.bak.crc32)) + { + mcuboot.print_string("\r\n------Backup app version success!------\r\n"); + ota_info.os.size = ota_info.bak.size; + ota_info.os.crc32 = ota_info.bak.crc32; + + memset(ota_info.os.version,0,sizeof(ota_info.os.version)); + strncpy(ota_info.os.version, ota_info.bak.version, sizeof(ota_info.bak.version)); + + memset(ota_info.os.description,0,sizeof(ota_info.os.description)); + strncpy(ota_info.os.description, ota_info.bak.description, sizeof(ota_info.bak.description)); + + UpdateOTAFlag(&ota_info); + } + else + { + mcuboot.print_string("\r\n------Backup app version failed!------\r\n"); + ota_info.status = OTA_STATUS_ERROR; + + memset(ota_info.error_message,0,sizeof(ota_info.error_message)); + strncpy(ota_info.error_message, "Backup app version failed!",sizeof(ota_info.error_message)); + + UpdateOTAFlag(&ota_info); + } +} + + +/******************************************************************************* +* 函 数 名: UpdateNewApplication +* 功能描述: 在bootloader里进行调用,根据Flash中Flag分区中的信息决定是否进行版本更新 +* 形 参: 无 +* 返 回 值: 无 +* 注 释: 该函数调用后如果不需要升级APP分区保持不变,否则APP分区的版本为新版本 +*******************************************************************************/ +static void UpdateNewApplication(void) +{ + status_t status; + ota_info_t ota_info; // 定义OTA信息结构体 + + memset(&ota_info, 0, sizeof(ota_info_t)); + // 从Flash中读取OTA信息 + mcuboot.op_flash_read(FLAG_FLAH_ADDRESS, (void*)&ota_info, sizeof(ota_info_t)); + + // 如果OTA升级状态为准备状态,且APP分区与download分区版本不同,才可以进行升级 + if((ota_info.status == OTA_STATUS_READY) && (ota_info.os.crc32 != ota_info.down.crc32)) + { + mcuboot.print_string("\r\n------Start to update the app!------\r\n"); + // 校验downlad分区固件CRC + if(calculate_crc32(DOWN_FLAH_ADDRESS, ota_info.down.size) == ota_info.down.crc32) + { + ota_info.status = OTA_STATUS_UPDATING; + UpdateOTAFlag(&ota_info); + + // 1.如果CRC校验通过,开始升级,逐字节拷贝Flash,先备份当前XiUOS System分区内容 + status = mcuboot.op_flash_copy(XIUOS_FLAH_ADDRESS, BAKUP_FLAH_ADDRESS, ota_info.os.size); + if((status == kStatus_Success) &&(calculate_crc32(BAKUP_FLAH_ADDRESS, ota_info.os.size) == ota_info.os.crc32)) + { + mcuboot.print_string("\r\n------Backup app success!------\r\n"); + ota_info.bak.size = ota_info.os.size; + ota_info.bak.crc32 = ota_info.os.crc32; + + memset(ota_info.bak.version,0,sizeof(ota_info.bak.version)); + strncpy(ota_info.bak.version, ota_info.os.version, sizeof(ota_info.os.version)); + + memset(ota_info.bak.description,0,sizeof(ota_info.bak.description)); + strncpy(ota_info.bak.description, ota_info.os.description, sizeof(ota_info.os.description)); + + UpdateOTAFlag(&ota_info);; + } + else + { + mcuboot.print_string("\r\n------Backup app failed!------\r\n"); + ota_info.status = OTA_STATUS_ERROR; + + memset(ota_info.error_message,0,sizeof(ota_info.error_message)); + strncpy(ota_info.error_message, "Backup app failed!",sizeof(ota_info.error_message)); + + UpdateOTAFlag(&ota_info);; + goto finish; + } + + // 2.拷贝download分区到XiUOS System分区 + status = mcuboot.op_flash_copy(DOWN_FLAH_ADDRESS, XIUOS_FLAH_ADDRESS, ota_info.down.size); + if((status == kStatus_Success) &&(calculate_crc32(XIUOS_FLAH_ADDRESS, ota_info.down.size) == ota_info.down.crc32)) + { + mcuboot.print_string("\r\n------The download partition is copied successfully!------\r\n"); + + ota_info.os.size = ota_info.down.size; + ota_info.os.crc32 = ota_info.down.crc32; + + memset(ota_info.os.version,0,sizeof(ota_info.os.version)); + strncpy(ota_info.os.version, ota_info.down.version, sizeof(ota_info.down.version)); + + memset(ota_info.os.description,0,sizeof(ota_info.os.description)); + strncpy(ota_info.os.description, ota_info.down.description, sizeof(ota_info.down.description)); + + ota_info.status == OTA_STATUS_IDLE; // 拷贝download分区到XiUOS System分区成功,将OTA升级状态设置为IDLE + UpdateOTAFlag(&ota_info);; + } + else + { + mcuboot.print_string("\r\n------The download partition copy failed!------\r\n"); + ota_info.status = OTA_STATUS_ERROR; + + memset(ota_info.error_message,0,sizeof(ota_info.error_message)); + strncpy(ota_info.error_message, "The download partition copy failed!",sizeof(ota_info.error_message)); + + UpdateOTAFlag(&ota_info);; + goto finish; + } + + mcuboot.print_string("\r\n------Update completed!------\r\n"); + goto finish; + } + else + { + // 如果download分区CRC校验失败,升级失败 + mcuboot.print_string("\r\n------Download Firmware CRC check failed!------\r\n"); + ota_info.status = OTA_STATUS_ERROR; + + memset(ota_info.error_message,0,sizeof(ota_info.error_message)); + strncpy(ota_info.error_message, "Download Firmware CRC check failed!",sizeof(ota_info.error_message)); + + UpdateOTAFlag(&ota_info);; + goto finish; + } + } + else + { + mcuboot.print_string("\r\n------No need to update the app!------\r\n"); + goto finish; + } +finish: + return; +} + + +/******************************************************************************* +* 函 数 名: Update +* 功能描述: 根据实际情况进行初始化版本的烧录或者新版本的升级 +* 形 参: 无 +* 返 回 值: 无 +*******************************************************************************/ +static void Update(void) +{ + ota_info_t ota_info; + mcuboot.flash_init(); + memset(&ota_info, 0, sizeof(ota_info_t)); + mcuboot.op_flash_read(FLAG_FLAH_ADDRESS, (void*)&ota_info, sizeof(ota_info_t)); + /* 此时APP分区还没有有效的固件,需要在bootloader下通过iap烧写出厂固件 */ + if((ota_info.os.size > APP_FLASH_SIZE) || (calculate_crc32(XIUOS_FLAH_ADDRESS, ota_info.os.size) != ota_info.os.crc32)) + { + mcuboot.print_string("\r\nNeed to flash initial firmware!\r\n"); + InitialVersion(); + } + else + { + UpdateNewApplication(); + } + mcuboot.flash_deinit(); +} + + +/******************************************************************************* +* 函 数 名: BootLoaderJumpApp +* 功能描述: 上次跳转若是失败的,先从BAKUP分区进行恢复,然后再进行跳转 +* 形 参: 无 +* 返 回 值: 无 +*******************************************************************************/ +static void BootLoaderJumpApp(void) +{ + ota_info_t ota_info; + + mcuboot.flash_init(); + memset(&ota_info, 0, sizeof(ota_info_t)); + mcuboot.op_flash_read(FLAG_FLAH_ADDRESS, (void*)&ota_info, sizeof(ota_info_t)); + + if(ota_info.lastjumpflag == JUMP_FAILED_FLAG) + { + mcuboot.print_string("\r\n------Bootloader false, begin backup!------\r\n"); + BackupVersion(); + } + else + { + ota_info.lastjumpflag = JUMP_FAILED_FLAG; + UpdateOTAFlag(&ota_info); + } + mcuboot.flash_deinit(); + mcuboot.op_jump(); +} + + +/********************************************************************************* +* 函 数 名: app_ota_by_iap +* 功能描述: 通过命令来进行ota升级,该函数与升级的命令关联,通过串口iap方式传输bin文件 +* 形 参: 无 +* 返 回 值: 无 +*********************************************************************************/ +static void app_ota_by_iap(void) { int32_t size; ota_info_t ota_info; mcuboot.flash_init(); mcuboot.serial_init(); - + memset(&ota_info, 0, sizeof(ota_info_t)); mcuboot.op_flash_read(FLAG_FLAH_ADDRESS, (void*)&ota_info, sizeof(ota_info_t)); ota_info.status = OTA_STATUS_DOWNLOADING; UpdateOTAFlag(&ota_info); @@ -329,22 +443,536 @@ static void app_ota(void) { ota_info.down.size = size; ota_info.down.crc32= calculate_crc32(DOWN_FLAH_ADDRESS, size); - ota_info.down.version = ota_info.os.version + 1; - strncpy(ota_info.down.description, "OTA Test!",sizeof(ota_info.down.description)); + + memset(ota_info.down.version,0,sizeof(ota_info.down.version)); + create_version(ota_info.os.version, ota_info.down.version); + + memset(ota_info.down.description,0,sizeof(ota_info.down.description)); + strncpy(ota_info.down.description, "OTA Test bin.",sizeof(ota_info.down.description)); + ota_info.status = OTA_STATUS_READY; + + memset(ota_info.error_message,0,sizeof(ota_info.error_message)); strncpy(ota_info.error_message, "No error message!",sizeof(ota_info.error_message)); UpdateOTAFlag(&ota_info); } else { ota_info.status = OTA_STATUS_ERROR; + + memset(ota_info.error_message,0,sizeof(ota_info.error_message)); strncpy(ota_info.error_message, "Failed to download firmware to download partition!",sizeof(ota_info.error_message)); + UpdateOTAFlag(&ota_info); } mcuboot.flash_deinit(); mcuboot.op_reset(); } -SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_PARAM_NUM(0),ota, app_ota, ota function); +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_PARAM_NUM(0),iap, app_ota_by_iap, ota by iap function); + + +#ifdef OTA_BY_TCPSERVER +static uint16_t calculate_crc16(uint8_t * data, uint32_t len); +static void get_start_signal(struct Adapter* adapter); +static int ota_data_recv(struct Adapter* adapter); +static ota_data start_msg; +static ota_data recv_msg; + +/******************************************************************************* +* 函 数 名: calculate_crc16 +* 功能描述: 计算给定长度的数据的crc16的值,用于OTA传输过程中数据帧的校验 +* 形 参: data:数据buffer + len:表示需要计算CRC16的数据长度 +* 返 回 值: 计算得到的CRC16值 +*******************************************************************************/ +static uint16_t calculate_crc16(uint8_t * data, uint32_t len) +{ + uint16_t reg_crc=0xFFFF; + while(len--) + { + reg_crc ^= *data++; + for(int j=0;j<8;j++) + { + if(reg_crc & 0x01) + reg_crc=reg_crc >>1 ^ 0xA001; + else + reg_crc=reg_crc >>1; + } + } + KPrintf("crc = [0x%x]\n",reg_crc); + return reg_crc; +} + + +/******************************************************************************* +* 函 数 名: get_start_signal +* 功能描述: 通过4G方式从服务端接收开始信号 +* 形 参: adapter:Adapter指针,指向注册的4G设备 +* 返 回 值: 0:传输成功,-1:传输失败 +*******************************************************************************/ +static void get_start_signal(struct Adapter* adapter) +{ + ota_info_t ota_info; + uint8_t reply[16] = {0}; + uint32_t flashdestination = DOWN_FLAH_ADDRESS; + + memset(&ota_info, 0, sizeof(ota_info_t)); + mcuboot.op_flash_read(FLAG_FLAH_ADDRESS, (void*)&ota_info, sizeof(ota_info_t)); + ota_info.status = OTA_STATUS_DOWNLOADING; + UpdateOTAFlag(&ota_info); + while(1) + { + memset(&start_msg, 0, sizeof(ota_data)); + /* step1:Confirm the start signal of transmission. */ + KPrintf("waiting for start msg...\n"); + if(AdapterDeviceRecv(adapter, &start_msg, sizeof(start_msg)) >= 0 && start_msg.header.frame_flag == STARTFLAG) + { + if(mcuboot.op_flash_erase(DOWN_FLAH_ADDRESS,start_msg.header.total_len) != kStatus_Success) + { + KPrintf("Failed to erase target fash!\n"); + break; + } + else + { + KPrintf("Erase flash successful,erase length is %d bytes.\n",start_msg.header.total_len); + } + memset(reply, 0, sizeof(reply)); + memcpy(reply, "ready", strlen("ready")); + KPrintf("receive start signal,send [ready] signal to server\n"); + while(AdapterDeviceSend(adapter, reply, strlen(reply)) < 0); + break; + } + else + { + memset(reply, 0, sizeof(reply)); + memcpy(reply, "notready", strlen("notready")); + KPrintf("not receive start signal,send [notready] signal to server\n"); + while(AdapterDeviceSend(adapter, reply, strlen(reply)) < 0); + continue; + } + } +} + + +/******************************************************************************* +* 函 数 名: ota_data_recv +* 功能描述: 通过4G方式从服务端接收数据 +* 形 参: adapter:Adapter指针,指向注册的4G设备 +* 返 回 值: 0:传输成功,-1:传输失败 +*******************************************************************************/ +static int ota_data_recv(struct Adapter* adapter) +{ + ota_info_t ota_info; + uint8_t reply[16] = {0}; + int ret = 0, frame_cnt = 0, try_times = 5; + uint32_t file_size = 0; + uint32_t flashdestination = DOWN_FLAH_ADDRESS; + + memset(&ota_info, 0, sizeof(ota_info_t)); + mcuboot.op_flash_read(FLAG_FLAH_ADDRESS, (void*)&ota_info, sizeof(ota_info_t)); + ota_info.status = OTA_STATUS_DOWNLOADING; + UpdateOTAFlag(&ota_info); + + while(1) + { + memset(&recv_msg, 0, sizeof(ota_data)); + if(AdapterDeviceRecv(adapter, &recv_msg, sizeof(recv_msg)) >= 0) + { + if(recv_msg.header.frame_flag == STARTFLAG) //这里不应该再出现开始帧,丢弃当前数据继续接收 + { + continue; + } + else if(recv_msg.header.frame_flag == DATAFLAG) //说明当前是bin包里数据封装成的数据帧 + { + frame_cnt = recv_msg.frame.frame_id; + if(recv_msg.frame.crc == calculate_crc16(recv_msg.frame.frame_data,recv_msg.frame.frame_len)) + { + KPrintf("current frame[%d],length %d bytes.\n",frame_cnt,recv_msg.frame.frame_len); + if(mcuboot.op_flash_write(flashdestination, recv_msg.frame.frame_data, recv_msg.frame.frame_len) != kStatus_Success) + { + KPrintf("current frame[%d] flash failed.\n",frame_cnt); + ret = -1; + break; + } + else + { + KPrintf("current frame[%d] is written to flash 0x%x address successful.\n", frame_cnt, flashdestination); + flashdestination += recv_msg.frame.frame_len; + } + } + else + { + KPrintf("current frame[%d] crc check failed,try again!\n",frame_cnt); + goto try_again; + } + } + else if(recv_msg.header.frame_flag == ENDTFLAG) //说明当前是结束帧 + { + KPrintf("total %d frames %d bytes crc[0x%x],receive successful.\n",frame_cnt,recv_msg.header.total_len,recv_msg.frame.crc); + memset(reply, 0, sizeof(reply)); + memcpy(reply, "ok", strlen("ok")); + AdapterDeviceSend(adapter, reply, strlen(reply)); + + ota_info.status = OTA_STATUS_DOWNLOADED; + UpdateOTAFlag(&ota_info); + + file_size = recv_msg.header.total_len; + ret = 0; + break; + } + else //说明当前接收的数据帧不是上述三种数据帧的任意一种 + { + goto try_again; + } + +send_ok_again: + memset(reply, 0, sizeof(reply)); + memcpy(reply, "ok", strlen("ok")); + + ret = AdapterDeviceSend(adapter, reply, strlen(reply)); + if(ret < 0) + { + KPrintf("send ok failed.\n"); + goto send_ok_again; + } + KPrintf("send reply[%s] done.\n",reply); + //send ok后把try_times重置为5 + try_times = 5; + continue; + } + + //没有接收到数据或者接收到的数据帧不满足条件,需要发个retry的命令告诉服务器需要重传 + else + { +try_again: + if(try_times == 0) + { + KPrintf("current frame[%d] try 5 times failed,break out!\n",frame_cnt); + ret = -1; + break; + } + memset(reply, 0, sizeof(reply)); + memcpy(reply, "retry", strlen("retry")); + KPrintf("current frame[%d] receive failed. retry\n",frame_cnt); + AdapterDeviceSend(adapter, reply, strlen(reply)); + try_times--; + continue; + } + } + + //download分区固件下载成功,更新Flag分区 + if(0 == ret) + { + ota_info.down.size = file_size; + ota_info.down.crc32= calculate_crc32(DOWN_FLAH_ADDRESS, file_size); + + memset(ota_info.down.version,0,sizeof(ota_info.down.version)); + create_version(ota_info.os.version, ota_info.down.version); + + memset(ota_info.down.description,0,sizeof(ota_info.down.description)); + strncpy(ota_info.down.description, "4G OTA bin.",sizeof(ota_info.down.description)); + + ota_info.status = OTA_STATUS_READY; + + memset(ota_info.error_message,0,sizeof(ota_info.error_message)); + strncpy(ota_info.error_message, "No error message!",sizeof(ota_info.error_message)); + + UpdateOTAFlag(&ota_info); + } + else + { + ota_info.status = OTA_STATUS_ERROR; + + memset(ota_info.error_message,0,sizeof(ota_info.error_message)); + strncpy(ota_info.error_message, "Failed to download firmware to download partition!",sizeof(ota_info.error_message)); + + UpdateOTAFlag(&ota_info); + } + return ret; +} + + +/******************************************************************************* +* 函 数 名: app_ota_by_4g +* 功能描述: 通过命令来进行ota升级,该函数与升级的命令关联,通过4g方式传输bin文件 +* 形 参: 无 +* 返 回 值: 无 +*******************************************************************************/ +static void app_ota_by_4g(void) +{ + uint32_t baud_rate = BAUD_RATE_115200; + uint8_t server_addr[16] = "115.238.53.60"; + uint8_t server_port[8] = "7777"; + + mcuboot.flash_init(); + + struct Adapter* adapter = AdapterDeviceFindByName(ADAPTER_4G_NAME); + adapter->socket.socket_id = 0; + + AdapterDeviceOpen(adapter); + AdapterDeviceControl(adapter, OPE_INT, &baud_rate); + AdapterDeviceConnect(adapter, CLIENT, server_addr, server_port, IPV4); + MdelayKTask(100); + while(1) + { + /* step1:Confirm the start signal of transmission. */ + get_start_signal(adapter); + KPrintf("start receive ota bin file.\n"); + /* step2:start receive bin file,first wait for 4s. */ + MdelayKTask(4000); + if(0 == ota_data_recv(adapter)) + { + break; + } + } + mcuboot.flash_deinit(); + KPrintf("ota file transfer complete,start reboot!\n"); + MdelayKTask(2000); + mcuboot.op_reset(); +} + +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_PARAM_NUM(0),ota, app_ota_by_4g, ota by 4g function); +#endif + + +#ifdef OTA_BY_PLATFORM +#define FRAME_LEN 2048 //每帧数据的数据包长度 +static uint8_t MqttRxbuf[3072]; +static uint8_t FrameBuf[FRAME_LEN]; +static OTA_TCB AliOTA; + +/******************************************************************************* +* 函 数 名: PropertyVersion +* 功能描述: 向服务器上传当前设备版本信息 +* 形 参: 无 +* 返 回 值: 无 +*******************************************************************************/ +static void PropertyVersion(void) +{ + uint8_t topicdatabuff[64]; + uint8_t tempdatabuff[128]; + ota_info_t ota_info; + + memset(topicdatabuff,0,64); + sprintf(topicdatabuff,"/ota/device/inform/%s/%s", PLATFORM_PRODUCTKEY, CLIENT_DEVICENAME); + + memset(&ota_info, 0, sizeof(ota_info_t)); + mcuboot.op_flash_read(FLAG_FLAH_ADDRESS, (void*)&ota_info, sizeof(ota_info_t)); + + memset(tempdatabuff,0,128); + sprintf(tempdatabuff,"{\"id\": \"1\",\"params\": {\"version\": \"%s\"}}",ota_info.os.version); + + MQTT_PublishDataQs1(topicdatabuff,tempdatabuff,strlen(tempdatabuff)); //发送等级QS=1的PUBLISH报文 +} + + +/*-------------------------------------------------*/ +/*函数名:OTA下载数据 */ +/*参 数:size:本次下载量 */ +/*参 数:offset:本次下载偏移量 */ +/*返回值:无 */ +/*-------------------------------------------------*/ +void OTA_Download(int size, int offset) +{ + uint8_t topicdatabuff[64]; + uint8_t tempdatabuff[128]; + + memset(topicdatabuff,0,64); + sprintf(topicdatabuff,"/sys/%s/%s/thing/file/download",PLATFORM_PRODUCTKEY,CLIENT_DEVICENAME); + + memset(tempdatabuff,0,128); + sprintf(tempdatabuff,"{\"id\": \"1\",\"params\": {\"fileInfo\":{\"streamId\":%d,\"fileId\":1},\"fileBlock\":{\"size\":%d,\"offset\":%d}}}",AliOTA.streamId,size,offset); + + MQTT_PublishDataQs0(topicdatabuff, tempdatabuff, strlen(tempdatabuff)); +} + +/******************************************************************************* +* 函 数 名: app_ota_by_platform +* 功能描述: 通过云平台MQTT进行升级 +* 形 参: 无 +* 返 回 值: 无 +*******************************************************************************/ +static void app_ota_by_platform(void* parameter) +{ + int datalen; + int ret = 0; + int freecnt = 0; + ota_info_t ota_info; + uint32_t heart_time = 0; + uint32_t flashdestination = DOWN_FLAH_ADDRESS; + uint8_t topicdatabuff[64]; + char *ptr; + + mcuboot.flash_init(); + memset(&ota_info, 0, sizeof(ota_info_t)); + mcuboot.op_flash_read(FLAG_FLAH_ADDRESS, (void*)&ota_info, sizeof(ota_info_t)); + ota_info.status = OTA_STATUS_DOWNLOADING; + UpdateOTAFlag(&ota_info); + memset(topicdatabuff,0,64); + sprintf(topicdatabuff,"/sys/%s/%s/thing/file/download_reply",PLATFORM_PRODUCTKEY,CLIENT_DEVICENAME); + +reconnect: + if((AdapterNetActive() == 0) && (MQTT_Connect() == 0) && MQTT_SubscribeTopic(topicdatabuff) == 0) + { + KPrintf("Log in to the cloud platform and subscribe to the topic successfully.\n"); + PropertyVersion(); + } + else + { + KPrintf("Log in to the cloud platform failed, retry!\n"); + goto reconnect; + } + + while(1) + { + memset(MqttRxbuf,0,sizeof(MqttRxbuf)); + datalen = MQTT_Recv(MqttRxbuf, sizeof(MqttRxbuf)); + if(datalen <= 0) + { + freecnt++; + } + else if(MqttRxbuf[0] == 0x30) + { + freecnt = 0; + MQTT_DealPublishData(MqttRxbuf, datalen); + ptr = strstr((char *)Platform_mqtt.cmdbuff,"{\"code\":\"1000\""); + if(ptr != NULL) + { + if(sscanf(ptr,"{\"code\":\"1000\",\"data\":{\"size\":%d,\"streamId\":%d,\"sign\":\"%*32s\",\"dProtocol\":\"mqtt\",\"version\":\"%11s\"",&AliOTA.size,&AliOTA.streamId,AliOTA.version)==3) + { + KPrintf("ota file size:%d\r\n",AliOTA.size); + KPrintf("ota file id:%d\r\n",AliOTA.streamId); + KPrintf("ota file version:%s\r\n",AliOTA.version); + if(mcuboot.op_flash_erase(DOWN_FLAH_ADDRESS,AliOTA.size != kStatus_Success)) + { + KPrintf("Failed to erase target fash!\n"); + ret = -1; + break; + } + AliOTA.counter = (AliOTA.size%FRAME_LEN != 0)? (AliOTA.size/FRAME_LEN + 1):(AliOTA.size/FRAME_LEN); + AliOTA.num = 1; //下载次数,初始值为1 + AliOTA.downlen = FRAME_LEN; //记录本次下载量 + OTA_Download(AliOTA.downlen,(AliOTA.num - 1)*FRAME_LEN); //发送要下载的数据信息给服务器 + } + else + { + KPrintf("Failed to get ota information!\n"); + ret = -1; + break; + } + } + + if(strstr((char *)Platform_mqtt.cmdbuff,"download_reply")) + { + memset(FrameBuf,0,sizeof(FrameBuf)); + memcpy(FrameBuf, &MqttRxbuf[datalen-AliOTA.downlen-2], AliOTA.downlen); + if(mcuboot.op_flash_write(flashdestination,FrameBuf,AliOTA.downlen) != kStatus_Success) + { + KPrintf("current frame[%d] flash failed.\n",AliOTA.num-1); + ret = -1; + break; + } + else + { + KPrintf("current frame[%d] is written to flash 0x%x address successful.\n", AliOTA.num -1, flashdestination); + KPrintf("Current progress is %d/%d\r\n",AliOTA.num,AliOTA.counter); + flashdestination += AliOTA.downlen; + AliOTA.num++; + } + + if(AliOTA.num < AliOTA.counter) //如果小于总下载次数 + { + AliOTA.downlen = FRAME_LEN; //记录本次下载量 + OTA_Download(AliOTA.downlen,(AliOTA.num - 1)*FRAME_LEN); //发送要下载的数据信息给服务器 + } + else if(AliOTA.num == AliOTA.counter) //如果等于总下载次数,说明是最后一次下载 + { + if(AliOTA.size%FRAME_LEN == 0) //判断固件大小是否是FRAME_LEN的整数倍 + { + AliOTA.downlen = FRAME_LEN; //记录本次下载量 + OTA_Download(AliOTA.downlen,(AliOTA.num - 1)*FRAME_LEN); //发送要下载的数据信息给服务器 + } + else + { + AliOTA.downlen = AliOTA.size%FRAME_LEN; //记录本次下载量 + OTA_Download(AliOTA.downlen,(AliOTA.num - 1)*FRAME_LEN); //发送要下载的数据信息给服务器 + } + } + + else //下载完毕 + { + ret = 0; + break; + } + } + + } + else + { + freecnt = 0; + continue; + } + + if((freecnt >= 10) && (CalculateTimeMsFromTick(CurrentTicksGain()) - heart_time >= HEART_TIME)) //连续10次未收到数据默认为为空闲状态,需每隔一段时间发送需要发送心跳包保活 + { + heart_time = CalculateTimeMsFromTick(CurrentTicksGain()); + if(MQTT_SendHeart() != 0) //发送心跳包失败可能连接断开,需要重连 + { + KPrintf("The connection has been disconnected, reconnecting!\n"); + freecnt = 0; + heart_time = 0; + goto reconnect; + } + KPrintf("Send heartbeat packet successful!\n"); + } + } + + if(0 == ret) + { + ota_info.down.size = AliOTA.size; + ota_info.down.crc32= calculate_crc32(DOWN_FLAH_ADDRESS, AliOTA.size); + + memset(ota_info.down.version,0,sizeof(ota_info.down.version)); + strncpy(ota_info.down.version, AliOTA.version, sizeof(ota_info.down.version)); + + memset(ota_info.down.description,0,sizeof(ota_info.down.description)); + strncpy(ota_info.down.description, "MQTT OTA bin.",sizeof(ota_info.down.description)); + + ota_info.status = OTA_STATUS_READY; + + memset(ota_info.error_message,0,sizeof(ota_info.error_message)); + strncpy(ota_info.error_message, "No error message!",sizeof(ota_info.error_message)); + + UpdateOTAFlag(&ota_info); + } + else + { + ota_info.status = OTA_STATUS_ERROR; + memset(ota_info.error_message,0,sizeof(ota_info.error_message)); + strncpy(ota_info.error_message, "Failed to download firmware to download partition!",sizeof(ota_info.error_message)); + UpdateOTAFlag(&ota_info); + } + MQTT_UnSubscribeTopic(topicdatabuff); + MQTT_Disconnect(); + mcuboot.flash_deinit(); + KPrintf("ota file transfer complete,start reboot!\n"); + MdelayKTask(2000); + mcuboot.op_reset(); +} + +int OtaTask(void) +{ + int32 ota_task = 0; + ota_task = KTaskCreate("ota_platform", app_ota_by_platform, NULL,8192, 10); + if(ota_task < 0) { + KPrintf("ota_task create failed ...%s %d.\n", __FUNCTION__,__LINE__); + return ERROR; + } + + StartupKTask(ota_task); + return 0; +} +#endif /******************************************************************************* @@ -355,9 +983,10 @@ SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHE *******************************************************************************/ void app_clear_jumpflag(void) { + ota_info_t ota_info; mcuboot.flash_init(); //跳转成功设置lastjumpflag为JUMP_SUCCESS_FLAG - ota_info_t ota_info; + memset(&ota_info, 0, sizeof(ota_info_t)); mcuboot.op_flash_read(FLAG_FLAH_ADDRESS, (void*)&ota_info, sizeof(ota_info_t)); ota_info.lastjumpflag = JUMP_SUCCESS_FLAG; UpdateOTAFlag(&ota_info); @@ -375,7 +1004,6 @@ void ota_entry(void) { uint8_t ch1, ch2; uint32_t ret; - ota_info_t ota_info; uint32_t timeout = 1000; mcuboot.board_init(); @@ -410,20 +1038,7 @@ void ota_entry(void) break; case 0x32: - mcuboot.flash_init(); - mcuboot.op_flash_read(FLAG_FLAH_ADDRESS, (void*)&ota_info, sizeof(ota_info_t)); - /* 此时APP分区还没有有效的固件,需要在bootloader下通过iap烧写出厂固件 */ - if((ota_info.os.size > APP_FLASH_SIZE) || (calculate_crc32(XIUOS_FLAH_ADDRESS, ota_info.os.size) != ota_info.os.crc32)) - { - mcuboot.print_string("\r\nNeed to flash initial firmware!\r\n"); - InitialVersion(); - } - else - { - UpdateApplication(); - } - - mcuboot.flash_deinit(); + Update(); BootLoaderJumpApp(); break; @@ -433,9 +1048,11 @@ void ota_entry(void) break; } } + //10s内不按下空格键默然进行升级,升级完成后跳转 else { + Update(); BootLoaderJumpApp(); } } -} +} \ No newline at end of file diff --git a/Ubiquitous/XiZi_IIoT/tool/bootloader/ota/ota.h b/Ubiquitous/XiZi_IIoT/tool/bootloader/ota/ota.h index 1eab71bdb..48880a85c 100644 --- a/Ubiquitous/XiZi_IIoT/tool/bootloader/ota/ota.h +++ b/Ubiquitous/XiZi_IIoT/tool/bootloader/ota/ota.h @@ -1,16 +1,22 @@ /* - * Copyright 2018-2020 NXP - * All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + /** -* @file ota.h -* @brief file ota.h -* @version 2.0 -* @author AIIT XUOS Lab -* @date 2023-04-03 +* @file: ota.h +* @brief: file ota.h +* @version: 1.0 +* @author: AIIT XUOS Lab +* @date: 2023/4/23 +* */ #ifndef __OTA_DEF_H__ #define __OTA_DEF_H__ @@ -19,6 +25,10 @@ #define JUMP_FAILED_FLAG 0XABABABAB #define JUMP_SUCCESS_FLAG 0XCDCDCDCD +#define STARTFLAG 0x1A2B //数据帧开始信号标记 +#define DATAFLAG 0x3C4D //数据帧数据信号标记 +#define ENDTFLAG 0x5E6F //数据帧结束信号标记 +#define LENGTH 1024 //每帧数据的数据包长度 typedef enum { OTA_STATUS_IDLE = 0, // 空闲状态,没有进行OTA升级 @@ -35,9 +45,8 @@ typedef enum { typedef struct { uint32_t size; // 应用程序大小,记录分区固件的大小 uint32_t crc32; // 应用程序CRC32校验值,记录分区固件的crc32值 - uint32_t version; // 应用程序版本号,记录分区固件的版本号 - uint32_t reserve; // 保留字段 - uint8_t description[128]; // 固件的描述信息,最多128个字符 + uint8_t version[32]; // 应用程序版本号,记录分区固件的版本 + uint8_t description[32]; // 固件的描述信息,最多32个字符 } firmware_t; @@ -48,10 +57,45 @@ typedef struct { firmware_t down; // Download分区属性信息 uint32_t status; // 升级状态,取值来自于ota_status_t类型 uint32_t lastjumpflag; // bootloaer跳转失败的标志,bootloader里置0xABABABAB,跳转成功后在应用里置0xCDCDCDCD - uint32_t reserve[2]; // 保留字段 - uint8_t error_message[128]; // 错误信息,最多128个字符 + uint8_t error_message[64]; // 错误信息,最多64个字符 } ota_info_t; + +#ifdef OTA_BY_TCPSERVER +/*bin包传输过程中的数据帧相关的结构体*/ +typedef struct +{ + uint16_t frame_flag; // frame start flag 2 Bytes + uint16_t dev_sid; // device software version + uint32_t total_len; // send data total length caculated from each frame_len +} ota_header_t; + +typedef struct +{ + uint32_t frame_id; // Current frame id + uint8_t frame_data[LENGTH]; // Current frame data + uint16_t frame_len; // Current frame data length + uint16_t crc; // Current frame data crc +} ota_frame_t; + +typedef struct +{ + ota_header_t header; + ota_frame_t frame; +} ota_data; +#endif + +#ifdef OTA_BY_PLATFORM +typedef struct{ + uint32_t size; //OTA固件大小 + uint32_t streamId; //OTA固件下载时ID编号 + uint32_t counter; //OTA总下载次数 + uint32_t num; //OTA当前下载次数 + uint32_t downlen; //OTA当前下载次数的下载量 + uint8_t version[32]; //OTA下载时存储版本号的缓存区 +}OTA_TCB; +#endif + void app_clear_jumpflag(void); void ota_entry(void); #endif diff --git a/Ubiquitous/XiZi_IIoT/tool/bootloader/ota/server_tcp.c b/Ubiquitous/XiZi_IIoT/tool/bootloader/ota/server_tcp.c new file mode 100644 index 000000000..d1833b824 --- /dev/null +++ b/Ubiquitous/XiZi_IIoT/tool/bootloader/ota/server_tcp.c @@ -0,0 +1,508 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file: ota_server.c +* @brief: a application ota task of system running in Linux +* @version: 1.0 +* @author: AIIT XUOS Lab +* @date: 2023/5/26 +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STARTFLAG 0x1A2B //数据帧开始信号标记 +#define DATAFLAG 0x3C4D //数据帧数据信号标记 +#define ENDTFLAG 0x5E6F //数据帧结束信号标记 +#define PORT 7777 //socket端口号 +#define SIZE 100 //socket链接限制为100 +#define LENGTH 1024 //每帧数据的数据包长度 +#define BIN_PATH "/home/aep05/wgz/XiZi-xidatong-arm32-app.bin" //bin包的路径 + +typedef struct +{ + uint16_t frame_flag; // frame start flag 2 Bytes + uint16_t dev_sid; // device software version + uint32_t total_len; // send data total length caculated from each frame_len +} ota_header_t; + +typedef struct +{ + uint32_t frame_id; // Current frame id + uint8_t frame_data[LENGTH]; // Current frame data + uint16_t frame_len; // Current frame data length + uint16_t crc; // Current frame data crc +} ota_frame_t; + +typedef struct +{ + ota_header_t header; + ota_frame_t frame; +} ota_data; + + +static int serverfd; // 服务器socket +static int clientfd[SIZE] = {0}; // 客户端的socketfd,100个元素,clientfd[0]~clientfd[99] + + +/******************************************************************************* +* 函 数 名: calculate_crc16 +* 功能描述: 计算给定长度的数据的crc16的值,用于OTA传输过程中数据帧的校验 +* 形 参: data:数据buffer + len:表示需要计算CRC16的数据长度 +* 返 回 值: 计算得到的CRC16值 +*******************************************************************************/ +static uint16_t calculate_crc16(uint8_t * data, uint32_t len) +{ + uint16_t reg_crc=0xFFFF; + while(len--) + { + reg_crc ^= *data++; + for(int j=0;j<8;j++) + { + if(reg_crc & 0x01) + reg_crc=reg_crc >>1 ^ 0xA001; + else + reg_crc=reg_crc >>1; + } + } + printf("crc = [0x%x]\n",reg_crc); + return reg_crc; +} + + +/******************************************************************************* +* 函 数 名: sockt_init +* 功能描述: 用于在TCP Server上创建socet监听 +* 形 参: data:数据buffer + len:表示需要计算CRC16的数据长度 +* 返 回 值: 计算得到的CRC16值 +*******************************************************************************/ +void sockt_init(void) +{ + struct sockaddr_in addr, *sa;//存储套接字的信息 + struct ifaddrs *ifap, *ifa; + char *ipaddr; + + serverfd = socket(AF_INET,SOCK_STREAM,0); + + if(serverfd == -1) + { + perror("Failed to create socket"); + exit(-1); + } + + //为套接字设置ip协议 设置端口号并自动获取本机ip转化为网络ip + addr.sin_family = AF_INET;//地址族 + addr.sin_port = htons(PORT);//设置server端端口号,随便设置,当sin_port = 0时,系统随机选择一个未被使用的端口号 + addr.sin_addr.s_addr = htons(INADDR_ANY);//当sin_addr=INADDR_ANY时表示从本机的任一网卡接收数据 + + /*显示当前TCP server的*/ + getifaddrs(&ifap); + for(ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) + { + if(ifa->ifa_addr->sa_family == AF_INET) + { + sa = (struct sockaddr_in *) ifa->ifa_addr; + ipaddr = inet_ntoa(sa->sin_addr); + printf("Interface:%-16s Address:%-16s\n", ifa->ifa_name, ipaddr); + } + } + freeifaddrs(ifap); + + //绑定套接字 + struct timeval timeout; + timeout.tv_sec = 5; + timeout.tv_usec = 0; + if(setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, &timeout, sizeof(struct timeval)) < 0) + { + perror("Failed to set setsock option"); + exit(-1); + } + + if(bind(serverfd,(struct sockaddr*)&addr,sizeof(addr)) == -1) + { + perror("Failed to bind socket port"); + exit(-1); + } + + //监听最大连接数 + if(listen(serverfd,SIZE) == -1) + { + perror("Failed to set socket listening"); + exit(-1); + } +} + + +/******************************************************************************* +* 函 数 名: ota_start_signal +* 功能描述: 发送开始信号,等待接收端回应ready +* 形 参: fd:监听的客户端连接的fd +* 返 回 值: 0:成功,-1:失败 +*******************************************************************************/ +void ota_start_signal(int fd) +{ + ota_data data; + struct stat st; + uint8_t buf[32]; + int file_size = 0, file_frame_cnt = 0, length = 0; + + if (access(BIN_PATH, F_OK) == 0) + { + printf("%s exists.\n", basename(BIN_PATH)); + } + else + { + printf("%s does not exist,please cheack!\n", BIN_PATH); + exit(-1); + } + + //获取文件大小(以字节为单位) + if(stat(BIN_PATH, &st) == 0) + { + file_size = st.st_size; + file_frame_cnt = ((file_size%LENGTH) != 0)? (file_size/LENGTH + 1):(file_size/LENGTH); + printf("%s size is %d bytes,frame count is %d!\n",basename(BIN_PATH), file_size, file_frame_cnt); + } + else + { + printf("Failed to get file size\n"); + exit(-1); + } + + while(1) + { + memset(&data, 0x0, sizeof(ota_data)); + data.header.frame_flag = STARTFLAG; + //发送起始帧时把bin文件的大小一并发送出去 + data.header.total_len = file_size; + + while(send(fd, &data, sizeof(data), MSG_NOSIGNAL) <= 0); + printf("send start signal to client %d.\n", fd); + + memset(buf, 0, sizeof(buf)); + length = recv(fd, buf, sizeof(buf), 0); + if(length == 0) + { + printf("The current socket %d is disconnected,please check it!\n",fd); + close(fd); + pthread_exit(0); + } + else if(length > 0 && (0 == strncmp(buf, "ready", length))) + { + printf("recv buf %s length %d from client %d.\n", buf, length, fd); + break; + } + else + { + continue; + } + + } + +} + + +/******************************************************************************* +* 函 数 名: ota_file_send +* 功能描述: 用于在TCP Server发送bin文件 +* 形 参: fd:监听的客户端连接的fd +* 返 回 值: 发送成功返回0,失败返回-1 +*******************************************************************************/ +int ota_file_send(int fd) +{ + unsigned char buf[32] = { 0 }; + ota_data data; + FILE *file_fd; + int length = 0; + int try_times; + int recv_end_times = 3; + int ret = 0; + int frame_cnt = 0; + int file_length = 0; + char * file_buf = NULL; + + file_fd = fopen(BIN_PATH, "r"); + if(NULL == file_fd) + { + printf("open file failed.\n"); + fclose(file_fd); + return -1; + } + fseek(file_fd, 0, SEEK_SET); + printf("start send bin file to client %d.\n", fd); + + + while(!feof(file_fd)) + { + memset(&data, 0, sizeof(data)); + + data.header.frame_flag = DATAFLAG; + length = fread(data.frame.frame_data, 1, LENGTH, file_fd); + if(length == LENGTH) + { + printf("read %d bytes\n",length); + data.frame.frame_id = frame_cnt; + data.frame.frame_len = length; + data.frame.crc = calculate_crc16(data.frame.frame_data, length); + file_length += length; + } + else if(length > 0 && length < LENGTH) + { + if(ferror(file_fd)) + { + printf("read %s file error!\n", basename(BIN_PATH)); + ret = -1; + break; + } + else + { + printf("read %d bytes\n",length); + data.frame.frame_id = frame_cnt; + data.frame.frame_len = length; + data.frame.crc = calculate_crc16(data.frame.frame_data, length); + file_length += length; + } + } + //fread返回值为0,此时是个空包,不需要再发送了否则是冗余数据 + else + { + printf("read %s file done!\n", basename(BIN_PATH)); + break; + } + +send_again: + printf("send frame[%d] to client %d.\n", frame_cnt, fd); + length = send(fd, &data, sizeof(data), MSG_NOSIGNAL); + if(length < 0) + { + printf("send frame[%d] to client %d failed,send again\n", frame_cnt, fd); + goto send_again; + } + +recv_again: + memset(buf, 0, sizeof(buf)); + length = recv(fd, buf, sizeof(buf), 0); + if(length == 0) + { + printf("current socket %d is disconnected,please check it!\n", fd); + ret = -1; + close(fd); + pthread_exit(0); + break; + } + else if(length < 0 ) + { + printf("send frame[%d] to client %d waiting for ok timeout,receive again.\n", frame_cnt, fd); + goto recv_again; + } + else if(0 == strncmp(buf, "ok", length)) + { + printf("receive buf[%s] length %d from client %d.\n", buf, length, fd); + try_times = 5; + printf("send to client %d frame[%d] data send done.\n",fd, frame_cnt); + frame_cnt++; + continue; + } + + //接收到的回复不是ok,说明刚发的包有问题,需要再发一次 + else + { + if(try_times > 0) + { + try_times--; + goto send_again; + } + else + { + printf("send to client %d frame[%d] 5 times failed.\n",fd, frame_cnt); + ret = -1; + break; + } + } + } + + /* finally,crc check total bin file.*/ + if(ret == 0) + { + printf("total send file length %d bytes, %d frames to client %d.\n", file_length, frame_cnt, fd); + printf("now crc check total bin file.\n"); + file_buf = malloc(file_length); + memset(file_buf, 0, file_length); + memset(&data, 0, sizeof(data)); + + data.header.frame_flag = ENDTFLAG; + + file_fd = fopen(BIN_PATH, "r"); + if(NULL == file_fd) + { + printf("open file failed.\n"); + return -1; + } + fseek(file_fd, 0, SEEK_SET); + length = fread(file_buf,1, file_length, file_fd); + printf("read file length = %d\n",length); + if(length > 0) + { + data.frame.frame_id = frame_cnt; + data.header.total_len = file_length; + data.frame.crc = calculate_crc16(file_buf, length); + } + +send_end_signal: + printf("send ota end signal to client %d.\n", fd); + length = send(fd, &data, sizeof(data), MSG_NOSIGNAL); + if(length < 0) + { + printf("send to client %d ota end signal faile,send again\n",fd); + goto send_end_signal; + } + +recv_end_signal: + memset(buf, 0, sizeof(buf)); + length = recv(fd, buf, sizeof(buf), 0); + if(length == 0) + { + printf("current socket %d is disconnected,please check it!\n",fd); + ret = -1; + free(file_buf); + fclose(file_fd); + close(fd); + pthread_exit(0); + + } + if(length < 0 || (0 != strncmp(buf, "ok", length))) + { + recv_end_times--; + printf("from client %d end signal waiting for ok timeout,receive again.\n", fd); + if(recv_end_times > 0) + { + goto recv_end_signal; + } + else + { + printf("client %d error end !!!\n", fd); + ret = -1; + } + } + + free(file_buf); + } + + fclose(file_fd); + return ret; +} + + +/******************************************************************************* +* 函 数 名: server_thread +* 功能描述: TCP Server的服务线程入口函数 +* 形 参: p:入口函数的参数 +* 返 回 值: 无 +*******************************************************************************/ +void* server_thread(void* p) +{ + int fd = *(int*)p; + unsigned char buf[32] = { 0 }; + ota_data data; + + printf("pthread = %d\n",fd); + sleep(3); + while(1) + { + /* if ota failed then restart the ota process */ + ota_start_signal(fd); + sleep(5); + if(0 == ota_file_send(fd)) + { + printf("ota file send to client %d successful.\n", fd); + break; + } + } + printf("exit fd = %d\n",fd); + close(fd); + pthread_exit(0); +} + + +/******************************************************************************* +* 函 数 名: server +* 功能描述: TCP Server的服务函数 +* 形 参: 无 +* 返 回 值: 无 +*******************************************************************************/ +void server(void) +{ + int i = 0; + printf("ota Server startup\n"); + while(1) + { + struct sockaddr_in fromaddr; + socklen_t len = sizeof(fromaddr); + int fd = accept(serverfd,(struct sockaddr*)&fromaddr,&len);//调用accept进入堵塞状态,等待客户端的连接 + + if(fd == -1) + { + printf("The client connection is wrong...\n"); + continue; + } + + for(i = 0;i < SIZE;i++) + { + if(clientfd[i] == 0) + { + //记录客户端的socket + clientfd[i] = fd; + + //有客户端连接之后,启动线程给此客户服务 + pthread_t tid; + pthread_create(&tid,0,server_thread,&fd); + break; + } + + if(SIZE == i) + { + //发送给客户端聊天室满了 + char* str = "Devices full"; + printf("%s", str); + send(fd,str,strlen(str),0); + close(fd); + } + } + } +} + +int main(void) +{ + sockt_init(); + server(); +} +