/*---------------------------------------------------------------------------- * Copyright (c) Fenda Technologies Co., Ltd. 2022. All rights reserved. * * Description: ble_protocol_file_upload.c * * Author: saimen * * Create: 2024-12-12 *--------------------------------------------------------------------------*/ #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 "broadcast_feature.h" #include "ble_protocol_file_upload.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 UPLOAD_FILE_TASK_SIZE 0x1200 #define UPLOAD_FILE_TASK_PRIO 20 #define UPLOAD_FILE_TIMEOUT_MS 3000 FileDescriptionInfo_t g_files_upload_description = {0}; FileTransferInfo_t g_files_upload_transfer = {0}; FileUploadEndEnum g_state_temp = UPLOAD_END_SUCCEED; typedef void (*file_transfer_end_callback_t)(FileUploadEndEnum value); typedef void (*file_transfer_start_callback_t)(uint8_t cmd_ret); typedef struct { file_transfer_start_callback_t start; file_transfer_end_callback_t end; } FileTransferCallback_t; FileTransferCallback_t g_files_upload_transfer_Callback[FileType_MAX] = {0}; osal_task *g_tjd_ble_upload_file_task_handle = NULL; static osal_semaphore g_tjd_ble_upload_file_mutex = {0}; extern void file_upload_callback_start_type_recode(uint8_t cmd_ret); extern void file_upload_callback_end_type_recode(FileUploadEndEnum value); extern void file_upload_callback_start_type_sport_record(uint8_t cmd_ret); extern void file_upload_callback_end_type_sport_record(FileUploadEndEnum value); extern void file_upload_callback_start_type_js_app_list(uint8_t cmd_ret); extern void file_upload_callback_end_type_js_app_list(FileUploadEndEnum value); void tjd_ble_protocol_file_upload_callback_init(void) { static_print_debug("tjd_ble_protocol_file_upload_callback_init():...........\n"); memset_s(g_files_upload_transfer_Callback, sizeof(g_files_upload_transfer_Callback), 0, sizeof(g_files_upload_transfer_Callback)); g_files_upload_transfer_Callback[TYPE_SPORT_RECORD].start = file_upload_callback_start_type_sport_record; g_files_upload_transfer_Callback[TYPE_SPORT_RECORD].end = file_upload_callback_end_type_sport_record; g_files_upload_transfer_Callback[TYPE_RECODE].start = file_upload_callback_start_type_recode; g_files_upload_transfer_Callback[TYPE_RECODE].end = file_upload_callback_end_type_recode; // g_files_upload_transfer_Callback[TYPE_JS_APP_LIST].start = file_upload_callback_start_type_js_app_list; //处理掩码 // g_files_upload_transfer_Callback[TYPE_JS_APP_LIST].end = file_upload_callback_end_type_js_app_list; //处理掩码 } //基础接口 static void upload_file_decription(FileDescriptionInfo_t *file_description_packet) { static_print_debug("upload file description"); uint16_t file_description_len = 17; static_print_debug("file_description_len: %d", file_description_len); // file_description_len -= sizeof(file_description_packet->file_name); // static_print_debug("file_description_len: %d", file_description_len); uint8_t lt_len = 2 * 4; // 2 : type + len 6: FileDescriptionPacket的字段个数 switch (file_description_packet->file_type) { case TYPE_PICTURE: file_description_len -= sizeof(uint16_t) * 2; // lt_len -= 2; // 2 : type + len break; case TYPE_VIDEO: file_description_len -= sizeof(uint16_t) * 2; // lt_len -= 2; // 2 : type + len break; default: file_description_len -= sizeof(uint16_t) * 4; // lt_len -= 4; // 4 : 2 个 type + len break; } // file_description_packet->file_name = "/user/tjd_res/2000-8-4.mp3" // 从file_description_packet->file_name 提取到 2000-8-4.mp3 // 怎么计算 file_name 的长度 static_print_debug("file_description_packet->file_name: %s", file_description_packet->file_name); char *file_name = strrchr((const char *)(file_description_packet->file_name), '/') + 1; static_print_debug("file_name: %s", file_name); static_print_debug("strlen(file_name) : %d", strlen(file_name)); // file_name++; // 跳过'/' file_description_len += strlen(file_name); static_print_debug("file_description_len: %d", file_description_len); // if (file_description_packet->file_usage) { // file_description_len -= sizeof(uint16_t); // } file_description_len += lt_len; static_print_debug("file_description_len: %d", file_description_len); uint8_t *file_description_data = malloc(file_description_len); memset_s(file_description_data, file_description_len, 0, file_description_len); int data_index = 0; file_description_data[data_index++] = sizeof(file_description_packet->file_type) + 2; file_description_data[data_index++] = 0x01; file_description_data[data_index++] = file_description_packet->file_type; file_description_data[data_index++] = sizeof(file_description_packet->file_size) + 2; file_description_data[data_index++] = 0x02; file_description_data[data_index++] = file_description_packet->file_size >> 24; file_description_data[data_index++] = file_description_packet->file_size >> 16; file_description_data[data_index++] = file_description_packet->file_size >> 8; file_description_data[data_index++] = file_description_packet->file_size & 0xff; file_description_data[data_index++] = sizeof(file_description_packet->file_crc32) + 2; file_description_data[data_index++] = 0x03; file_description_data[data_index++] = file_description_packet->file_crc32 >> 24; file_description_data[data_index++] = file_description_packet->file_crc32 >> 16; file_description_data[data_index++] = file_description_packet->file_crc32 >> 8; file_description_data[data_index++] = file_description_packet->file_crc32 & 0xff; file_description_data[data_index++] = strlen(file_name) + 2; file_description_data[data_index++] = 0x04; memcpy_s(&file_description_data[data_index], strlen(file_name), file_name, strlen(file_name)); data_index += strlen(file_name); uint32_t mtu_size = tjd_ble_get_mtu_size() - 3 > 509 ? 509 : tjd_ble_get_mtu_size() - 3; // if (mtu_size < file_description_len) { // // 长包分包 uint16_t sub_package_size = mtu_size - 9; // 9是包头长度包括末尾crc8 uint16_t sub_package_count = file_description_len / sub_package_size; if (file_description_len % sub_package_size) { sub_package_count++; // 如果有余数,则多一包 } uint16_t sub_package_index = 0; uint16_t sub_package_offset = 0; uint8_t *sub_package_data = malloc(sub_package_size + 9); // 9是包头长度包括末尾crc8 // memset_s(sub_package_data, sub_package_size + 9, 0, sub_package_size + 9); while (sub_package_index < sub_package_count) { memset_s(sub_package_data, sub_package_size + 9, 0, sub_package_size + 9); uint16_t sub_package_length = mtu_size; if (sub_package_index == sub_package_count - 1) { // 最后一包 sub_package_length = file_description_len - sub_package_offset + 9; // 9是包头长度包括末尾crc8 } int index = 0; sub_package_data[index++] = PROTOCOL_RES_FRAME_HEAD_2; // *(uint16_t *)(&sub_package_data[index]) = sub_package_length >> 8 | (sub_package_length & 0xff); // index += sizeof(uint16_t); sub_package_data[index++] = sub_package_length >> 8; sub_package_data[index++] = sub_package_length & 0xff; sub_package_data[index++] = UPLOAD_FILE_DESCRIPTION; // *(uint16_t *)(&sub_package_data[index]) = file_description_len >> 8 | (file_description_len & 0xff); sub_package_data[index++] = file_description_len >> 8; sub_package_data[index++] = file_description_len & 0xff; // file_description_data[4] = file_description_len & 0xFF; // index += sizeof(uint16_t); // *(uint16_t *)(&sub_package_data[index]) = sub_package_offset >> 8 | (sub_package_offset & 0xff); sub_package_data[index++] = sub_package_offset >> 8; sub_package_data[index++] = sub_package_offset & 0xff; // memcpy_s(&file_description_data[7], sizeof(data), file_description_packet, file_description_len); // index += sizeof(uint16_t); memcpy_s(&sub_package_data[index], sub_package_length - 9, &file_description_data[sub_package_offset], sub_package_length - 9); index += sub_package_length - 9; sub_package_data[index] = do_crc(sub_package_data, sub_package_length - 1); for(int i = 0 ; i < sub_package_length; i++){ static_print_info("%02x ",sub_package_data[i]); if(i % 10 == 0){ static_print_info("\r\n"); } } static_print_info("\r\n"); gatt_send_response(sub_package_data, sub_package_length,gatt_server_id,g_server_conn_id); sub_package_index++; sub_package_offset += sub_package_length - 9; //等待信号量 /* wait for trans */ if (osal_sem_down_timeout(&g_tjd_ble_upload_file_mutex, UPLOAD_FILE_TIMEOUT_MS) != OSAL_SUCCESS) { //摧毁上传文件描述信号量 // osal_kthread_destroy(g_tjd_ble_upload_file_task_handle,1); osal_sem_destroy(&g_tjd_ble_upload_file_mutex); if(g_files_upload_transfer_Callback[g_files_upload_description.file_type].start) { g_files_upload_transfer_Callback[g_files_upload_description.file_type].start(0);//通知发送失败 } break; } } free(sub_package_data); sub_package_data = NULL; free(file_description_data); file_description_data = NULL; } void tjd_ble_protocol_upload_file_description(const char *file_name, uint8_t file_type) { //主动发起 static_print_debug("upload file:[%s]file_type=%d", file_name, file_type); if(file_name == NULL){ static_print_error("file name is null"); return; } //初始化信号量 (void)memset_s((osal_semaphore *)&(g_tjd_ble_upload_file_mutex), sizeof(g_tjd_ble_upload_file_mutex), 0, sizeof(g_tjd_ble_upload_file_mutex)); (void)osal_sem_init(&g_tjd_ble_upload_file_mutex,0); memset(&g_files_upload_description, 0, sizeof(g_files_upload_description)); memset(&g_files_upload_transfer, 0, sizeof(g_files_upload_transfer)); g_files_upload_description.file_type = file_type; g_files_upload_transfer.operation_file_size = 0; g_files_upload_transfer.file_status = FileTransfer_NULL; //读取文件 获取文件大小 文件crc32 发送文件描述信息 memcpy(g_files_upload_description.file_name, file_name, strlen(file_name)); g_files_upload_description.file_size = tjd_ble_get_file_size((const char *)file_name); g_files_upload_description.file_crc32 = calculateFileCRC32(file_name, &g_files_upload_description.file_size); upload_file_decription(&g_files_upload_description); } void tjd_ble_protocol_file_upload_description_recover_handle(uint8_t server_id, uint16_t conn_id, uint8_t *write_cb_para, uint16_t len, uint8_t cmd_id) { //APP回复处理 static_print_debug("tjd_ble_protocol_upload_file_description_recover_handle()"); osal_sem_up(&g_tjd_ble_upload_file_mutex); if (write_cb_para[4] == 0x02) { tjd_ble_protocol_file_upload_start(server_id, conn_id); } else { static_print_debug("upload file description faile"); if(g_files_upload_transfer_Callback[g_files_upload_description.file_type].start) { g_files_upload_transfer_Callback[g_files_upload_description.file_type].start(0);//通知发送失败 } } } void tjd_ble_protocol_file_upload_start(uint8_t server_id, uint16_t conn_id) { static_print_debug("tjd_ble_protocol_file_upload_start()"); uint8_t data[5] = {0}; data[0] = 0x5B; data[1] = 0x00; data[2] = 0x05; data[3] = UPLOAD_FILE_SRART; data[4] = do_crc(data, 4); gatt_send_response(data, sizeof(data), server_id, conn_id); } void tjd_ble_protocol_file_upload_start_recover_handle(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"); if (write_cb_para[4] == 0x01) { //开线程传输文件 g_tjd_ble_upload_file_task_handle = osal_kthread_create((osal_kthread_handler)tjd_ble_protocol_file_upload_data, NULL, "upload_file", UPLOAD_FILE_TASK_SIZE); if (g_tjd_ble_upload_file_task_handle == NULL) { // osal_msg_queue_is_full(g_app_queue); static_print_error("create upload file thread fail!"); } osal_kthread_set_priority(g_tjd_ble_upload_file_task_handle, UPLOAD_FILE_TASK_PRIO); } if(g_files_upload_transfer_Callback[g_files_upload_description.file_type].start) { g_files_upload_transfer_Callback[g_files_upload_description.file_type].start(write_cb_para[4]); } } int tjd_ble_protocol_file_upload_data(void * arg) { static_print_debug("tjd_ble_protocol_file_upload_data"); g_state_temp = UPLOAD_END_SUCCEED; // 分块读取文件数据传输文件 const size_t bufferSize = 4105; // 缓冲区大小,可根据实际情况调整 uint8_t *buffer = malloc(bufferSize); if (buffer == NULL) { static_print_error("malloc failed"); g_state_temp = UPLOAD_END_SYS; } g_files_upload_transfer.operation_file_size = 0; uint32_t mtu_size = tjd_ble_get_mtu_size() - 3 > 509 ? 509 : tjd_ble_get_mtu_size() - 3;; size_t read_size = bufferSize - 9; uint8_t * tmp_buffer = malloc(mtu_size); if(tmp_buffer == NULL){ static_print_error("malloc failed"); g_state_temp = UPLOAD_END_SYS; } osal_sem_up(&g_tjd_ble_upload_file_mutex);//首次手动释放信号信量,后续收到APP回复时释放信号信量 // 长包分包传输 while(g_files_upload_transfer.operation_file_size < g_files_upload_description.file_size) { if(g_state_temp == UPLOAD_END_USER || g_state_temp == UPLOAD_END_SYS ) { break; } if(g_files_upload_transfer.operation_file_size == g_files_upload_description.file_size){ g_state_temp = UPLOAD_END_SUCCEED; break; } //等待信号量 static_print_debug("wait for semaphore ..."); if (osal_sem_down_timeout(&g_tjd_ble_upload_file_mutex, UPLOAD_FILE_TIMEOUT_MS) != OSAL_SUCCESS) { //上超时退出 static_print_warn("osal_sem_down_timeout!"); g_state_temp = UPLOAD_END_TIMEOUT; break; } uint16_t sub_package_offset = 0; uint16_t sub_package_index = 0; uint16_t sub_package_size = mtu_size; // 509是包体大小,可根据实际情况调整 uint16_t sub_package_count = bufferSize / mtu_size; memset_s(buffer, bufferSize, 0, bufferSize); int index = 0; buffer[index++] = PROTOCOL_RES_FRAME_HEAD_2; buffer[index++] = (bufferSize >> 8) & 0xFF; buffer[index++] = (bufferSize & 0xFF); buffer[index++] = UPLOAD_FILE_DATA; buffer[index++] = ((g_files_upload_transfer.operation_file_size + sub_package_offset) >> 24) & 0xFF; buffer[index++] = ((g_files_upload_transfer.operation_file_size + sub_package_offset) >> 16) & 0xFF; buffer[index++] = ((g_files_upload_transfer.operation_file_size + sub_package_offset) >> 8) & 0xFF; buffer[index++] = (g_files_upload_transfer.operation_file_size + sub_package_offset) & 0xFF; // tjd_fs_api_file_load((const char *)(g_file_description_packet.file_name), g_files_upload_transfer.operation_file_size, buffer, read_size); if (g_files_upload_transfer.operation_file_size + bufferSize > g_files_upload_description.file_size) { read_size = g_files_upload_description.file_size - g_files_upload_transfer.operation_file_size; sub_package_count = read_size / mtu_size; buffer[1] = ((read_size + 9) >> 8) & 0xFF; buffer[2] = (read_size + 9) & 0xFF; } tjd_ble_load_file_data((const char *)(g_files_upload_description.file_name), g_files_upload_transfer.operation_file_size, buffer + index, read_size); index += read_size; buffer[index] = do_crc(buffer, index); static_print_debug("read_size : %d, g_files_upload_transfer.operation_file_size: %d, file_size: %d, index: %d", read_size, g_files_upload_transfer.operation_file_size, index, g_files_upload_description.file_size); while (sub_package_index <= sub_package_count) { static_print_debug("sub_package_index %d , sub_package_count %d " , sub_package_index , sub_package_count); static_print_debug("sub_package_offset %d , sub_package_size %d " , sub_package_offset , sub_package_size); if (sub_package_index == sub_package_count && sub_package_index != 0) { // 最后一包 sub_package_size = read_size - sub_package_offset + 1 + 8 + 1; // 9是包头长度包括末尾crc8 static_print_debug("last package read_size : %d , sub_package_offset : %d , sub_package_size %d " , read_size , sub_package_offset , sub_package_size); } else if(sub_package_index == sub_package_count && sub_package_index == 0){ //只发一包 sub_package_size = read_size - sub_package_offset + 1 + 8 ; // 9是包头长度包括末尾crc8 static_print_debug("last package read_size : %d , sub_package_offset : %d , sub_package_size %d " , read_size , sub_package_offset , sub_package_size); } if(sub_package_index == 0){ gatt_send_response(buffer + sub_package_offset, sub_package_size,gatt_server_id,g_server_conn_id); sub_package_offset += sub_package_size; g_files_upload_transfer.operation_file_size += sub_package_size - 8; }else{ // uint8_t tmp_buffer[509] = {0}; tmp_buffer[0] = sub_package_index - 1; memcpy_s(tmp_buffer + 1, mtu_size - 1, buffer + sub_package_offset, sub_package_size - 1); gatt_send_response(tmp_buffer, sub_package_size,gatt_server_id,g_server_conn_id); sub_package_offset += sub_package_size - 1; if(sub_package_index == sub_package_count){ g_files_upload_transfer.operation_file_size += sub_package_size - 1 - 1; }else{ g_files_upload_transfer.operation_file_size += sub_package_size - 1; } } // gatt_send_response(sub_package_data, sub_package_size,gatt_server_id,g_server_conn_id); sub_package_index++; // sub_package_offset += sub_package_size; static_print_debug("sub_package_offset: %d , sub_package_size : %d g_files_upload_transfer.operation_file_size : %d ",sub_package_offset , sub_package_size , g_files_upload_transfer.operation_file_size); } } free(buffer); buffer = NULL; free(tmp_buffer); tmp_buffer = NULL; g_files_upload_transfer.file_status = FileTransfer_End; tjd_ble_protocol_file_upload_end(g_state_temp); return 0; } void tjd_ble_protocol_file_upload_data_recover_handle(uint8_t server_id, uint16_t conn_id, uint8_t *write_cb_para, uint16_t len ,uint8_t cmd_id) { if (write_cb_para[4] == 0x01) { static_print_debug("upload file data success"); osal_sem_up(&g_tjd_ble_upload_file_mutex); if(g_files_upload_transfer.file_status == FileTransfer_End) { //tjd_ble_protocol_file_upload_end(g_state_temp); } } else if (write_cb_para[4] == 0x00) { static_print_debug("upload file data faile"); } } void tjd_ble_protocol_file_upload_end(FileUploadEndEnum status) { static_print_debug("tjd_ble_protocol_file_upload_end(status=%d) file_type:%d-end(%p)", status, g_files_upload_description.file_type, g_files_upload_transfer_Callback[g_files_upload_description.file_type].end); uint8_t pack_head = PROTOCOL_RES_FRAME_HEAD_2; tjd_ble_protocol_send_lefun_data(&pack_head, NULL, 0, UPLOAD_FILE_END_DATA); if(g_state_temp != status) { g_state_temp = status; } osal_sem_destroy(&g_tjd_ble_upload_file_mutex); //osal_kthread_destroy(g_tjd_ble_upload_file_task_handle,1); osThreadDetach(g_tjd_ble_upload_file_task_handle); //osThreadDetach() 空闲删除 g_tjd_ble_upload_file_task_handle = NULL; if(g_files_upload_transfer_Callback[g_files_upload_description.file_type].end) { static_print_debug("g_files_upload_transfer_Callback[g_files_upload_description.file_type].end()"); g_files_upload_transfer_Callback[g_files_upload_description.file_type].end(status); } } void tjd_ble_protocol_file_upload_end_recover_handle(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_upload_end_recover_handle"); #if 0 int ret = osal_event_write(&g_sport_record_event, FILE_SEND_FAILE); if(ret != OSAL_SUCCESS){ static_print_error("write event FILE_SEND_SUCCESS failed!"); } osal_sem_destroy(&g_tjd_ble_upload_file_mutex); osal_kthread_destroy(g_tjd_ble_upload_file_task_handle,1); g_tjd_ble_upload_file_task_handle = NULL; if (write_cb_para[4]) { static_print_debug("upload file end data success"); if(get_upload_file_end_callback() != NULL){ get_upload_file_end_callback()(UPLOAD_SUCESS); } } else { static_print_debug("upload file end data faile"); if(get_upload_file_end_callback() != NULL){ get_upload_file_end_callback()(UPLOAD_FAILE); } } #endif }