/*================================================================ Copyright (c) 2021, Quectel Wireless Solutions Co., Ltd. All rights reserved. Quectel Wireless Solutions Proprietary and Confidential. =================================================================*/ #include "ql_power.h" #include "ql_data_call.h" #include "sockets.h" #include #include "ql_application.h" #include "ql_fota.h" #include "ql_http_client.h" #include "ql_ftp_client.h" #define fota_exam_log(fmt, ...) printf("[FOTA_EXAM][%s, %d] "fmt"\r\n", __FUNCTION__, __LINE__, ##__VA_ARGS__) #define HTTP_FOTA_TEST 1 #define PROFILE_IDX 1 #define FOTA_SSL_EN 0 QL_HTTP_CLIENT_T* fota_httpclient = NULL; void * fota_ftpclient = NULL; int fota_process = 0; qlFotaImgProcCtxPtr ctx = NULL; static qlFotaProgressCB_t fota_progress_cb = NULL; int total_len = 0; #define PRINT_BUF_SIZE 65 static char print_buf[PRINT_BUF_SIZE]; static void print_string(const char * string,int len) { int printed = 0; while (printed != len) { if ((len - printed) > (PRINT_BUF_SIZE - 1)) { memcpy(print_buf, string + printed, (PRINT_BUF_SIZE - 1)); printed += (PRINT_BUF_SIZE - 1); print_buf[PRINT_BUF_SIZE - 1] = '\0'; } else { snprintf(print_buf,len - printed+1, "%s", string + printed); printed = len; } printf("%s", print_buf); } printf("\n"); } static int http_response_cb( QL_HTTP_CLIENT_T *client, QL_HTTP_CLIENT_EVENT_E event, int status_code, char *data, int data_len, void *private_data) { int ret=0,write_ret=0; char para[64]={0}; static int recv_len = 0; switch(event) { case QL_HTTP_CLIENT_EVENT_SEND_FAIL: fota_exam_log("http send failed!\n"); fota_process =-1; break; case QL_HTTP_CLIENT_EVENT_SEND_SUCCESSED: fota_exam_log("http send successed!\n"); break; case QL_HTTP_CLIENT_EVENT_RECV_HEADER_FAIL: fota_exam_log("http parse response header failed!\n"); fota_process = -1; break; case QL_HTTP_CLIENT_EVENT_RECV_HEADER_FINISHED: fota_exam_log("http recv header status_code:%d;header_len:%d!\n",status_code,data_len); print_string(data,data_len); ret=ql_http_client_get_header(data,"Content-Length",0,para,sizeof(para)); total_len = atoi(para); if(ret>0) fota_exam_log("Content-length[]:%d!\n",total_len); if(status_code==200) fota_exam_log("http recv response ok!\n"); ret=1; //��������body���ݣ�����0������������body���� break; case QL_HTTP_CLIENT_EVENT_RECV_BODY: fota_exam_log("http recv body status_code:%d;recv_body_len:%d!\n",status_code,data_len); print_string(data,data_len); recv_len+=data_len; write_ret = ql_fota_image_write(ctx, (void *)data, data_len); fota_exam_log("write_ret:%d",write_ret); if(write_ret) { fota_exam_log("*** fota image write fail ***"); ret =0; break; } if(total_len>0) fota_process = ((recv_len*100) /total_len); if(fota_progress_cb) fota_progress_cb(QL_FOTA_INPROGRESS, fota_process); ret=1; //��������body���ݣ�����0������������body���� break; case QL_HTTP_CLIENT_EVENT_RECV_BODY_FINISHED: #if 1 if(fota_progress_cb) fota_progress_cb(QL_FOTA_SUCCEED, 100); fota_process = 100; ret = ql_fota_image_flush(ctx); if(ret) { fota_exam_log("*** fota image flush fail ***"); } ret = ql_fota_image_verify(ctx); if(ret) { fota_exam_log("*** fota image verify fail ***"); break; } if(fota_progress_cb) fota_progress_cb(QL_FOTA_SETFLAG, 100); #endif fota_exam_log("http recv body finished!\n"); break; case QL_HTTP_CLIENT_EVENT_DISCONNECTED: fota_exam_log("http be closed by server!\n"); fota_process =-1; break; default: break; } return ret; } int ftp_write_data(unsigned char *data,int data_len,void*user_data) { int ret = 0; static int writen_len = 0; ret = ql_fota_image_write(ctx, (void *)data, data_len); fota_exam_log("write_ret:%d",ret); if(ret) { fota_exam_log("*** fota image write fail ***"); if(fota_progress_cb) fota_progress_cb(QL_FOTA_FAIL, -1); fota_process = -1; return -1; } writen_len += data_len; fota_process =(writen_len*100) /total_len; if(writen_len == total_len) { if(fota_progress_cb) fota_progress_cb(QL_FOTA_SUCCEED, 100); ret = ql_fota_image_flush(ctx); if(ret) { fota_exam_log("*** fota image flush fail ***"); } ret = ql_fota_image_verify(ctx); if(ret) { fota_exam_log("*** fota image verify fail ***"); if(fota_progress_cb) fota_progress_cb(QL_FOTA_FAIL, -1); return -1; } if(fota_progress_cb) fota_progress_cb(QL_FOTA_SETFLAG, 100); } else { if(fota_progress_cb) fota_progress_cb(QL_FOTA_INPROGRESS, fota_process); } return 1; } static int parse_ftp_url(char * address, char hostname[],char pathname[],char filename[]) { const char * data = address; char * pos = NULL; // Strip ftp:// or ftps:// prefix if (!memcmp(data, "ftp", 3)) { if (*(data + 3) == 's') { // ftps ? if (!memcmp(data, "ftps://", 7)) { data += 7; } } else if (*(data + 3) == ':') { if (!memcmp(data, "ftp://", 6)) { data += 6; } } } fota_exam_log("Set url raw data:%s", data); // Handle IPv6 address if ((*data == '[')) { // Find end of IPv6 address char *ipv6_end = strstr(data, "]"); if (ipv6_end) { // Extract the IPv6 address WITHOUT the brackets char *path_start = strchr(ipv6_end, '/'); int host_len = path_start - data; strncpy(hostname, data, host_len); fota_exam_log("host ipv6 addr"); data = ipv6_end; if (*(data + 1) == ':') { data = strchr(data + 1, '/'); } else { data = strchr(data + 1, '/'); } if (data) { strcpy(pathname, data); pos = strrchr(pathname, '/'); if (pos && *(pos+1) != '\0') { strcpy(filename, pos+1); *pos = '\0'; // Terminate pathname before the filename } } } else { fota_exam_log("Invalid IPv6 address format"); return -1; } } else { pos = strrchr(data, '/'); if (pos == NULL) { fota_exam_log("ftp url is not match"); return -1; } else { fota_exam_log("pos,%s", pos); strcpy(filename, pos+1); *pos = '\0'; } pos = strchr(data, '/'); if (pos == NULL) { fota_exam_log("ftp url is not match"); return -1; } else { strcpy(pathname, pos); } // Extract hostname memcpy(hostname, data, pos-data); hostname[pos-data] = '\0'; } fota_exam_log("host: %s", hostname); fota_exam_log("filename: %s", filename); fota_exam_log("subpathname: %s", pathname); return 0; } int ql_fota_get_progress(void) { return fota_process; } void ql_fota_ssl_config(SSLConfig * sslCfg) { #if HTTP_FOTA_TEST fota_httpclient = ql_http_client_init(); ql_http_client_setopt(fota_httpclient, QL_HTTP_CLIENT_OPT_SSL_CTX, sslCfg); #else fota_ftpclient = ql_ftp_client_new(); ql_ftp_client_setopt(fota_ftpclient, QL_FTP_CLIENT_OPT_SSL_CTX,sslCfg); #endif return ; } int ql_fota_firmware_download(int profile_idx, qlFotaDwnldMod mode, char * address, char * username, char * password, qlFotaProgressCB_t cb) { int ret=0; ctx = ql_fota_init(); if(!ctx) { fota_exam_log("*** fota init fail ***"); goto exit; } fota_progress_cb = cb; fota_process = 0; if(mode == QL_FOTA_DWNLD_MOD_HTTP) { if(!fota_httpclient) fota_httpclient = ql_http_client_init(); ql_http_client_setopt(fota_httpclient, QL_HTTP_CLIENT_OPT_ASYN, 0); //ͬ��ģʽ ql_http_client_setopt(fota_httpclient, QL_HTTP_CLIENT_OPT_PDP_CID, profile_idx); ql_http_client_setopt(fota_httpclient, QL_HTTP_CLIENT_OPT_PROTOCOL_VER, 1); fota_exam_log("fota_client:%p address:%s",fota_httpclient,address); ql_http_client_request(fota_httpclient,address,QL_HTTP_CLIENT_REQUEST_GET,QL_HTTP_CLIENT_AUTH_TYPE_NONE,username,password,NULL,0,http_response_cb,NULL); if(fota_httpclient) ql_http_client_release(fota_httpclient); } else if(mode ==QL_FOTA_DWNLD_MOD_FTP) { QL_FTP_CLIENT_FLIE_INFO_T file_list[1]={0}; char hostname[64] = {0}; char pathname[256] = {0}; char filename[256] = {0}; char buf[256] = {0}; if(!fota_ftpclient) fota_ftpclient = ql_ftp_client_new(); if(strstr(address, "://[") != NULL) { ql_ftp_client_setopt(fota_ftpclient, QL_FTP_CLIENT_OPT_DAT_ADDR_TYPE, 0); } parse_ftp_url(address, hostname,pathname,filename); ret=ql_ftp_client_setopt(fota_ftpclient, QL_FTP_CLIENT_OPT_PDP_CID, profile_idx); ret=ql_ftp_client_open(fota_ftpclient,hostname,username,password); printf("\nql_ftp_client_open[%s] ret=%d;\n",hostname,ret); if(ret==-1) { printf("ql_ftp_client_open error_code=%d\n",ql_ftp_client_get_error_code(fota_ftpclient)); } ret=ql_ftp_client_pwd(fota_ftpclient, buf, sizeof(buf)); printf("\nql_ftp_client_pwd ret=%d; remotedir:%s\n",ret,buf); ret=ql_ftp_client_list(fota_ftpclient, pathname,filename,file_list, 1); printf("\nql_ftp_client_list[%s] ret=%d file_list[0].size:%d\n",filename,ret,file_list[0].size); total_len = file_list[0].size; ret=ql_ftp_client_cwd(fota_ftpclient, pathname); printf("\nql_ftp_client_cwd[%s] ret=%d\n",pathname, ret); ret= ql_ftp_client_get(fota_ftpclient,filename,NULL,ftp_write_data,NULL); printf("\nql_ftp_client_get ret:%d",ret); ql_rtos_task_sleep_s(2); ql_ftp_client_release(fota_ftpclient); } exit: if(ctx) ql_fota_deinit(ctx); } #if FOTA_SSL_EN #define FOTA_SERVER_ADDRESS "https://220.180.239.212:8059/Jacinda/dfota.bin" const static SSLCertPathPtr rootCA_path = "\ -----BEGIN CERTIFICATE-----\n\ MIIE5zCCA0+gAwIBAgIJANUsoTOuGtIeMA0GCSqGSIb3DQEBCwUAMIGJMQswCQYD\n\ VQQGEwJDTjELMAkGA1UECAwCQUgxCzAJBgNVBAcMAkhGMRAwDgYDVQQKDAdRVUVD\n\ VEVMMQwwCgYDVQQLDANEVFcxGDAWBgNVBAMMDzIyMC4xODAuMjM5LjIxMjEmMCQG\n\ CSqGSIb3DQEJARYXZWRkaWUuemhhbmdAcXVlY3RlbC5jb20wHhcNMjAwMTAyMTEy\n\ NTQzWhcNMjMwMTAxMTEyNTQzWjCBiTELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkFI\n\ MQswCQYDVQQHDAJIRjEQMA4GA1UECgwHUVVFQ1RFTDEMMAoGA1UECwwDRFRXMRgw\n\ FgYDVQQDDA8yMjAuMTgwLjIzOS4yMTIxJjAkBgkqhkiG9w0BCQEWF2VkZGllLnpo\n\ YW5nQHF1ZWN0ZWwuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA\n\ tDYkNFB5TYS7XZ2F+eW6zGWOmy2xV3OcTpnz10JHUwSdvUI5xWQ//ACCxG9QeN4f\n\ ALAzkj8tVzeKj5oP9Gz8LnVqhIxv13m8GJDO/dVZEXtpBNgjv7DvsXa1TMjy3QcV\n\ bXfqtWuTSzVCovMkvj+R16uWRWgqSMLCG73FLPE2E2vRGlBQVzAVi2fXYPq346QT\n\ ZLyELoFErGCFgVEaAF1Pnwu1R8Ly0CI45y/riC47Sb3bE/MaBUbR0R7+T5jhnK8G\n\ E0GTLorOZq6CBf8NliRjnEQVtjhUniQju1Dx/35Tifel460kK9ThkmxEddn1w+VH\n\ ZQYG9XWuqdPA6am5rBI+AKUCPeoMRVVlY3lK3J7L4OyRTovY0+IyVT4MKucrzK4z\n\ FfnACTgKr151yMxY3Ar4EPI1iQeuKu5Ha2bZuXLnVZau8NohbdUXBSN+ifPH2uu7\n\ BT91jG6zfS9k6AKhBkGncf0dpuT9c6w06UAplxeakSpnSz5p7O9XGeAZeemahq43\n\ AgMBAAGjUDBOMB0GA1UdDgQWBBSHgJS+a77WT6PHLTVoW+qqVK80uTAfBgNVHSME\n\ GDAWgBSHgJS+a77WT6PHLTVoW+qqVK80uTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3\n\ DQEBCwUAA4IBgQA6WSdvLq+MMmNJAAI8XCANzWq69fxcrvRhLl69hipueomivrFx\n\ cqiG6oI38CBxqiPC9y69fXBY9B7f29emMHnjpC5xoOqfBSVKqR+EbhYUx/n0/Ykx\n\ Pcz6Lad0/8JRLa0QthhnLzMPpeAY7Z3HmcHR0mZFu+vu0irw/3E1GkDm2MQ2958Y\n\ 5geLGbktFKF2yGueajcoR8qSmeD+tspBqTWqmdPaDEipPNBvLduOzcGqOnQMKy4/\n\ LLQMZ/OuXTjuJjYE4YtN2wV3/ks02Ae94FE8ZaaMYThMHBz4NITKbLhbMn9cP66w\n\ 27B+nt5+DG4zc9AYZRuKhaW6AiMfAewAvFxIyofmPxAKMpC+JBwzDxdbo0ez/Dqr\n\ CFbKuQeWAY3YwgG33pdzOuREMphdGZnJgkd1MqUCNFxMGnZF6WhV31I/e1+uCMYr\n\ 4Rw86GtDfs697qxBOXHmNBVqLLekayQxGh7d2UxIVghdCuO6rHjTfmkfvzNH/Vxf\n\ t/YY6UlQvij187Q=\n\ -----END CERTIFICATE-----\n\ "; #else #define FOTA_SERVER_ADDRESS "http://220.180.239.212:8300/fawei/fbf_dfota.bin" #define FOTA_FTP_SERVER_ADDRESS "ftp://[2401:2d00:c3e7:2::4]:3180/fawei/fbf_dfota.bin" #endif #define FOTA_SERVER_USERNAME NULL #define FOTA_SERVER_PASSWD NULL #define FOTA_FTP_SERVER_USERNAME "test" #define FOTA_FTP_SERVER_PASSWD "test" static void qlFotaProgressCB(qlFotaSta sta, int progress) { if(sta == QL_FOTA_INPROGRESS) { fota_exam_log("fota test downloading (%d)%d ...", sta, progress); } else if(sta == QL_FOTA_SUCCEED) { fota_exam_log("fota test downloading (%d)%d ...", sta, progress); } else if(sta == QL_FOTA_SETFLAG) { fota_exam_log("fota test update flag setted, will restart to update ..."); //ql_power_reset(); } else if(sta == QL_FOTA_FAIL) { fota_exam_log("fota test download failed (%d)%d", sta, progress); fota_exam_log("========== fota test end =========="); } } static void ql_nw_status_callback(int profile_idx, int nw_status) { fota_exam_log("profile(%d) status: %d", profile_idx, nw_status); } static int datacall_satrt(void) { struct ql_data_call_info info = {0}; char ip4_addr_str[16] = {0}; char ip6_addr_str[40] = {0}; fota_exam_log("wait for network register done"); if(ql_network_register_wait(120) != 0) { fota_exam_log("*** network register fail ***"); return -1; } else { fota_exam_log("doing network activating ..."); ql_wan_start(ql_nw_status_callback); ql_set_auto_connect(PROFILE_IDX, TRUE); // Setting IP Version to IPv6 if(ql_start_data_call(PROFILE_IDX, 1, "celcom4g", NULL, NULL, 0) == 0) { struct ql_data_call_info info = {0}; memset(&info, 0, sizeof(info)); // Fetching datacall info for IPv6 // if IPv4 set, change data call info to IPv4 ql_get_data_call_info(1, 2, &info); fota_exam_log("info.profile_idx: %d", info.profile_idx); fota_exam_log("info.ip_version: %d", info.ip_version); fota_exam_log("info.v6.state: %d", info.v6.state); fota_exam_log("info.v6.reconnect: %d", info.v6.reconnect); inet_ntop(AF_INET6, &info.v6.addr.ip, ip6_addr_str, sizeof(ip6_addr_str)); fota_exam_log("info.v6.addr.ip: %s", ip6_addr_str); inet_ntop(AF_INET6, &info.v6.addr.pri_dns, ip6_addr_str, sizeof(ip6_addr_str)); fota_exam_log("info.v6.addr.pri_dns: %s", ip6_addr_str); inet_ntop(AF_INET6, &info.v6.addr.sec_dns, ip6_addr_str, sizeof(ip6_addr_str)); fota_exam_log("info.v6.addr.sec_dns: %s", ip6_addr_str); return 0; } fota_exam_log("*** network activated fail ***"); return -1; } } static void httpfotaTest(void *argv) { (void)argv; fota_exam_log("========== fota test satrt22222222222222222222 =========="); fota_exam_log("========== fota test satrt =========="); if(datacall_satrt() == 0) { #if FOTA_SSL_EN SSLConfig sslConfig = { .en = 1, .profileIdx = PROFILE_IDX, .serverName = "220.180.239.212", .serverPort = 8059, .protocol = 0, .dbgLevel = 0, .sessionReuseEn = 0, .vsn = SSL_VSN_ALL, .verify = SSL_VERIFY_MODE_OPTIONAL, .cert.from = SSL_CERT_FROM_BUF, .cert.path.rootCA = rootCA_path, .cert.path.clientKey = NULL, .cert.path.clientCert = NULL, .cert.clientKeyPwd.data = NULL, .cert.clientKeyPwd.len = 0, .cipherList = "ALL", .CTRDRBGSeed.data = NULL, .CTRDRBGSeed.len = 0 }; ql_fota_ssl_config(&sslConfig); #endif fota_exam_log("ql_fota_firmware_download enter"); #if HTTP_FOTA_TEST ql_fota_firmware_download(PROFILE_IDX, QL_FOTA_DWNLD_MOD_HTTP, FOTA_SERVER_ADDRESS, FOTA_SERVER_USERNAME, FOTA_SERVER_PASSWD, qlFotaProgressCB); #else ql_fota_firmware_download(PROFILE_IDX, QL_FOTA_DWNLD_MOD_FTP, FOTA_FTP_SERVER_ADDRESS, FOTA_FTP_SERVER_USERNAME, FOTA_FTP_SERVER_PASSWD, qlFotaProgressCB); #endif } else fota_exam_log("========== fota test end =========="); } //application_init(httpfotaTest, "httpfotaTest", 10, 2);