#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "eemcs_kal.h" #include "eemcs_debug.h" #include "eemcs_boot.h" #include "eemcs_boot_trace.h" #include "eemcs_md.h" #include "lte_main.h" static KAL_UINT32 g_eemcs_file_pkt_len = BOOT_TX_MAX_PKT_LEN; #define EEMCS_MBXRD_TO 10*HZ //10 sec EEMCS_BOOT_SET eemcs_boot_inst; eemcs_cdev_node_t *boot_node = NULL; static KAL_INT32 boot_reset_state = 0; #ifdef _EEMCS_BOOT_UT #define EEMCS_BOOT_UT_BL_CNT (1) // Choose the boot-loader count you want to simulate #if (EEMCS_BOOT_UT_BL_CNT >= 2) #define EEMCS_BOOT_UT_BL_LOOP_START (12) // index of CMDID_BIN_LOAD_START for multiple boot-loader #define EEMCS_BOOT_UT_BL_LOOP_END (22) // index of CMDID_BIN_LOAD_END for multiple boot-loader #endif // These commands are for a specific modem.img. // If modem.img changes, these command content may be different. // But the commands flow are almost similar. static EEMCS_BOOT_UT_CMD ut_md_xcmd[] = { { BOOT_UT_MBX, .data.mbx = { SDIOMAIL_BOOTING_REQ, 0 } }, { BOOT_UT_XCMD, .data.xcmd = { MAGIC_MD_CMD, CMDID_BIN_LOAD_START, XBOOT_STAGE_BROM, {0} } }, { BOOT_UT_XCMD_GETBIN, .data.xcmd_getbin = { MAGIC_MD_CMD, CMDID_GET_BIN, 0x14, 0x4, {0} } }, { BOOT_UT_XCMD_GETBIN, .data.xcmd_getbin = { MAGIC_MD_CMD, CMDID_GET_BIN, 0xB000, 0xC, {0} } }, { BOOT_UT_XCMD_GETBIN, .data.xcmd_getbin = { MAGIC_MD_CMD, CMDID_GET_BIN, 0xB00C, 0x4, {0} } }, { BOOT_UT_XCMD_GETBIN, .data.xcmd_getbin = { MAGIC_MD_CMD, CMDID_GET_BIN, 0x10000, 0x10, {0} } }, { BOOT_UT_XCMD_GETBIN, .data.xcmd_getbin = { MAGIC_MD_CMD, CMDID_GET_BIN, 0x10010, 0xD724, {0} } }, { BOOT_UT_XCMD, .data.xcmd = { MAGIC_MD_CMD, CMDID_BIN_LOAD_END, 0, {0} } }, { BOOT_UT_MBX, .data.mbx = { SDIOMAIL_DL_REQ, 0 } }, { BOOT_UT_MSD_OUTPUT, .data.msd_print = { MAGIC_MD_CMD, CMDID_MD_MSD_OUTPUT, 0x14, "MT7208 BROM XBOOT OK" } }, { BOOT_UT_MSD_OUTPUT, .data.msd_print = { MAGIC_MD_CMD, CMDID_MD_MSD_OUTPUT, 0x1, "" } }, { BOOT_UT_MSD_FLUSH, .data.msd_print = { MAGIC_MD_CMD, CMDID_MSG_FLUSH, 0x0, "" } }, #if (EEMCS_BOOT_UT_BL_CNT >= 2) { BOOT_UT_MBX, .data.mbx = { SDIOMAIL_BOOTING_REQ, 0 } }, { BOOT_UT_XCMD, .data.xcmd = { MAGIC_MD_CMD, CMDID_BIN_LOAD_START, XBOOT_STAGE_BROM, {0} } }, { BOOT_UT_XCMD_GETBIN, .data.xcmd_getbin = { MAGIC_MD_CMD, CMDID_GET_BIN, 0x14, 0x4, {0} } }, { BOOT_UT_XCMD_GETBIN, .data.xcmd_getbin = { MAGIC_MD_CMD, CMDID_GET_BIN, 0xB000, 0xC, {0} } }, { BOOT_UT_XCMD_GETBIN, .data.xcmd_getbin = { MAGIC_MD_CMD, CMDID_GET_BIN, 0xB00C, 0x4, {0} } }, { BOOT_UT_XCMD_GETBIN, .data.xcmd_getbin = { MAGIC_MD_CMD, CMDID_GET_BIN, 0x10000, 0x10, {0} } }, { BOOT_UT_XCMD_GETBIN, .data.xcmd_getbin = { MAGIC_MD_CMD, CMDID_GET_BIN, 0x10010, 0xD724, {0} } }, { BOOT_UT_MSD_OUTPUT, .data.msd_print = { MAGIC_MD_CMD, CMDID_MD_MSD_OUTPUT, 0x12, "MT7208 BL OK" } }, { BOOT_UT_MSD_OUTPUT, .data.msd_print = { MAGIC_MD_CMD, CMDID_MD_MSD_OUTPUT, 0x1, "" } }, { BOOT_UT_MSD_FLUSH, .data.msd_print = { MAGIC_MD_CMD, CMDID_MSG_FLUSH, 0x0, "" } }, { BOOT_UT_XCMD, .data.xcmd = { MAGIC_MD_CMD, CMDID_BIN_LOAD_END, 0, {0} } }, #endif { BOOT_UT_MBX, .data.mbx = { SDIOMAIL_BOOTING_REQ, 0 } }, { BOOT_UT_XCMD, .data.xcmd = { MAGIC_MD_CMD, CMDID_BIN_LOAD_START, XBOOT_STAGE_BROM, {0} } }, { BOOT_UT_XCMD_GETBIN, .data.xcmd_getbin = { MAGIC_MD_CMD, CMDID_GET_BIN, 0x50000, 0x4, {0} } }, { BOOT_UT_XCMD_GETBIN, .data.xcmd_getbin = { MAGIC_MD_CMD, CMDID_GET_BIN, 0x50004, 0x103E74, {0} } }, { BOOT_UT_XCMD, .data.xcmd = { MAGIC_MD_CMD, CMDID_MD_BOOT_END, 0, {0} } }, }; #endif // _EEMCS_BOOT_UT // // Boot State String // unsigned char* g_md_sta_str[] = { "MD_INVALID", "MD_INIT", "MD_BROM_SDIO_INIT", "MD_BROM_SDIO_MBX_HS", "MD_BROM_DL_START", "MD_BROM_DL_GET", "MD_BROM_DL_END", "MD_BROM_SEND_STATUS", "MD_BROM_SDDL_HS", "MD_BL_SDIO_INIT", "MD_BL_SDIO_MBX_HS", "MD_BL_DL_START", "MD_BL_DL_GET", "MD_BL_DL_END", "MD_BOOT_END", "MD_ROM_SDIO_INIT", "MD_ROM_SDIO_MBX_HS", "MD_ROM_BOOTING", "MD_ROM_BOOT_READY", "MD_ROM_EXCEPTION", NULL, }; #define BOOT_CDEV_NAME "eemcs_ctrl" extern void lte_sdio_on(void); extern void lte_sdio_off(void); /*modem image version definitions*/ static char * product_str[] = { [INVALID_VARSION]=INVALID_STR, [DEBUG_VERSION]=DEBUG_STR, [RELEASE_VERSION]=RELEASE_STR}; static char * type_str[] = { [modem_invalid]=VER_INVALID_STR, [modem_2g]=VER_2G_STR, [modem_3g]=VER_3G_STR, [modem_wg]=VER_WG_STR, [modem_tg]=VER_TG_STR, [modem_lwg]=VER_LWG_STR, [modem_ltg]=VER_LTG_STR, [modem_sglte]=VER_SGLTE_STR, }; typedef struct { char post_fix_name[EXT_MD_POST_FIX_LEN]; modem_type_t modem_type; } MD_IMG_LIST; static MD_IMG_LIST md_img_list[MD_IMG_MAX_CNT] = { {"2g_n", modem_2g}, {"3g_n", modem_3g}, {"wg_n", modem_wg}, {"tg_n", modem_tg}, {"lwg_n", modem_lwg}, {"ltg_n", modem_ltg}, {"sglte_n", modem_sglte}, }; #if 0 static void memory_dump(void * start_addr, int size){ unsigned int *curr_p = (unsigned int *)start_addr; int i = 0; for(i=0; i*4 img_info[MD_INDEX].file_name, "%smodem_%s.img", CONFIG_MODEM_FIRMWARE_FOLDER, str); //sprintf(md_info->img_info[DSP_INDEX].file_name, "%sdsp_%s.bin", CONFIG_MODEM_FIRMWARE_FOLDER, str); // Find and set MD for(i=0; iimg_info[MD_INDEX].file_name, "%s", eemcs_boot_inst.md_img_file_list[i].full_path); break; } } else { ret = -1; DBGLOG(BOOT, ERR, "modem_%s.img not exist", str); break; } } // Find and set DSP for(i=0; iimg_info[DSP_INDEX].file_name, "%s", eemcs_boot_inst.dsp_img_file_list[i].full_path); break; } } else { ret = -1; DBGLOG(BOOT, ERR, "dsp_%s.bin not exist", str); break; } } get_ap_platform_ver(md_info->ap_platform); md_info->ap_info.image_type=type_str[get_ext_modem_support(eemcs_boot_inst.md_id)]; md_info->ap_info.platform = md_info->ap_platform; return ret; } KAL_INT32 eemcs_get_md_info_str(char* info_str){ MD_CHECK_HEADER *img_info= &eemcs_boot_inst.md_info.img_info[MD_INDEX].img_chk_header; AP_CHECK_INFO *ap_info = &eemcs_boot_inst.md_info.ap_info; /* Construct image information string */ return sprintf(info_str, "MD:%s*%s*%s*%s\nAP:%s*%s\n(MD)%s\n", type_str[img_info->image_type],img_info->platform, img_info->build_ver,img_info->build_time, ap_info->image_type,ap_info->platform, product_str[img_info->product_ver]); } KAL_UINT32 eemcs_check_md_img_exist_list(void){ struct file *filp = NULL; char full_path[64]={0}; int i = 0, img_list_index = 0; DEBUG_LOG_FUNCTION_ENTRY; memset(eemcs_boot_inst.md_img_exist_list, 0, sizeof(eemcs_boot_inst.md_img_exist_list)); for (i = 0; i< MD_IMG_MAX_CNT; i++){ snprintf(full_path, 64, "%smodem_%d_%s.img", CONFIG_MODEM_FIRMWARE_FOLDER, eemcs_boot_inst.md_id + 1, md_img_list[i].post_fix_name); filp = filp_open(full_path, O_RDONLY, 0777); if (IS_ERR(filp)) { DBGLOG(BOOT, ERR, "MD ROM %s not exist!", full_path); continue; } filp_close(filp, NULL); eemcs_boot_inst.md_img_exist_list[img_list_index++] = md_img_list[i].modem_type; } DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } KAL_UINT32* eemcs_get_md_img_exist_list(void){ DEBUG_LOG_FUNCTION_ENTRY; eemcs_check_md_img_exist_list(); DEBUG_LOG_FUNCTION_LEAVE; return eemcs_boot_inst.md_img_exist_list; } KAL_UINT32 eemcs_get_md_img_exist_list_size(void){ DEBUG_LOG_FUNCTION_ENTRY; DEBUG_LOG_FUNCTION_LEAVE; return sizeof(eemcs_boot_inst.md_img_exist_list); } KAL_UINT32 eemcs_get_md_id(void){ DEBUG_LOG_FUNCTION_ENTRY; DEBUG_LOG_FUNCTION_LEAVE; return eemcs_boot_inst.md_id; } KAL_INT32 eemcs_boot_get_ext_md_post_fix(char *post_fix) { DEBUG_LOG_FUNCTION_ENTRY; memcpy(post_fix, eemcs_boot_inst.ext_modem_post_fix, EXT_MD_POST_FIX_LEN); DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } void eemcs_set_reload_image(KAL_INT32 need_reload){ DBGLOG(BOOT, TRA, "eemcs_set_reload_image: md%d=%d", eemcs_boot_inst.md_id+1, need_reload); eemcs_boot_inst.md_need_reload = need_reload; memset(&eemcs_boot_inst.md_info.img_info[MD_INDEX].img_chk_header, 0, sizeof(MD_CHECK_HEADER)); } /****************************************************************************/ #define ENABLE_MD_TYPE_CHECK static int check_md_header( int md_id, unsigned int parse_addr, IMG_INFO *image) { int ret; bool md_type_check = false; bool md_plat_check = false; bool md_sys_match = false; bool md_size_check = false; MD_CHECK_HEADER *head = &image->img_chk_header; AP_CHECK_INFO *ap_info = &eemcs_boot_inst.md_info.ap_info; #ifdef ENABLE_MD_IMG_SECURITY_FEATURE memcpy(head, (void*)(parse_addr - sizeof(MD_CHECK_HEADER)), sizeof(MD_CHECK_HEADER)); #else { //get MD Check Header from none cipher image struct file *filp = NULL; mm_segment_t curr_fs = get_fs(); set_fs(KERNEL_DS); DBGLOG(BOOT, INF, "Get header form MD%d image name=%s", eemcs_boot_inst.md_id+1, image->file_name); filp = filp_open(image->file_name, O_RDONLY, 0777); if (IS_ERR(filp)) { DBGLOG(BOOT, ERR, "open MD ROM %s fail", image->file_name); return -CCCI_ERR_LOAD_IMG_NOMEM; } filp->f_op->llseek(filp, 0, SEEK_END); filp->f_pos = filp->f_pos - sizeof(MD_CHECK_HEADER); filp->f_op->read(filp, (char*)head, sizeof(MD_CHECK_HEADER), &filp->f_pos); filp_close(filp, NULL); set_fs(curr_fs); } #endif DBGLOG(BOOT, INF, "**********************MD image check***************************"); ret = strncmp(head->check_header, MD_HEADER_MAGIC_NO, 12); if(ret) { DBGLOG(BOOT, INF, "md check header not exist!"); ret = 0; } else { if(head->header_verno != MD_HEADER_VER_NO) { DBGLOG(BOOT, INF, "[Error]md check header version mis-match to AP:[%d]!", head->header_verno); } else { #ifdef ENABLE_MD_TYPE_CHECK if((head->image_type != modem_invalid) && (head->image_type == get_ext_modem_support(md_id))) { md_type_check = true; } #else md_type_check = true; #endif #ifdef ENABLE_CHIP_VER_CHECK if(!strncmp(head->platform, ap_info->ap_platorm, AP_PLATFORM_LEN)) { md_plat_check = true; } #else md_plat_check = true; #endif if(head->bind_sys_id == (md_id+1)) { md_sys_match = true; } #ifdef ENABLE_MEM_SIZE_CHECK if(head->header_verno >= 2) { unsigned int md_size = 0; md_size = get_ext_md_mem_size(md_id); if (head->mem_size == md_size) { md_size_check = true; } else if(head->mem_size < md_size) { md_size_check = true; DBGLOG(BOOT, WAR, "[Warning]md size in md header isn't sync to DFO setting: (%08x, %08x)", head->mem_size, md_size); } } else { md_size_check = true; } #else md_size_check = true; #endif if(md_type_check && md_plat_check && md_sys_match && md_size_check) { DBGLOG(BOOT, INF, "Modem header check OK!"); } else { DBGLOG(BOOT, INF, "[Error]Modem header check fail!"); if(!md_type_check){ DBGLOG(BOOT, INF, "[Reason]MD type(2G/3G/LTE) mis-match to AP!"); aee_kernel_warning("[EEMCS]", "[ERROR] MD type(2G/3G/LTE) mis-match to AP!"); } if(!md_plat_check) DBGLOG(BOOT, INF, "[Reason]MD platform mis-match to AP!"); if(!md_sys_match) DBGLOG(BOOT, INF, "[Reason]MD image is not for MD SYS%d!", md_id+1); if(!md_size_check) DBGLOG(BOOT, INF, "[Reason]MD mem size mis-match to AP setting!"); ret = -CCCI_ERR_LOAD_IMG_MD_CHECK; } DBGLOG(BOOT, INF, "(MD)[type]=%s, (AP)[type]=%s",type_str[image->img_chk_header.image_type], ap_info->image_type); DBGLOG(BOOT, INF, "(MD)[plat]=%s, (AP)[plat]=%s",image->img_chk_header.platform, ap_info->platform); if(head->header_verno >= 2) DBGLOG(BOOT, INF, "(AP)[size]=%x", image->mem_size); DBGLOG(BOOT, INF, "(MD)[build_ver]=%s, [build_time]=%s",(char*)image->img_chk_header.build_ver, (char*)image->img_chk_header.build_time); DBGLOG(BOOT, INF, "(MD)[product_ver]=%s", product_str[image->img_chk_header.product_ver]); } } DBGLOG(BOOT, INF, "**********************MD image check***************************"); return ret; } /*********************************************************************************/ /* API about security check */ /* */ /*********************************************************************************/ #if defined(ENABLE_MD_IMG_SECURITY_FEATURE) || defined(ENABLE_MD_SECURITY_RO_FEATURE) static int sec_lib_version_check(void) { int ret = 0; int sec_lib_ver = masp_ccci_version_info(); if(sec_lib_ver != CURR_SEC_CCCI_SYNC_VER){ DBGLOG(BOOT, ERR, "[Error]sec lib for ccci mismatch: sec_ver:%d, ccci_ver:%d", sec_lib_ver, CURR_SEC_CCCI_SYNC_VER); ret = -1; } return ret; } #endif #ifdef ENABLE_MD_IMG_SECURITY_FEATURE //--------------------------------------------------------------------------------------------------// // New signature check version. 2012-2-2. // Change to use masp_ccci_signfmt_verify_file(char *file_path, unsigned int *data_offset, unsigned int *data_sec_len) // masp_ccci_signfmt_verify_file parameter description // @ file_path: such as etc/firmware/modem.img // @ data_offset: the offset address that bypass signature header // @ data_sec_len: length of signature header + tail // @ return value: 0-success; //---------------------------------------------------------------------------------------------------// static int signature_check_v2(int md_id, char* file_path, unsigned int *sec_tail_length) { unsigned int bypass_sec_header_offset = 0; unsigned int sec_total_len = 0; if( masp_ccci_signfmt_verify_file(file_path, &bypass_sec_header_offset, &sec_total_len) == 0 ){ //signature lib check success //-- check return value if(bypass_sec_header_offset > sec_total_len){ DBGLOG(BOOT, ERR, "sign check fail(0x%x, 0x%x!)", bypass_sec_header_offset, sec_total_len); return -CCCI_ERR_LOAD_IMG_SIGN_FAIL; } else { DBGLOG(BOOT, TRA, "sign check success(0x%x, 0x%x)", bypass_sec_header_offset, sec_total_len); *sec_tail_length = sec_total_len - bypass_sec_header_offset; return (int)bypass_sec_header_offset; // Note here, offset is more than 2G is not hoped } } else { DBGLOG(BOOT, ERR, "sign check fail!"); return -CCCI_ERR_LOAD_IMG_SIGN_FAIL; } } static struct file *open_img_file(char *name, int *sec_fp_id) { #ifdef ENABLE_MD_IMG_SECURITY_FEATURE int fp_id = OSAL_FILE_NULL; fp_id = osal_filp_open_read_only(name); DBGLOG(BOOT, TRA, "sec_open: fd=%d", fp_id); if(sec_fp_id != NULL) *sec_fp_id = fp_id; DBGLOG(BOOT, TRA, "sec_open: file_ptr=0x%x", (unsigned int)osal_get_filp_struct(fp_id)); return (struct file *)osal_get_filp_struct(fp_id); #else //CCCI_DBG_COM_MSG("std_open!\n"); return filp_open(name, O_RDONLY, 0644);// 0777 #endif } static void close_img_file(struct file *filp_id, int sec_fp_id) { #ifdef ENABLE_MD_IMG_SECURITY_FEATURE DBGLOG(BOOT, TRA, "sec_close: fd=%d", sec_fp_id); osal_filp_close(sec_fp_id); #else filp_close(filp_id,current->files); #endif } /*********************************************************************************/ /* load modem&dsp image function */ /* */ /*********************************************************************************/ static int load_cipher_firmware_v2( int md_id, int fp_id, IMG_INFO *img, unsigned int cipher_img_offset, unsigned int cipher_img_len) { int ret; unsigned int data_offset; void *addr = ioremap_nocache(img->address,cipher_img_len); img->remap_address = (unsigned int)addr; if (addr==NULL) { DBGLOG(BOOT, ERR, "ioremap image fail!"); ret = -CCCI_ERR_LOAD_IMG_NO_ADDR; goto out; } if(SEC_OK != (ret = masp_ccci_decrypt_cipherfmt(fp_id, cipher_img_offset, (char*)addr, cipher_img_len, &data_offset)) ) { DBGLOG(BOOT, ERR, "cipher image decrypt fail:ret=%d!", ret); ret = -CCCI_ERR_LOAD_IMG_CIPHER_FAIL; goto unmap_out; } img->size = cipher_img_len; img->offset += data_offset; addr+=cipher_img_len; ret=check_md_header(md_id, ((unsigned int)addr), img); unmap_out: // iounmap(img->remap_address); out: return ret; } static int load_std_firmware(int md_id, struct file *filp, IMG_INFO *img) { void *start; int ret = 0; int check_ret = 0; int read_size = 0; mm_segment_t curr_fs; unsigned long load_addr; unsigned int end_addr; const int size_per_read = 1024 * 1024; // const int size = 1024; curr_fs = get_fs(); set_fs(KERNEL_DS); load_addr = img->address; filp->f_pos = img->offset; while (1) { // Map 1M memory start = ioremap_nocache((load_addr + read_size), size_per_read); //DBGLOG(BOOT, ERR, "map %08x --> %08x\n", (unsigned int)(load_addr+read_size), (unsigned int)start); if (start <= 0) { DBGLOG(BOOT, ERR, "image ioremap fail: %d", (unsigned int)start); set_fs(curr_fs); return -CCCI_ERR_LOAD_IMG_NOMEM; } ret = filp->f_op->read(filp, start, size_per_read, &filp->f_pos); //if ((ret < 0) || (ret > size_per_read)) { if ((ret < 0) || (ret > size_per_read) || ((ret == 0) && (read_size == 0))) { //make sure image size isn't 0 DBGLOG(BOOT, ERR, "image read fail: size=%d", ret); ret = -CCCI_ERR_LOAD_IMG_FILE_READ; goto error; } else if(ret == size_per_read) { read_size += ret; iounmap(start); } else { read_size += ret; img->size = read_size - img->tail_length; /* Note here, signatured image has file tail info. */ DBGLOG(BOOT, INF, "%s, image_size=0x%x, read_size=0x%x, tail=0x%x", img->file_name, img->size, read_size, img->tail_length); iounmap(start); break; } } start = ioremap_nocache(round_down(load_addr + img->size - 0x4000, 0x4000), round_up(img->size, 0x4000) - round_down(img->size - 0x4000, 0x4000)); // Make sure in one scope end_addr = ((unsigned int)start + img->size - round_down(img->size - 0x4000, 0x4000)); if((check_ret = check_md_header(md_id, end_addr, img)) < 0) { ret = check_ret; goto error; } iounmap(start); set_fs(curr_fs); DBGLOG(BOOT, INF, "Load %s (size=0x%x) to 0x%lx", img->file_name, read_size, load_addr); img->remap_address = (unsigned int)ioremap_nocache(img->address, img->size); return read_size; error: iounmap(start); set_fs(curr_fs); return ret; } /***********************************************************************************************/ KAL_INT32 eemcs_boot_load_image(unsigned int md_id) { struct file *filp = NULL; int fp_id; int ret = KAL_SUCCESS; int offset = 0; unsigned int sec_tail_length = 0; #ifdef ENABLE_MD_IMG_SECURITY_FEATURE unsigned int img_len=0; #endif IMG_INFO *img = &eemcs_boot_inst.md_info.img_info[MD_INDEX]; if (eemcs_boot_inst.md_need_reload == 0){ DBGLOG(BOOT, INF, "md%d image no need reload", md_id+1); return KAL_SUCCESS; } DEBUG_LOG_FUNCTION_ENTRY; img->address = get_ext_md_mem_start_addr(eemcs_boot_inst.md_id); img->mem_size = get_ext_md_mem_size(eemcs_boot_inst.md_id); //clear_md_region_protection(eemcs_boot_inst.md_id); get_ext_md_post_fix(eemcs_boot_inst.md_id, eemcs_boot_inst.ext_modem_post_fix, NULL); if (set_md_info(eemcs_boot_inst.ext_modem_post_fix) < 0) { ret = -CCCI_ERR_LOAD_IMG_FILE_OPEN; goto out; } filp = open_img_file(img->file_name, &fp_id); if (IS_ERR(filp)) { DBGLOG(BOOT, ERR, "open %s fail: %ld", img->file_name, PTR_ERR(filp)); ret = -CCCI_ERR_LOAD_IMG_FILE_OPEN; filp = NULL; goto out; } else { DBGLOG(BOOT, INF, "open %s OK", img->file_name); } //Begin to check header //only modem.img need check signature and cipher header //sign_check = false; sec_tail_length = 0; //step1:check if need to signature //offset=signature_check(filp); #ifdef ENABLE_MD_IMG_SECURITY_FEATURE offset = signature_check_v2(md_id, img->file_name, &sec_tail_length); DBGLOG(BOOT, INF, "signature_check offset:%d, tail:%d", offset, sec_tail_length); if (offset<0) { DBGLOG(BOOT, ERR, "signature_check fail: %d", offset); ret=offset; goto out; } #endif img->offset=offset; img->tail_length = sec_tail_length; iounmap((void*)img->remap_address); //step2:check if need to cipher #ifdef ENABLE_MD_IMG_SECURITY_FEATURE if (masp_ccci_is_cipherfmt(fp_id, offset, &img_len)) {// Cipher image DBGLOG(BOOT, INF, "cipher image"); //ret=load_cipher_firmware(filp,img,&cipher_header); ret=load_cipher_firmware_v2(md_id, fp_id, img, offset, img_len); if(ret<0) { DBGLOG(BOOT, ERR, "load_cipher_firmware fail: %d", ret); goto out; } DBGLOG(BOOT, INF, "load_cipher_firmware done: %d", ret); } else #endif { DBGLOG(BOOT, INF, "Not cipher image"); ret=load_std_firmware(md_id, filp, img); if(ret<0) { DBGLOG(BOOT, ERR, "load_firmware fail: %d", ret); goto out; } } out: if(filp != NULL){ close_img_file(filp, fp_id); } DEBUG_LOG_FUNCTION_LEAVE; if (ret == KAL_SUCCESS){ eemcs_boot_inst.md_need_reload = 0; } return ret; } #endif KAL_INT32 eemcs_boot_swint_callback(KAL_UINT32 status) { DEBUG_LOG_FUNCTION_ENTRY; DBGLOG(BOOT, DBG, "[SWINT CALLBACK] status = 0x%08X", status); KAL_ASSERT(status != 0); eemcs_boot_inst.mailbox.d2h_wakeup_cond = 1; wake_up(&eemcs_boot_inst.mailbox.d2h_wait_q); DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } KAL_INT32 eemcs_boot_rx_callback(struct sk_buff *skb, KAL_UINT32 private_data) { CCCI_BUFF_T *buf = NULL; DEBUG_LOG_FUNCTION_ENTRY; KAL_ASSERT(boot_node != NULL); if (skb) { buf = (CCCI_BUFF_T *)skb->data; DBGLOG(BOOT, DBG, "CCCI_H: 0x%08X, 0x%08X, %02d, 0x%08X", buf->magic, buf->id, buf->channel, buf->reserved); /* check is a exception packet or not*/ if(buf->magic == MD_EX_MAGIC){ eemcs_expt_handshake(skb); return KAL_SUCCESS; } else if (buf->magic == MD_TSID_MAGIC) { eemcs_bootup_trace(skb); return KAL_SUCCESS; } } if (CDEV_OPEN == atomic_read(&boot_node->cdev_state)) { skb_queue_tail(&boot_node->rx_skb_list, skb); // spin_lock_ireqsave inside, refering skbuff.c atomic_inc(&boot_node->rx_pkt_cnt); // increase rx_pkt_cnt wake_up(&boot_node->rx_waitq); // wake up rx_waitq } else { DBGLOG(BOOT, WAR, "PKT DROP when PORT%d(%s) closed", boot_node->eemcs_port_id, \ boot_node->cdev_name); dev_kfree_skb(skb); } DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } KAL_UINT32 eemcs_boot_get_state(void) { DEBUG_LOG_FUNCTION_ENTRY; DEBUG_LOG_FUNCTION_LEAVE; return eemcs_boot_inst.boot_state; } static KAL_INT32 eemcs_boot_change_state(KAL_UINT32 new_state) { DEBUG_LOG_FUNCTION_ENTRY; DBGLOG(BOOT, INF, "XBOOT_STA: %s(%d) -> %s(%d)", g_md_sta_str[eemcs_boot_inst.boot_state], \ eemcs_boot_inst.boot_state, g_md_sta_str[new_state], new_state); eemcs_boot_inst.boot_state = new_state; eemcs_boot_trace_boot(new_state); DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } static KAL_INT32 eemcs_boot_mbx_read_noack(KAL_UINT32 *val, KAL_UINT32 size) { //int value = -1; KAL_INT32 result = KAL_FAIL; DEBUG_LOG_FUNCTION_ENTRY; if (val == NULL || size < sizeof(KAL_UINT32) * 2) { DBGLOG(BOOT, ERR, "Invalid mailbox arguments"); return -EMCS_ERR_INVALID_PARA; } wait_event(eemcs_boot_inst.mailbox.d2h_wait_q, eemcs_boot_inst.mailbox.d2h_wakeup_cond == 1 || time_out_abort); if (time_out_abort) { time_out_abort = 0; DBGLOG(BOOT, ERR, "[RX]PORT0 get boot mailbox timeout"); return -EMCS_ERR_TIMEOUT; } else { eemcs_boot_inst.mailbox.d2h_wakeup_cond = 0; } do { if (ccci_boot_mbx_read(val, size) != KAL_SUCCESS) break; result = KAL_SUCCESS; } while (0); if (*val == 0 && *(val + 1) == 0) { DBGLOG(BOOT, ERR, "D2H mailbox are not available"); return -EMCS_ERR_CMDCRC; } DEBUG_LOG_FUNCTION_LEAVE; return result; } static KAL_INT32 eemcs_boot_mbx_write_noack(KAL_UINT32 *val, KAL_UINT32 size) { KAL_INT32 result = KAL_FAIL; DEBUG_LOG_FUNCTION_ENTRY; if (!val || size < sizeof(KAL_UINT32)*2) { DBGLOG(BOOT, ERR, "Invalid mailbox arguments"); return -EMCS_ERR_INVALID_PARA; } do { DBGLOG(BOOT, DBG, "boot mailbox: 0x%X, 0x%X", *val, *(val + 1)); if (ccci_boot_mbx_write(val, size) != KAL_SUCCESS) break; result = KAL_SUCCESS; } while (0); DEBUG_LOG_FUNCTION_LEAVE; return result; } static KAL_INT32 eemcs_boot_mbx_ack(void) { KAL_INT32 result = KAL_SUCCESS; KAL_UINT32 mbx_data[2] = {0}; DEBUG_LOG_FUNCTION_ENTRY; mbx_data[0] = SDIOMAIL_BOOTING_ACK; mbx_data[1] = 0; if (eemcs_boot_mbx_write_noack(mbx_data, sizeof(KAL_INT32)*2) != KAL_SUCCESS) result = KAL_FAIL; else eemcs_boot_trace_mbx(mbx_data, sizeof(KAL_UINT32)*2, TRA_TYPE_MBX_TX); DEBUG_LOG_FUNCTION_LEAVE; return result; } static KAL_INT32 eemcs_boot_mbx_refuse(void) { KAL_INT32 result = KAL_SUCCESS; KAL_UINT32 mbx_data[2] = {0}; DEBUG_LOG_FUNCTION_ENTRY; mbx_data[0] = SDIOMAIL_REF; mbx_data[1] = 0; if (eemcs_boot_mbx_write_noack(mbx_data, sizeof(KAL_INT32) * 2) != KAL_SUCCESS) result = KAL_FAIL; else eemcs_boot_trace_mbx(mbx_data, sizeof(KAL_UINT32) * 2, TRA_TYPE_MBX_TX); DEBUG_LOG_FUNCTION_LEAVE; return result; } struct sk_buff *prepare_xcmd(KAL_UINT32 magic, KAL_UINT32 msg_id, KAL_UINT32 status) { struct sk_buff *new_skb = NULL; XBOOT_CMD *xcmd_header = NULL; new_skb = ccci_boot_mem_alloc(sizeof(XBOOT_CMD) + CCCI_BOOT_HEADER_ROOM, GFP_ATOMIC); if (new_skb == NULL) { DBGLOG(BOOT, ERR, "alloc skb fail"); goto _fail; } skb_reserve(new_skb, CCCI_BOOT_HEADER_ROOM); xcmd_header = (XBOOT_CMD *)skb_put(new_skb, sizeof(XBOOT_CMD)); memset(new_skb->data, 0, sizeof(XBOOT_CMD)); xcmd_header->magic = magic; xcmd_header->msg_id = msg_id; xcmd_header->status = status; return new_skb; _fail: return NULL; } KAL_INT32 eemcs_boot_cmd_ack(KAL_UINT32 ack_id) { KAL_INT32 result = KAL_FAIL; struct sk_buff *new_skb = NULL; XBOOT_CMD *xcmd_header = NULL; DEBUG_LOG_FUNCTION_ENTRY; if (ccci_boot_write_space_check() == 0) { DBGLOG(BOOT, ERR, "Memory space is not available on TX_Q_0!"); goto _fail; } new_skb = prepare_xcmd(MAGIC_MD_CMD_ACK, ack_id, XBOOT_OK); if (new_skb == NULL) { DBGLOG(BOOT, ERR, "Failed to alloc skb!"); goto _fail; } xcmd_header = (XBOOT_CMD *)new_skb->data; eemcs_boot_trace_xcmd(xcmd_header, TRA_TYPE_XCMD_TX); if (ccci_boot_write_desc_to_q(new_skb) != KAL_SUCCESS) { DBGLOG(BOOT, ERR, "Failed to send xBoot ack!"); goto _write_fail; } result = KAL_SUCCESS; goto _ok; _write_fail: dev_kfree_skb(new_skb); _fail: _ok: DEBUG_LOG_FUNCTION_LEAVE; return result; } KAL_UINT8 eemcs_boot_calc_checksum(KAL_UINT8 *data, KAL_UINT32 len, KAL_UINT8 *sum) { KAL_UINT8 cs = 0; KAL_UINT32 idx = 0; for (idx = 0; idx < len; idx++) { cs += data[idx]; } if (sum != NULL) *sum = cs; return ~cs; } //#define _EEMCS_BOOT_FILE_PACKET_TRACE // Trace each file packet //#define _EEMCS_BOOT_DEBUG_IMG // Debug sent image data KAL_INT32 eemcs_boot_file_packet_ack(KAL_UINT32 file_offset, KAL_UINT32 size) { KAL_UINT8 checksum = 0; KAL_UINT32 bin_block_count = 0; #if defined(_EEMCS_BOOT_FILE_PACKET_TRACE) || defined(_EEMCS_TRACE_SUPPORT) KAL_UINT8 sum = 0; KAL_UINT8 sum_tmp = 0; #endif KAL_UINT32 tx_pkt_room = 0; KAL_INT32 result = KAL_FAIL; KAL_UINT32 bin_offset = file_offset; mm_segment_t curr_fs; struct file *filp = NULL; #ifdef _EEMCS_BOOT_DEBUG_IMG struct file *s_filp = NULL; char s_name[256] = {0}; #endif // _EEMCS_BOOT_DEBUG_IMG struct sk_buff *new_skb = NULL; char full_path[64]; DEBUG_LOG_FUNCTION_ENTRY; curr_fs = get_fs(); set_fs(KERNEL_DS); // Open modem.img snprintf(full_path, 64, "%s", eemcs_boot_inst.md_info.img_info[MD_INDEX].file_name); DBGLOG(BOOT, DBG, "MD%d image name=%s", eemcs_boot_inst.md_id+1, full_path); filp = filp_open(full_path, O_RDONLY, 0777); if (IS_ERR(filp)) { DBGLOG(BOOT, ERR, "open MD ROM %s fail", full_path); goto _open_fail; } #ifdef _EEMCS_BOOT_DEBUG_IMG sprintf(s_name, "/data/app/dbg_0x%08X_0x%08X.dat", file_offset, size); s_filp = filp_open(s_name, O_WRONLY | O_CREAT, 0777); if (IS_ERR(s_filp)) { DBGLOG(BOOT, ERR, "open debug image file %s fail: %d", s_name); } else { DBGLOG(BOOT, DBG, "Debug image file to %s", s_name); } #endif // _EEMCS_BOOT_DEBUG_IMG DBGLOG(BOOT, DBG, "Prepare md image:(OFFSET,LEN)=(0x%X,0x%X)", file_offset, size); while (size > 0) { KAL_UINT32 skb_size = 0; KAL_UINT32 bin_size = 0; unsigned char *bin_head = NULL; new_skb = NULL; while ((tx_pkt_room = ccci_boot_write_space_check()) <= 0){ if (time_out_abort) { DBGLOG(BOOT, ERR, "send md image timeout"); time_out_abort = 0; result = -EMCS_ERR_TIMEOUT; goto _skb_fail; } }; if (tx_pkt_room > 0) { #ifdef CCCI_SDIO_HEAD if (size > (g_eemcs_file_pkt_len - CCCI_BOOT_HEADER_ROOM)) { skb_size = g_eemcs_file_pkt_len; bin_size = g_eemcs_file_pkt_len - CCCI_BOOT_HEADER_ROOM; } else { skb_size = size + CCCI_BOOT_HEADER_ROOM; bin_size = size; } #else if (size > g_eemcs_file_pkt_len) { skb_size = g_eemcs_file_pkt_len; bin_size = g_eemcs_file_pkt_len; } else { skb_size = size; bin_size = size; } #endif new_skb = ccci_boot_mem_alloc(skb_size, GFP_ATOMIC); } else { DBGLOG(BOOT, WAR, "SWQ space is not available"); } if (new_skb == NULL) { DBGLOG(BOOT, ERR, "boot_mem_alloc_skb fail"); goto _skb_fail; } // Reserve a header room for feature usage #ifdef CCCI_SDIO_HEAD skb_reserve(new_skb, CCCI_BOOT_HEADER_ROOM); #endif bin_head = skb_put(new_skb, bin_size); // Read data from modem.img #ifdef ENABLE_MD_IMG_SECURITY_FEATURE { IMG_INFO *img = &eemcs_boot_inst.md_info.img_info[MD_INDEX]; //DBGLOG(BOOT, DBG, "Read image from memory %#X", (unsigned int)(img->remap_address + bin_offset) ); memcpy(bin_head, (void *)(img->remap_address + bin_offset), bin_size); } #else filp->f_pos = bin_offset; result = filp->f_op->read(filp, bin_head, bin_size, &filp->f_pos); if (result <= 0) { DBGLOG(BOOT, ERR, "read %s %dB(%d, off=%d) fail: %d", full_path, bin_size, \ size, bin_offset, result); goto _read_write_fail; } #endif #ifdef _EEMCS_BOOT_DEBUG_IMG s_filp->f_op->write(s_filp, bin_head, bin_size, &s_filp->f_pos); DBGLOG(BOOT, INF, "Write debug image file. Size = %d, Pos = %d", bin_size, s_filp->f_pos); #endif // _EEMCS_BOOT_DEBUG_IMG #if defined(_EEMCS_BOOT_FILE_PACKET_TRACE) || defined(_EEMCS_TRACE_SUPPORT) checksum = eemcs_boot_calc_checksum(bin_head, bin_size, &sum_tmp); #if defined(_EEMCS_BOOT_FILE_PACKET_TRACE) eemcs_boot_trace_xcmd_file(bin_offset, bin_size, checksum); #else // !_EEMCS_BOOT_FILE_PACKET_TRACE sum += sum_tmp; #endif // _EEMCS_BOOT_FILE_PACKET_TRACE #endif // _EEMCS_BOOT_FILE_PACKET_TRACE || _EEMCS_TRACE_SUPPORT // Write out if (ccci_boot_write_desc_to_q(new_skb) != KAL_SUCCESS) { DBGLOG(BOOT, ERR, "send xBoot file fail"); goto _read_write_fail; } size = size - bin_size; bin_offset = bin_offset + bin_size; KAL_ASSERT(size >= 0); if ((bin_block_count++ % 100) == 0){ // printk(KERN_ERR "."); // Print with prefix // printk_emit(0,3,NULL,0,"."); // Print without prefix DBGLOG(BOOT, INF, "MD Image Loading.."); // Print line by line } // msleep(50); }; #ifdef _EEMCS_BOOT_DEBUG_IMG filp_close(s_filp, NULL); #endif #if defined(_EEMCS_TRACE_SUPPORT) checksum = ~sum; #endif // _EEMCS_TRACE_SUPPORT eemcs_boot_trace_xcmd_file(file_offset, bin_offset - file_offset, checksum); result = KAL_SUCCESS; goto _ok; _read_write_fail: dev_kfree_skb(new_skb); _skb_fail: _ok: filp_close(filp, NULL); set_fs(curr_fs); _open_fail: DEBUG_LOG_FUNCTION_LEAVE; return result; } static KAL_INT32 eemcs_boot_mbx_process(void) { KAL_INT32 result = -1; KAL_UINT32 mbx_data[2] = {0}; DEBUG_LOG_FUNCTION_ENTRY; DBGLOG(BOOT, INF, "wait for boot mailbox ...."); result = eemcs_boot_mbx_read_noack(mbx_data, sizeof(KAL_UINT32)*2); if (result != KAL_SUCCESS) { //DBGLOG(BOOT, ERR, "read mailbox fail: %d",result); return result; } DBGLOG(BOOT, DBG, "XBOOT_MBX: 0x%08X, 0x%08X", mbx_data[0], mbx_data[1]); eemcs_boot_trace_mbx(mbx_data, sizeof(KAL_UINT32)*2, TRA_TYPE_MBX_RX); switch(mbx_data[0]) { case SDIOMAIL_BOOTING_REQ: DBGLOG(BOOT, INF, "XBOOT_MBX: BOOTING_REQ @%s", g_md_sta_str[eemcs_boot_inst.boot_state]); if (eemcs_boot_get_state() == MD_BROM_SDIO_MBX_HS) eemcs_boot_change_state(MD_BROM_DL_START); else if(eemcs_boot_get_state() == MD_BL_SDIO_MBX_HS) eemcs_boot_change_state(MD_BL_DL_START); else DBGLOG(BOOT, ERR, "XBOOT_MBX: BOOTING_REQ @Invalid Status(%d)", eemcs_boot_inst.boot_state); result = eemcs_boot_mbx_ack(); break; case SDIOMAIL_DL_REQ: DBGLOG(BOOT, INF, "XBOOT_MBX: DL_REQ @%s", g_md_sta_str[eemcs_boot_inst.boot_state]); if(eemcs_boot_get_state() == MD_BROM_SDDL_HS) eemcs_boot_change_state(MD_BROM_SEND_STATUS); else DBGLOG(BOOT, ERR, "XBOOT_MBX: BOOT_REQ @Invalid Status(%d)", eemcs_boot_inst.boot_state); result = eemcs_boot_mbx_refuse(); break; default: DBGLOG(BOOT, INF, "XBOOT_MBX:(0x%08X, 0x%08X) @%s", mbx_data[0], mbx_data[1], \ g_md_sta_str[eemcs_boot_inst.boot_state]); result = eemcs_boot_mbx_refuse(); } DEBUG_LOG_FUNCTION_LEAVE; if (result != KAL_SUCCESS) return KAL_FAIL; else return KAL_SUCCESS; } static KAL_INT32 eemcs_boot_cmd_process(void) { KAL_INT32 result = KAL_FAIL; KAL_UINT32 offset = 0; KAL_UINT32 len = 0; KAL_UINT32 do_tx_transfer = 0; KAL_UINT32 rx_pkt_cnt = 0; KAL_UINT32 ack_id = 0; struct sk_buff *rx_skb = NULL; XBOOT_CMD *p_xcmd = NULL; XBOOT_CMD_PRINT *print_cmd = NULL; int i = 0; DEBUG_LOG_FUNCTION_ENTRY; KAL_ASSERT(boot_node != NULL); DBGLOG(BOOT, INF, "Wait for boot cmd ..."); wake_lock_timeout(&eemcs_boot_inst.eemcs_boot_wake_lock, HZ/2); do { wait_event(boot_node->rx_waitq, (atomic_read(&boot_node->rx_pkt_cnt) > 0) || time_out_abort); if (time_out_abort) { result = -EMCS_ERR_TIMEOUT; time_out_abort = 0; DBGLOG(BOOT, ERR, "[RX]PORT0 get boot cmd timeout"); goto _exit; } else { break; } } while(1); DBGLOG(BOOT, DBG, "Wait boot cmd done"); rx_skb = skb_dequeue(&boot_node->rx_skb_list); // There should be rx_skb in the list KAL_ASSERT(NULL != rx_skb); atomic_dec(&boot_node->rx_pkt_cnt); rx_pkt_cnt = atomic_read(&boot_node->rx_pkt_cnt); KAL_ASSERT(rx_pkt_cnt >= 0); p_xcmd = (XBOOT_CMD *)rx_skb->data; DBGLOG(BOOT, DBG, "XBOOT_CMD: 0x%08X, 0x%08X, 0x%08X, 0x%08X",\ p_xcmd->magic, p_xcmd->msg_id, p_xcmd->status, p_xcmd->reserved[0]); eemcs_boot_trace_xcmd(p_xcmd, TRA_TYPE_XCMD_RX); /* For reset in boot modem function*/ if ((p_xcmd->magic == CCCI_MAGIC_NUM)){ DBGLOG(BOOT, ERR, "Reset modem at md boot stage!"); eemcs_cdev_msg(CCCI_PORT_CTRL, p_xcmd->msg_id, 0); result = -EMCS_ERR_RESET_MD; goto _free_exit; } if (p_xcmd->magic != (unsigned int)MAGIC_MD_CMD) { DBGLOG(BOOT, ERR, "invalid XBOOT CMD Magic: 0x%X!=0x%X", p_xcmd->magic, MAGIC_MD_CMD); result = -EMCS_ERR_CMDCRC; goto _free_exit; } switch(p_xcmd->msg_id) { case CMDID_MD_BUF_SIZE_CHANGE: { XBOOT_CMD_BUFSIZE *p_cmd = (XBOOT_CMD_BUFSIZE*)p_xcmd; g_eemcs_file_pkt_len = p_cmd->buf_size; DBGLOG(BOOT, INF, "XBOOT_CMD: MD_BUF_SIZE_CHANGE=%d", g_eemcs_file_pkt_len); ack_id = CMDID_ACK_MD_BUF_SIZE_CHANGE; break; } case CMDID_BIN_LOAD_START: { DBGLOG(BOOT, INF, "XBOOT_CMD: BIN_LOAD_START @%s", g_md_sta_str[eemcs_boot_inst.boot_state]); if (eemcs_boot_get_state() == MD_BROM_DL_START) eemcs_boot_change_state(MD_BROM_DL_GET); else if (eemcs_boot_get_state() == MD_BL_DL_START) eemcs_boot_change_state(MD_BL_DL_GET); else { DBGLOG(BOOT, ERR, "unexpected XBOOT_CMD: BIN_LOAD_START @%s", g_md_sta_str[eemcs_boot_inst.boot_state]); result = -EMCS_ERR_BT_STATUS; goto _free_exit; } ack_id = CMDID_ACK_BIN_LOAD_START; break; } case CMDID_GET_BIN: { XBOOT_CMD_GETBIN *p_cmd = (XBOOT_CMD_GETBIN *)p_xcmd; offset = p_cmd->offset; len = p_cmd->len; DBGLOG(BOOT, INF, "XBOOT_CMD: GET_BIN(%x, %x) @%s", offset, len, \ g_md_sta_str[eemcs_boot_inst.boot_state]); if (eemcs_boot_get_state() == MD_BROM_DL_GET) eemcs_boot_change_state(MD_BROM_DL_END); else if (eemcs_boot_get_state() == MD_BL_DL_GET) eemcs_boot_change_state(MD_BL_DL_END); else { if ((eemcs_boot_get_state() != MD_BL_DL_END) && (eemcs_boot_get_state() != MD_BROM_DL_END)) { DBGLOG(BOOT, ERR, "unexpected XBOOT_CMD: GET_BIN @%s", g_md_sta_str[eemcs_boot_inst.boot_state]); result = -EMCS_ERR_BT_STATUS; goto _free_exit; } } ack_id = CMDID_ACK_GET_BIN; // set flag for transfer. do_tx_transfer = 1; break; } case CMDID_BIN_LOAD_END: { DBGLOG(BOOT, INF, "XBOOT_CMD: BIN_LOAD_END @%s", g_md_sta_str[eemcs_boot_inst.boot_state]); if (eemcs_boot_get_state() == MD_BROM_DL_END) eemcs_boot_change_state(MD_BROM_SDDL_HS); else if (eemcs_boot_get_state() == MD_BL_DL_END) eemcs_boot_change_state(MD_BL_SDIO_INIT); else { DBGLOG(BOOT, ERR, "unexpected XBOOT_CMD: BIN_LOAD_END @%s", g_md_sta_str[eemcs_boot_inst.boot_state]); result = -EMCS_ERR_BT_STATUS; goto _free_exit; } ack_id = CMDID_ACK_BIN_LOAD_END; break; } case CMDID_MD_BOOT_END: { DBGLOG(BOOT, INF, "XBOOT_CMD: MD_BOOT_END @%s", g_md_sta_str[eemcs_boot_get_state()]); if (eemcs_boot_get_state() == MD_BL_DL_END || eemcs_boot_get_state() == MD_BL_DL_GET) { eemcs_boot_change_state(MD_ROM_SDIO_INIT); change_device_state(EEMCS_MOLY_HS_P1); } else { DBGLOG(BOOT, ERR, "unexpected XBOOT_CMD: MD_BOOT_END @%s", g_md_sta_str[eemcs_boot_inst.boot_state]); result = -EMCS_ERR_BT_STATUS; goto _free_exit; } ack_id = CMDID_ACK_MD_BOOT_END; break; } case CMDID_MD_MSD_OUTPUT: { DBGLOG(BOOT, INF, "XBOOT_CMD: MD_MSD_OUTPUT @%s", g_md_sta_str[eemcs_boot_inst.boot_state]); print_cmd = (XBOOT_CMD_PRINT *)p_xcmd; if (unlikely((eemcs_boot_inst.debug_offset + print_cmd->str_len) >= BOOT_DEBUG_BUF_LEN)) { DBGLOG(BOOT, ERR, "xBoot print command overflow: len=%d", print_cmd->str_len); result = -EMCS_ERR_MSG_OVERFLOW; goto _free_exit; } memcpy(eemcs_boot_inst.debug_buff + eemcs_boot_inst.debug_offset, print_cmd->str, print_cmd->str_len); eemcs_boot_inst.debug_offset += print_cmd->str_len; ack_id = CMDID_ACK_MD_MSD_OUTPUT; break; } case CMDID_MSG_FLUSH: { DBGLOG(BOOT, INF, "XBOOT_CMD: MSG_FLUSH @%s", g_md_sta_str[eemcs_boot_inst.boot_state]); print_cmd = (XBOOT_CMD_PRINT *)p_xcmd; if (unlikely((eemcs_boot_inst.debug_offset + print_cmd->str_len) >= BOOT_DEBUG_BUF_LEN)) { DBGLOG(BOOT, ERR, "xBoot print command overflow: len=%d", print_cmd->str_len); result = -EMCS_ERR_MSG_OVERFLOW; goto _free_exit; } memcpy(eemcs_boot_inst.debug_buff + eemcs_boot_inst.debug_offset, print_cmd->str, print_cmd->str_len); eemcs_boot_inst.debug_offset += print_cmd->str_len; // output debug message whenever emcs debug level setting. printk("[EEMCS/BOOT] MD_Debug_Log(len=%d):\n", eemcs_boot_inst.debug_offset); for (i = 0; i < eemcs_boot_inst.debug_offset; i++) printk("%c", *(eemcs_boot_inst.debug_buff + i)); printk("\n"); // Reset debug buffer eemcs_boot_inst.debug_offset = 0; memset(eemcs_boot_inst.debug_buff, 0, BOOT_DEBUG_BUF_LEN); if (eemcs_boot_get_state() == MD_BROM_SEND_STATUS) eemcs_boot_change_state(MD_BL_SDIO_INIT); else if (eemcs_boot_get_state() == MD_BL_DL_END) DBGLOG(BOOT, DBG, "[CMDID_MSG_FLUSH] NOTHING TO DO ..."); ack_id = CMDID_ACK_MSG_FLUSH; break; } default: { DBGLOG(BOOT, ERR, "Invalid XBOOT_CMD id=0x%x", p_xcmd->msg_id); result = -EMCS_ERR_BT_STATUS; goto _free_exit; } } dev_kfree_skb(rx_skb); result = eemcs_boot_cmd_ack(ack_id); if (result != KAL_SUCCESS) goto _exit; if (do_tx_transfer) { result = eemcs_boot_file_packet_ack(offset, len); } DEBUG_LOG_FUNCTION_LEAVE; return result; _free_exit: dev_kfree_skb(rx_skb); _exit: DEBUG_LOG_FUNCTION_LEAVE; return result; } static KAL_INT32 eemcs_boot_mbx_init(void) { KAL_INT32 result = KAL_SUCCESS; DEBUG_LOG_FUNCTION_ENTRY; init_waitqueue_head(&eemcs_boot_inst.mailbox.d2h_wait_q); init_waitqueue_head(&eemcs_boot_inst.mailbox.h2d_wait_q); #if 0 /*20130528 move to MD_INIT*/ /* registered at MD_INIT, unregistered at MD_ROM_BOOT_READY*/ eemcs_boot_inst.cb_id = ccci_boot_mbx_register(eemcs_boot_swint_callback); if (eemcs_boot_inst.cb_id == KAL_FAIL) { DBGLOG(BOOT, ERR, "Failed to register SWINT callback !!"); result = KAL_FAIL; } #endif DEBUG_LOG_FUNCTION_LEAVE; return result; } static int eemcs_boot_open(struct inode *inode, struct file *file) { int id = iminor(inode); int ret = 0; DEBUG_LOG_FUNCTION_ENTRY; DBGLOG(BOOT, DEF, "open PORT%d(%s)", id, eemcs_boot_inst.boot_node.cdev_name); //4 <1> check multiple open if(CDEV_OPEN == atomic_read(&eemcs_boot_inst.boot_node.cdev_state)){ DBGLOG(BOOT, ERR, "PORT%d(%s) multi-open fail", id, eemcs_boot_inst.boot_node.cdev_name); return -EIO; } //4 <2> clear the rx_skb_list skb_queue_purge(&eemcs_boot_inst.boot_node.rx_skb_list); atomic_set(&eemcs_boot_inst.boot_node.rx_pkt_cnt, 0); //4 <3> register ccci channel ret = ccci_boot_register(eemcs_boot_inst.boot_node.ccci_ch.rx, eemcs_boot_rx_callback, 0); if(ret != KAL_SUCCESS){ DBGLOG(BOOT, ERR, "PORT%d(%s) open fail", id, eemcs_boot_inst.boot_node.cdev_name); return -EIO; } file->private_data = &eemcs_boot_inst.boot_node; nonseekable_open(inode, file); atomic_set(&eemcs_boot_inst.boot_node.cdev_state, CDEV_OPEN); DEBUG_LOG_FUNCTION_LEAVE; return ret; } static int eemcs_boot_release(struct inode *inode, struct file *file) { int id = iminor(inode); DEBUG_LOG_FUNCTION_ENTRY; DBGLOG(BOOT, INF, "release boot device: %s(%d)", eemcs_boot_inst.boot_node.cdev_name, id); atomic_set(&eemcs_boot_inst.boot_node.cdev_state, CDEV_CLOSE); skb_queue_purge(&eemcs_boot_inst.boot_node.rx_skb_list); atomic_set(&eemcs_boot_inst.boot_node.rx_pkt_cnt, 0); DEBUG_LOG_FUNCTION_LEAVE; return 0; } static ssize_t eemcs_boot_read(struct file *fp, char *buf, size_t count, loff_t *ppos) { unsigned int flag; eemcs_cdev_node_t *curr_node = (eemcs_cdev_node_t *)fp->private_data; KAL_UINT8 port_id = curr_node->eemcs_port_id; /* port_id */ KAL_UINT32 p_type, rx_pkt_cnt, read_len; struct sk_buff *rx_skb; unsigned char *payload=NULL; CCCI_BUFF_T *ccci_header; int ret = 0; DEBUG_LOG_FUNCTION_ENTRY; flag=fp->f_flags; DBGLOG(BOOT, TRA, "[RX]PORT%d User expect read_len=%d", port_id, count); p_type = ccci_get_port_type(port_id); if (p_type != EX_T_USER) { DBGLOG(BOOT, ERR, "[RX]PORT%d refuse user read: %d", port_id, p_type); goto _exit; } rx_pkt_cnt = atomic_read(&curr_node->rx_pkt_cnt); KAL_ASSERT(rx_pkt_cnt >= 0); if (rx_pkt_cnt == 0) { if (flag & O_NONBLOCK) { ret = -EAGAIN; DBGLOG(BOOT, TRA, "[RX]PORT%d no data to read",port_id); goto _exit; } ret = wait_event_interruptible(curr_node->rx_waitq, (atomic_read(&curr_node->rx_pkt_cnt) > 0)||(time_out_abort)); if (ret) { ret = -EINTR; DBGLOG(BOOT, ERR, "[RX]PORT%d Interrupted by syscall.signal=%lld", port_id, \ *(long long *)current->pending.signal.sig); goto _exit; } else if (time_out_abort) { time_out_abort = 0; ret = -EMCS_ERR_TIMEOUT; DBGLOG(BOOT, ERR, "[RX]PORT%d Interrupted by timeout abort", port_id); goto _exit; } } /*Cached memory from last read fail */ DBGLOG(BOOT, DBG, "[RX]PORT%d read rx_skb_list(pkt_len=%d)", port_id, rx_pkt_cnt); rx_skb = skb_dequeue(&curr_node->rx_skb_list); /* There should be rx_skb in the list */ KAL_ASSERT(NULL != rx_skb); atomic_dec(&curr_node->rx_pkt_cnt); rx_pkt_cnt = atomic_read(&curr_node->rx_pkt_cnt); KAL_ASSERT(rx_pkt_cnt >= 0); ccci_header = (CCCI_BUFF_T *)rx_skb->data; DBGLOG(BOOT, DBG, "[RX]PORT%d data: 0x%08x, 0x%08x, %02d, 0x%08x",\ port_id, ccci_header->data[0],ccci_header->data[1], ccci_header->channel, ccci_header->reserved); /*If not match please debug EEMCS CCCI demux skb part*/ //KAL_ASSERT(ccci_header->channel == curr_node->ccci_ch.rx); if (ccci_header->channel != curr_node->ccci_ch.rx){ dev_kfree_skb(rx_skb); DBGLOG(BOOT, ERR, "[RX]PORT%d drop pkt when Ch%d error: 0x%08x, 0x%08x, %02d, 0x%08x", \ port_id, curr_node->ccci_ch.rx, ccci_header->data[0],ccci_header->data[1], ccci_header->channel, ccci_header->reserved); atomic_inc(&curr_node->rx_pkt_drop_cnt); goto _exit; } if (!(ccci_get_port_cflag(port_id) & EXPORT_CCCI_H)) { read_len = ccci_header->data[1] - sizeof(CCCI_BUFF_T); /* remove CCCI_HEADER */ skb_pull(rx_skb, sizeof(CCCI_BUFF_T)); } else { if (ccci_header->data[0] == CCCI_MAGIC_NUM) { read_len = sizeof(CCCI_BUFF_T); } else { read_len = ccci_header->data[1]; } } payload = (unsigned char*)rx_skb->data; if (count < read_len) { DBGLOG(BOOT, ERR, "[RX]PORT%d User has not enough memory(%d, %d)", port_id, count, read_len); atomic_inc(&curr_node->rx_pkt_drop_cnt); ret = -ENOMEM; goto _exit; } DBGLOG(BOOT, DBG, "[RX]PORT%d copy_to_user(len=%d, %p->%p)\n", port_id, read_len, payload, buf); ret = copy_to_user(buf, payload, read_len); if (ret) { DBGLOG(BOOT, ERR, "[RX]PORT%d copy_to_user(len=%d, %p->%p) fail: %d", \ port_id, read_len, payload, buf, ret); } dev_kfree_skb(rx_skb); if (ret == 0) { DEBUG_LOG_FUNCTION_LEAVE; return read_len; } _exit: DEBUG_LOG_FUNCTION_LEAVE; return ret; } static ssize_t eemcs_boot_write(struct file *fp, const char __user *buf, size_t in_sz, loff_t *ppos) { ssize_t ret = -EMCS_ERR_INVAL_PARA; eemcs_cdev_node_t *curr_node = (eemcs_cdev_node_t *)fp->private_data; KAL_UINT8 port_id = curr_node->eemcs_port_id; /* port_id */ KAL_UINT32 p_type, control_flag; struct sk_buff *new_skb; CCCI_BUFF_T *ccci_header; size_t count = in_sz; size_t skb_alloc_size; KAL_UINT32 alloc_time = 0, curr_time = 0; DEBUG_LOG_FUNCTION_ENTRY; DBGLOG(BOOT, TRA, "[TX]write deivce iminor(%d) (%s),length(%d)",port_id,curr_node->cdev_name,count); p_type = ccci_get_port_type(port_id); if (p_type != EX_T_USER) { DBGLOG(BOOT, ERR, "[TX]PORT%d refuse port(%d) access user port", port_id, p_type); goto _exit; } if (port_id != CCCI_PORT_CTRL) { DBGLOG(BOOT, ERR, "[TX]PORT%d is not CCCI_PORT_CTRL", port_id); goto _exit; } control_flag = ccci_get_port_cflag(port_id); if((control_flag & EXPORT_CCCI_H) && (count < sizeof(CCCI_BUFF_T))) { DBGLOG(BOOT,ERR,"[TX]PORT%d invalid wirte len(%d)", port_id, count); goto _exit; } if(control_flag & EXPORT_CCCI_H){ if(count > (MAX_TX_BYTE+sizeof(CCCI_BUFF_T))){ DBGLOG(BOOT,WAR,"[TX]PORT%d wirte len (%d) > MTU (%d)!", port_id, count, MAX_TX_BYTE); count = MAX_TX_BYTE+sizeof(CCCI_BUFF_T); } skb_alloc_size = count - sizeof(CCCI_BUFF_T); }else{ if(count > MAX_TX_BYTE){ DBGLOG(BOOT,WAR,"[TX]PORT%d wirte len (%d) > MTU (%d)!", port_id, count, MAX_TX_BYTE); count = MAX_TX_BYTE; } skb_alloc_size = count; } if (ccci_boot_write_space_check() == 0) { ret = -EMCS_ERROR_BUSY; DBGLOG(BOOT, ERR, "[TX]PORT%d ccci_boot_write_space_check return 0", port_id); goto _exit; } new_skb = ccci_boot_mem_alloc((skb_alloc_size + CCCI_CDEV_HEADER_ROOM), GFP_ATOMIC); if (NULL == new_skb) { alloc_time = jiffies; DBGLOG(BOOT, INF, "[TX]PORT%d alloc skb with wait flag", port_id); new_skb = ccci_boot_mem_alloc((skb_alloc_size + CCCI_CDEV_HEADER_ROOM), GFP_KERNEL); if (NULL == new_skb) { ret = -EMCS_ERROR_BUSY; DBGLOG(BOOT, ERR, "[TX]PORT%d alloc skb with wait flag fail", port_id); goto _exit; } curr_time = jiffies; if ((curr_time - alloc_time) >= 1) { DBGLOG(BOOT, ERR, "[TX]PORT%d alloc skb with wait flag delay: time=%dms", \ port_id, 10*(curr_time - alloc_time)); } } /* reserve SDIO_H header room */ #ifdef CCCI_SDIO_HEAD skb_reserve(new_skb, CCCI_BOOT_HEADER_ROOM); #endif if (control_flag & EXPORT_CCCI_H) { ccci_header = (CCCI_BUFF_T *)new_skb->data; } else { ccci_header = (CCCI_BUFF_T *)skb_put(new_skb, sizeof(CCCI_BUFF_T)) ; } if (copy_from_user(skb_put(new_skb, count), buf, count)) { DBGLOG(BOOT, ERR, "[TX]PORT%d copy_from_user(len=%d, %p->%p) fail", \ port_id, count, buf, new_skb->data); dev_kfree_skb(new_skb); goto _exit; } if(control_flag & EXPORT_CCCI_H) { /* user bring down the ccci header */ if (count == sizeof(ccci_header)){ ccci_header->data[0]= CCCI_MAGIC_NUM; } else { ccci_header->data[1]= count; } if(ccci_header->channel != curr_node->ccci_ch.tx){ DBGLOG(BOOT, WAR, "[TX]PORT%d ch mis-match (%d) vs (%d)!! will correct by char_dev ", \ port_id, ccci_header->channel, curr_node->ccci_ch.tx); } } else { /* user bring down the payload only */ ccci_header->data[1] = count + sizeof(CCCI_BUFF_T); ccci_header->reserved = 0; } ccci_header->channel = curr_node->ccci_ch.tx; DBGLOG(BOOT, TRA, "[TX]PORT%d CCCI_MSG(0x%x, 0x%x, %d, 0x%x)", port_id, ccci_header->data[0], ccci_header->data[1], ccci_header->channel, ccci_header->reserved); ret = ccci_boot_write_desc_to_q(new_skb); if (KAL_SUCCESS != ret) { DBGLOG(BOOT, ERR, "[TX]PORT%d PKT DROP of ch%d!", port_id, curr_node->ccci_ch.tx); dev_kfree_skb(new_skb); ret = -EMCS_ERROR_BUSY; } else { atomic_inc(&curr_node->tx_pkt_cnt); wake_up(&curr_node->tx_waitq); } _exit: DEBUG_LOG_FUNCTION_LEAVE; if(!ret){ return count; } return ret; } unsigned int eemcs_boot_poll(struct file *fp,poll_table *wait) { eemcs_cdev_node_t *curr_node = (eemcs_cdev_node_t *)fp->private_data; unsigned int mask = 0; DEBUG_LOG_FUNCTION_ENTRY; DBGLOG(BOOT, DEF, "eemcs_boot_poll emcs poll enter"); poll_wait(fp,&curr_node->tx_waitq, wait); /* non-blocking, wake up to indicate the state change */ poll_wait(fp,&curr_node->rx_waitq, wait); /* non-blocking, wake up to indicate the state change */ if (ccci_boot_write_space_check() != 0) { DBGLOG(BOOT, DEF, "eemcs_boot_poll TX avaliable"); mask |= POLLOUT|POLLWRNORM; } if (0 != atomic_read(&curr_node->rx_pkt_cnt)) { DBGLOG(BOOT, DEF, "eemcs_boot_poll RX avaliable"); mask |= POLLIN|POLLRDNORM; } DEBUG_LOG_FUNCTION_LEAVE; return mask; } long eemcs_ctrl_ioctl(struct file *fp, unsigned int cmd, unsigned long arg); static struct file_operations eemcs_boot_ops = { .owner = THIS_MODULE, .open = eemcs_boot_open, .read = eemcs_boot_read, .write = eemcs_boot_write, .release = eemcs_boot_release, .unlocked_ioctl = eemcs_ctrl_ioctl, .poll = eemcs_boot_poll, }; #define EEMCS_BOOT_CDEV_MAX_NUM 1 extern struct class *eemcs_char_get_class(void); /* * @brief Store packets of CCCI_PORT_CTRL port when * MD exception occurs. * @param * None * @return * None */ void eemcs_boot_exception_log_pkts(void) { KAL_UINT32 j = 0, pkt_cnt = 0; struct sk_buff *skb = NULL; // if (!is_valid_exception_rx_port(CCCI_PORT_CTRL)) { // get packet number in this port pkt_cnt = atomic_read(&boot_node->rx_pkt_cnt); if (pkt_cnt != 0) { DBGLOG(BOOT, TRA, "%d packets in Rx list of port%d", pkt_cnt, boot_node->eemcs_port_id); for (j = 0; j < pkt_cnt; j++) { skb = skb_dequeue(&boot_node->rx_skb_list); if (skb != NULL) { atomic_dec(&boot_node->rx_pkt_cnt); // store skb to exception structure eemcs_expt_log_port(skb, boot_node->eemcs_port_id); } else { DBGLOG(BOOT, TRA, "dequeue NULL skb from port%d list", boot_node->eemcs_port_id); } } } // } } /* * @brief Exception callback function. * @param * msg_id [in] Exception indicator. * @return * None */ void eemcs_boot_exception_callback(KAL_UINT32 msg_id) { DBGLOG(BOOT, INF, "Boot exception Callback 0x%X", msg_id); switch (msg_id) { /* 1st stage, MD exception occurs */ case EEMCS_EX_INIT: eemcs_boot_exception_log_pkts(); break; /* 2nd stage, DHL DL channel is ready */ case EEMCS_EX_DHL_DL_RDY: break; /* 3rd stage, all MD exception initialization is done */ case EEMCS_EX_INIT_DONE: break; default: DBGLOG(BOOT, ERR, "Unknown boot exception callback 0x%X", msg_id); } } void eemcs_boot_reset(void) { /* registered at MD_INIT, unregistered at MD_ROM_BOOT_READY*/ eemcs_boot_inst.cb_id = ccci_boot_mbx_register(eemcs_boot_swint_callback); KAL_ASSERT(eemcs_boot_inst.cb_id != KAL_FAIL); /* Rest Boot DL size*/ g_eemcs_file_pkt_len = BOOT_TX_MAX_PKT_LEN; /* Clear boot rx queue*/ skb_queue_purge(&eemcs_boot_inst.boot_node.rx_skb_list); atomic_set(&eemcs_boot_inst.boot_node.rx_pkt_cnt, 0); return; } KAL_INT32 eemcs_boot_mod_init(void) { //#ifdef __EEMCS_XBOOT_SUPPORT__ KAL_INT32 result = KAL_SUCCESS; KAL_INT32 ret = 0; dev_t boot_dev; ccci_port_cfg *curr_port_info = NULL; struct device *boot_devices; DEBUG_LOG_FUNCTION_ENTRY; memset(&eemcs_boot_inst, 0, sizeof(EEMCS_BOOT_SET)); eemcs_boot_inst.md_id = MD_SYS5; eemcs_boot_inst.md_need_reload = 1; KAL_ALLOCATE_PHYSICAL_MEM(eemcs_boot_inst.debug_buff, BOOT_DEBUG_BUF_LEN); eemcs_boot_inst.boot_state = MD_INVALID; eemcs_boot_inst.ccci_hs_bypass = 0; // register characer device region ret = register_chrdev_region(MKDEV(EEMCS_DEV_MAJOR, START_OF_BOOT_PORT), EEMCS_BOOT_CDEV_MAX_NUM, EEMCS_DEV_NAME); if (ret) { DBGLOG(BOOT, ERR, "register_chrdev_region %s fail: %d", BOOT_CDEV_NAME, ret); ret = KAL_FAIL; goto _boot_register_chrdev_fail; } // allocate character device eemcs_boot_inst.eemcs_boot_chrdev = cdev_alloc(); if (eemcs_boot_inst.eemcs_boot_chrdev == NULL) { DBGLOG(BOOT, ERR, "cdev_alloc %s fail", BOOT_CDEV_NAME); ret = KAL_FAIL; goto _boot_cdev_alloc_fail; } cdev_init(eemcs_boot_inst.eemcs_boot_chrdev, &eemcs_boot_ops); eemcs_boot_inst.eemcs_boot_chrdev->owner = THIS_MODULE; ret = cdev_add(eemcs_boot_inst.eemcs_boot_chrdev, MKDEV(EEMCS_DEV_MAJOR, START_OF_BOOT_PORT), EEMCS_BOOT_CDEV_MAX_NUM); if (ret) { DBGLOG(BOOT, ERR, "cdev_add %s fail: %d", BOOT_CDEV_NAME, ret); goto _boot_cdev_add_fail; } // Get device class from eemcs_char eemcs_boot_inst.dev_class = eemcs_char_get_class(); boot_dev = MKDEV(EEMCS_DEV_MAJOR, START_OF_BOOT_PORT); boot_devices = device_create((struct class *)eemcs_boot_inst.dev_class, NULL, boot_dev, NULL, "%s", BOOT_CDEV_NAME); if (IS_ERR(boot_devices)) { ret = KAL_FAIL; DBGLOG(BOOT, ERR, "device_create %s fail: %ld", BOOT_CDEV_NAME, PTR_ERR(boot_devices)); } memset(eemcs_boot_inst.boot_node.cdev_name, 0, sizeof(eemcs_boot_inst.boot_node.cdev_name)); strncpy(eemcs_boot_inst.boot_node.cdev_name, BOOT_CDEV_NAME, sizeof(BOOT_CDEV_NAME)); eemcs_boot_inst.boot_node.eemcs_port_id = CCCI_PORT_CTRL; curr_port_info = ccci_get_port_info(CCCI_PORT_CTRL); eemcs_boot_inst.boot_node.ccci_ch.rx = curr_port_info->ch.rx; eemcs_boot_inst.boot_node.ccci_ch.tx = curr_port_info->ch.tx; atomic_set(&eemcs_boot_inst.boot_node.cdev_state, CDEV_CLOSE); skb_queue_head_init(&eemcs_boot_inst.boot_node.rx_skb_list); atomic_set(&eemcs_boot_inst.boot_node.rx_pkt_cnt, 0); atomic_set(&eemcs_boot_inst.boot_node.rx_pkt_drop_cnt, 0); init_waitqueue_head(&eemcs_boot_inst.boot_node.rx_waitq); init_waitqueue_head(&eemcs_boot_inst.boot_node.tx_waitq); atomic_set(&eemcs_boot_inst.boot_node.tx_pkt_cnt, 0); boot_node = &eemcs_boot_inst.boot_node; init_waitqueue_head(&eemcs_boot_inst.rst_waitq); snprintf(eemcs_boot_inst.eemcs_boot_wakelock_name, sizeof(eemcs_boot_inst.eemcs_boot_wakelock_name), "eemcs_boot_wakelock"); wake_lock_init(&eemcs_boot_inst.eemcs_boot_wake_lock, WAKE_LOCK_SUSPEND, eemcs_boot_inst.eemcs_boot_wakelock_name); DBGLOG(BOOT, DBG, "char dev(%s)(%d), rx_ch(%d), tx_ch(%d)",\ eemcs_boot_inst.boot_node.cdev_name, eemcs_boot_inst.boot_node.eemcs_port_id,\ eemcs_boot_inst.boot_node.ccci_ch.rx, eemcs_boot_inst.boot_node.ccci_ch.tx); result = eemcs_boot_mbx_init(); /* register exception callback function */ eemcs_boot_inst.expt_cb_id = ccci_boot_expt_register(eemcs_boot_exception_callback); if (eemcs_boot_inst.expt_cb_id == KAL_FAIL){ DBGLOG(BOOT, ERR, "register exception callback fail"); } atomic_set(&eemcs_boot_inst.md_reset_cnt, 0); #ifdef _EEMCS_BOOT_UT bootut_init(); #endif DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; _boot_cdev_add_fail: cdev_del(eemcs_boot_inst.eemcs_boot_chrdev); _boot_cdev_alloc_fail: unregister_chrdev_region(MKDEV(EEMCS_DEV_MAJOR, START_OF_BOOT_PORT), EEMCS_BOOT_CDEV_MAX_NUM); _boot_register_chrdev_fail: DEBUG_LOG_FUNCTION_LEAVE; return ret; //#else // !__EEMCS_XBOOT_SUPPORT__ // DEBUG_LOG_FUNCTION_ENTRY; // DEBUG_LOG_FUNCTION_LEAVE; // return KAL_SUCCESS; //#endif // __EEMCS_XBOOT_SUPPORT__ } void eemcs_boot_exit(void) { DEBUG_LOG_FUNCTION_ENTRY; //#ifdef __EEMCS_XBOOT_SUPPORT__ if (eemcs_boot_inst.expt_cb_id != -1){ ccci_boot_expt_unregister(eemcs_boot_inst.expt_cb_id); } device_destroy(eemcs_boot_inst.dev_class, MKDEV(EEMCS_DEV_MAJOR, START_OF_BOOT_PORT)); if (eemcs_boot_inst.eemcs_boot_chrdev) { DBGLOG(BOOT, DBG, "eemcs_boot_chrdev unregister "); cdev_del(eemcs_boot_inst.eemcs_boot_chrdev); } unregister_chrdev_region(MKDEV(EEMCS_DEV_MAJOR,START_OF_BOOT_PORT), EEMCS_BOOT_CDEV_MAX_NUM); KAL_FREE_PHYSICAL_MEM(eemcs_boot_inst.cmd_buff); KAL_FREE_PHYSICAL_MEM(eemcs_boot_inst.debug_buff); wake_lock_destroy(&eemcs_boot_inst.eemcs_boot_wake_lock); //#else // !__EEMCS_XBOOT_SUPPORT__ //#endif // __EEMCS_XBOOT_SUPPORT__ DEBUG_LOG_FUNCTION_LEAVE; } #define BOOT_DEBUG_STATE \ do { \ DBGLOG(BOOT, DBG, "xBoot State : %s", \ g_md_sta_str[eemcs_boot_inst.boot_state]); \ } while (0) #ifdef _EEMCS_BOOT_UT KAL_INT32 bootut_start_ccci_handshake(void); KAL_INT32 bootut_lb(struct sk_buff *); #endif // _EEMCS_BOOT_UT static KAL_INT32 eemcs_boot_modem(void) { KAL_INT32 result = -1; kal_bool state = false; DEBUG_LOG_FUNCTION_ENTRY; #if !defined(__EEMCS_XBOOT_SUPPORT__) /* 20130418 ian bybass the emcs_boot_modem process and entering state MD_ROM_BOOT_READY */ state = change_device_state(EEMCS_XBOOT); if (unlikely(state == false)) { DBGLOG(BOOT, ERR, "change state to XBOOT fail"); goto _boot_err; } /* still go through each state */ eemcs_boot_change_state(MD_INIT); lte_sdio_off(); lte_sdio_on(); eemcs_boot_change_state(MD_BROM_SDIO_MBX_HS); eemcs_boot_change_state(MD_BROM_DL_START); eemcs_boot_change_state(MD_BROM_DL_GET); eemcs_boot_change_state(MD_BROM_DL_END); eemcs_boot_change_state(MD_BROM_SEND_STATUS); eemcs_boot_change_state(MD_BL_SDIO_INIT); eemcs_boot_change_state(MD_BL_SDIO_MBX_HS); eemcs_boot_change_state(MD_BL_DL_START); eemcs_boot_change_state(MD_BL_DL_GET); eemcs_boot_change_state(MD_BL_DL_END); eemcs_boot_change_state(MD_ROM_SDIO_INIT); eemcs_boot_change_state(MD_ROM_SDIO_MBX_HS); eemcs_boot_change_state(MD_ROM_BOOTING); eemcs_boot_change_state(MD_ROM_BOOT_READY); state = change_device_state(EEMCS_MOLY_HS_P1); if (unlikely(state == false)) { DBGLOG(BOOT, ERR, "change state to MOLY_HS_P1 fail"); goto _boot_err; } DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; #endif atomic_set(&eemcs_boot_inst.md_reset_cnt, 0); state = change_device_state(EEMCS_XBOOT); if (unlikely(state == false)) { DBGLOG(BOOT, ERR, "change state to XBOOT fail"); goto _boot_err; } #if 0 /* 20130418 remove the part which reset the embedded MD */ *(volatile unsigned int*)(0xC0001830) = 0x06; printk("0xc0001830 =%d\n",*(volatile unsigned int*)(0xC0001830)); #endif eemcs_boot_change_state(MD_INIT); scan_all_md_img(md_img_list); #if defined(ENABLE_MD_IMG_SECURITY_FEATURE) || defined(ENABLE_MD_SECURITY_RO_FEATURE) static int masp_boot_inited = 0; int sec_ret = 0; if (!masp_boot_inited) { DBGLOG(BOOT, TRA, "masp_boot_init\n"); masp_boot_inited = 1; sec_ret = masp_boot_init(); if (sec_ret != 0) { DBGLOG(BOOT, ERR, "masp_boot_init fail: %d\n",sec_ret); goto _boot_err; } sec_ret = sec_lib_version_check(); if (sec_ret != 0) { DBGLOG(BOOT, ERR, "sec lib version check error(%d)\n", sec_ret); goto _boot_err; } } #endif #ifdef ENABLE_MD_IMG_SECURITY_FEATURE if((result = eemcs_boot_load_image(eemcs_boot_inst.md_id)) < 0){ char error_srt[64] = {0}; DBGLOG(BOOT, ERR, "Load Modem Image Fail: %d", result); sprintf(error_srt, "MD%d: Load Security Modem Image Fail!!! ERROR=%d !!! \n", eemcs_boot_inst.md_id+1, result); aee_kernel_warning("[EEMCS]", error_srt); goto _boot_err; } #else get_ext_md_post_fix(eemcs_boot_inst.md_id, eemcs_boot_inst.ext_modem_post_fix, NULL); if( (result = set_md_info(eemcs_boot_inst.ext_modem_post_fix)) < 0) goto _boot_err; check_md_header(eemcs_boot_inst.md_id, 0, &eemcs_boot_inst.md_info.img_info[MD_INDEX]); #endif // A big loop to do booting process while (1) { switch (eemcs_boot_get_state()) { case MD_INIT: { eemcs_boot_change_state(MD_BROM_SDIO_MBX_HS); /* power off MD */ lte_sdio_off(); /* reset excecption info*/ eemcs_expt_reset(); /* reset boot dev node*/ eemcs_boot_reset(); /* reset char dev node*/ eemcs_cdev_reset(); /* reset ccci layer*/ eemcs_ccci_reset(); /* power on MD and SDIO card identify */ lte_sdio_on(); DBGLOG(BOOT, INF, "wait for sdio driver probe done"); del_timer(&bootup_to_monitor_timer); #ifdef MT_LTE_AUTO_CALIBRATION mtlte_sys_sdio_wait_probe_done(); #endif mod_timer(&bootup_to_monitor_timer,jiffies+boot_time_out_val*HZ); #ifdef _EEMCS_BOOT_UT result = bootut_md_send_command(); if (result != KAL_SUCCESS) goto _boot_err; #endif // _EEMCS_BOOT_UT DBGLOG(BOOT, INF, "MD%d start to run...", eemcs_boot_inst.md_id+1); break; } case MD_BROM_SDIO_MBX_HS: case MD_BL_SDIO_MBX_HS: case MD_BROM_SDDL_HS: { result = eemcs_boot_mbx_process(); if (result != KAL_SUCCESS) { DBGLOG(BOOT, ERR, "Mailbox process fail when eemcs_boot_state=%d", eemcs_boot_get_state()); goto _boot_err; } #ifdef _EEMCS_BOOT_UT ccci_boot_write_desc_to_q(NULL); #endif // _EEMCS_BOOT_UT break; } case MD_BROM_DL_START: case MD_BROM_DL_GET: case MD_BROM_DL_END: case MD_BL_DL_START: case MD_BL_DL_GET: case MD_BL_DL_END: case MD_BOOT_END: case MD_BROM_SEND_STATUS: result = eemcs_boot_cmd_process(); if (result != KAL_SUCCESS) { DBGLOG(BOOT, ERR, "xBoot commands process is failed !!"); goto _boot_err; } break; case MD_BL_SDIO_INIT: eemcs_boot_change_state(MD_BL_SDIO_MBX_HS); break; case MD_ROM_SDIO_INIT: eemcs_boot_change_state(MD_ROM_SDIO_MBX_HS); break; case MD_ROM_SDIO_MBX_HS: eemcs_boot_change_state(MD_ROM_BOOTING); break; case MD_ROM_BOOTING: eemcs_boot_change_state(MD_ROM_BOOT_READY); break; case MD_ROM_BOOT_READY: /* registered at MD_INIT, unregistered at MD_ROM_BOOT_READY*/ if (eemcs_boot_inst.cb_id >= 0) ccci_boot_mbx_unregister(eemcs_boot_inst.cb_id); #ifdef _EEMCS_BOOT_UT bootut_start_ccci_handshake(); #else // !_EEMCS_BOOT_UT // If CCCI handshake is bypassed, we change EEMCS state to ready if (eemcs_boot_inst.ccci_hs_bypass) { change_device_state(EEMCS_BOOTING_DONE); } #endif // _EEMCS_BOOT_UT return KAL_SUCCESS; break; case MD_ROM_EXCEPTION: break; default: DBGLOG(BOOT, ERR, "Error State (%d) ???", eemcs_boot_get_state()); } // DBGLOG(BOOT, DBG, "xBoot State : %s", // g_md_sta_str[eemcs_boot_get_state()]); BOOT_DEBUG_STATE; if (boot_reset_state){ printk(KERN_ERR "@@@@@@@@@ Boot Reset Test: %s @@@@@@@@@@\n", g_md_sta_str[boot_reset_state]); while( boot_reset_state == eemcs_boot_get_state()); } } DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; _boot_err: DBGLOG(BOOT, ERR, "xBoot fail: %d", result); DEBUG_LOG_FUNCTION_LEAVE; return result; } static void eemcs_md_boot_up_timeout_func(unsigned int stage, unsigned int timeout) { KAL_UINT32 mbx_data[2] = {0}; char ex_info[EE_BUF_LEN]=""; KAL_INT32 ret; switch (stage) { case MD_BOOT_XBOOT_FAIL: if (timeout > 0) { ret = ccci_boot_mbx_read(mbx_data, 2*sizeof(KAL_UINT32)); if (ret != KAL_SUCCESS) { DBGLOG(BOOT, ERR, "[MD_BOOT_XBOOT_FAIL]read mbx fail: %d", ret); } else { DBGLOG(BOOT, INF, "[MD_BOOT_XBOOT_FAIL]XBOOT_MBX: 0x%08X, 0x%08X", mbx_data[0], mbx_data[1]); } snprintf(ex_info, EE_BUF_LEN,"\n[Others] MD_BOOT_UP_FAIL(XBOOT >%ds)(TSID=0x%X)\n", \ timeout, mbx_data[1]); eemcs_aed(0, ex_info); } else if (timeout == 0) { DBGLOG(BOOT, INF, "[MD_BOOT_XBOOT_FAIL](other)"); snprintf(ex_info, EE_BUF_LEN,"\n[Others] MD_BOOT_UP_FAIL(Other)\n"); eemcs_aed(0, ex_info); } break; case MD_BOOT_HS1_FAIL: ret = ccci_boot_mbx_read(mbx_data, 2*sizeof(KAL_UINT32)); if (ret != KAL_SUCCESS) { DBGLOG(BOOT, ERR, "[MD_BOOT_HS1_FAIL]read mbx fail: %d", ret); } else { DBGLOG(BOOT, INF, "[MD_BOOT_HS1_FAIL]XBOOT_MBX: 0x%08X, 0x%08X", mbx_data[0], mbx_data[1]); } snprintf(ex_info, EE_BUF_LEN,"\n[Others] MD_BOOT_UP_FAIL(HS1 >%ds)(TSID=0x%X)\n", \ timeout, mbx_data[1]); eemcs_aed(0, ex_info); break; case MD_BOOT_HS2_FAIL: DBGLOG(BOOT, INF, "[MD_BOOT_HS2_FAIL]HS2 timeout>%ds", timeout); snprintf(ex_info, EE_BUF_LEN,"\n[Others] MD_BOOT_UP_FAIL(HS2 >%ds)\n", timeout); eemcs_aed(CCCI_AED_DUMP_EX_MEM, ex_info); break; default: break; } return; } static void bootup_to_monitor(unsigned long val) { time_out_abort = 1; mb(); wake_up(&eemcs_boot_inst.mailbox.d2h_wait_q); wake_up(&boot_node->rx_waitq); DBGLOG(BOOT, INF, "Boot time out trigger(%d)", boot_time_out_val); } static int set_bootup_timeout_value(unsigned int val) { if(val) mod_timer(&bootup_to_monitor_timer,jiffies+val*HZ); else del_timer(&bootup_to_monitor_timer); boot_time_out_val = val; return 0; } /* for executable "cli_eemcs" */ #if TEST_DRV #if EMCS_SDIO_DRVTST extern int call_function(char *buf); #endif //static char boot_wr_buf[200]; //static char boot_rd_buf[200] = "IOCTL_READ TEST STRING"; #define CLI_MAGIC 'CLI' /* this cause compile warning, please ask Wei-De to solve it */ #define IOCTL_READ _IOR(CLI_MAGIC, 0, int) #define IOCTL_WRITE _IOW(CLI_MAGIC, 1, int) #endif void eemcs_md_logger_notify(void); KAL_INT32 eemcs_power_off_md(KAL_INT32 md_id, KAL_UINT32 timeout) { DEBUG_LOG_FUNCTION_ENTRY; eemcs_md_logger_notify(); // eemcs_ccci_remove(); lte_sdio_off(); DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } KAL_INT32 eemcs_power_on_md(KAL_INT32 md_id, KAL_UINT32 timeout) { int ret = CCCI_ERR_MODULE_INIT_OK; DEBUG_LOG_FUNCTION_ENTRY; //lte_sdio_on(); // ret = eemcs_ccci_init(); // if(ret != CCCI_ERR_MODULE_INIT_OK){ // DBGLOG(INIT,ERR, "eemcs_ccci_power_on_md error !!!"); // } DEBUG_LOG_FUNCTION_LEAVE; return ret; } KAL_INT32 eemcs_md_reset(void) { DEBUG_LOG_FUNCTION_ENTRY; /* prevent another reset modem action from wdt timeout IRQ during modem reset */ if(atomic_inc_and_test(&eemcs_boot_inst.md_reset_cnt) > 1) { DBGLOG(BOOT, ERR, "One reset flow is on-going"); return KAL_SUCCESS; } change_device_state(EEMCS_GATE); wake_lock_timeout(&eemcs_boot_inst.eemcs_boot_wake_lock, 10*HZ); //wake lock 10s eemcs_cdev_msg(CCCI_PORT_CTRL, CCCI_MD_MSG_RESET, 0); DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } void eemcs_boot_user_exit_notify(void){ wake_up(&eemcs_boot_inst.rst_waitq); } KAL_INT32 eemcs_wait_usr_n_rst(int port_id){ long ret; while(false==eemcs_cdev_rst_port_closed()){ ret = wait_event_interruptible(eemcs_boot_inst.rst_waitq, (true==eemcs_cdev_rst_port_closed())); if (-ERESTARTSYS == ret) { DBGLOG(BOOT, INF, "wait_user_ready_to_reset signaled by -ERESTARTSYS(%ld)", ret); } } DBGLOG(BOOT, INF, "send CCCI_MD_MSG_READY_TO_RESET to daemon by PORT%d", port_id); eemcs_cdev_msg(CCCI_PORT_CTRL, CCCI_MD_MSG_READY_TO_RESET, 0); return KAL_SUCCESS; } extern void apply_pre_md_run_setting(void); extern void apply_post_md_run_setting(void); long eemcs_ctrl_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { int ret = 0; EEMCS_STATE state; KAL_UINT8 port_id = 0; eemcs_cdev_node_t *curr_node = (eemcs_cdev_node_t *)fp->private_data; port_id = curr_node->eemcs_port_id; DEBUG_LOG_FUNCTION_ENTRY; switch(cmd) { case CCCI_IOC_BOOT_MD: { DBGLOG(BOOT, INF, "CCCI_IOC_BOOT_MD by %s", current->comm); ret = eemcs_boot_modem(); apply_pre_md_run_setting(); } break; // NOTE : We rename CCCI_IOC_CHECK_MD_STATE to CCCI_IOC_CHECK_STATE in EEMCS case CCCI_IOC_CHECK_STATE: { state = check_device_state(); DBGLOG(BOOT, INF, "CCCI_IOC_CHECK_STATE: %d", state); ret = put_user((unsigned int)state, (unsigned int __user *)arg); } break; // NOTE : We rename CCCI_IOC_SET_MD_STATE to CCCI_IOC_SET_STATE in EEMCS case CCCI_IOC_SET_STATE: { if (get_user(state, (int __user *)arg)) { return -EFAULT; } DBGLOG(BOOT, INF, "CCCI_IOC_SET_STATE: %d", state); ret = change_device_state(state); } break; case CCCI_IOC_GET_EXCEPTION_LENGTH: { DBGLOG(BOOT, INF, "CCCI_IOC_GET_EXCEPTION_LENGTH"); ret = put_user(MD_EX_LOG_SIZE, (unsigned int __user *)arg); } break; case CCCI_IOC_GET_MD_BOOT_INFO: { ret = put_user((unsigned int)eemcs_boot_inst.boot_state, (unsigned int __user *)arg); DBGLOG(BOOT, INF, "CCCI_IOC_GET_MD_BOOT_INFO: %d", eemcs_boot_inst.boot_state); } break; case CCCI_IOC_START_BOOT: { DBGLOG(BOOT, INF, "CCCI_IOC_START_BOOT"); change_device_state(EEMCS_MOLY_HS_P2); // TODO : This function is empty in EMCS //emcs_md_ctrl_start_boot(); } break; case CCCI_IOC_BOOT_DONE: { DBGLOG(BOOT, INF, "CCCI_IOC_BOOT_DONE"); change_device_state(EEMCS_BOOTING_DONE); DBGLOG(BOOT, INF, "CCCI_IOC_BOOT_DONE: Modem is ready ..."); mtlte_sys_sdio_driver_init_after_phase2(); // TODO : In EMCS, this function invokes a call chain. // Functions in call chain are registered by other users who // care about MD is booting to ready. // What do we do ??? Do we need this ??? //emcs_md_ctrl_boot_done(); } break; case CCCI_IOC_REBOOT: { DBGLOG(BOOT, INF, "CCCI_IOC_REBOOT"); //change_device_state(EEMCS_INIT); // TODO : This function is empty in EMCS //emcs_md_ctrl_reboot(); } break; case CCCI_IOC_MD_EXCEPTION: { DBGLOG(BOOT, INF, "CCCI_IOC_MD_EXCEPTION"); change_device_state(EEMCS_EXCEPTION); // TODO : Needed ??? //emcs_md_ctrl_md_expection(); } break; case CCCI_IOC_MD_EX_REC_OK: { DBGLOG(BOOT, INF, "CCCI_IOC_MD_EX_REC_OK"); // TODO : Needed ??? //emcs_md_ctrl_md_expection_rec(); } break; case CCCI_IOC_GET_MD_INFO: { int mode = is_modem_debug_ver(); ret = put_user((unsigned int)mode, (unsigned int __user *)arg); break; } case CCCI_IOC_GET_RUNTIME_DATA: { void __user *argp = NULL; void *runtime_data = NULL; KAL_UINT32 data_size = 0; DBGLOG(BOOT, INF, "CCCI_IOC_GET_RUNTIME_DATA"); argp = (void __user *)arg; data_size = eemcs_md_gen_runtime_data(&runtime_data); DBGLOG(BOOT, INF, "Runtime data size: %d", data_size); if (unlikely(runtime_data == 0)) { return -ENOMEM; } if (copy_to_user(argp, runtime_data, data_size)) { DBGLOG(BOOT, ERR, "copy_to_user RUNTIME_DATA failed !!"); eemcs_md_destroy_runtime_data(runtime_data); return -EFAULT; } eemcs_md_destroy_runtime_data(runtime_data); } break; case CCCI_IOC_SET_EXCEPTION_DATA: { DBGLOG(BOOT, INF,"CCCI_IOC_SET_EXCEPTION_DATA"); #if 0 extern EX_LOG_T md_ex_log; void __user *argp = (void __user *)arg; if(copy_from_user(&md_ex_log,argp,MD_EX_LOG_SIZE)) { DBGLOG(PORT,ERR,"copy_from_user failed."); return -EFAULT; } md_exception(&md_ex_log); #endif } break; case CCCI_IOC_MD_RESET: { DBGLOG(BOOT, INF, "CCCI_IOC_MD_RESET by PORT%d", port_id); eemcs_md_reset(); } break; case CCCI_IOC_DO_MD_RST: { DBGLOG(BOOT, INF, "CCCI_IOC_DO_MD_RST"); // TODO : This is a dummy function in EMCS, only do following code // md_boot_stage = MD_BOOT_STAGE_0; // What do we do ??? //ret = md_reset(); } break; case CCCI_IOC_DO_STOP_MD: { DBGLOG(BOOT, INF, "CCCI_IOC_DO_STOP_MD by PORT%d", port_id); ret = eemcs_power_off_md(0, 0); return ret; #if 0 KAL_INT32 result; result = eemcs_wait_usr_n_rst(port_id); if(KAL_SUCCESS != result){ return result; } #endif } break; case CCCI_IOC_WAIT_RDY_RST: { KAL_INT32 result; DBGLOG(BOOT, INF, "CCCI_IOC_WAIT_RDY_RST by PORT%d", port_id); result = eemcs_wait_usr_n_rst(port_id); if(KAL_SUCCESS != result){ return result; } } break; case CCCI_IOC_SET_HEADER: { KAL_UINT32 ori_port_flag = 0; KAL_UINT32 new_port_flag = 0; ori_port_flag = ccci_get_port_cflag(port_id); ccci_set_port_type(port_id, (ori_port_flag|EXPORT_CCCI_H)); new_port_flag = ccci_get_port_cflag(port_id); DBGLOG(BOOT, INF, "CCCI_IOC_SET_HEADER(%d, %d) by PORT%d", ori_port_flag, new_port_flag, port_id); } break; case CCCI_IOC_CLR_HEADER: { KAL_UINT32 ori_port_flag = 0; KAL_UINT32 new_port_flag = 0; ori_port_flag = ccci_get_port_cflag(port_id); ccci_set_port_type(port_id, (ori_port_flag&(~EXPORT_CCCI_H))); new_port_flag = ccci_get_port_cflag(port_id); DBGLOG(BOOT, INF, "CCCI_IOC_CLR_HEADER(%d, %d) by PORT%d", ori_port_flag, new_port_flag, port_id); } break; case CCCI_IOC_DO_START_MD: { DBGLOG(BOOT, INF, "CCCI_IOC_DO_START_MD by PORT%d", port_id); ret = eemcs_power_on_md(0, 0); if(ret != KAL_SUCCESS){ DBGLOG(BOOT, ERR, "CCCI_IOC_DO_START_MD fail"); } change_device_state(EEMCS_INIT); } break; case CCCI_IOC_RELOAD_MD_TYPE: { int md_type = 0; if(copy_from_user(&md_type, (void __user *)arg, sizeof(unsigned int))) { DBGLOG(BOOT, ERR, "IOC_RELOAD_MD_TYPE: copy_from_user fail!"); ret = -EFAULT; break; } if (md_type > modem_invalid && md_type < modem_max_type){ //DBGLOG(BOOT, DBG, "IOC_RELOAD_MD_TYPE: storing md type(%d)", md_type); ret = set_ext_modem_support(eemcs_get_md_id(), md_type); } else{ DBGLOG(BOOT, ERR, "IOC_RELOAD_MD_TYPE: invalid md type(%d)", md_type); ret = -EFAULT; } eemcs_set_reload_image(true); } break; case CCCI_IOC_FLOW_CTRL_SETTING: { unsigned int limit[2] = {0, 0}; if(copy_from_user(&limit, (void __user *)arg, 2*sizeof(unsigned int))) { DBGLOG(BOOT, ERR, "IOC_FLOW_CTRL_SETTING: copy_from_user fail!"); ret = -EFAULT; break; } else { DBGLOG(BOOT, INF, "IOC_FLOW_CTRL_SETTING: limit=%d, thresh=%d", limit[0], limit[1]); mtlte_df_Init_DL_flow_ctrl(RXQ_Q2, false, limit[0], limit[1]); } } break; case CCCI_IOC_GET_MD_TYPE: { int md_type = get_ext_modem_support(eemcs_get_md_id()); DBGLOG(BOOT, INF, "CCCI_IOC_GET_MD_TYPE(%d)", md_type); ret = put_user((unsigned int)md_type, (unsigned int __user *)arg); } break; case CCCI_IOC_BOOT_UP_TIMEOUT: { unsigned int para[2] = {0, 0}; if(copy_from_user(¶, (void __user *)arg, 2*sizeof(unsigned int))) { DBGLOG(BOOT, ERR, "IOC_BOOT_UP_TIMEOUT: copy_from_user fail!"); ret = -EFAULT; break; } else { DBGLOG(BOOT, INF, "IOC_BOOT_UP_TIMEOUT: boot_sta=%d, timeout=%d", para[0], para[1]); eemcs_md_boot_up_timeout_func(para[0], para[1]); } } break; case CCCI_IOC_GET_CFG_SETTING: { char buffer[64]; void __user *argp = NULL; argp = (void __user *)arg; if(copy_from_user(buffer, (void __user *)arg, sizeof(buffer))) { DBGLOG(BOOT, ERR, "CCCI_IOC_FEATRUE_SET_QUERY: copy_from_user fail!"); ret = -EFAULT; } else { DBGLOG(BOOT, INF, "CCCI_IOC_FEATRUE_SET_QUERY: %s\n", buffer); // Query setting if(query_feature_setting(buffer) == 0) { if (copy_to_user(argp, buffer, sizeof(buffer))) { DBGLOG(BOOT, ERR, "Query copy_to_user failed !!"); ret = -EFAULT; } } } } break; case CCCI_IOC_SET_BOOT_TO_VAL: { unsigned int to_val; if(copy_from_user(&to_val, (void __user *)arg, sizeof(unsigned int))) { DBGLOG(BOOT, ERR, "CCCI_IOC_SET_BOOT_TO_VAL: copy_from_user fail!"); ret = -EFAULT; } else { DBGLOG(BOOT, INF, "CCCI_IOC_SET_BOOT_TO_VAL: %d", to_val); set_bootup_timeout_value(to_val); } } break; default: DBGLOG(BOOT, ERR, "invalid ioctl cmd(%d)", cmd); ret = -EFAULT; break; } DEBUG_LOG_FUNCTION_LEAVE; return ret; } // // For reset test // KAL_INT32 eemcs_boot_reset_test(KAL_INT32 reset_state){ boot_reset_state = reset_state; return boot_reset_state; } // // UT // #ifdef _EEMCS_BOOT_UT KAL_UINT32 eemcs_boot_ut_mbx[2] = {0}; XBOOT_CMD eemcs_boot_ut_cmd = {0}; KAL_UINT32 bootut_register_callback(CCCI_CHANNEL_T chn, EEMCS_CCCI_CALLBACK func_ptr , KAL_UINT32 private_data) { DEBUG_LOG_FUNCTION_ENTRY; DBGLOG(BOOT, DBG, "CCCI channel (%d) register callback", chn); DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } KAL_UINT32 bootut_unregister_callback(CCCI_CHANNEL_T chn) { DEBUG_LOG_FUNCTION_ENTRY; DBGLOG(BOOT, DBG, "CCCI channel (%d) UNregister callback", chn); DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } KAL_INT32 bootut_init(void) { DEBUG_LOG_FUNCTION_ENTRY; eemcs_boot_inst.ut_bl_cnt = 0; eemcs_boot_inst.ut_xcmd_idx = 0; eemcs_boot_inst.ut_h2d_intr = 0; DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } KAL_INT32 bootut_md_write_mbx(KAL_UINT32 data0, KAL_UINT32 data1) { DEBUG_LOG_FUNCTION_ENTRY; eemcs_boot_ut_mbx[0] = data0; eemcs_boot_ut_mbx[1] = data1; DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } KAL_INT32 bootut_md_read_mbx(KAL_UINT32 *data0, KAL_UINT32 *data1) { DEBUG_LOG_FUNCTION_ENTRY; *data0 = eemcs_boot_ut_mbx[0]; *data1 = eemcs_boot_ut_mbx[1]; eemcs_boot_inst.ut_h2d_intr = 0; DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } KAL_INT32 bootut_md_send_command(void) { KAL_INT32 result = KAL_SUCCESS; EEMCS_BOOT_UT_CMD *xcmd = NULL; DEBUG_LOG_FUNCTION_ENTRY; xcmd = &ut_md_xcmd[eemcs_boot_inst.ut_xcmd_idx]; switch (xcmd->type) { case BOOT_UT_MBX: { //ccci_boot_mbx_write(xcmd->data.mbx, sizeof(KAL_UINT32) * 2); bootut_md_write_mbx(xcmd->data.mbx.data0, xcmd->data.mbx.data1); eemcs_boot_swint_callback(1 << 16); break; } case BOOT_UT_XCMD: { struct sk_buff *new_skb = NULL; new_skb = prepare_xcmd(xcmd->data.xcmd.magic, xcmd->data.xcmd.msg_id, xcmd->data.xcmd.status); eemcs_boot_rx_callback(new_skb, 0); break; } case BOOT_UT_MSD_OUTPUT: case BOOT_UT_MSD_FLUSH: { struct sk_buff *new_skb = NULL; XBOOT_CMD_PRINT *header = NULL; new_skb = ccci_boot_mem_alloc(sizeof(XBOOT_CMD_PRINT)); if (new_skb == NULL) { DBGLOG(BOOT, ERR, "alloc skb fail"); result = KAL_FAIL; break; } header = (XBOOT_CMD_PRINT *)skb_put(new_skb, sizeof(XBOOT_CMD_PRINT)); memset(new_skb->data, 0, sizeof(XBOOT_CMD_PRINT)); header->magic = xcmd->data.msd_print.magic; header->msg_id = xcmd->data.msd_print.msg_id; header->str_len = xcmd->data.msd_print.str_len; memcpy(header->str, xcmd->data.msd_print.str, xcmd->data.msd_print.str_len); eemcs_boot_rx_callback(new_skb, 0); break; } case BOOT_UT_XCMD_GETBIN: { struct sk_buff *new_skb = NULL; XBOOT_CMD_GETBIN *header = NULL; new_skb = ccci_boot_mem_alloc(sizeof(XBOOT_CMD_GETBIN)); if (new_skb == NULL) { DBGLOG(BOOT, ERR, "Failed to alloc skb !!"); result = KAL_FAIL; break; } header = (XBOOT_CMD_GETBIN *)skb_put(new_skb, sizeof(XBOOT_CMD_GETBIN)); memset(new_skb->data, 0, sizeof(XBOOT_CMD_GETBIN)); header->magic = xcmd->data.xcmd_getbin.magic; header->msg_id = xcmd->data.xcmd_getbin.msg_id; header->offset = xcmd->data.xcmd_getbin.offset; header->len = xcmd->data.xcmd_getbin.len; eemcs_boot_rx_callback(new_skb, 0); break; } default: break; } DEBUG_LOG_FUNCTION_LEAVE; return result; } KAL_INT32 bootut_register_swint_callback(EEMCS_CCCI_SWINT_CALLBACK func_ptr) { DEBUG_LOG_FUNCTION_ENTRY; DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } KAL_INT32 bootut_unregister_swint_callback(KAL_UINT32 id) { DEBUG_LOG_FUNCTION_ENTRY; DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } KAL_INT32 bootut_download_handler(struct sk_buff *skb) { KAL_INT32 result = KAL_SUCCESS; KAL_UINT32 md_mbx[2] = {0}; EEMCS_BOOT_UT_CMD *xcmd = NULL; XBOOT_CMD *ack = NULL; KAL_UINT32 msg_id = -1; DEBUG_LOG_FUNCTION_ENTRY; xcmd = &ut_md_xcmd[eemcs_boot_inst.ut_xcmd_idx]; // H2D mailbox if (skb == NULL) { bootut_md_read_mbx(&md_mbx[0], &md_mbx[1]); if ((xcmd->data.mbx.data0 == SDIOMAIL_BOOTING_REQ && md_mbx[0] == SDIOMAIL_BOOTING_ACK) || (xcmd->data.mbx.data0 == SDIOMAIL_DL_REQ && md_mbx[0] == SDIOMAIL_REF)) { eemcs_boot_inst.ut_xcmd_idx++; } // Command } else { ack = (XBOOT_CMD *)skb->data; switch (xcmd->type) { case BOOT_UT_XCMD: msg_id = xcmd->data.xcmd.msg_id; goto _jump; case BOOT_UT_MSD_OUTPUT: case BOOT_UT_MSD_FLUSH: msg_id = xcmd->data.msd_print.msg_id; _jump: { if (msg_id + 1 != ack->msg_id) { DBGLOG(BOOT, ERR, "[BOOT_UT] ACK from AP is incorrect !! MD send (%d) but got (%d)", msg_id, ack->msg_id); result = KAL_FAIL; goto _ack_fail; } if (ack->msg_id == CMDID_ACK_BIN_LOAD_END) { eemcs_boot_inst.ut_bl_cnt++; #if (EEMCS_BOOT_UT_BL_CNT >= 2) // Repeat BL download simulation if (eemcs_boot_inst.ut_bl_cnt > 1 && eemcs_boot_inst.ut_bl_cnt < EEMCS_BOOT_UT_BL_CNT) { eemcs_boot_inst.ut_xcmd_idx = EEMCS_BOOT_UT_BL_LOOP_START; break; } #endif } // Go to next MD command if (ack->msg_id != CMDID_ACK_MD_BOOT_END) eemcs_boot_inst.ut_xcmd_idx++; else { DBGLOG(BOOT, TRA, "[BOOT_UT] +++ CMDID_ACK_MD_BOOT_END +++ Download is OK !!"); goto _boot_end; } break; } case BOOT_UT_XCMD_GETBIN: { // GETBIN ack from AP side if (!eemcs_boot_inst.ut_getbin_time) { DBGLOG(BOOT, DBG, "[BOOT_UT] CMDID_GET_BIN start !!"); if (ack->msg_id == CMDID_ACK_GET_BIN) { eemcs_boot_inst.ut_getbin_time = 1; eemcs_boot_inst.ut_getbin_size = 0; goto _wait_ack; } // SHOULD NOT BE HERE !! KAL_ASSERT(0); // GETBIN data from AP side } else { eemcs_boot_inst.ut_getbin_size += skb->len; DBGLOG(BOOT, DBG, "[BOOT_UT] CMDID_GET_BIN. Total : %d, skb : %d", eemcs_boot_inst.ut_getbin_size, skb->len); if (eemcs_boot_inst.ut_getbin_size < xcmd->data.xcmd_getbin.len) { DBGLOG(BOOT, DBG, "[BOOT_UT] CMDID_GET_BIN continue ..."); goto _wait_ack; } DBGLOG(BOOT, DBG, "[BOOT_UT] CMDID_GET_BIN end !!"); eemcs_boot_inst.ut_getbin_time = 0; eemcs_boot_inst.ut_xcmd_idx++; } break; } } } bootut_md_send_command(); _boot_end: _wait_ack: _ack_fail: dev_kfree_skb(skb); DEBUG_LOG_FUNCTION_LEAVE; return result; } struct sk_buff *bootut_gen_handshake_data(KAL_UINT32 data1, KAL_UINT32 reserved) { struct sk_buff *new_skb = NULL; CCCI_BUFF_T *header = NULL; new_skb = ccci_boot_mem_alloc(sizeof(CCCI_BUFF_T)); if (new_skb == NULL) return NULL; header = (CCCI_BUFF_T *)skb_put(new_skb, sizeof(CCCI_BUFF_T)); memset(new_skb->data, 0, sizeof(CCCI_BUFF_T)); header->channel = CH_CTRL_RX; header->data[0] = CCCI_MAGIC_NUM; header->data[1] = data1; header->reserved = reserved; return new_skb; } KAL_INT32 bootut_ccci_handshake_handler(struct sk_buff *skb) { KAL_INT32 result = KAL_SUCCESS; CCCI_BUFF_T *ccci = NULL; MODEM_RUNTIME *runtime = NULL; struct sk_buff *new_skb = NULL; DEBUG_LOG_FUNCTION_ENTRY; KAL_ASSERT(skb != NULL); // Check CCCI header ccci = (CCCI_BUFF_T *)skb->data; if (ccci->reserved != MD_INIT_CHK_ID) { result = KAL_FAIL; goto _check_fail; } // Check runtime data runtime = (MODEM_RUNTIME *)(skb->data + sizeof(CCCI_BUFF_T)); if ((runtime->Prefix != 0x46494343 || runtime->Postfix != 0x46494343) || (runtime->DriverVersion != 0x20110118)) { result = KAL_FAIL; goto _check_fail; } // Ack to AP side new_skb = bootut_gen_handshake_data(0, NORMAL_BOOT_ID); if (new_skb == NULL) { DBGLOG(BOOT, ERR, "[BOOT_UT] Failed to alloc skb !!"); result = KAL_FAIL; goto _alloc_fail; } eemcs_boot_rx_callback(new_skb, 0); _alloc_fail: _check_fail: dev_kfree_skb(skb); DEBUG_LOG_FUNCTION_LEAVE; return result; } KAL_INT32 bootut_start_ccci_handshake(void) { KAL_INT32 result = KAL_SUCCESS; struct sk_buff *new_skb = NULL; DEBUG_LOG_FUNCTION_ENTRY; new_skb = bootut_gen_handshake_data(MD_INIT_START_BOOT, MD_INIT_CHK_ID); if (new_skb == NULL) { DBGLOG(BOOT, ERR, "[BOOT_UT] Failed to alloc skb !!"); result = KAL_FAIL; goto _alloc_fail; } eemcs_boot_rx_callback(new_skb, 0); DEBUG_LOG_FUNCTION_LEAVE; _alloc_fail: return result; } inline KAL_INT32 bootut_UL_write_skb_to_swq(struct sk_buff *skb) { if (eemcs_boot_get_state() < MD_ROM_BOOT_READY) return bootut_download_handler(skb); else { if (!eemcs_device_ready()) return bootut_ccci_handshake_handler(skb); else return bootut_lb(skb); } } KAL_INT32 bootut_lb(struct sk_buff *skb) { CCCI_BUFF_T *pccci_h = (CCCI_BUFF_T *)skb->data; KAL_UINT8 port_id; KAL_UINT32 tx_ch, rx_ch; struct sk_buff *new_skb; DEBUG_LOG_FUNCTION_ENTRY; DBGLOG(BOOT, DBG, "[BOOT_UT] CCCI channel bootut_lb CCCI_H(0x%x)(0x%x)(0x%x)(0x%x)", pccci_h->data[0], pccci_h->data[1], pccci_h->channel, pccci_h->reserved); new_skb = ccci_boot_mem_alloc(skb->len); if (new_skb == NULL) { DBGLOG(BOOT, ERR, "[BOOT_UT] _EEMCS_BOOT_UT dev_alloc_skb fail sz(%d).", skb->len); dev_kfree_skb(skb); DEBUG_LOG_FUNCTION_LEAVE; return KAL_FAIL; } memcpy(skb_put(new_skb, skb->len), skb->data, skb->len); pccci_h = (CCCI_BUFF_T *)new_skb->data; port_id = ccci_ch_to_port(pccci_h->channel); tx_ch = pccci_h->channel; rx_ch = eemcs_boot_inst.boot_node.ccci_ch.rx; pccci_h->channel = rx_ch; DBGLOG(BOOT, DBG, "[BOOT_UT] ========= PORT(%d) tx_ch(%d) LB to rx_ch(%d)", port_id, tx_ch, rx_ch); eemcs_boot_rx_callback(new_skb, 0); dev_kfree_skb(skb); DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } inline KAL_INT32 bootut_UL_write_room_check(void) { DEBUG_LOG_FUNCTION_ENTRY; DEBUG_LOG_FUNCTION_LEAVE; return 32; } void bootut_reset_txq_count(void) { DEBUG_LOG_FUNCTION_ENTRY; DEBUG_LOG_FUNCTION_LEAVE; } int bootut_xboot_mb_rd(KAL_UINT32 *pBuffer, KAL_UINT32 size) { DEBUG_LOG_FUNCTION_ENTRY; KAL_ASSERT(pBuffer != NULL); if (size == sizeof(KAL_UINT32)) { *pBuffer = eemcs_boot_ut_mbx[0]; } else if (size == (sizeof(KAL_UINT32) * 2)) { *pBuffer = eemcs_boot_ut_mbx[0]; *(pBuffer + 1) = eemcs_boot_ut_mbx[1]; } else { DBGLOG(BOOT, ERR, "[BOOT_UT] Invalid mailbox read !! size = %d", size); } DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } int bootut_xboot_mb_wr(KAL_UINT32 *pBuffer, KAL_UINT32 size) { DEBUG_LOG_FUNCTION_ENTRY; KAL_ASSERT(pBuffer != NULL); if (size == sizeof(KAL_UINT32)) { eemcs_boot_ut_mbx[0] = *pBuffer; } else if (size == (sizeof(KAL_UINT32) * 2)) { eemcs_boot_ut_mbx[0] = *pBuffer; eemcs_boot_ut_mbx[1] = *(pBuffer + 1); } else { DBGLOG(BOOT, ERR, "[BOOT_UT] Invalid mailbox write !! size = %d", size); } DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } KAL_UINT32 bootut_register_expt_callback(EEMCS_CCCI_EXCEPTION_IND_CALLBACK func_ptr) { DEBUG_LOG_FUNCTION_ENTRY; DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } KAL_UINT32 bootut_unregister_expt_callback(KAL_UINT32 cb_id) { DEBUG_LOG_FUNCTION_ENTRY; DEBUG_LOG_FUNCTION_LEAVE; return KAL_SUCCESS; } #endif // _EEMCS_BOOT_UT