/*---------------------------------------------------------------------------- * Copyright (c) Fenda Technologies Co., Ltd. 2022. All rights reserved. * * Description: ble_protocol_file_upload.c * * Author: saimen * * Create: 2024-12-03 *--------------------------------------------------------------------------*/ #include "ble_port_protocol.h" #include "ble_api.h" #include "ble_port.h" #include "bts_br_gap.h" #include "bts_gatt_server.h" #include "bts_le_gap.h" #include "ctype.h" #include "osal_addr.h" #include "rtc_api.h" #include "securec.h" #include "sql_all.h" #include "stdlib.h" #include "string.h" #include "sys_config.h" #include "sys_typedef.h" #include "time.h" // #include "TjdUiMessagePopUpPage.h" #include "fcntl.h" #include "fs_api_ext.h" #include "fs_user_common.h" #include "linux/crc32.h" #include "los_ringbuf.h" #include "sys/statfs.h" #include "unistd.h" #include #include "power_display_service.h" #include "service_bt.h" #include "thread_init.h" // #include "semaphore.h" #include "upg_porting.h" #include "osal_task.h" #include "osal_semaphore.h" #include "soc_osal.h" #include #include "service_lucky_clover.h" #include "broadcast_feature.h" #include "service_charger.h" #include "service_sleep.h" #include "ble_protocol_file_download.h" #ifdef __cplusplus extern "C" { #endif #include "TjdUiAppCameraToC.h" // #include "TjdUiWatchFaceCtrl.h" #include "service_gps.h" #ifdef __cplusplus } #endif #define ENABLE_STATIC_PRINT 0 #define static_print_error(...) sys_bt_log_e(__VA_ARGS__) //错误信息打印一般常开 #define static_print_warn(...) sys_bt_log_w(__VA_ARGS__) //警告信息打印一般常开 #if ENABLE_STATIC_PRINT #define static_print_debug(...) sys_bt_log_d(__VA_ARGS__) #define static_print_info(...) sys_bt_log_i(__VA_ARGS__) #else #define static_print_debug(...) #define static_print_info(...) #endif #define BLE_FILE_TRANSFER_TIME_OUT (15) #define TJD_FS_DIR_GPS_BAT TJD_FS_DIR_GPS"bat" #define FS_VOLUME_USER "/user/" #define FS_VOLUME_UPDATE "/update/" #define FS_NODE_INFO_SPACE 16 FileDescriptionInfo_t g_file_description = {0}; FileTransferInfo_t g_files_transfer = {0}; DirDescriptionInfo_t g_dir_description = {0}; typedef void (*file_transfer_callback_t)(FileDescriptionInfo_t *p_description, FileTransferInfo_t *p_transfer); typedef struct { file_transfer_callback_t start; file_transfer_callback_t end; file_transfer_callback_t data; } FileTransferCallback_t; FileTransferCallback_t g_files_transfer_Callback[FileType_MAX]; UpgradeContext g_file_decription_context; //文件下载中回调集中处理 extern void file_download_callback_start_type_fiemware(FileDescriptionInfo_t *p_description, FileTransferInfo_t *p_transfer); extern void file_download_callback_end_type_fiemware(FileDescriptionInfo_t *p_description, FileTransferInfo_t *p_transfer); extern void file_download_callback_data_type_fiemware(FileDescriptionInfo_t *p_description, FileTransferInfo_t *p_transfer); extern void file_download_callback_start_type_dial_original(FileDescriptionInfo_t *p_description, FileTransferInfo_t *p_transfer); extern void file_download_callback_end_type_dial_original(FileDescriptionInfo_t *p_description, FileTransferInfo_t *p_transfer); extern void file_download_callback_start_type_dial_doc(FileDescriptionInfo_t *p_description, FileTransferInfo_t *p_transfer); extern void file_download_callback_end_type_dial_doc(FileDescriptionInfo_t *p_description, FileTransferInfo_t *p_transfer); extern void file_download_callback_end_type_dial_custom(FileDescriptionInfo_t *p_description, FileTransferInfo_t *p_transfer); extern void file_download_callback_end_type_gps(FileDescriptionInfo_t *p_description, FileTransferInfo_t *p_transfer); extern void file_download_callback_end_type_js(FileDescriptionInfo_t *p_description, FileTransferInfo_t *p_transfer); void tjd_ble_protocol_file_download_callback_init(void) { static_print_debug("tjd_ble_protocol_file_download_callback_init():...........\n"); memset_s(g_files_transfer_Callback, sizeof(g_files_transfer_Callback), 0, sizeof(g_files_transfer_Callback)); g_files_transfer_Callback[TYPE_FIRMWARE].start = file_download_callback_start_type_fiemware; g_files_transfer_Callback[TYPE_FIRMWARE].end = file_download_callback_end_type_fiemware; g_files_transfer_Callback[TYPE_FIRMWARE].data = file_download_callback_data_type_fiemware; g_files_transfer_Callback[TYPE_AIDIAL_ORIGINAL].start = file_download_callback_start_type_dial_original; g_files_transfer_Callback[TYPE_AIDIAL_ORIGINAL].end = file_download_callback_end_type_dial_original; g_files_transfer_Callback[TYPE_DIAL_DOC].start = file_download_callback_start_type_dial_doc; g_files_transfer_Callback[TYPE_DIAL_DOC].end = file_download_callback_end_type_dial_doc; g_files_transfer_Callback[TYPE_DIAL_DIAGRAM].end = file_download_callback_end_type_dial_custom; g_files_transfer_Callback[TYPE_DIAL_MULTIGRAPH].end = file_download_callback_end_type_dial_custom; g_files_transfer_Callback[TYPE_DIAL_VIDEO].end = file_download_callback_end_type_dial_custom; g_files_transfer_Callback[TYPE_GPS].end = file_download_callback_end_type_gps; g_files_transfer_Callback[TYPE_JS_APP].end = file_download_callback_end_type_js; } void tjd_file_transfer_err_operation(void) { switch(g_file_description.file_type){ case TYPE_E_BOOK: static_print_error("TYPE_E_BOOK file transfer error"); tjd_fs_api_path_remove((char *)g_file_description.file_name); break; case TYPE_JS_APP: static_print_error("TYPE_JS_APP file transfer error"); tjd_fs_api_path_remove((char *)g_file_description.file_name); break; default: static_print_error("file transfer error"); tjd_fs_api_path_remove((char *)g_file_description.file_name); break; } } int tjd_service_timer_cnt = 0; //文件下载通信超时检测 signed int tjd_task_service_timer_check_timeout(void *param) { unsigned long *p_param = (unsigned long *)param; uint64_t temp_time = 0; tjd_driver_rtc_get_ops()->get_timestamp(&temp_time); static_print_debug("tjd_task_service_timer_check_timeout(): param=%ld\r\n", *p_param); static_print_debug("tjd_task_service_timer_check_timeout(): temp_time=%ld\r\n", temp_time); static_print_debug("tjd_task_service_timer_check_timeout(): g_files_transfer.file_status=%d\r\n", g_files_transfer.file_status); if(temp_time - *p_param > BLE_FILE_TRANSFER_TIME_OUT) { if(g_files_transfer.file_status != FileTransfer_NULL) { tjd_ble_file_breakpoint_save(); g_files_transfer.file_status = FileTransfer_Timeout; if(g_files_transfer_Callback[g_file_description.file_type].end) { g_files_transfer_Callback[g_file_description.file_type].end(&g_file_description, &g_files_transfer); } } return 0; //自动删除超时检测 } else if(((temp_time - *p_param)%5==4) && g_files_transfer.file_status != FileTransfer_NULL) { static_print_debug("tjd_task_service_timer_check_timeout(): repeat =%ld\r\n", *p_param); tjd_service_timer_cnt ++; static_print_debug("tjd_task_service_timer_check_timeout(): tjd_service_timer_cnt =%d\r\n", tjd_service_timer_cnt); if(tjd_service_timer_cnt > 3) { tjd_service_timer_cnt = 0; ble_api_cmd_info_reset(); tjd_file_transfer_err_operation(); uint8_t resp_data[9] = {0}; resp_data[0] = 0x00; resp_data[1] = g_files_transfer.operation_file_size >> 24; resp_data[2] = (g_files_transfer.operation_file_size >> 16) & 0xFF; resp_data[3] = (g_files_transfer.operation_file_size >> 8) & 0xFF; resp_data[4] = g_files_transfer.operation_file_size & 0xFF; resp_data[5] = g_files_transfer.operation_file_size >> 24; resp_data[6] = (g_files_transfer.operation_file_size >> 16) & 0xFF; resp_data[7] = (g_files_transfer.operation_file_size >> 8) & 0xFF; resp_data[8] = g_files_transfer.operation_file_size & 0xFF; tjd_ble_protocol_send_data(PROTOCOL_FRAME_RSP_HEAD_2, resp_data, sizeof(resp_data), DOWNLOAD_FILE_DATA, gatt_server_id, g_server_conn_id); }else{ ble_api_cmd_info_reset(); uint8_t resp_data[9] = {0}; resp_data[0] = 0x01; resp_data[1] = g_files_transfer.operation_file_size >> 24; resp_data[2] = (g_files_transfer.operation_file_size >> 16) & 0xFF; resp_data[3] = (g_files_transfer.operation_file_size >> 8) & 0xFF; resp_data[4] = g_files_transfer.operation_file_size & 0xFF; resp_data[5] = g_files_transfer.operation_file_size >> 24; resp_data[6] = (g_files_transfer.operation_file_size >> 16) & 0xFF; resp_data[7] = (g_files_transfer.operation_file_size >> 8) & 0xFF; resp_data[8] = g_files_transfer.operation_file_size & 0xFF; tjd_ble_protocol_send_data(PROTOCOL_FRAME_RSP_HEAD_2, resp_data, sizeof(resp_data), DOWNLOAD_FILE_DATA, gatt_server_id, g_server_conn_id); } } return 1000; //定时执行间隔单位为ms } void tjd_task_service_check_timeout_start(void) { queue_default_info_t msg_data = { tjd_task_service_timer_check_timeout, &g_files_transfer.operation_last_time, 1000, NULL };//1000ms首次执行的等待时间。 osal_msg_queue_write_copy(tjd_task_service_timer_get_queue_id(), (void *)&msg_data, sizeof(queue_default_info_t), 0); } //基础接口:开始 uint32_t tjd_ble_get_file_download_current_offset(void) { if(g_files_transfer.file_status != FileTransfer_NULL) { return g_files_transfer.operation_file_size; } else { return 0; } } void tjd_ble_protocol_dir_transfer_enter_handle(uint8_t file_total_type, uint8_t file_total_number, uint32_t file_total_size) { static_print_debug("tjd_ble_protocol_dir_transfer_enter_handle():%d-%d-%d\n", file_total_type, file_total_number, file_total_size); g_dir_description.file_total_type = file_total_type; g_dir_description.file_total_number = file_total_number; g_dir_description.file_total_size = file_total_size; memset_s(&g_files_transfer, sizeof(g_files_transfer), 0, sizeof(g_files_transfer)); } void tjd_ble_protocol_dir_transfer_exit_handle(void) { static_print_debug("tjd_ble_protocol_dir_transfer_exit_handle()\n"); g_dir_description.file_total_type = FileType_MAX; g_dir_description.file_total_number = 0; g_dir_description.file_total_size = 0; memset_s(&g_files_transfer, sizeof(g_files_transfer), 0, sizeof(g_files_transfer)); } void tjd_ble_protocol_file_download_transfer_reset(void) { static_print_debug("tjd_ble_protocol_file_download_transfer_reset()\n"); memset_s(&g_file_description, sizeof(g_file_description), 0, sizeof(g_file_description)); memset_s(&g_files_transfer, sizeof(g_files_transfer), 0, sizeof(g_files_transfer)); g_dir_description.file_total_type = FileType_MAX; g_file_description.file_type = FileType_MAX; } //断点保存 void tjd_ble_file_breakpoint_save(void) { static_print_debug("tjd_ble_file_breakpoint_save()"); if(g_files_transfer.file_status != FileTransfer_NULL && g_files_transfer.operation_file_size != 0 && g_file_description.file_crc32!= 0 && g_file_description.file_name!= NULL){ if(g_files_transfer.breakpoint_en == true || g_file_description.file_size >= 1024*1000) { //使用断点功能 sql_bt_set_breakpoint_info(g_files_transfer.operation_file_size, g_file_description.file_crc32, (char *)g_file_description.file_name); static_print_debug("sql set breakpoint info, g_files_transfer.operation_file_size : %d, g_file_description.file_crc32 : %x, g_file_description.file_name : %s", g_files_transfer.operation_file_size, g_file_description.file_crc32, g_file_description.file_name); } } } //断点恢复 bool tjd_ble_file_breakpoint_recover(FileDescriptionInfo_t *p_description, FileTransferInfo_t *p_transfer) { breakpoint_info_t breakpoint_info = {0}; if(!sql_bt_get_breakpoint_info(&breakpoint_info.offset_byte, &breakpoint_info.crc_32,(char *)breakpoint_info.file_name)){ return false; } if(strcmp(breakpoint_info.file_name, (const char *)p_description->file_name) != 0){ return false; } struct stat file_stat ={0}; if(stat((char *)p_description->file_name, &file_stat) != 0) { return false; } if(file_stat.st_size != breakpoint_info.offset_byte) { return false; } if(p_description->file_crc32 != breakpoint_info.crc_32) { return false; } static_print_debug("breakpoint info offset_byte : %d, crc_32 : %x, file_name : %s",breakpoint_info.offset_byte, breakpoint_info.crc_32,breakpoint_info.file_name); p_transfer->operation_file_size = breakpoint_info.offset_byte; if(p_description->file_type != FileType_MAX && p_description->file_type == g_dir_description.file_total_type) { p_transfer->operation_total_size += p_transfer->operation_file_size; } return true; } DirDescriptionInfo_t *tjd_ble_get_dir_description(void) { return &g_dir_description; } FileTransferInfo_t *tjd_ble_get_transfer_info(void) { return &g_files_transfer; } //protocol 相关接口 start void tjd_file_pre_operation(void) { struct stat file_stat ={0}; int ret = stat((char *)g_file_description.file_name, &file_stat); if (ret == 0) { static_print_debug("file exist"); // 文件存在,删除文件 // ret = tjd_remove_rsvd_part((char *)base_dir, (char *)g_file_description.file_name); ret = unlink((char *)g_file_description.file_name); if (ret < 0) { static_print_error("remove file fail"); } static_print_debug("remove file success"); } } void tjd_ble_protocol_file_description(uint8_t server_id, uint16_t conn_id, uint8_t *write_cb_para, uint16_t len, uint8_t cmd_id) { // TODO: app下发文件描述:DOWNLOAD_FILE_DESCRIPTION static_print_debug("tjd_ble_protocol_file_description():%d", g_files_transfer.file_status); uint16_t valid_data_len = len - 9; uint8_t data_index = 4; uint16_t file_description_len = (write_cb_para[data_index] << 8) | write_cb_para[data_index + 1]; uint16_t rec_file_description_offset = (write_cb_para[data_index + 2] << 8) | write_cb_para[data_index + 3]; valid_data_len = (valid_data_len > file_description_len)? file_description_len : valid_data_len; uint64_t temp_time = 0; tjd_driver_rtc_get_ops()->get_timestamp(&temp_time); if(temp_time - g_files_transfer.operation_last_time > BLE_FILE_TRANSFER_TIME_OUT || g_files_transfer.file_status == FileTransfer_Timeout) { g_files_transfer.file_status = FileTransfer_NULL; } g_files_transfer.operation_last_time = temp_time; if (g_files_transfer.file_status == FileTransfer_NULL) { static_print_debug("%s , line is %d", __FUNCTION__, __LINE__); memset_s(&g_file_decription_context, sizeof(g_file_decription_context), 0, sizeof(g_file_decription_context)); g_file_decription_context.buffer_offset = 0; g_file_decription_context.expected_seq = file_description_len; if(g_file_decription_context.buffer == NULL) { g_file_decription_context.buffer = (uint8_t *)malloc(file_description_len); } memset_s(g_file_decription_context.buffer, file_description_len, 0, file_description_len); g_files_transfer.file_status = FileTransfer_Description; } if (g_file_decription_context.buffer_offset != rec_file_description_offset) { static_print_error("g_file_decription_context.buffer_offset != rec_file_description_offset"); uint8_t data[3] = {0}; data[0] = 0x00; data[1] = (g_file_decription_context.buffer_offset >> 8) & 0xFF; data[2] = g_file_decription_context.buffer_offset & 0xFF; tjd_ble_protocol_send_data(PROTOCOL_FRAME_RSP_HEAD_2, data, sizeof(data), cmd_id, server_id, conn_id); free(g_file_decription_context.buffer); g_file_decription_context.buffer = NULL; g_file_decription_context.buffer_offset = 0; g_file_decription_context.expected_seq = 0; return; } static_print_debug("file description len : %d, rec_file_description_offset : %d", file_description_len, rec_file_description_offset); memcpy_s(g_file_decription_context.buffer + rec_file_description_offset, file_description_len - rec_file_description_offset, &write_cb_para[data_index + 4], valid_data_len); g_file_decription_context.buffer_offset = rec_file_description_offset + valid_data_len; if (g_files_transfer.file_status == FileTransfer_Description && g_file_decription_context.buffer_offset == file_description_len) { //文件描述,文件传输信息重置 g_files_transfer.file_status = FileTransfer_DescriptionDone; memset_s(&g_file_description, sizeof(g_file_description), 0, sizeof(g_file_description)); g_files_transfer.operation_file_size = 0; // 表盘LTV文件描述如 0301040602123456780603F1F1F1F10804313233343536 // 0301040602123456780603F1F1F1F10804313233343536 for (int i = 0; i < file_description_len;) { // 遍历文件描述 uint8_t len = g_file_decription_context.buffer[i]; uint8_t type = g_file_decription_context.buffer[i + 1]; // static_print_debug("i : %u, type : %u, len : %u", i, type, len); switch (type) { case 0x01: { // 文件类型 memcpy_s(&g_file_description.file_type, sizeof(g_file_description.file_type), &g_file_decription_context.buffer[i + 2], len - 2); static_print_warn("file type : %u", g_file_description.file_type); break; } case 0x02: { // 文件总字节 g_file_description.file_size = (g_file_decription_context.buffer[i + 2] << 24) | (g_file_decription_context.buffer[i + 3] << 16) | (g_file_decription_context.buffer[i + 4] << 8) | g_file_decription_context.buffer[i + 5]; static_print_warn("file total bytes : %u ", g_file_description.file_size); break; } case 0x03: { // 文件crc32 g_file_description.file_crc32 = (g_file_decription_context.buffer[i + 2] << 24) | (g_file_decription_context.buffer[i + 3] << 16) | (g_file_decription_context.buffer[i + 4] << 8) | g_file_decription_context.buffer[i + 5]; static_print_warn("file total crc32 : %08x", g_file_description.file_crc32); break; } case 0x04: { // 文件名称 const char *suffix = (const char *)&g_file_decription_context.buffer[i+2]; if(len - 2 > PROTOCOL_FILE_NAME_LEN_MAX-1){ static_print_error("error:file_name len:%d, len max:%d", (len - 2), (PROTOCOL_FILE_NAME_LEN_MAX-1)); break; } switch (g_file_description.file_type) { case TYPE_LOG: { static_print_debug("log file"); // strcat(g_rec_file_path, TJD_FS_DIR_UPDATE); break; } case TYPE_FIRMWARE: { static_print_debug("firmware file"); // 如果文件后缀名是 .fwkpg 则认为是固件文件 // 需存放在“/update/”目录下,否则存放在“/user/tjd_res/”目录下 const char *extension_update = ".fwpkg"; int str_len = len - 2; int extension_update_len = strlen(extension_update); // int extension_res_len = strlen(extension_res); if(str_len >= extension_update_len && strncmp(suffix + str_len - extension_update_len,extension_update,extension_update_len) == 0){ strcat((const char *)g_file_description.file_name, TJD_FS_DIR_UPDATE); tjd_ota_set_fimrware_size(g_file_description.file_size); }else { strcat((const char *)g_file_description.file_name, TJD_FS_DIR_RES); } break; } case TYPE_RESOURCE_FILES: { static_print_debug("resource files"); strcat((const char *)g_file_description.file_name, TJD_FS_DIR_RES); break; } case TYPE_MULTILINGUAL_DOC: { static_print_debug("multilingual doc file"); // strcat(g_rec_file_path, TJD_FS_DIR_DOC); break; } case TYPE_DIAL_DOC: { static_print_debug("dial doc file"); strcat((const char *)g_file_description.file_name, TJD_FS_DIR_WF); break; } case TYPE_PICTURE: { static_print_debug("picture file"); strcat((const char *)g_file_description.file_name, TJD_FS_DIR_ALBUM); break; } case TYPE_VIDEO: { static_print_debug("video file"); strcat((const char *)g_file_description.file_name, TJD_FS_DIR_ALBUM); break; } case TYPE_E_BOOK: { static_print_debug("e-book file"); strcat((const char *)g_file_description.file_name, TJD_FS_DIR_BOOK); break; } case TYPE_MUSIC: { static_print_debug("music file"); strcat((const char *)g_file_description.file_name, TJD_FS_DIR_MUSIC); break; } case TYPE_AIDIAL_ORIGINAL: { static_print_debug("aidial original file"); strcat((const char *)g_file_description.file_name, TJD_FS_DIR_WFP); if(access(TJD_FS_DIR_WFP, 0) != 0){ mkdir(TJD_FS_DIR_WFP, 0777); static_print_debug("mkdir /user/tjd_wfp dirent success"); } break; } case TYPE_AIDIAL_THUMBNAIL: { static_print_debug("aidial thumbnail file"); strcat((const char *)g_file_description.file_name, TJD_FS_DIR_WFP); if(access(TJD_FS_DIR_WFP, 0) != 0){ mkdir(TJD_FS_DIR_WFP, 0777); static_print_debug("mkdir /user/tjd_wfp dirent success"); } break; } case TYPE_AIDIAL_PREVIEW: { static_print_debug("aidial preview file"); strcat((const char *)g_file_description.file_name, TJD_FS_DIR_WFP); if(access(TJD_FS_DIR_WFP, 0) != 0){ mkdir(TJD_FS_DIR_WFP, 0777); static_print_debug("mkdir /user/tjd_wfp dirent success"); } break; } case TYPE_GPS: { static_print_debug("gps file"); strcat((const char *)g_file_description.file_name, TJD_FS_DIR_GPS_BAT); //如果 ""/user/tjd_gpsbat"" 目录不存在,则创建该目录 if(access(TJD_FS_DIR_GPS_BAT, 0) != 0){ mkdir(TJD_FS_DIR_GPS_BAT, 0777); static_print_debug("mkdir /user/tjd_gpsbat dirent success"); } break; } case TYPE_DIAL_DIAGRAM: { static_print_debug("dial diagram file"); strcat((const char *)g_file_description.file_name, TJD_FS_DIR_WFC); //如果 ""/user/tjd_wfc"" 目录不存在,则创建该目录 if(access(TJD_FS_DIR_WFC, 0) != 0){ mkdir(TJD_FS_DIR_WFC, 0777); static_print_debug("mkdir /user/tjd_wfc dirent success"); } break; } case TYPE_DIAL_MULTIGRAPH: { static_print_debug("dial multigraph file"); strcat((const char *)g_file_description.file_name, TJD_FS_DIR_WFC); //如果 ""/user/tjd_wfc"" 目录不存在,则创建该目录 if(access(TJD_FS_DIR_WFC, 0) != 0){ mkdir(TJD_FS_DIR_WFC, 0777); static_print_debug("mkdir /user/tjd_wfc dirent success"); } break; } case TYPE_DIAL_VIDEO: { static_print_debug("dial video file"); strcat((const char *)g_file_description.file_name, TJD_FS_DIR_WFC); //如果 ""/user/tjd_wfc"" 目录不存在,则创建该目录 if(access(TJD_FS_DIR_WFC, 0) != 0){ mkdir(TJD_FS_DIR_WFC, 0777); static_print_debug("mkdir /user/tjd_wfc dirent success"); } break; } case TYPE_DIAL_PREVIEW: { static_print_debug("dial preview file"); strcat((const char *)g_file_description.file_name, TJD_FS_DIR_WFC); //如果 ""/user/tjd_wfc"" 目录不存在,则创建该目录 if(access(TJD_FS_DIR_WFC, 0) != 0){ mkdir(TJD_FS_DIR_WFC, 0777); static_print_debug("mkdir /user/tjd_wfc dirent success"); } break; } case TYPE_JS_APP: { static_print_debug("js app file"); strcat((const char *)g_file_description.file_name, TJD_FS_DIR_JS); //如果 "/system/tjd_extapp"" 目录不存在,则创建该目录 if(access(TJD_FS_DIR_JS, 0) != 0){ mkdir(TJD_FS_DIR_JS, 0777); static_print_debug("mkdir /system/tjd_extapp dirent success"); } break; } default: { static_print_error("unknown file type"); break; } } /* 1、一文件名不能包括#号。 2、如果文件名中出#号说明文件完整路径要被重定义。将"#"替换为"/"就是真实目标路径。OTA时方便全固件升级 3、例:APP下发文件名为:user#log#abc.txt=user/log/abc.txt */ if(strnstr(suffix, "#", len - 2) == NULL){ strcat((char *)g_file_description.file_name, "/"); strncat((char *)g_file_description.file_name, (char *)&g_file_decription_context.buffer[i + 2], len - 2); } else { g_file_description.file_name[0] = 0; strncat((char *)g_file_description.file_name, (char *)&g_file_decription_context.buffer[i + 2], len - 2); char * ret = strstr((char *)g_file_description.file_name, "#"); while(ret != NULL){ memset(ret, '/', 1); ret = strstr(ret + 1, "#"); } } static_print_warn("file file name : %s", g_file_description.file_name); break; } // case 0x05: { // 文件用途枚举 // memcpy_s(&g_file_description.file_usage, sizeof(g_file_description.file_usage), // &g_file_decription_context.buffer[i + 2], len - 2); // static_print_debug("file purpose : %u", g_file_description.file_usage); // break; // } case 0x06: { // 图片宽度 图片高度 memcpy_s(&g_file_description.pictures_width, sizeof(g_file_description.pictures_width), &g_file_decription_context.buffer[i + 2], 2); memcpy_s(&g_file_description.pictures_height, sizeof(g_file_description.pictures_height), &g_file_decription_context.buffer[i + 4], 2); static_print_warn("image width : %u, height : %u", g_file_description.pictures_width, g_file_description.pictures_height); break; } case 0x07: { // 视频宽度 视频高度 memcpy_s(&g_file_description.video_width, sizeof(g_file_description.video_width), &g_file_decription_context.buffer[i + 2], 2); memcpy_s(&g_file_description.video_height, sizeof(g_file_description.video_height), &g_file_decription_context.buffer[i + 4], 2); static_print_warn("video width : %u, height : %u", g_file_description.video_width, g_file_description.video_height); break; } default: { static_print_error("unknown type : %u", type); break; } } i += len; } uint8_t data[3] = {0}; data[0] = 0x02; data[1] = (g_file_decription_context.buffer_offset >> 8) & 0xFF; data[2] = g_file_decription_context.buffer_offset & 0xFF; tjd_ble_protocol_send_data(PROTOCOL_FRAME_RSP_HEAD_2, data, sizeof(data), cmd_id, server_id, conn_id); free(g_file_decription_context.buffer); g_file_decription_context.buffer = NULL; g_file_decription_context.buffer_offset = 0; g_file_decription_context.expected_seq = 0; return; } else { uint8_t data[3] = {0}; data[0] = 0x01; data[1] = (g_file_decription_context.buffer_offset >> 8) & 0xFF; data[2] = g_file_decription_context.buffer_offset & 0xFF; tjd_ble_protocol_send_data(PROTOCOL_FRAME_RSP_HEAD_2, data, sizeof(data), cmd_id, server_id, conn_id); return; } } void tjd_ble_protocol_file_start(uint8_t server_id, uint16_t conn_id, uint8_t *write_cb_para, uint16_t len, uint8_t cmd_id) { static_print_debug("send file start:%d", g_files_transfer.file_status); if(g_files_transfer.file_status == FileTransfer_DescriptionDone) { g_files_transfer.file_status = FileTransfer_Start; } if(g_files_transfer_Callback[g_file_description.file_type].start) { g_files_transfer_Callback[g_file_description.file_type].start(&g_file_description, &g_files_transfer); } // 电量检测 /* 待完善 */ // 存在检测 // 如果CRC与size一致,则发送02直接跳过当前文件传输,否则,发送01,等待app下发文件数据 static_print_debug("g_file_description.file_name: %s", g_file_description.file_name); struct stat file_stat = {0}; int stat_ret = stat((char *)g_file_description.file_name,&file_stat); static_print_debug("access ret is %d", stat_ret); if(stat_ret == 0){ uint32_t crc32_local = calculateFileCRC32((char *)g_file_description.file_name, g_file_description.file_size); static_print_debug("%s , line is %d", __FUNCTION__, __LINE__); uint32_t crc32_remote = g_file_description.file_crc32; static_print_debug("crc32_local is %08x, crc32_remote is %08x", crc32_local, crc32_remote); if (crc32_local == crc32_remote && g_file_description.file_size == file_stat.st_size) { static_print_debug("crc32 is same, send 02 to skip current file"); g_files_transfer.operation_file_size += g_file_description.file_size; if(g_dir_description.file_total_type == g_file_description.file_type) { g_files_transfer.operation_total_size += g_file_description.file_size; g_files_transfer.operation_total_number++; } g_files_transfer.file_status = FileTransfer_NULL; static_print_debug("files transfer end: total_size=%d,file_size=%d", g_files_transfer.operation_total_size, g_files_transfer.operation_file_size); uint8_t data[] = {0x02}; tjd_ble_protocol_send_data(PROTOCOL_FRAME_RSP_HEAD_2, data, sizeof(data), DOWNLOAD_FILE_SRART, server_id, conn_id); return; } } //空间检测 struct statfs fs_stat = {0}; char fs_volume[64] = {0}; char *str_ret = NULL; if(g_file_description.file_name[0] == '/'){ str_ret = strnstr((const char *)&g_file_description.file_name[1], "/", PROTOCOL_FILE_NAME_LEN_MAX); if(str_ret) { memcpy_s(fs_volume, sizeof(fs_volume), g_file_description.file_name, (size_t)((uint32_t)str_ret - (uint32_t)g_file_description.file_name)); } } else { str_ret = strnstr((const char *)g_file_description.file_name, "/", PROTOCOL_FILE_NAME_LEN_MAX); if(str_ret) { memcpy_s(fs_volume, sizeof(fs_volume), g_file_description.file_name, (size_t)((uint32_t)str_ret - (uint32_t)g_file_description.file_name)); } } if (statfs(fs_volume, &fs_stat) != 0) { static_print_error("error:statfs() = null"); } static_print_debug("fs_stat.f_bfree :[%s] %ld-%ld", fs_volume, fs_stat.f_bfree, fs_stat.f_bsize); if (fs_stat.f_bfree >= FS_NODE_INFO_SPACE) { fs_stat.f_bfree = fs_stat.f_bfree - FS_NODE_INFO_SPACE; } else { fs_stat.f_bfree = 0; } if(stat_ret != 0) { static_print_debug("file stat() = null:[%s]", g_file_description.file_name); } else { uint32_t file_blocks = file_stat.st_size / file_stat.st_blksize ; fs_stat.f_bfree += file_blocks; static_print_debug("file stat() = ok:[%s] st_size=%ld", g_file_description.file_name, file_stat.st_size); } uint32_t free_space = fs_stat.f_bfree * fs_stat.f_bsize; if ((free_space < g_file_description.file_size) && g_file_description.file_size != 0 ) { static_print_error("not enough space! free space : %d , file size : %d",free_space, g_file_description.file_size); uint8_t data[] = {0x03}; tjd_ble_protocol_send_data(PROTOCOL_FRAME_RSP_HEAD_2, data, sizeof(data), DOWNLOAD_FILE_SRART, server_id, conn_id); return; } //正常开始 // gap_conn_param_update_t conn_param = { 0 }; // conn_param.conn_handle = conn_id; /* param 0: conn_handle */ // conn_param.interval_min = 6; /* param 1: interval_min */ // conn_param.interval_max = 10; /* param 2: interval_max */ // conn_param.slave_latency = 0; /* param 3: slave_latency */ // conn_param.timeout_multiplier = 300; /* param 4: timeout_multiplier */ // errcode_t update_ret = gap_ble_connect_param_update(&conn_param); // static_print_debug("gap ble update connection parameters ret: %u", update_ret); uint8_t data[] = {0x01}; tjd_ble_protocol_send_data(PROTOCOL_FRAME_RSP_HEAD_2, data, sizeof(data), DOWNLOAD_FILE_SRART, server_id, conn_id); } void tjd_ble_protocol_file_data(uint8_t server_id, uint16_t conn_id, uint8_t *write_cb_para, uint16_t len ,uint8_t cmd_id) { int ret = 0; uint16_t write_len = write_cb_para[1] << 8 | write_cb_para[2]; uint32_t remaining_size = g_file_description.file_size - g_files_transfer.operation_file_size; uint16_t data_len = write_len - 9; uint16_t valid_data_len = len - 9; uint32_t cmd_offset = write_cb_para[4]; cmd_offset <<= 8; cmd_offset += write_cb_para[5]; cmd_offset <<= 8; cmd_offset += write_cb_para[6]; cmd_offset <<= 8; cmd_offset += write_cb_para[7]; tjd_service_timer_cnt = 0; tjd_driver_rtc_get_ops()->get_timestamp(&g_files_transfer.operation_last_time); if(g_files_transfer.file_status == FileTransfer_Start) { g_files_transfer.file_status = FileTransfer_Data; tjd_task_service_check_timeout_start(); static_print_debug("write_len : %d, remaining_size : %d, data_len : %d, valid_data_len : %d, cmd_offset=%d\n", write_len, remaining_size, data_len, valid_data_len, cmd_offset); if(tjd_ble_file_breakpoint_recover(&g_file_description, &g_files_transfer) == false) //无断点内容 { tjd_file_pre_operation(); goto ACCEPTFILEDATA; } else { if(cmd_offset == g_files_transfer.operation_file_size) { goto ACCEPTFILEDATA; } uint8_t resp_data[9] = {0}; resp_data[0] = 0x02; resp_data[1] = g_files_transfer.operation_file_size >> 24; resp_data[2] = (g_files_transfer.operation_file_size >> 16) & 0xFF; resp_data[3] = (g_files_transfer.operation_file_size >> 8) & 0xFF; resp_data[4] = g_files_transfer.operation_file_size & 0xFF; resp_data[5] = g_files_transfer.operation_file_size >> 24; resp_data[6] = (g_files_transfer.operation_file_size >> 16) & 0xFF; resp_data[7] = (g_files_transfer.operation_file_size >> 8) & 0xFF; resp_data[8] = g_files_transfer.operation_file_size & 0xFF; // (uint32_t *)(&resp_data[1]) = g_files_transfer.operation_file_size; // (uint32_t *)(&resp_data[5]) = download_breakpoint_resume_offset; tjd_ble_protocol_send_data(PROTOCOL_FRAME_RSP_HEAD_2, resp_data, sizeof(resp_data), cmd_id, server_id, conn_id); return; } }else if(g_files_transfer.file_status == FileTransfer_Data) { ACCEPTFILEDATA: if(cmd_offset != g_files_transfer.operation_file_size) { static_print_error("error:g_files_transfer.operation_file_size:%d,cmd_offset:%d", g_files_transfer.operation_file_size, cmd_offset); uint8_t resp_data[9] = {0}; resp_data[0] = 0x02; resp_data[1] = g_files_transfer.operation_file_size >> 24; resp_data[2] = (g_files_transfer.operation_file_size >> 16) & 0xFF; resp_data[3] = (g_files_transfer.operation_file_size >> 8) & 0xFF; resp_data[4] = g_files_transfer.operation_file_size & 0xFF; resp_data[5] = g_files_transfer.operation_file_size >> 24; resp_data[6] = (g_files_transfer.operation_file_size >> 16) & 0xFF; resp_data[7] = (g_files_transfer.operation_file_size >> 8) & 0xFF; resp_data[8] = g_files_transfer.operation_file_size & 0xFF; tjd_ble_protocol_send_data(PROTOCOL_FRAME_RSP_HEAD_2, resp_data, sizeof(resp_data), cmd_id, server_id, conn_id); } else { if(data_len > 0){ if(remaining_size <= data_len){ ret = tjd_fs_api_file_append((const char *)g_file_description.file_name, write_cb_para + 8, remaining_size); g_files_transfer.operation_file_size += remaining_size; if(g_dir_description.file_total_type == g_file_description.file_type) { g_files_transfer.operation_total_size += remaining_size; } }else{ ret = tjd_fs_api_file_append((const char *)g_file_description.file_name, write_cb_para + 8, len - 9); g_files_transfer.operation_file_size += valid_data_len; if(g_dir_description.file_total_type == g_file_description.file_type) { g_files_transfer.operation_total_size += valid_data_len; } } if(g_files_transfer_Callback[g_file_description.file_type].data) { g_files_transfer_Callback[g_file_description.file_type].data(&g_file_description, &g_files_transfer); } uint8_t resp_data[9] = {0}; resp_data[0] = 0x01; resp_data[1] = g_files_transfer.operation_file_size >> 24; resp_data[2] = (g_files_transfer.operation_file_size >> 16) & 0xFF; resp_data[3] = (g_files_transfer.operation_file_size >> 8) & 0xFF; resp_data[4] = g_files_transfer.operation_file_size & 0xFF; resp_data[5] = g_files_transfer.operation_file_size >> 24; resp_data[6] = (g_files_transfer.operation_file_size >> 16) & 0xFF; resp_data[7] = (g_files_transfer.operation_file_size >> 8) & 0xFF; resp_data[8] = g_files_transfer.operation_file_size & 0xFF; tjd_ble_protocol_send_data(PROTOCOL_FRAME_RSP_HEAD_2, resp_data, sizeof(resp_data), cmd_id, server_id, conn_id); }else{ static_print_error("error: valid_data_len is In vain"); tjd_file_transfer_err_operation(); uint8_t resp_data[9] = {0}; resp_data[0] = 0x00; resp_data[1] = g_files_transfer.operation_file_size >> 24; resp_data[2] = (g_files_transfer.operation_file_size >> 16) & 0xFF; resp_data[3] = (g_files_transfer.operation_file_size >> 8) & 0xFF; resp_data[4] = g_files_transfer.operation_file_size & 0xFF; resp_data[5] = g_files_transfer.operation_file_size >> 24; resp_data[6] = (g_files_transfer.operation_file_size >> 16) & 0xFF; resp_data[7] = (g_files_transfer.operation_file_size >> 8) & 0xFF; resp_data[8] = g_files_transfer.operation_file_size & 0xFF; tjd_ble_protocol_send_data(PROTOCOL_FRAME_RSP_HEAD_2, resp_data, sizeof(resp_data), cmd_id, server_id, conn_id); } } // static_print_debug("send resp data"); if (g_files_transfer.operation_file_size == g_file_description.file_size) { static_print_debug("files transfer end: total_size=%d,file_size=%d", g_files_transfer.operation_total_size, g_files_transfer.operation_file_size); static_print_debug("file_type : %d",g_file_description.file_type); g_files_transfer.file_status = FileTransfer_NULL; if(g_dir_description.file_total_type == g_file_description.file_type) { g_files_transfer.operation_total_number++; } } }else{ static_print_error("error:g_files_transfer.file_status:%d,file:%s", g_files_transfer.file_status, g_file_description.file_name); } } void tjd_ble_protocol_file_end(uint8_t server_id, uint16_t conn_id, uint8_t *write_cb_para, uint16_t len, uint8_t cmd_id) { static_print_debug("tjd_ble_protocol_file_end():%d", g_files_transfer.file_status); if(g_files_transfer_Callback[g_file_description.file_type].end) { g_files_transfer_Callback[g_file_description.file_type].end(&g_file_description, &g_files_transfer); } g_files_transfer.file_status = FileTransfer_NULL; // g_file_transport_buf = NULL; // memset(&g_file_description, 0, sizeof(g_file_description)); // gap_conn_param_update_t conn_param = { 0 }; // conn_param.conn_handle = conn_id; /* param 0: conn_handle */ // conn_param.interval_min = 16; /* param 1: interval_min */ // conn_param.interval_max = 32; /* param 2: interval_max */ // conn_param.slave_latency = 50; /* param 3: slave_latency */ // conn_param.timeout_multiplier = 300; /* param 4: timeout_multiplier */ // errcode_t ret = gap_ble_connect_param_update(&conn_param); // static_print_debug("gap ble update connection parameters ret: %u", ret); uint8_t data[] = {0x01}; tjd_ble_protocol_send_data(PROTOCOL_FRAME_RSP_HEAD_2, data, sizeof(data), DOWNLOAD_FILE_END_DATA, server_id, conn_id); //sql_bt_set_breakpoint_info(0, 0, " "); }