From fdd5dc370a8cc0b24e9bf11df8dfe3a125233e34 Mon Sep 17 00:00:00 2001 From: "ethan.du" Date: Mon, 17 May 2021 13:20:56 +0800 Subject: [PATCH] support ota --- CMakeLists.txt | 9 + Makefile | 4 + make.settings | 1 + platform/os/linux/HAL_Flash_linux.c | 3 +- samples/CMakeLists.txt | 9 + samples/ota/ota_sample.c | 122 +++++ samples/samples.mk | 9 + src/certs/ca.c | 90 +++- src/certs/ca.h | 1 + src/ota/include/ota_config.h | 50 ++ src/ota/include/ota_internal.h | 96 ++++ src/ota/src/ota_client.c | 700 +++++++++++++++++++++++++++ src/ota/src/ota_fetch.c | 119 +++++ src/ota/src/ota_lib.c | 201 ++++++++ src/ota/src/ota_mqtt.c | 172 +++++++ src/sdk-impl/uiot_export_ota.h | 232 +++++++++ src/utils/utils_httpc.c | 52 +- src/utils/utils_httpc.h | 5 +- tools/scripts/default_settings.mk | 1 + tools/scripts/parse_make_settings.mk | 1 + tools/scripts/rules-final.mk | 4 +- 21 files changed, 1827 insertions(+), 54 deletions(-) create mode 100644 samples/ota/ota_sample.c create mode 100644 src/ota/include/ota_config.h create mode 100644 src/ota/include/ota_internal.h create mode 100644 src/ota/src/ota_client.c create mode 100644 src/ota/src/ota_fetch.c create mode 100644 src/ota/src/ota_lib.c create mode 100644 src/ota/src/ota_mqtt.c create mode 100644 src/sdk-impl/uiot_export_ota.h diff --git a/CMakeLists.txt b/CMakeLists.txt index dc8956f..0f05c15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ option(ENABLE_LOG_DEBUG "是否打开DEBUG日志输出" OFF) option(ENABLE_IOT_TRACE "是否打开函数跟踪" OFF) option(ENABLE_FEATURE_MQTT_COMM "是否打开MQTT通道的总开关" ON) +option(ENABLE_FEATURE_OTA "是否打开OTA固件升级总开关" ON) option(ENABLE_FEATURE_AUTH_MODE_DYNAMIC "是否打开设备动态注册" ON) option(ENABLE_TLS_SUPPORT "是否打开TLS支持" ON) option(ENABLE_SDK_TESTS "是否打开SDK测试用例编译" ON) @@ -77,6 +78,14 @@ else () message("FEATURE_AUTH_MODE_DYNAMIC = n") endif () +if (ENABLE_FEATURE_OTA) + message("ENABLE_FEATURE_OTA = y") + include_directories(src/ota/include) + AUX_SOURCE_DIRECTORY(src/ota/src OTA_SRC) + set(src_iot_sdk ${src_iot_sdk} ${OTA_SRC}) +else () + message("ENABLE_FEATURE_OTA = n") +endif () if (ENABLE_TLS_SUPPORT) message("FEATURE_SUPPORT_TLS = y") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSUPPORT_TLS") diff --git a/Makefile b/Makefile index 8b92fa4..4b1cba9 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ COMP_LIB_COMPONENTS := \ $(call CompLib_Map, MQTT_COMM_ENABLED, src/mqtt/src) +$(call CompLib_Map, OTA_ENABLED, src/ota/src) $(call CompLib_Map, SUPPORT_AT_CMD, src/at/src src/at/class/$(PLATFORM_MODULE) platform/module) IOTSDK_SRC_FILES := \ @@ -53,6 +54,9 @@ $(call CompInc_Map, MQTT_COMM_ENABLED, \ src/mqtt/include \ ) +$(call CompInc_Map, OTA_ENABLED, \ + src/ota/include \ +) $(call CompInc_Map, SUPPORT_TLS, \ external_libs/mbedtls/include \ ) diff --git a/make.settings b/make.settings index eef1081..0dd1ea9 100644 --- a/make.settings +++ b/make.settings @@ -15,6 +15,7 @@ PLATFORM_MODULE = m26 # PLATFORM_AR = armar FEATURE_MQTT_COMM_ENABLED = y # 是否打开MQTT连接云平台 +FEATURE_OTA_ENABLED = y # 是否打开OTA固件升级 FEATURE_AUTH_MODE_DYNAMIC = y # 是否打开设备动态注册 FEATURE_SUPPORT_TLS = y # 是否打开TLS支持 FEATURE_SUPPORT_AT_CMD = n # 是否打开AT命令支持 diff --git a/platform/os/linux/HAL_Flash_linux.c b/platform/os/linux/HAL_Flash_linux.c index c7b8292..5a45240 100644 --- a/platform/os/linux/HAL_Flash_linux.c +++ b/platform/os/linux/HAL_Flash_linux.c @@ -25,7 +25,7 @@ static int _utils_parse_name(const char *url, char *name) { } host_ptr += 3; - path_ptr = strchr(host_ptr, '/'); + path_ptr = strrchr(host_ptr, '/'); if (NULL == path_ptr) { return -2; } @@ -39,6 +39,7 @@ static int _utils_parse_name(const char *url, char *name) { memcpy(name, path_ptr + 1, name_len - 1); name[name_len] = '\0'; + LOG_ERROR("file name:%s",name); return SUCCESS_RET; } diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index b946c6e..d0547a6 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -5,7 +5,16 @@ if (ENABLE_FEATURE_MQTT_COMM) add_dependencies(mqtt_sample iot_sdk iot_platform) endif () +if (ENABLE_FEATURE_OTA) + add_executable(ota_sample ota/ota_sample.c) + add_dependencies(ota_sample iot_sdk iot_platform) +endif () if (ENABLE_FEATURE_AUTH_MODE_DYNAMIC) add_executable(dynamic_auth_sample dynamic_auth/dynamic_auth_sample.c) add_dependencies(dynamic_auth_sample iot_sdk iot_platform) endif () + +if (ENABLE_FEATURE_OTA) + add_executable(ota_sample ota/ota_sample.c) + add_dependencies(ota_sample iot_sdk iot_platform) +endif () \ No newline at end of file diff --git a/samples/ota/ota_sample.c b/samples/ota/ota_sample.c new file mode 100644 index 0000000..27ec939 --- /dev/null +++ b/samples/ota/ota_sample.c @@ -0,0 +1,122 @@ +/* +* Copyright (C) 2012-2019 UCloud. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include +#include +#include +#include +#include + +#include "uiot_export.h" +#include "uiot_import.h" +#include "uiot_export_ota.h" + +#define UIOT_MY_PRODUCT_SN "productSN" + +#define UIOT_MY_DEVICE_SN "deviceSN" + +#define UIOT_MY_DEVICE_SECRET "device_secret" + +#define OTA_BUF_LEN (1024) + +static void event_handler(void *pClient, void *handle_context, MQTTEventMsg *msg) +{ + switch(msg->event_type) { + case MQTT_EVENT_UNDEF: + LOG_INFO("undefined event occur.\n"); + break; + + case MQTT_EVENT_DISCONNECT: + LOG_INFO("MQTT disconnect.\n"); + break; + + case MQTT_EVENT_RECONNECT: + LOG_INFO("MQTT reconnect.\n"); + break; + + case MQTT_EVENT_SUBSCRIBE_SUCCESS: + LOG_INFO("subscribe success.\n"); + break; + + case MQTT_EVENT_SUBSCRIBE_TIMEOUT: + LOG_INFO("subscribe wait ack timeout.\n"); + break; + + case MQTT_EVENT_SUBSCRIBE_NACK: + LOG_INFO("subscribe nack.\n"); + break; + + case MQTT_EVENT_PUBLISH_SUCCESS: + LOG_INFO("publish success.\n"); + break; + + case MQTT_EVENT_PUBLISH_TIMEOUT: + LOG_INFO("publish timeout.\n"); + break; + + case MQTT_EVENT_PUBLISH_NACK: + LOG_INFO("publish nack.\n"); + break; + default: + LOG_INFO("Should NOT arrive here.\n"); + break; + } +} + + +static int _setup_connect_init_params(MQTTInitParams* initParams) +{ + initParams->device_sn = UIOT_MY_DEVICE_SN; + initParams->product_sn = UIOT_MY_PRODUCT_SN; + initParams->device_secret = UIOT_MY_DEVICE_SECRET; + initParams->command_timeout = UIOT_MQTT_COMMAND_TIMEOUT; + initParams->keep_alive_interval = UIOT_MQTT_KEEP_ALIVE_INTERNAL; + initParams->auto_connect_enable = 1; + initParams->event_handler.h_fp = event_handler; + + return SUCCESS_RET; +} + +int main(int argc, char **argv) +{ + int rc; + + MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS; + rc = _setup_connect_init_params(&init_params); + if (rc != SUCCESS_RET) { + return rc; + } + + void *client = IOT_MQTT_Construct(&init_params); + if (client != NULL) { + LOG_INFO("MQTT Construct Success"); + } else { + LOG_ERROR("MQTT Construct Failed"); + return FAILURE_RET; + } + + void *h_ota = IOT_OTA_Init(UIOT_MY_PRODUCT_SN, UIOT_MY_DEVICE_SN, client); + if (NULL == h_ota) { + IOT_MQTT_Destroy(&client); + LOG_ERROR("init OTA failed"); + return FAILURE_RET; + } + + while(1) + { + IOT_MQTT_Yield(client, 5000); + } +} + diff --git a/samples/samples.mk b/samples/samples.mk index 8744d1c..4e54e71 100644 --- a/samples/samples.mk +++ b/samples/samples.mk @@ -23,6 +23,15 @@ dynamic_auth_sample: mv $@ $(FINAL_DIR)/bin endif +ifneq (,$(filter -DOTA_ENABLED,$(CFLAGS))) +ota_sample: + $(TOP_Q) \ + $(PLATFORM_CC) $(CFLAGS) $(SAMPLE_DIR)/ota/$@.c $(LDFLAGS) -o $@ + + $(TOP_Q) \ + mv $@ $(FINAL_DIR)/bin +endif + samples_final: $(TOP_Q) \ cp -rf $(TOP_DIR)/src/sdk-impl/*port*.h $(FINAL_DIR)/include/ diff --git a/src/certs/ca.c b/src/certs/ca.c index 47d5b36..9e29bfd 100644 --- a/src/certs/ca.c +++ b/src/certs/ca.c @@ -24,26 +24,69 @@ extern "C" { #ifdef SUPPORT_TLS static const char *iot_ca_crt = \ { - "-----BEGIN CERTIFICATE-----\r\n" - "MIIDLDCCAhQCCQCnVOYTQCyIGjANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJD\r\n" - "TjELMAkGA1UECAwCU0gxCzAJBgNVBAcMAlNIMQ8wDQYDVQQKDAZVQ0xPVUQxDTAL\r\n" - "BgNVBAsMBFBBQVMxDzANBgNVBAMMBlVDTE9VRDAeFw0yMDEyMTYwNTI2MzVaFw00\r\n" - "MDEyMTEwNTI2MzVaMFgxCzAJBgNVBAYTAkNOMQswCQYDVQQIDAJTSDELMAkGA1UE\r\n" - "BwwCU0gxDzANBgNVBAoMBlVDTE9VRDENMAsGA1UECwwEUEFBUzEPMA0GA1UEAwwG\r\n" - "VUNMT1VEMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA312YPHXmEEP/\r\n" - "PaPdSB4+jFQGHRE+BbOme5NOOL9AmFi+jxJb/4UHD6WBdl4nb0fOuw2dfbqv4Qrl\r\n" - "Vh/ZwOQoQdzIZJTHMQNZoOd/5nwa/WPBAldNzWH/IqjFrhCD9tfA0rldH0QTE1s2\r\n" - "vQ8WpNi5pyJJMGoBs7q1QY0GqIRJc3iTIqsNADZhNJ4zLRa05nBzXgKQRIz1Rumx\r\n" - "MpRNuKFz4EHLDkRUZCYqw5SmtpaW4ja/sSC4UzGfTEkXmNyN9UrteyvUMPbN6vTz\r\n" - "igymgHyES3ygm+sEHJXFvpvZVfq8anwyNs0uUlHhWcx+MqF93vd+T1RRAq+s0BmK\r\n" - "YobbIrhCuQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBvvBmgA2IjcKca0mTZ9uvM\r\n" - "EyskZMTnywGuYaSu0JrgY063RsoB/p9z86TqYzLxWR0en4S4VuBWERxuaYATHs6f\r\n" - "sKhSq9W9RqRj8PAhjLbLPKbhp6hM/VdpcIgJNrqlmFrAcZvddiozb2U832qp65/M\r\n" - "1m2Tq9i1/NosoIoolirCiK4iEotK0zYsUig1dgRTvSGVbkrmIaFesQGU503dYct/\r\n" - "Uz2jEmg8o36cMk011PcbkcTV6Way2g0zXs5PJu3PsJkFgL9jsBFlF1/6sHRan2V9\r\n" - "7HFkvgolP7RSYFSJT2Hx1NQ71NoK18lnQpvjn2bZndGOACAadpWKvDAIouaB8v8w\r\n" - "-----END CERTIFICATE-----\r\n" + "-----BEGIN CERTIFICATE-----\r\n" + "MIIErjCCA5agAwIBAgIQBYAmfwbylVM0jhwYWl7uLjANBgkqhkiG9w0BAQsFADBh\r\n" + "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\r\n" + "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\r\n" + "QTAeFw0xNzEyMDgxMjI4MjZaFw0yNzEyMDgxMjI4MjZaMHIxCzAJBgNVBAYTAkNO\r\n" + "MSUwIwYDVQQKExxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMR0wGwYDVQQL\r\n" + "ExREb21haW4gVmFsaWRhdGVkIFNTTDEdMBsGA1UEAxMUVHJ1c3RBc2lhIFRMUyBS\r\n" + "U0EgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgWa9X+ph+wAm8\r\n" + "Yh1Fk1MjKbQ5QwBOOKVaZR/OfCh+F6f93u7vZHGcUU/lvVGgUQnbzJhR1UV2epJa\r\n" + "e+m7cxnXIKdD0/VS9btAgwJszGFvwoqXeaCqFoP71wPmXjjUwLT70+qvX4hdyYfO\r\n" + "JcjeTz5QKtg8zQwxaK9x4JT9CoOmoVdVhEBAiD3DwR5fFgOHDwwGxdJWVBvktnoA\r\n" + "zjdTLXDdbSVC5jZ0u8oq9BiTDv7jAlsB5F8aZgvSZDOQeFrwaOTbKWSEInEhnchK\r\n" + "ZTD1dz6aBlk1xGEI5PZWAnVAba/ofH33ktymaTDsE6xRDnW97pDkimCRak6CEbfe\r\n" + "3dXw6OV5AgMBAAGjggFPMIIBSzAdBgNVHQ4EFgQUf9OZ86BHDjEAVlYijrfMnt3K\r\n" + "AYowHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDgYDVR0PAQH/BAQD\r\n" + "AgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAG\r\n" + "AQH/AgEAMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au\r\n" + "ZGlnaWNlcnQuY29tMEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2lj\r\n" + "ZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwTAYDVR0gBEUwQzA3Bglg\r\n" + "hkgBhv1sAQIwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29t\r\n" + "L0NQUzAIBgZngQwBAgEwDQYJKoZIhvcNAQELBQADggEBAK3dVOj5dlv4MzK2i233\r\n" + "lDYvyJ3slFY2X2HKTYGte8nbK6i5/fsDImMYihAkp6VaNY/en8WZ5qcrQPVLuJrJ\r\n" + "DSXT04NnMeZOQDUoj/NHAmdfCBB/h1bZ5OGK6Sf1h5Yx/5wR4f3TUoPgGlnU7EuP\r\n" + "ISLNdMRiDrXntcImDAiRvkh5GJuH4YCVE6XEntqaNIgGkRwxKSgnU3Id3iuFbW9F\r\n" + "UQ9Qqtb1GX91AJ7i4153TikGgYCdwYkBURD8gSVe8OAco6IfZOYt/TEwii1Ivi1C\r\n" + "qnuUlWpsF1LdQNIdfbW3TSe0BhQa7ifbVIfvPWHYOu3rkg1ZeMo6XRU9B4n5VyJY\r\n" + "RmE=\r\n" + "-----END CERTIFICATE-----\r\n" + }; + +static const char *iot_https_ca_crt = \ +{ + "-----BEGIN CERTIFICATE-----\r\n" + "MIIErjCCA5agAwIBAgIQBYAmfwbylVM0jhwYWl7uLjANBgkqhkiG9w0BAQsFADBh\r\n" + "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\r\n" + "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\r\n" + "QTAeFw0xNzEyMDgxMjI4MjZaFw0yNzEyMDgxMjI4MjZaMHIxCzAJBgNVBAYTAkNO\r\n" + "MSUwIwYDVQQKExxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMR0wGwYDVQQL\r\n" + "ExREb21haW4gVmFsaWRhdGVkIFNTTDEdMBsGA1UEAxMUVHJ1c3RBc2lhIFRMUyBS\r\n" + "U0EgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCgWa9X+ph+wAm8\r\n" + "Yh1Fk1MjKbQ5QwBOOKVaZR/OfCh+F6f93u7vZHGcUU/lvVGgUQnbzJhR1UV2epJa\r\n" + "e+m7cxnXIKdD0/VS9btAgwJszGFvwoqXeaCqFoP71wPmXjjUwLT70+qvX4hdyYfO\r\n" + "JcjeTz5QKtg8zQwxaK9x4JT9CoOmoVdVhEBAiD3DwR5fFgOHDwwGxdJWVBvktnoA\r\n" + "zjdTLXDdbSVC5jZ0u8oq9BiTDv7jAlsB5F8aZgvSZDOQeFrwaOTbKWSEInEhnchK\r\n" + "ZTD1dz6aBlk1xGEI5PZWAnVAba/ofH33ktymaTDsE6xRDnW97pDkimCRak6CEbfe\r\n" + "3dXw6OV5AgMBAAGjggFPMIIBSzAdBgNVHQ4EFgQUf9OZ86BHDjEAVlYijrfMnt3K\r\n" + "AYowHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDgYDVR0PAQH/BAQD\r\n" + "AgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAG\r\n" + "AQH/AgEAMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3Au\r\n" + "ZGlnaWNlcnQuY29tMEIGA1UdHwQ7MDkwN6A1oDOGMWh0dHA6Ly9jcmwzLmRpZ2lj\r\n" + "ZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwTAYDVR0gBEUwQzA3Bglg\r\n" + "hkgBhv1sAQIwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29t\r\n" + "L0NQUzAIBgZngQwBAgEwDQYJKoZIhvcNAQELBQADggEBAK3dVOj5dlv4MzK2i233\r\n" + "lDYvyJ3slFY2X2HKTYGte8nbK6i5/fsDImMYihAkp6VaNY/en8WZ5qcrQPVLuJrJ\r\n" + "DSXT04NnMeZOQDUoj/NHAmdfCBB/h1bZ5OGK6Sf1h5Yx/5wR4f3TUoPgGlnU7EuP\r\n" + "ISLNdMRiDrXntcImDAiRvkh5GJuH4YCVE6XEntqaNIgGkRwxKSgnU3Id3iuFbW9F\r\n" + "UQ9Qqtb1GX91AJ7i4153TikGgYCdwYkBURD8gSVe8OAco6IfZOYt/TEwii1Ivi1C\r\n" + "qnuUlWpsF1LdQNIdfbW3TSe0BhQa7ifbVIfvPWHYOu3rkg1ZeMo6XRU9B4n5VyJY\r\n" + "RmE=\r\n" + "-----END CERTIFICATE-----\r\n" +}; + #endif const char *iot_ca_get() { @@ -54,6 +97,15 @@ const char *iot_ca_get() { #endif } +const char *iot_https_ca_get() { +#ifdef SUPPORT_TLS + return iot_https_ca_crt; +#else + return NULL; +#endif +} + + #ifdef __cplusplus } #endif diff --git a/src/certs/ca.h b/src/certs/ca.h index 8544b6d..a721a9c 100644 --- a/src/certs/ca.h +++ b/src/certs/ca.h @@ -21,6 +21,7 @@ extern "C" { #endif const char *iot_ca_get(void); +const char *iot_https_ca_get(); #ifdef __cplusplus } diff --git a/src/ota/include/ota_config.h b/src/ota/include/ota_config.h new file mode 100644 index 0000000..0698875 --- /dev/null +++ b/src/ota/include/ota_config.h @@ -0,0 +1,50 @@ +/* +* Copyright (C) 2012-2019 UCloud. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#ifndef C_SDK_OTA_CONFIG_H_ +#define C_SDK_OTA_CONFIG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define TYPE_FIELD "Method" +#define MD5_FIELD "Payload.MD5" +#define MODULE_FIELD "Payload.Module" +#define VERSION_FIELD "Payload.Version" +#define CURRENT_VERSION_FIELD "Payload.CurrentVersion" +#define URL_FIELD "Payload.URL" +#define SIZE_FIELD "Payload.Size" +#define TASKID_FILED "Payload.TaskID" +#define NOTIFY_METHOD "notify" + +#define REPORT_VER_TEMPLATE "{\"Method\": \"report\", \"Payload\":{\"Module\":\"%s\", \"Version\":\"%s\"}}" +#define REPORT_SUCCESS_MSG_TEMPLATE "{\"Method\": \"success\", \"Payload\": {\"TaskID\": \"%s\"}}" +#define REPORT_FAIL_MSG_TEMPLATE "{\"Method\": \"fail\", \"Payload\": {\"TaskID\": %d, \"ErrMsg\":\"%s\"}}" +#define NOTIFY_MSG_TEMPLATE "{\"Method\": \"notify\", \"Payload\":{\"TaskID\":%d}}" +#define UPGRADING_MSG_TEMPLATE "{\"Method\": \"upgrading\", \"Payload\":{\"TaskID\":%d}}" + +#define OTA_UPSTREAM_MSG_BUF_LEN (129) +#define OTA_TOPIC_BUF_LEN (129) + +#define OTA_UPSTREAM_TOPIC_TYPE "upstream" +#define OTA_DOWNSTREAM_TOPIC_TYPE "downstream" +#define OTA_TOPIC_TEMPLATE "/$system/%s/%s/ota/%s" + +#ifdef __cplusplus +} +#endif + +#endif //C_SDK_OTA_CONFIG_H_ diff --git a/src/ota/include/ota_internal.h b/src/ota/include/ota_internal.h new file mode 100644 index 0000000..e41ca0d --- /dev/null +++ b/src/ota/include/ota_internal.h @@ -0,0 +1,96 @@ +/* +* Copyright (C) 2012-2019 UCloud. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#ifndef C_SDK_OTA_INTERNAL_H_ +#define C_SDK_OTA_INTERNAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "ota_config.h" +#include "uiot_export_ota.h" +#include "utils_httpc.h" +#include "utils_list.h" + +// OTA Signal Channel +typedef void (*OnOTAMessageCallback)(void *pContext, const char *msg, uint32_t msgLen); + +typedef struct{ + char *payload; // MQTT 消息负载 + size_t payload_len; // MQTT 消息负载长度 +}OTA_UPLOAD_Msg; + +typedef struct { + + const char *url; + http_client_t http; /* http client */ + http_client_data_t http_data; /* http client data */ + +} OTA_Http_Client; + +void *osc_init(const char *product_sn, const char *device_sn, void *channel, OnOTAMessageCallback callback, + void *context); + +/* OSC, OTA signal channel */ +typedef struct { + void *mqtt; + const char *product_sn; + const char *device_sn; + char topic_upgrade[OTA_TOPIC_BUF_LEN]; + OnOTAMessageCallback msg_callback; + List *msg_list; /* recv update msg */ + void *msg_mutex; /* mutex for msg list */ + void *context; +} OTA_MQTT_Struct_t; + +int osc_deinit(void *handle); + +int osc_report_progress(void *handle, const char *msg); + +int osc_upstream_publish(void *handle, const char *msg); + +// OTA Fetch Channel +void *ofc_init(const char *url); + +int32_t ofc_connect(void *handle); + +int32_t ofc_fetch(void *handle, uint32_t size_fetched, char *buf, uint32_t buf_len, size_t range_len, uint32_t timeout_s); + +int ofc_deinit(void *handle); + +// ota_lib +void *ota_lib_md5_init(void); + +void ota_lib_md5_update(void *md5, const char *buf, size_t buf_len); + +void ota_lib_md5_finalize(void *md5, char *output_str); + +void ota_lib_md5_deinit(void *md5); + +int ota_lib_get_msg_type(char *json, char **type); + +int ota_lib_get_params(char *json, char **url, char **module, char **download_name, char **version, char **current_version, char **md5, + char **taskID, uint32_t *fileSize); + +int ota_lib_gen_upstream_msg(char *buf, size_t bufLen, const char *module, const char *version, char *taskID, + IOT_OTA_UpstreamMsgType reportType); + +#ifdef __cplusplus +} +#endif + +#endif //C_SDK_OTA_INTERNAL_H_ diff --git a/src/ota/src/ota_client.c b/src/ota/src/ota_client.c new file mode 100644 index 0000000..ef42e41 --- /dev/null +++ b/src/ota/src/ota_client.c @@ -0,0 +1,700 @@ +/* +* Copyright (C) 2012-2019 UCloud. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include +#include +#include + +#include "uiot_defs.h" +#include "uiot_export_ota.h" +#include "uiot_internal.h" + +#include "ota_config.h" +#include "ota_internal.h" + +#include "utils_timer.h" + +#define HTTP_OTA_BUFF_LEN 1024 + +/* http slice len config HTTP_OTA_BUFF_LEN > HTTP_OTA_RANGE_LEN*/ +#define HTTP_OTA_RANGE_LEN 1000 + +static void print_progress(uint32_t percent) +{ + static unsigned char progress_sign[100 + 1]; + uint8_t i; + + if (percent > 100) + { + percent = 100; + } + + for (i = 0; i < 100; i++) + { + if (i < percent) + { + progress_sign[i] = '='; + } + else if (percent == i) + { + progress_sign[i] = '>'; + } + else + { + progress_sign[i] = ' '; + } + } + + progress_sign[sizeof(progress_sign) - 1] = '\0'; + + LOG_INFO("Download: [%s] %d%%\r\n", progress_sign, percent); +} + +static int send_upstream_msg_with_version(void *handle, const char *module, const char *version, char *taskID, IOT_OTA_UpstreamMsgType reportType) +{ + POINTER_VALID_CHECK(handle, ERR_OTA_INVALID_PARAM); + + int ret; + char *msg_upstream; + OTA_Struct_t *h_ota = (OTA_Struct_t *)handle; + + if (OTA_STATE_UNINITED == h_ota->state) { + LOG_ERROR("handle is uninitialized"); + h_ota->err = ERR_OTA_INVALID_STATE; + return ERR_OTA_INVALID_STATE; + } + + if (NULL == (msg_upstream = HAL_Malloc(OTA_UPSTREAM_MSG_BUF_LEN))) { + LOG_ERROR("allocate for msg_informed failed"); + h_ota->err = ERR_OTA_NO_MEMORY; + return ERR_OTA_NO_MEMORY; + } + + ret = ota_lib_gen_upstream_msg(msg_upstream, OTA_UPSTREAM_MSG_BUF_LEN, module, version, taskID, reportType); + if (SUCCESS_RET != ret) { + LOG_ERROR("generate upstream message failed"); + h_ota->err = ret; + goto do_exit; + } + + ret = osc_upstream_publish(h_ota->ch_signal, msg_upstream); + if (ret < 0) { + LOG_ERROR("Report result failed"); + h_ota->err = ret; + goto do_exit; + } + +do_exit: + if (NULL != msg_upstream) { + HAL_Free(msg_upstream); + } + return ret; +} + +static void _ota_push_upload_msg(void *handle, const char *msg, uint32_t msg_len) +{ + OTA_Struct_t *h_ota = (OTA_Struct_t *) handle; + OTA_MQTT_Struct_t *h_osc = h_ota->ch_signal; + + OTA_UPLOAD_Msg *push_msg = (OTA_UPLOAD_Msg *)HAL_Malloc(sizeof(OTA_UPLOAD_Msg)); + if (NULL == (push_msg->payload = HAL_Malloc(msg_len + 1))) { + LOG_ERROR("HAL_Malloc failed!"); + return; + } + + HAL_Snprintf(push_msg->payload, msg_len + 1, "%s", msg); + push_msg->payload_len = msg_len; + + ListNode *node = list_node_new((void *)push_msg); + if (NULL == node) + { + LOG_ERROR("run list_node_new is error!\n"); + HAL_Free(push_msg->payload); + HAL_Free(push_msg); + return; + } + + HAL_MutexLock(h_osc->msg_mutex); + list_rpush(h_osc->msg_list, node); + HAL_MutexUnlock(h_osc->msg_mutex); + return; +} + +static void _ota_pop_upload_msg(void *handle) +{ + OTA_Struct_t *h_ota = (OTA_Struct_t *) handle; + OTA_MQTT_Struct_t *h_osc = h_ota->ch_signal; + + if(h_osc->msg_list->len > 0) + { + HAL_MutexLock(h_osc->msg_mutex); + ListNode *node = list_lpop(h_osc->msg_list); + HAL_MutexUnlock(h_osc->msg_mutex); + OTA_UPLOAD_Msg *pop_msg = node->val; + h_osc->msg_callback(h_ota, pop_msg->payload, pop_msg->payload_len); + HAL_Free(pop_msg->payload); + HAL_Free(pop_msg); + HAL_Free(node); + return; + } + return; +} + +static void _ota_callback(void *pContext, const char *msg, uint32_t msg_len) +{ + char *msg_method = NULL; + char *msg_str = NULL; + + OTA_Struct_t *h_ota = (OTA_Struct_t *) pContext; + + if (h_ota == NULL || msg == NULL) { + LOG_ERROR("pointer is NULL"); + return; + } + + if (NULL == (msg_str = HAL_Malloc(msg_len + 1))) { + LOG_ERROR("HAL_Malloc failed!"); + return; + } + + HAL_Snprintf(msg_str, msg_len + 1, "%s", msg); + + if (SUCCESS_RET != ota_lib_get_msg_type(msg_str, &msg_method)) { + LOG_ERROR("Get message type failed!"); + goto do_exit; + } + + if (0 == strcmp(msg_method, NOTIFY_METHOD)) + { + /* downloading, push update msg to list */ + if (h_ota->state == OTA_STATE_FETCHING) { + LOG_INFO("In OTA_STATE_FETCHING state"); + _ota_push_upload_msg(h_ota, msg_str, msg_len); + goto do_exit; + } + + if (SUCCESS_RET != ota_lib_get_params(msg_str, &h_ota->url, &h_ota->module, &h_ota->download_name, + &h_ota->version, &h_ota->current_version, &h_ota->md5sum, &h_ota->taskID, &h_ota->size_file)) { + LOG_ERROR("Get firmware parameter failed"); + goto do_exit; + } + + /* received updating msg */ + send_upstream_msg_with_version(h_ota, "", "", h_ota->taskID, OTA_REPORT_START); + + if (NULL == (h_ota->ch_fetch = ofc_init(h_ota->url))) { + LOG_ERROR("Initialize fetch module failed"); + goto do_exit; + } + + if (SUCCESS_RET != ofc_connect(h_ota->ch_fetch)) { + LOG_ERROR("Connect fetch module failed"); + h_ota->state = OTA_STATE_DISCONNECTED; + goto do_exit; + } + + h_ota->state = OTA_STATE_FETCHING; + + if(SUCCESS_RET != IOT_OTA_fw_download(h_ota)) { + LOG_ERROR("download file failed"); + h_ota->state = OTA_STATE_DISCONNECTED; + } + + /* download over, pop first pushed msg to download */ + _ota_pop_upload_msg(h_ota); + + } + +do_exit: + HAL_Free(msg_str); + HAL_Free(msg_method); + return; +} + +void *IOT_OTA_Init(const char *product_sn, const char *device_sn, void *ch_signal) +{ + POINTER_VALID_CHECK(product_sn, NULL); + POINTER_VALID_CHECK(device_sn, NULL); + POINTER_VALID_CHECK(ch_signal, NULL); + + OTA_Struct_t *h_ota = NULL; + + if (NULL == (h_ota = HAL_Malloc(sizeof(OTA_Struct_t)))) { + LOG_ERROR("allocate failed"); + return NULL; + } + memset(h_ota, 0, sizeof(OTA_Struct_t)); + h_ota->state = OTA_STATE_UNINITED; + + h_ota->ch_signal = osc_init(product_sn, device_sn, ch_signal, _ota_callback, h_ota); + if (NULL == h_ota->ch_signal) { + LOG_ERROR("initialize signal channel failed"); + goto do_exit; + } + + h_ota->md5 = ota_lib_md5_init(); + if (NULL == h_ota->md5) { + LOG_ERROR("initialize md5 failed"); + goto do_exit; + } + + h_ota->state = OTA_STATE_INITED; + return h_ota; + +do_exit: + if (NULL != h_ota->ch_signal) { + osc_deinit(h_ota->ch_signal); + } + + if (NULL != h_ota->md5) { + ota_lib_md5_deinit(h_ota->md5); + } + + if (NULL != h_ota) { + HAL_Free(h_ota); + } + + return NULL; +} + + +int IOT_OTA_Destroy(void *handle) +{ + POINTER_VALID_CHECK(handle, ERR_OTA_INVALID_PARAM); + + OTA_Struct_t *h_ota = (OTA_Struct_t*) handle; + + if (OTA_STATE_UNINITED == h_ota->state) { + LOG_ERROR("handle is uninitialized"); + return FAILURE_RET; + } + + osc_deinit(h_ota->ch_signal); + ofc_deinit(h_ota->ch_fetch); + ota_lib_md5_deinit(h_ota->md5); + + if (NULL != h_ota->url) { + HAL_Free(h_ota->url); + } + + if (NULL != h_ota->module) { + HAL_Free(h_ota->module); + } + + if (NULL != h_ota->version) { + HAL_Free(h_ota->version); + } + + if (NULL != h_ota->md5sum) { + HAL_Free(h_ota->md5sum); + } + + if (NULL != h_ota->download_name) { + HAL_Free(h_ota->download_name); + } + + HAL_Free(h_ota); + return SUCCESS_RET; +} + +void IOT_OTA_Clear(void *handle) +{ + OTA_Struct_t *h_ota = (OTA_Struct_t *)handle; + + ofc_deinit(h_ota->ch_fetch); + + if (NULL != h_ota->url) { + memset(h_ota->url, 0, strlen(h_ota->url)); + } + + if (NULL != h_ota->module) { + memset(h_ota->module, 0, strlen(h_ota->module)); + } + + if(NULL != h_ota->download_name){ + memset(h_ota->download_name, 0, strlen(h_ota->download_name)); + } + + if (NULL != h_ota->version) { + memset(h_ota->version, 0, strlen(h_ota->version)); + } + + if (NULL != h_ota->md5sum) { + memset(h_ota->md5sum, 0, strlen(h_ota->md5sum)); + } + + h_ota->size_last_fetched = 0; + h_ota->size_fetched = 0; + h_ota->size_file = 0; + ota_lib_md5_deinit(h_ota->md5); + h_ota->md5 = ota_lib_md5_init(); + h_ota->state = OTA_STATE_INITED; + return; +} + +int IOT_OTA_ReportVersion(void *handle, const char *module, const char *version) +{ + return send_upstream_msg_with_version(handle, module, version, 0, OTA_REPORT_VERSION); +} + + +int IOT_OTA_ReportSuccess(void *handle) +{ + OTA_Struct_t *h_ota = (OTA_Struct_t *)handle; + + if(h_ota->fetch_callback_func != NULL) + { + h_ota->fetch_callback_func(handle, OTA_REPORT_SUCCESS); + } + h_ota->current_version = h_ota->version; + return send_upstream_msg_with_version(handle, "", "", h_ota->taskID, OTA_REPORT_SUCCESS); +} + +int IOT_OTA_ReportUpgrading(void *handle) +{ + OTA_Struct_t *h_ota = (OTA_Struct_t *)handle; + + return send_upstream_msg_with_version(handle, "", "", h_ota->taskID, OTA_REPORT_UPGRADING); +} + +int IOT_OTA_ReportFail(void *handle, IOT_OTA_ReportErrCode err_code) +{ + POINTER_VALID_CHECK(handle, ERR_OTA_INVALID_PARAM); + + int ret; + char *msg_upstream; + OTA_Struct_t *h_ota = (OTA_Struct_t *)handle; + + if (OTA_STATE_UNINITED == h_ota->state) { + LOG_ERROR("handle is uninitialized"); + h_ota->err = ERR_OTA_INVALID_STATE; + return ERR_OTA_INVALID_STATE; + } + + if (NULL == (msg_upstream = HAL_Malloc(OTA_UPSTREAM_MSG_BUF_LEN))) { + LOG_ERROR("allocate for msg_informed failed"); + h_ota->err = ERR_OTA_NO_MEMORY; + return ERR_OTA_NO_MEMORY; + } + + ret = ota_lib_gen_upstream_msg(msg_upstream, OTA_UPSTREAM_MSG_BUF_LEN, "", "", h_ota->taskID, (IOT_OTA_UpstreamMsgType)err_code); + if (SUCCESS_RET != ret) { + LOG_ERROR("generate upstream message failed"); + h_ota->err = ret; + goto do_exit; + } + + ret = osc_upstream_publish(h_ota->ch_signal, msg_upstream); + if (ret < 0) { + LOG_ERROR("Report result failed"); + h_ota->err = ret; + goto do_exit; + } + + if(h_ota->fetch_callback_func != NULL) + h_ota->fetch_callback_func(handle, (IOT_OTA_UpstreamMsgType)err_code); + + do_exit: + if (NULL != msg_upstream) { + HAL_Free(msg_upstream); + } + return ret; +} + + +int IOT_OTA_IsFetching(void *handle) +{ + OTA_Struct_t *h_ota = (OTA_Struct_t *)handle; + + if (NULL == handle) { + LOG_ERROR("handle is NULL"); + return 0; + } + + if (OTA_STATE_UNINITED == h_ota->state) { + LOG_ERROR("handle is uninitialized"); + h_ota->err = ERR_OTA_INVALID_STATE; + return 0; + } + + return (OTA_STATE_FETCHING == h_ota->state); +} + + +int IOT_OTA_IsFetchFinish(void *handle) +{ + OTA_Struct_t *h_ota = (OTA_Struct_t *) handle; + + if (NULL == handle) { + LOG_ERROR("handle is NULL"); + return 0; + } + + if (OTA_STATE_UNINITED == h_ota->state) { + LOG_ERROR("handle is uninitialized"); + h_ota->err = ERR_OTA_INVALID_STATE; + return 0; + } + + return (OTA_STATE_FETCHED == h_ota->state); +} + +int IOT_OTA_Yield(void *handle, uint32_t timeout_ms) +{ + POINTER_VALID_CHECK(handle, FAILURE_RET); + + OTA_Struct_t *h_ota = (OTA_Struct_t*) handle; + + return IOT_MQTT_Yield(((OTA_MQTT_Struct_t *)h_ota->ch_signal)->mqtt, timeout_ms); +} + +int IOT_OTA_FetchYield(void *handle, char *buf, size_t buf_len, size_t range_len, uint32_t timeout_s) +{ + int ret; + OTA_Struct_t *h_ota = (OTA_Struct_t *) handle; + int retry_time = 0; + + POINTER_VALID_CHECK(handle, ERR_OTA_INVALID_PARAM); + POINTER_VALID_CHECK(buf, ERR_OTA_INVALID_PARAM); + NUMERIC_VALID_CHECK(buf_len, ERR_OTA_INVALID_PARAM); + + if (OTA_STATE_FETCHING != h_ota->state) { + h_ota->err = ERR_OTA_INVALID_STATE; + return ERR_OTA_INVALID_STATE; + } + + for(retry_time = 0; retry_time < 5; retry_time++) + { + /* fetch fail,try again utill 5 time */ + ret = ofc_fetch(h_ota->ch_fetch, h_ota->size_fetched ,buf, buf_len, range_len, timeout_s); + /* range download send request too often maybe cutdown by server, need reconnect and continue to download. */ + if((ret == ERR_HTTP_CONN_ERROR) && (h_ota->state != OTA_STATE_DISCONNECTED)) { + ofc_deinit(h_ota->ch_fetch); + h_ota->ch_fetch = ofc_init(h_ota->url); + ofc_connect(h_ota->ch_fetch); + h_ota->state = OTA_STATE_FETCHING; + continue; + } + if (ret < 0) { + LOG_ERROR("Fetch firmware failed"); + h_ota->state = OTA_STATE_FETCHED; + h_ota->err = ret; + + if (ret == ERR_OTA_FETCH_AUTH_FAIL) { // 上报签名过期 + IOT_OTA_ReportFail(h_ota, OTA_ERRCODE_SIGNATURE_EXPIRED); + } else if (ret == ERR_OTA_FILE_NOT_EXIST) { // 上报文件不存在 + IOT_OTA_ReportFail(h_ota, OTA_ERRCODE_FIRMWARE_NOT_EXIST); + } else if (ret == ERR_OTA_FETCH_TIMEOUT) { // 上报下载超时 + IOT_OTA_ReportFail(h_ota, OTA_ERRCODE_DOWNLOAD_TIMEOUT); + } else { + h_ota->err = ERR_OTA_FETCH_FAILED; + } + HAL_SleepMs(1000); + } else{ + break; + } + } + if (ret > 0) { + ota_lib_md5_update(h_ota->md5, buf, ret); + h_ota->size_last_fetched = ret; + h_ota->size_fetched += ret; + } + else + { + return ret; + } + + /* report percent every second. */ + uint32_t percent = (h_ota->size_fetched * 100) / h_ota->size_file; + print_progress(percent); + + if (h_ota->size_fetched >= h_ota->size_file) { + h_ota->state = OTA_STATE_FETCHED; + } + + return ret; +} + + +int IOT_OTA_Ioctl(void *handle, IOT_OTA_CmdType type, void *buf, size_t buf_len) +{ + OTA_Struct_t * h_ota = (OTA_Struct_t *) handle; + + POINTER_VALID_CHECK(handle, ERR_OTA_INVALID_PARAM); + POINTER_VALID_CHECK(buf, ERR_OTA_INVALID_PARAM); + NUMERIC_VALID_CHECK(buf_len, ERR_OTA_INVALID_PARAM); + + if (h_ota->state < OTA_STATE_FETCHING) { + h_ota->err = ERR_OTA_INVALID_STATE; + return ERR_OTA_INVALID_STATE; + } + + switch (type) { + case OTA_IOCTL_FETCHED_SIZE: + if ((4 != buf_len) || (0 != ((unsigned long)buf & 0x3))) { + LOG_ERROR("Invalid parameter"); + h_ota->err = ERR_OTA_INVALID_PARAM; + return FAILURE_RET; + } else { + *((uint32_t *)buf) = h_ota->size_fetched; + return SUCCESS_RET; + } + + case OTA_IOCTL_FILE_SIZE: + if ((4 != buf_len) || (0 != ((unsigned long)buf & 0x3))) { + LOG_ERROR("Invalid parameter"); + h_ota->err = ERR_OTA_INVALID_PARAM; + return FAILURE_RET; + } else { + *((uint32_t *)buf) = h_ota->size_file; + return SUCCESS_RET; + } + + case OTA_IOCTL_VERSION: + strncpy(buf, h_ota->version, buf_len); + ((char *)buf)[buf_len - 1] = '\0'; + break; + + case OTA_IOCTL_MD5SUM: + strncpy(buf, h_ota->md5sum, buf_len); + ((char *)buf)[buf_len - 1] = '\0'; + break; + + case OTA_IOCTL_CHECK_FIRMWARE: + if ((4 != buf_len) || (0 != ((unsigned long)buf & 0x3))) { + LOG_ERROR("Invalid parameter"); + h_ota->err = ERR_OTA_INVALID_PARAM; + return FAILURE_RET; + } else if (h_ota->state != OTA_STATE_FETCHED) { + h_ota->err = ERR_OTA_INVALID_STATE; + LOG_ERROR("Firmware can be checked in OTA_STATE_FETCHED state only"); + return FAILURE_RET; + } else { + char md5_str[33]; + ota_lib_md5_finalize(h_ota->md5, md5_str); + LOG_DEBUG("origin=%s, now=%s", h_ota->md5sum, md5_str); + if (0 == strcmp(h_ota->md5sum, md5_str)) { + *((uint32_t *)buf) = 1; + } else { + *((uint32_t *)buf) = 0; + // 上报MD5不匹配 + h_ota->err = ERR_OTA_MD5_MISMATCH; + IOT_OTA_ReportFail(h_ota, OTA_ERRCODE_MD5_MISMATCH); + } + return SUCCESS_RET; + } + + default: + LOG_ERROR("invalid cmd type"); + h_ota->err = ERR_OTA_INVALID_PARAM; + return FAILURE_RET; + } + + return SUCCESS_RET; +} + + +int IOT_OTA_GetLastError(void *handle) +{ + OTA_Struct_t * h_ota = (OTA_Struct_t *) handle; + + if (NULL == handle) { + LOG_ERROR("handle is NULL"); + return ERR_OTA_INVALID_PARAM; + } + + return h_ota->err; +} + +int IOT_OTA_fw_download(void *handle) +{ + int ret = 0; + int file_size = 0, length, firmware_valid, total_length = 0; + char *buffer_read = NULL; + OTA_Struct_t * h_ota = (OTA_Struct_t *) handle; + void * download_handle = NULL; + + IOT_OTA_Ioctl(h_ota, OTA_IOCTL_FILE_SIZE, &file_size, 4); + + download_handle = HAL_Download_Init(h_ota->download_name); + if(download_handle == NULL) + { + ret = FAILURE_RET; + goto __exit; + } + + buffer_read = (char *)HAL_Malloc(HTTP_OTA_BUFF_LEN); + if (buffer_read == NULL) + { + LOG_ERROR("No memory for http ota!"); + ret = FAILURE_RET; + goto __exit; + } + memset(buffer_read, 0x00, HTTP_OTA_BUFF_LEN); + + IOT_OTA_ReportUpgrading(h_ota); + + LOG_INFO("OTA file size is (%d)", file_size); + do + { + length = IOT_OTA_FetchYield(h_ota, buffer_read, HTTP_OTA_BUFF_LEN, HTTP_OTA_RANGE_LEN, 10); + if (length > 0) + { + /* Write the data to the corresponding partition address */ + if(HAL_Download_Write(download_handle, total_length, buffer_read, length) == FAILURE_RET){ + ret = FAILURE_RET; + goto __exit; + } + + total_length += length; + //wait cancel cmd + IOT_OTA_Yield(handle, 100); + } + else + { + LOG_ERROR("Exit: server return err (%d)!", length); + ret = ERR_OTA_FETCH_FAILED; + goto __exit; + } + } while (!IOT_OTA_IsFetchFinish(h_ota)); + + if (total_length == file_size) + { + ret = SUCCESS_RET; + IOT_OTA_Ioctl(h_ota, OTA_IOCTL_CHECK_FIRMWARE, &firmware_valid, 4); + if (0 == firmware_valid) { + LOG_ERROR("The firmware is invalid"); + ret = IOT_OTA_GetLastError(h_ota); + goto __exit; + } else { + LOG_INFO("The firmware is valid"); + IOT_OTA_ReportSuccess(h_ota); + } + + if(HAL_Download_End(download_handle)) + ret = FAILURE_RET; + + LOG_INFO("Download firmware to flash success."); + } + +__exit: + if (buffer_read != NULL) + HAL_Free(buffer_read); + IOT_OTA_Clear(h_ota); + + return ret; +} + + diff --git a/src/ota/src/ota_fetch.c b/src/ota/src/ota_fetch.c new file mode 100644 index 0000000..39284ff --- /dev/null +++ b/src/ota/src/ota_fetch.c @@ -0,0 +1,119 @@ +/* +* Copyright (C) 2012-2019 UCloud. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include + +#include "uiot_export_ota.h" +#include "uiot_import.h" +#include "ota_internal.h" +#include "ca.h" +#include "utils_httpc.h" + +void *ofc_init(const char *url) +{ + FUNC_ENTRY; + + OTA_Http_Client *h_ofc; + + if (NULL == (h_ofc = HAL_Malloc(sizeof(OTA_Http_Client)))) { + LOG_ERROR("allocate for h_odc failed"); + FUNC_EXIT_RC(NULL); + } + + memset(h_ofc, 0, sizeof(OTA_Http_Client)); + + /* set http request-header parameter */ + h_ofc->http.header = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" \ + "Accept-Encoding: gzip, deflate\r\n"; + + h_ofc->url = url; + + FUNC_EXIT_RC(h_ofc); +} + + +int32_t ofc_connect(void *handle) +{ + FUNC_ENTRY; + + OTA_Http_Client * h_ofc = (OTA_Http_Client *)handle; + +#ifdef SUPPORT_TLS + const char *ca_crt = iot_https_ca_get(); +#else + const char *ca_crt = NULL; +#endif + + int32_t rc = http_client_connect(&h_ofc->http, h_ofc->url, ca_crt); + + FUNC_EXIT_RC(rc); +} + + +int32_t ofc_fetch(void *handle, uint32_t size_fetched, char *buf, uint32_t buf_len, size_t range_len, uint32_t timeout_s) +{ + FUNC_ENTRY; + + int diff; + OTA_Http_Client * h_ofc = (OTA_Http_Client *)handle; + + /* 分片请求 */ + int rc = _http_send_request(&h_ofc->http, h_ofc->url, HTTP_GET, size_fetched, range_len, &h_ofc->http_data, 5000); + if (rc != SUCCESS_RET) { + LOG_ERROR("http_send_request error, rc = %d, size_fetched = %d\r\n", rc, size_fetched); + FUNC_EXIT_RC(rc); + } + +#ifdef SUPPORT_AT_CMD + /* wait at module recv data */ + HAL_SleepMs(5000); +#endif + h_ofc->http_data.response_buf = buf; + h_ofc->http_data.response_buf_len = buf_len; + h_ofc->http_data.response_content_len = 0; + h_ofc->http_data.response_received_len = 0; + diff = h_ofc->http_data.response_content_len - h_ofc->http_data.retrieve_len; + + rc = http_client_recv_data(&h_ofc->http, timeout_s * 1000, &h_ofc->http_data); + if (SUCCESS_RET != rc) { + if (rc == ERR_HTTP_NOT_FOUND) + FUNC_EXIT_RC(ERR_OTA_FILE_NOT_EXIST); + + if (rc == ERR_HTTP_AUTH_ERROR) + FUNC_EXIT_RC(ERR_OTA_FETCH_AUTH_FAIL); + + if (rc == ERR_HTTP_TIMEOUT) + FUNC_EXIT_RC(ERR_OTA_FETCH_TIMEOUT); + + FUNC_EXIT_RC(rc); + } + + FUNC_EXIT_RC(h_ofc->http_data.response_content_len - h_ofc->http_data.retrieve_len - diff); +} + + +int ofc_deinit(void *handle) +{ + FUNC_ENTRY; + + OTA_Http_Client *h_ofc = (OTA_Http_Client *)handle; + + http_client_close(&h_ofc->http); + if (NULL != handle) { + HAL_Free(handle); + } + + FUNC_EXIT_RC(SUCCESS_RET); +} diff --git a/src/ota/src/ota_lib.c b/src/ota/src/ota_lib.c new file mode 100644 index 0000000..82b49c9 --- /dev/null +++ b/src/ota/src/ota_lib.c @@ -0,0 +1,201 @@ +/* +* Copyright (C) 2012-2019 UCloud. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include +#include + +#include "ota_config.h" +#include "ota_internal.h" + +#include "utils_md5.h" +#include "lite-utils.h" + + +void *ota_lib_md5_init(void) { + iot_md5_context *ctx = HAL_Malloc(sizeof(iot_md5_context)); + if (NULL == ctx) { + return NULL; + } + + utils_md5_init(ctx); + utils_md5_starts(ctx); + + return ctx; +} + + +void ota_lib_md5_update(void *md5, const char *buf, size_t buf_len) { + utils_md5_update(md5, (unsigned char *) buf, buf_len); +} + + +void ota_lib_md5_finalize(void *md5, char *output_str) { + utils_md5_finish_hb2hex(md5, output_str); +} + + +void ota_lib_md5_deinit(void *md5) { + if (NULL != md5) { + HAL_Free(md5); + } +} + + +int ota_lib_get_msg_type(char *json, char **type) { + FUNC_ENTRY; + + if (NULL == (*type = LITE_json_value_of(TYPE_FIELD, json))) { + LOG_ERROR("get value of type key failed"); + FUNC_EXIT_RC(ERR_OTA_GENERAL_FAILURE); + } + + FUNC_EXIT_RC(SUCCESS_RET); +} + +int ota_lib_get_params(char *json, char **url, char **module, char **download_name, char **version, char **current_version, char **md5, char **taskID, + uint32_t *fileSize) { + FUNC_ENTRY; + + char *module_str; + char *file_size_str; + char *version_str; + char *current_version_str; + char *url_str; + char *md5_str; + char *taskID_str; + + /* get module */ + if (NULL == (module_str = LITE_json_value_of(MODULE_FIELD, json))) { + LOG_ERROR("get value of module key failed"); + FUNC_EXIT_RC(ERR_OTA_GENERAL_FAILURE); + } + if (NULL != *module) { + HAL_Free(*module); + } + *module = module_str; + + /* get version */ + if (NULL == (version_str = LITE_json_value_of(VERSION_FIELD, json))) { + LOG_ERROR("get value of version key failed"); + FUNC_EXIT_RC(ERR_OTA_GENERAL_FAILURE); + } + if (NULL != *version) { + HAL_Free(*version); + } + *version = version_str; + + /* get current version */ + if (NULL == (current_version_str = LITE_json_value_of(CURRENT_VERSION_FIELD, json))) { + LOG_ERROR("get value of current version key failed"); + FUNC_EXIT_RC(ERR_OTA_GENERAL_FAILURE); + } + if (NULL != *current_version) { + HAL_Free(*current_version); + } + *current_version = current_version_str; + + /* get URL */ + if (NULL == (url_str = LITE_json_value_of(URL_FIELD, json))) { + LOG_ERROR("get value of url key failed"); + FUNC_EXIT_RC(ERR_OTA_GENERAL_FAILURE); + } + if (NULL != *url) { + HAL_Free(*url); + } + *url = url_str; + + *download_name = HAL_Download_Name_Set((void*)url_str); + + /* get md5 */ + if (NULL == (md5_str = LITE_json_value_of(MD5_FIELD, json))) { + LOG_ERROR("get value of md5 failed"); + FUNC_EXIT_RC(ERR_OTA_GENERAL_FAILURE); + } + if (NULL != *md5) { + HAL_Free(*md5); + } + *md5 = md5_str; + + /* get md5 */ + if (NULL == (taskID_str = LITE_json_value_of(TASKID_FILED, json))) { + LOG_ERROR("get value of taskID failed"); + FUNC_EXIT_RC(ERR_OTA_GENERAL_FAILURE); + } + + if (NULL != *taskID) { + HAL_Free(*taskID); + } + *taskID = taskID_str; + + /* get file size */ + if (NULL == (file_size_str = LITE_json_value_of(SIZE_FIELD, json))) { + LOG_ERROR("get value of file size failed"); + FUNC_EXIT_RC(ERR_OTA_GENERAL_FAILURE); + } + + if (SUCCESS_RET != LITE_get_uint32(fileSize, file_size_str)) { + LOG_ERROR("get uint32 failed"); + HAL_Free(file_size_str); + FUNC_EXIT_RC(ERR_OTA_GENERAL_FAILURE); + } + HAL_Free(file_size_str); + FUNC_EXIT_RC(SUCCESS_RET); +} + +int ota_lib_gen_upstream_msg(char *buf, size_t bufLen, const char *module, const char *version, char *taskID, + IOT_OTA_UpstreamMsgType reportType) { + FUNC_ENTRY; + + int ret; + + switch (reportType) { + case OTA_REPORT_DOWNLOAD_TIMEOUT: + case OTA_REPORT_FIRMWARE_NOT_EXIST: + case OTA_REPORT_MD5_MISMATCH: + case OTA_REPORT_SIGNATURE_EXPIRED: + case OTA_REPORT_FIRMWARE_BURN_FAILED: + case OTA_REPORT_UNDEFINED_ERROR: + ret = HAL_Snprintf(buf, bufLen, REPORT_FAIL_MSG_TEMPLATE, reportType); + break; + + case OTA_REPORT_START: + ret = HAL_Snprintf(buf, bufLen, NOTIFY_MSG_TEMPLATE, taskID); + break; + + case OTA_REPORT_UPGRADING: + ret = HAL_Snprintf(buf, bufLen, UPGRADING_MSG_TEMPLATE, taskID); + break; + + case OTA_REPORT_SUCCESS: + ret = HAL_Snprintf(buf, bufLen, REPORT_SUCCESS_MSG_TEMPLATE, taskID); + break; + + case OTA_REPORT_VERSION: + ret = HAL_Snprintf(buf, bufLen, REPORT_VER_TEMPLATE, module, version); + break; + + default: FUNC_EXIT_RC(ERR_OTA_GENERAL_FAILURE); + } + + if (ret < 0) { + LOG_ERROR("HAL_Snprintf failed"); + FUNC_EXIT_RC(ERR_OTA_GENERAL_FAILURE); + } else if (ret >= bufLen) { + LOG_ERROR("msg is too long"); + FUNC_EXIT_RC(ERR_OTA_STR_TOO_LONG); + } + + FUNC_EXIT_RC(SUCCESS_RET); +} diff --git a/src/ota/src/ota_mqtt.c b/src/ota/src/ota_mqtt.c new file mode 100644 index 0000000..479a4ad --- /dev/null +++ b/src/ota/src/ota_mqtt.c @@ -0,0 +1,172 @@ +/* +* Copyright (C) 2012-2019 UCloud. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include + +#include "uiot_defs.h" +#include "uiot_export_mqtt.h" +#include "uiot_export_ota.h" +#include "uiot_import.h" + +#include "ota_config.h" +#include "ota_internal.h" + +static int _ota_mqtt_gen_topic_name(char *buf, size_t buf_len, const char *ota_topic_type, const char *product_sn, + const char *device_sn) +{ + FUNC_ENTRY; + + int ret; + ret = HAL_Snprintf(buf, buf_len, OTA_TOPIC_TEMPLATE, product_sn, device_sn, ota_topic_type); + + if(ret >= buf_len) { + FUNC_EXIT_RC(ERR_OTA_GENERAL_FAILURE); + } + + if (ret < 0) { + LOG_ERROR("HAL_Snprintf failed"); + FUNC_EXIT_RC(ERR_OTA_GENERAL_FAILURE); + } + + FUNC_EXIT_RC(SUCCESS_RET); +} + +static int _ota_mqtt_publish(OTA_MQTT_Struct_t *handle, const char *topic_type, int qos, const char *msg) +{ + FUNC_ENTRY; + + int ret; + char topic_name[OTA_TOPIC_BUF_LEN]; + PublishParams pub_params = DEFAULT_PUB_PARAMS; + + //暂不支持QOS2 + if (0 == qos) { + pub_params.qos = QOS0; + } else { + pub_params.qos = QOS1; + } + pub_params.payload = (void *)msg; + pub_params.payload_len = strlen(msg); + + ret = _ota_mqtt_gen_topic_name(topic_name, OTA_TOPIC_BUF_LEN, topic_type, handle->product_sn, handle->device_sn); + if (ret < 0) { + LOG_ERROR("generate topic name of info failed"); + FUNC_EXIT_RC(ERR_OTA_GENERAL_FAILURE); + } + + ret = IOT_MQTT_Publish(handle->mqtt, topic_name, &pub_params); + if (ret < 0) { + LOG_ERROR("publish to topic: %s failed", topic_name); + FUNC_EXIT_RC(ERR_OTA_OSC_FAILED); + } + + FUNC_EXIT_RC(ret); +} + +static void _ota_mqtt_upgrade_cb(void *pClient, MQTTMessage *message, void *pContext) +{ + FUNC_ENTRY; + + OTA_MQTT_Struct_t *handle = (OTA_MQTT_Struct_t *) pContext; + + LOG_DEBUG("topic=%s", message->topic); + LOG_INFO("len=%u, topic_msg=%.*s", message->payload_len, message->payload_len, (char *)message->payload); + + if (NULL != handle->msg_callback) { + handle->msg_callback(handle->context, message->payload, message->payload_len); + } + + FUNC_EXIT; +} + +void *osc_init(const char *product_sn, const char *device_sn, void *channel, OnOTAMessageCallback callback, + void *context) +{ + int ret; + OTA_MQTT_Struct_t *h_osc = NULL; + + if (NULL == (h_osc = HAL_Malloc(sizeof(OTA_MQTT_Struct_t)))) { + LOG_ERROR("allocate for h_osc failed"); + goto do_exit; + } + + memset(h_osc, 0, sizeof(OTA_MQTT_Struct_t)); + + h_osc->mqtt = channel; + h_osc->product_sn = product_sn; + h_osc->device_sn = device_sn; + h_osc->msg_callback = callback; + h_osc->context = context; + h_osc->msg_list = list_new(); + h_osc->msg_mutex = HAL_MutexCreate(); + if (h_osc->msg_mutex == NULL) + goto do_exit; + + /* subscribe the OTA topic: "/$system/$(product_sn)/$(device_sn)/ota/downstream" */ + ret = _ota_mqtt_gen_topic_name(h_osc->topic_upgrade, OTA_TOPIC_BUF_LEN, OTA_DOWNSTREAM_TOPIC_TYPE, product_sn, + device_sn); + if (ret < 0) { + LOG_ERROR("generate topic name of upgrade failed"); + goto do_exit; + } + + SubscribeParams sub_params = DEFAULT_SUB_PARAMS; + sub_params.on_message_handler = _ota_mqtt_upgrade_cb; + sub_params.qos = QOS1; + sub_params.user_data = h_osc; + + ret = IOT_MQTT_Subscribe(channel, h_osc->topic_upgrade, &sub_params); + if (ret < 0) { + LOG_ERROR("ota mqtt subscribe failed!"); + goto do_exit; + } + + return h_osc; + +do_exit: + if (NULL != h_osc) { + HAL_Free(h_osc); + } + + return NULL; +} + +int osc_deinit(void *handle) +{ + FUNC_ENTRY; + + OTA_MQTT_Struct_t *h_osc = handle; + + list_destroy(h_osc->msg_list); + HAL_MutexDestroy(h_osc->msg_mutex); + + if (NULL != handle) { + HAL_Free(handle); + } + + FUNC_EXIT_RC(SUCCESS_RET); +} + +/* report progress of OTA */ +int osc_report_progress(void *handle, const char *msg) +{ + return _ota_mqtt_publish(handle, OTA_UPSTREAM_TOPIC_TYPE, QOS0, msg); +} + +/* report version of firmware */ +int osc_upstream_publish(void *handle, const char *msg) +{ + return _ota_mqtt_publish(handle, OTA_UPSTREAM_TOPIC_TYPE, QOS1, msg); +} diff --git a/src/sdk-impl/uiot_export_ota.h b/src/sdk-impl/uiot_export_ota.h new file mode 100644 index 0000000..4a4707f --- /dev/null +++ b/src/sdk-impl/uiot_export_ota.h @@ -0,0 +1,232 @@ +/* +* Copyright (C) 2012-2019 UCloud. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#ifndef C_SDK_UIOT_EXPORT_OTA_H_ +#define C_SDK_UIOT_EXPORT_OTA_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "uiot_import.h" + +typedef enum { + + OTA_IOCTL_FETCHED_SIZE, /* 固件已经下载的大小 */ + OTA_IOCTL_FILE_SIZE, /* 固件总大小 */ + OTA_IOCTL_MD5SUM, /* md5(字符串类型) */ + OTA_IOCTL_VERSION, /* 版本号(字符串类型)t */ + OTA_IOCTL_CHECK_FIRMWARE /* 对固件进行校验 */ + +} IOT_OTA_CmdType; + +typedef enum { + + OTA_ERRCODE_FIRMWARE_NOT_EXIST = -1, /* 固件文件不存在(URL无法访问) */ + OTA_ERRCODE_SIGNATURE_EXPIRED = -2, /* URL签名过期 */ + OTA_ERRCODE_DOWNLOAD_TIMEOUT = -3, /* 下载超时 */ + OTA_ERRCODE_MD5_MISMATCH = -4, /* MD5不匹配 */ + OTA_ERRCODE_FIRMWARE_BURN_FAILED = -5, /* 固件烧录失败 */ + OTA_ERRCODE_UNDEFINED_ERROR = -6 /* 未定义错误 */ + +} IOT_OTA_ReportErrCode; + +typedef enum { + + OTA_REPORT_UNDEFINED_ERROR = -6, + OTA_REPORT_FIRMWARE_BURN_FAILED = -5, + OTA_REPORT_MD5_MISMATCH = -4, + OTA_REPORT_DOWNLOAD_TIMEOUT = -3, + OTA_REPORT_SIGNATURE_EXPIRED = -2, + OTA_REPORT_FIRMWARE_NOT_EXIST = -1, + OTA_REPORT_NONE = 0, + OTA_REPORT_START = 1, + OTA_REPORT_UPGRADING = 2, + OTA_REPORT_SUCCESS = 3, + OTA_REPORT_VERSION = 4, + +} IOT_OTA_UpstreamMsgType; + +typedef int (*IOT_OTA_FetchCallback)(void *handle, IOT_OTA_UpstreamMsgType state); + +/* OTA状态 */ +typedef enum { + + OTA_STATE_UNINITED = 0, /* 未初始化 */ + OTA_STATE_INITED, /* 初始化完成 */ + OTA_STATE_FETCHING, /* 正在下载固件 */ + OTA_STATE_FETCHED, /* 固件下载完成 */ + OTA_STATE_DISCONNECTED /* 连接已经断开 */ + +} IOT_OTA_State; + +typedef struct { + uint32_t id; /* message id */ + IOT_OTA_State state; /* OTA state */ + uint32_t size_last_fetched; /* size of last downloaded */ + uint32_t size_fetched; /* size of already downloaded */ + uint32_t size_file; /* size of file */ + + char *taskID; /* point to taskID */ + char *url; /* point to URL */ + char *download_name; /* download partition name */ + char *module; /* download module name */ + char *version; /* point to version */ + char *current_version; /* point to current version */ + char *md5sum; /* MD5 string */ + + void *md5; /* MD5 handle */ + void *ch_signal; /* channel handle of signal exchanged with OTA server */ + void *ch_fetch; /* channel handle of download */ + + int err; /* last error code */ + + Timer report_timer; + IOT_OTA_FetchCallback fetch_callback_func; +} OTA_Struct_t; + +/** + * @brief 初始化OTA模块和返回句柄 + * MQTT客户端必须在调用此接口之前进行初始化 + * + * @param product_sn: 指定产品序列号 + * @param device_sn: 指定设备序列号 + * @param ch_signal: 指定的信号通道. + * + * @retval : 成功则返回句柄,失败返回NULL + */ +void *IOT_OTA_Init(const char *product_sn, const char *device_sn, void *ch_signal); + + +/** + * @brief 释放OTA相关的资源 + * 如果在下载之后没有调用重新启动,则须调用该接口以释放资源 + * + * @param handle: 指定OTA模块 + * + * @retval 0 : 成功 + * @retval < 0 : 失败,返回具体错误码 + */ +int IOT_OTA_Destroy(void *handle); + + +/** + * @brief 向OTA服务器报告固件版本信息。 + * NOTE: 进行OTA前请保证先上报一次本地固件的版本信息,以便服务器获取到设备目前的固件信息 + * + * @param handle: 指定OTA模块 + * @param module: 版本所属类型 + * @param version: 以字符串格式指定固件版本 + * + * @retval > 0 : 对应publish的packet id + * @retval < 0 : 失败,返回具体错误码 + */ +int IOT_OTA_ReportVersion(void *handle, const char *module, const char *version); + +/** + * @brief 向OTA服务器上报升级成功 + * + * @param handle: 指定OTA模块 + * + * @retval > 0 : 对应publish的packet id + * @retval < 0 : 失败,返回具体错误码 + */ +int IOT_OTA_ReportSuccess(void *handle); + +/** + * @brief 向OTA服务器上报失败信息 + * + * @param handle: 指定OTA模块 + * @param err_code: 错误码 + * + * @retval > 0 : 对应publish的packet id + * @retval < 0 : 失败,返回具体错误码 + */ +int IOT_OTA_ReportFail(void *handle, IOT_OTA_ReportErrCode err_code); + +/** + * @brief 检查固件是否已经下载完成 + * + * @param handle: 指定OTA模块 + * + * @retval 1 : Yes. + * @retval 0 : No. + */ +int IOT_OTA_IsFetchFinish(void *handle); + + +/** + * @brief 从具有特定超时值的远程服务器获取固件 + * 注意:如果你想要下载的更快,那么应该给出更大的“buf” + * + * @param handle: 指定OTA模块 + * @param buf: 指定存储固件数据的空间 + * @param buf_len: 用字节指定“buf”的长度 + * @param range_len: 用字节指定分片的长度 + * @param timeout_s: 超时时间 + * + * @retval < 0 : 对应的错误码 + * @retval 0 : 在“timeout_s”超时期间没有任何数据被下载 + * @retval (0, len] : 在“timeout_s”超时时间内以字节的方式下载数据的长度 + */ +int IOT_OTA_FetchYield(void *handle, char *buf, size_t buf_len, size_t range_len, uint32_t timeout_s); + + +/** + * @brief 获取指定的OTA信息 + * 通过这个接口,可以获得诸如状态、文件大小、文件的md5等信息 + * + * @param handle: 指定OTA模块 + * @param type: 指定您想要的信息,请参见详细信息“IOT_OTA_CmdType” + * @param buf: 为数据交换指定缓冲区 + * @param buf_len: 在字节中指定“buf”的长度 + * @return + NOTE: + 1) 如果 type==OTA_IOCTL_FETCHED_SIZE, 'buf' 需要传入 uint32_t 类型指针, 'buf_len' 需指定为 4 + 2) 如果 type==OTA_IOCTL_FILE_SIZE, 'buf' 需要传入 uint32_t 类型指针, 'buf_len' 需指定为 4 + 3) 如果 type==OTA_IOCTL_MD5SUM, 'buf' 需要传入 buffer, 'buf_len' 需指定为 33 + 4) 如果 type==OTA_IOCTL_VERSION, 'buf' 需要传入 buffer, 'buf_len' 需指定为 OTA_VERSION_LEN_MAX + 5) 如果 type==OTA_IOCTL_CHECK_FIRMWARE, 'buf' 需要传入 uint32_t 类型指针, 'buf_len'需指定为 4 + 0, 固件MD5校验不通过, 固件是无效的; 1, 固件是有效的. + * + * @retval 0 : 执行成功 + * @retval < 0 : 执行失败,返回对应的错误码 + */ +int IOT_OTA_Ioctl(void *handle, IOT_OTA_CmdType type, void *buf, size_t buf_len); + + +/** + * @brief 得到最后一个错误代码 + * + * @param handle: 指定OTA模块 + * + * @return 对应错误的错误码. + */ +int IOT_OTA_GetLastError(void *handle); + +/** + * @brief 下载固件,下载结束后重启设备 + * + * @param handle: 指定OTA模块 + * + * @return 对应错误的错误码. + */ +int IOT_OTA_fw_download(void *handle); + +#ifdef __cplusplus +} +#endif + +#endif //C_SDK_UIOT_EXPORT_OTA_H_ diff --git a/src/utils/utils_httpc.c b/src/utils/utils_httpc.c index d1f3215..497bac8 100644 --- a/src/utils/utils_httpc.c +++ b/src/utils/utils_httpc.c @@ -20,6 +20,7 @@ extern "C" { #include #include #include +#include #include "uiot_defs.h" #include "utils_httpc.h" @@ -33,17 +34,20 @@ extern "C" { #define HTTP_CLIENT_READ_BUF_SIZE (1024) /* read payload */ #define HTTP_CLIENT_READ_HEAD_SIZE (32) /* read header */ #define HTTP_CLIENT_SEND_BUF_SIZE (1024) /* send */ -#define HTTP_CLIENT_REQUEST_BUF_SIZE (300) /* send */ -#define HTTP_CLIENT_MAX_URL_LEN (256) +#define HTTP_CLIENT_REQUEST_BUF_SIZE (1024) /* send */ +#define HTTP_CLIENT_MAX_URL_LEN (512) #define HTTP_RETRIEVE_MORE_DATA (1) /**< More data needs to be retrieved. */ #define HTTP_CLIENT_CHUNK_SIZE (1024) -static int _utils_parse_url(const char *url, char *host, char *path) { +static int _utils_parse_url(const char *url, char *host, char *path, int *port) { char *host_ptr = (char *) strstr(url, "://"); uint32_t host_len = 0; uint32_t path_len; + uint32_t port_len; /* char *port_ptr; */ char *path_ptr; char *fragment_ptr; + char port_str[HTTP_CLIENT_MAX_URL_LEN] = {0}; + char *port_ptr; if (host_ptr == NULL) { return ERR_PARAM_INVALID; /* URL is invalid */ @@ -69,6 +73,17 @@ static int _utils_parse_url(const char *url, char *host, char *path) { memcpy(path, path_ptr, path_len); path[path_len] = '\0'; + port_ptr = strchr(host, ':'); + if (NULL == port_ptr) { + return SUCCESS_RET; + }else{ + port_len = port_ptr - host; + memcpy(port_str, port_ptr+1, strlen(port_ptr)-1); + port_str[strlen(port_ptr)-1] = '\0'; + if(port_str != NULL) + *port = atoi(port_str); + host[port_len] = '\0'; + } return SUCCESS_RET; } @@ -355,7 +370,7 @@ static int _http_parse_response_header(http_client_t *client, char *data, int le client_data->response_content_len = atoi(tmp_ptr + strlen("Content-Length: ")); client_data->retrieve_len = client_data->response_content_len; } else { - LOG_ERROR("Could not parse header"); + LOG_ERROR("Could not parse header data:%s",data); return ERR_HTTP_CONN_ERROR; } @@ -405,8 +420,9 @@ int _http_send_request(http_client_t *client, const char *url, HTTP_Request_Meth int rc; char host[HTTP_CLIENT_MAX_URL_LEN] = {0}; char path[HTTP_CLIENT_MAX_URL_LEN] = {0}; + int port; - rc = _utils_parse_url(url, host, path); + rc = _utils_parse_url(url, host, path, &port); if (rc != SUCCESS_RET) { return rc; } @@ -456,17 +472,18 @@ static int _http_client_recv_response(http_client_t *client, uint32_t timeout_ms return ret; } -int http_client_connect(http_client_t *client, const char *url, int port, const char *ca_crt) { +int http_client_connect(http_client_t *client, const char *url, const char *ca_crt) { if (client->net.handle != 0) { LOG_ERROR("http client has connected to host!"); return ERR_HTTP_CONN_ERROR; } int rc; + int port; char host[HTTP_CLIENT_MAX_URL_LEN] = {0}; char path[HTTP_CLIENT_MAX_URL_LEN] = {0}; - rc = _utils_parse_url(url, host, path); + rc = _utils_parse_url(url, host, path, &port); if (rc != SUCCESS_RET) { return rc; } @@ -486,27 +503,6 @@ int http_client_connect(http_client_t *client, const char *url, int port, const return rc; } -int http_client_common(http_client_t *client, const char *url, int port, const char *ca_crt, - HTTP_Request_Method method, http_client_data_t *client_data, uint32_t timeout_ms) { - int rc; - - if (client->net.handle == 0) { - rc = http_client_connect(client, url, port, ca_crt); - if (rc != SUCCESS_RET) { - return rc; - } - } - - rc = _http_send_request(client, url, method, 0, 0, client_data, timeout_ms); - if (rc != SUCCESS_RET) { - LOG_ERROR("http_send_request error, rc = %d", rc); - http_client_close(client); - return rc; - } - - return SUCCESS_RET; -} - int http_client_recv_data(http_client_t *client, uint32_t timeout_ms, http_client_data_t *client_data) { int rc; diff --git a/src/utils/utils_httpc.h b/src/utils/utils_httpc.h index e6b6ee2..e1f0596 100644 --- a/src/utils/utils_httpc.h +++ b/src/utils/utils_httpc.h @@ -57,10 +57,7 @@ typedef struct { } http_client_data_t; -int http_client_connect(http_client_t *client, const char *url, int port, const char *ca_crt); - -int http_client_common(http_client_t *client, const char *url, int port, const char *ca_crt, - HTTP_Request_Method method, http_client_data_t *client_data, uint32_t timeout_ms); +int http_client_connect(http_client_t *client, const char *url, const char *ca_crt); int http_client_recv_data(http_client_t *client, uint32_t timeout_ms, http_client_data_t *client_data); diff --git a/tools/scripts/default_settings.mk b/tools/scripts/default_settings.mk index 5da8b50..7e8e861 100644 --- a/tools/scripts/default_settings.mk +++ b/tools/scripts/default_settings.mk @@ -4,6 +4,7 @@ PLATFORM_OS ?= linux PLATFORM_MODULE ?= esp8266 FEATURE_MQTT_COMM_ENABLED ?= y +FEATURE_OTA_ENABLED ?= n FEATURE_AUTH_MODE_DYNAMIC ?= n FEATURE_SUPPORT_TLS ?= n FEATURE_SUPPORT_AT_CMD ?= n diff --git a/tools/scripts/parse_make_settings.mk b/tools/scripts/parse_make_settings.mk index 7928f0c..5ad982d 100644 --- a/tools/scripts/parse_make_settings.mk +++ b/tools/scripts/parse_make_settings.mk @@ -9,6 +9,7 @@ SETTING_VARS := \ SWITCH_VARS := \ FEATURE_MQTT_COMM_ENABLED \ + FEATURE_OTA_ENABLED \ FEATURE_SUPPORT_TLS \ FEATURE_MQTT_RMDUP_MSG_ENABLED \ FEATURE_AUTH_MODE_DYNAMIC \ diff --git a/tools/scripts/rules-final.mk b/tools/scripts/rules-final.mk index 4fdb508..78b45c6 100644 --- a/tools/scripts/rules-final.mk +++ b/tools/scripts/rules-final.mk @@ -16,6 +16,6 @@ endif $(TOP_Q) \ rm -rf ${TEMP_DIR} -.PHONY: mqtt_sample dynamic_auth_sample +.PHONY: mqtt_sample dynamic_auth_sample ota_sample -final : mqtt_sample dynamic_auth_sample \ No newline at end of file +final : mqtt_sample dynamic_auth_sample ota_sample \ No newline at end of file