/****************************************************************************** * * Copyright (C) 2012-2014 Broadcom Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ #include #include "nfc_hal_int.h" #include "userial.h" /***************************************************************************** * Definitions *****************************************************************************/ /* Internal flags */ /* Application provided patchram in a single buffer */ #define NFC_HAL_PRM_FLAGS_USE_PATCHRAM_BUF 0x01 /* Reserved for future use */ #define NFC_HAL_PRM_FLAGS_RFU 0x02 #define NFC_HAL_PRM_FLAGS_SIGNATURE_SENT 0x04 /* Signature sent to NFCC */ #define NFC_HAL_PRM_FLAGS_I2C_FIX_REQUIRED 0x08 /* PreI2C patch required */ /* B3 Patch (no RESET_NTF after patch download) */ #define NFC_HAL_PRM_FLAGS_BCM20791B3 0x10 #define NFC_HAL_PRM_FLAGS_RM_RF 0x20 /* Erase Personality data */ /* Secure patch download definitions */ /* PRJID + MAJORVER + MINORVER + COUNT */ #define NFC_HAL_PRM_NCD_PATCHFILE_HDR_LEN 7 /* Enumeration of power modes IDs */ #define NFC_HAL_PRM_SPD_POWER_MODE_LPM 0 #define NFC_HAL_PRM_SPD_POWER_MODE_FPM 1 /* Version string for BCM20791B3 */ const uint8_t NFC_HAL_PRM_BCM20791B3_STR[] = "20791B3"; #define NFC_HAL_PRM_BCM20791B3_STR_LEN (sizeof(NFC_HAL_PRM_BCM20791B3_STR) - 1) /* timeout for SPD events (in ms) */ #define NFC_HAL_PRM_SPD_TOUT (6000) /* delay before sending any new command (ms)*/ #define NFC_HAL_PRM_END_DELAY (250) #if (NFC_HAL_PRM_DEBUG == TRUE) #define NFC_HAL_PRM_STATE(str) \ HAL_TRACE_DEBUG2("%s st: %d", str, nfc_hal_cb.prm.state) #else #define NFC_HAL_PRM_STATE(str) #endif void nfc_hal_prm_post_baud_update(tHAL_NFC_STATUS status); typedef struct { uint16_t offset; uint8_t len; } tNFC_HAL_PRM_RM_RF; const tNFC_HAL_PRM_RM_RF nfc_hal_prm_rm_rf_20795a1[] = { {0x0000, 0xFB}, {0x019C, 0x08}, {0x05E8, 0xFB}, {0, 0}}; static bool nfc_hal_prm_nvm_rw_cmd(void); /***************************************************************************** ** Extern variable from nfc_hal_dm_cfg.c *****************************************************************************/ extern tNFC_HAL_CFG* p_nfc_hal_cfg; /******************************************************************************* ** ** Function nfc_hal_prm_spd_handle_download_complete ** ** Description Patch download complete (for secure patch download) ** ** Returns void ** *******************************************************************************/ void nfc_hal_prm_spd_handle_download_complete(uint8_t event) { nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_IDLE; /* Notify application now */ if (nfc_hal_cb.prm.p_cback) (nfc_hal_cb.prm.p_cback)(event); } /******************************************************************************* ** ** Function nfc_hal_prm_spd_send_next_segment ** ** Description Send next patch segment (for secure patch download) ** ** Returns void ** *******************************************************************************/ void nfc_hal_prm_spd_send_next_segment(void) { uint8_t* p_src; uint16_t len, offset = nfc_hal_cb.prm.cur_patch_offset; uint8_t hcit, oid, hdr0, type; uint8_t chipverlen; uint8_t chipverstr[NCI_SPD_HEADER_CHIPVER_LEN]; uint8_t patch_hdr_size = NCI_MSG_HDR_SIZE + 1; /* 1 is for HCIT */ /* Validate that segment is at least big enought to have NCI_MSG_HDR_SIZE + 1 * (hcit) */ if (nfc_hal_cb.prm.cur_patch_len_remaining < patch_hdr_size) { HAL_TRACE_ERROR0("Unexpected end of patch."); nfc_hal_prm_spd_handle_download_complete( NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT); return; } /* Parse NCI command header */ p_src = (uint8_t*)(nfc_hal_cb.prm.p_cur_patch_data + offset); STREAM_TO_UINT8(hcit, p_src); STREAM_TO_UINT8(hdr0, p_src); STREAM_TO_UINT8(oid, p_src); STREAM_TO_UINT8(len, p_src); STREAM_TO_UINT8(type, p_src); /* Update number of bytes comsumed */ nfc_hal_cb.prm.cur_patch_offset += (len + patch_hdr_size); nfc_hal_cb.prm.cur_patch_len_remaining -= (len + patch_hdr_size); /* Check if sending signature byte */ if ((oid == NCI_MSG_SECURE_PATCH_DOWNLOAD) && (type == NCI_SPD_TYPE_SIGNATURE)) { nfc_hal_cb.prm.flags |= NFC_HAL_PRM_FLAGS_SIGNATURE_SENT; } /* Check for header */ else if ((oid == NCI_MSG_SECURE_PATCH_DOWNLOAD) && (type == NCI_SPD_TYPE_HEADER)) { /* Check if patch is for BCM20791B3 */ p_src += NCI_SPD_HEADER_OFFSET_CHIPVERLEN; STREAM_TO_UINT8(chipverlen, p_src); if (memcmp(nfc_hal_cb.nvm_cb.chip_ver, p_src, chipverlen) != 0) { HAL_TRACE_ERROR0("Unexpected chip ver."); nfc_hal_prm_spd_handle_download_complete( NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT); return; } STREAM_TO_ARRAY(chipverstr, p_src, NCI_SPD_HEADER_CHIPVER_LEN); if (memcmp(NFC_HAL_PRM_BCM20791B3_STR, chipverstr, NFC_HAL_PRM_BCM20791B3_STR_LEN) == 0) { /* Patch is for BCM2079B3 - do not wait for RESET_NTF after patch download */ nfc_hal_cb.prm.flags |= NFC_HAL_PRM_FLAGS_BCM20791B3; } else { /* Patch is for BCM2079B4 or newer - wait for RESET_NTF after patch * download */ nfc_hal_cb.prm.flags &= ~NFC_HAL_PRM_FLAGS_BCM20791B3; } } /* Send the command (not including HCIT here) */ nfc_hal_dm_send_nci_cmd( (uint8_t*)(nfc_hal_cb.prm.p_cur_patch_data + offset + 1), (uint8_t)(len + NCI_MSG_HDR_SIZE), nfc_hal_prm_nci_command_complete_cback); } /******************************************************************************* ** ** Function nfc_hal_prm_spd_handle_next_patch_start ** ** Description Handle start of next patch (for secure patch download) ** ** Returns void ** *******************************************************************************/ void nfc_hal_prm_spd_handle_next_patch_start(void) { uint32_t cur_patch_mask; uint32_t cur_patch_len; bool found_patch_to_download = false; while (!found_patch_to_download) { /* Get length of current patch */ cur_patch_len = nfc_hal_cb.prm.spd_patch_desc[nfc_hal_cb.prm.spd_cur_patch_idx].len; /* Check if this is a patch we need to download */ cur_patch_mask = ((uint32_t)1 << nfc_hal_cb.prm .spd_patch_desc[nfc_hal_cb.prm.spd_cur_patch_idx] .power_mode); if (nfc_hal_cb.prm.spd_patch_needed_mask & cur_patch_mask) { found_patch_to_download = true; } else { /* Do not need to download this patch. Skip to next patch */ HAL_TRACE_DEBUG1( "Skipping patch for power_mode %i.", nfc_hal_cb.prm.spd_patch_desc[nfc_hal_cb.prm.spd_cur_patch_idx] .power_mode); nfc_hal_cb.prm.spd_cur_patch_idx++; if (nfc_hal_cb.prm.spd_cur_patch_idx >= nfc_hal_cb.prm.spd_patch_count) { /* No more to download */ nfc_hal_prm_spd_handle_download_complete(NFC_HAL_PRM_COMPLETE_EVT); return; } else if (!(nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_USE_PATCHRAM_BUF)) { /* Notify adaptation layer to call HAL_NfcPrmDownloadContinue with the * next patch header */ (nfc_hal_cb.prm.p_cback)(NFC_HAL_PRM_SPD_GET_NEXT_PATCH); return; } else { /* Patch in buffer. Skip over current patch. Check next patch */ nfc_hal_cb.prm.cur_patch_len_remaining -= (uint16_t)cur_patch_len; nfc_hal_cb.prm.cur_patch_offset += (uint16_t)cur_patch_len; } } } /* Begin downloading patch */ HAL_TRACE_DEBUG1( "Downloading patch for power_mode %i.", nfc_hal_cb.prm.spd_patch_desc[nfc_hal_cb.prm.spd_cur_patch_idx] .power_mode); nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_SPD_DOWNLOADING; nfc_hal_prm_spd_send_next_segment(); } #if (NFC_HAL_PRE_I2C_PATCH_INCLUDED == TRUE) /******************************************************************************* ** ** Function nfc_hal_prm_spd_download_i2c_fix ** ** Description Start downloading patch for i2c fix ** ** Returns void ** *******************************************************************************/ void nfc_hal_prm_spd_download_i2c_fix(void) { uint8_t *p, *p_start; uint16_t patchfile_project_id; uint16_t patchfile_ver_major; uint16_t patchfile_ver_minor; uint16_t patchfile_patchsize; uint8_t u8; HAL_TRACE_DEBUG0("Downloading I2C fix..."); /* Save pointer and offset of patchfile, so we can resume after downloading * the i2c fix */ nfc_hal_cb.prm.spd_patch_offset = nfc_hal_cb.prm.cur_patch_offset; nfc_hal_cb.prm.spd_patch_len_remaining = nfc_hal_cb.prm.cur_patch_len_remaining; /* Initialize pointers for downloading i2c fix */ nfc_hal_cb.prm.p_cur_patch_data = nfc_hal_cb.prm_i2c.p_patch; nfc_hal_cb.prm.cur_patch_offset = 0; nfc_hal_cb.prm.cur_patch_len_remaining = nfc_hal_cb.prm_i2c.len; /* Parse the i2c patchfile */ if (nfc_hal_cb.prm.cur_patch_len_remaining >= NFC_HAL_PRM_NCD_PATCHFILE_HDR_LEN) { /* Parse patchfile header */ p = (uint8_t*)nfc_hal_cb.prm.p_cur_patch_data; p_start = p; STREAM_TO_UINT16(patchfile_project_id, p); STREAM_TO_UINT16(patchfile_ver_major, p); STREAM_TO_UINT16(patchfile_ver_minor, p); /* RFU */ p++; /* Check how many patches are in the patch file */ STREAM_TO_UINT8(u8, p); /* Should only be one patch */ if (u8 > 1) { HAL_TRACE_ERROR1("Invalid i2c fix: invalid number of patches (%i)", u8); nfc_hal_prm_spd_handle_download_complete( NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT); return; } /* Get info about the i2c patch*/ STREAM_TO_UINT8(u8, p); /* power mode (not needed for i2c patch) */ STREAM_TO_UINT16(patchfile_patchsize, p); /* size of patch */ /* 5 byte RFU */ p += 5; /* Adjust length to exclude patchfiloe header */ nfc_hal_cb.prm.cur_patch_len_remaining -= (uint16_t)(p - p_start); /* Adjust size of patchfile */ nfc_hal_cb.prm.cur_patch_offset += (uint16_t)( p - p_start); /* Bytes of patchfile transmitted/processed so far */ /* Begin sending patch to the NFCC */ nfc_hal_prm_spd_send_next_segment(); } else { /* ERROR: Bad length for patchfile */ HAL_TRACE_ERROR0("Invalid i2c fix: unexpected end of patch"); nfc_hal_prm_spd_handle_download_complete( NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT); } } #endif /* NFC_HAL_PRE_I2C_PATCH_INCLUDED */ /******************************************************************************* ** ** Function nfc_hal_prm_spd_check_version_continue ** ** Description Check patchfile version with current downloaded version ** ** Returns void ** *******************************************************************************/ static void nfc_hal_prm_spd_check_version_continue(void) { HAL_TRACE_DEBUG1("nfc_hal_prm_spd_check_version_continue 0x%02x", nfc_hal_cb.prm.flags); if (nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_RM_RF) { HAL_TRACE_DEBUG0("erase relevant blocks in NVM"); nfc_hal_cb.prm.flags &= ~NFC_HAL_PRM_FLAGS_RM_RF; if (!nfc_hal_prm_nvm_rw_cmd()) { /* nvm rw started successfully */ return; } } #if (NFC_HAL_PRE_I2C_PATCH_INCLUDED == TRUE) if (nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_I2C_FIX_REQUIRED) { HAL_TRACE_DEBUG0("I2C patch fix required."); /* Download i2c fix first */ nfc_hal_prm_spd_download_i2c_fix(); return; } #endif /* NFC_HAL_PRE_I2C_PATCH_INCLUDED */ /* Download first segment */ nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_SPD_GET_PATCH_HEADER; if (!(nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_USE_PATCHRAM_BUF)) { /* Notify adaptation layer to call HAL_NfcPrmDownloadContinue with the next * patch segment */ (nfc_hal_cb.prm.p_cback)(NFC_HAL_PRM_SPD_GET_NEXT_PATCH); } else { nfc_hal_prm_spd_handle_next_patch_start(); } } /******************************************************************************* ** ** Function nfc_hal_prm_spd_check_version ** ** Description Check patchfile version with current downloaded version ** ** Returns void ** *******************************************************************************/ void nfc_hal_prm_spd_check_version(void) { uint8_t *p, *p_start, i; uint32_t nvm_patch_present_mask = 0; uint32_t patchfile_patch_present_mask; uint16_t patchfile_project_id = 0; uint16_t patchfile_ver_major = 0; uint16_t patchfile_ver_minor = 0; uint16_t patchfile_patchsize; uint8_t return_code = NFC_HAL_PRM_COMPLETE_EVT; /* Initialize patchfile offset pointers */ p = p_start = NULL; patchfile_patchsize = 0; /* the good patches in NVM */ if (nfc_hal_cb.nvm_cb.lpm_size && !(nfc_hal_cb.nvm_cb.flags & (NFC_HAL_NVM_FLAGS_LPM_BAD))) nvm_patch_present_mask |= (1 << NFC_HAL_PRM_SPD_POWER_MODE_LPM); if (nfc_hal_cb.nvm_cb.fpm_size && !(nfc_hal_cb.nvm_cb.flags & (NFC_HAL_NVM_FLAGS_FPM_BAD))) nvm_patch_present_mask |= (1 << NFC_HAL_PRM_SPD_POWER_MODE_FPM); /* Get patchfile version */ if (nfc_hal_cb.prm.cur_patch_len_remaining >= NFC_HAL_PRM_NCD_PATCHFILE_HDR_LEN) { /* Parse patchfile header */ p = (uint8_t*)nfc_hal_cb.prm.p_cur_patch_data; p_start = p; STREAM_TO_UINT16(patchfile_project_id, p); STREAM_TO_UINT16(patchfile_ver_major, p); STREAM_TO_UINT16(patchfile_ver_minor, p); /* RFU */ p++; /* Check how many patches are in the patch file */ STREAM_TO_UINT8(nfc_hal_cb.prm.spd_patch_count, p); if (nfc_hal_cb.prm.spd_patch_count > NFC_HAL_PRM_MAX_PATCH_COUNT) { HAL_TRACE_ERROR2( "Unsupported patchfile (number of patches (%i) exceeds maximum (%i)", nfc_hal_cb.prm.spd_patch_count, NFC_HAL_PRM_MAX_PATCH_COUNT); } /* Mask of patches that are present in the patchfile */ patchfile_patch_present_mask = 0; /* Get lengths for each patch */ for (i = 0; i < nfc_hal_cb.prm.spd_patch_count; i++) { /* Get power mode for this patch */ STREAM_TO_UINT8(nfc_hal_cb.prm.spd_patch_desc[i].power_mode, p); /* Update mask of power-modes present in the patchfile */ patchfile_patch_present_mask |= ((uint32_t)1 << nfc_hal_cb.prm.spd_patch_desc[i].power_mode); /* Get length of patch */ STREAM_TO_UINT16(nfc_hal_cb.prm.spd_patch_desc[i].len, p); /* Add total size of patches */ patchfile_patchsize += nfc_hal_cb.prm.spd_patch_desc[i].len; /* 5 byte RFU */ p += 5; } /* Adjust offset to after the patch file header */ nfc_hal_cb.prm.cur_patch_offset += (uint16_t)( p - p_start); /* Bytes of patchfile transmitted/processed so far */ nfc_hal_cb.prm.cur_patch_len_remaining -= (uint16_t)(p - p_start); /* Adjust size of patchfile */ HAL_TRACE_DEBUG4( "NVM Patch info: flags=0x%04x, Ver=%i.%i, PatchMask=0x%08x", nfc_hal_cb.nvm_cb.flags, nfc_hal_cb.nvm_cb.ver_major, nfc_hal_cb.nvm_cb.ver_minor, nvm_patch_present_mask); HAL_TRACE_DEBUG6( "Patchfile info: ProjID=0x%04x, Ver=%i.%i, Num patches=%i, " "PatchMask=0x%08x, PatchSize=%i", patchfile_project_id, patchfile_ver_major, patchfile_ver_minor, nfc_hal_cb.prm.spd_patch_count, patchfile_patch_present_mask, patchfile_patchsize); /********************************************************************* * Version check of patchfile against NVM *********************************************************************/ /* Download the patchfile if no patches in NVM */ if ((nfc_hal_cb.nvm_cb.project_id == 0) || !(nfc_hal_cb.nvm_cb.flags & NFC_HAL_NVM_FLAGS_PATCH_PRESENT)) { /* No patch in NVM, need to download all */ nfc_hal_cb.prm.spd_patch_needed_mask = patchfile_patch_present_mask; if (nfc_hal_cb.dev_cb.brcm_hw_id == BRCM_20795A1_ID) { nfc_hal_cb.prm.flags |= NFC_HAL_PRM_FLAGS_RM_RF; nfc_hal_cb.prm.p_param = (void*)nfc_hal_prm_rm_rf_20795a1; nfc_hal_cb.prm.param_idx = 0; } HAL_TRACE_DEBUG2("No previous patch detected. Downloading patch %i.%i", patchfile_ver_major, patchfile_ver_minor); } /* Skip download if project ID of patchfile does not match NVM */ else if (nfc_hal_cb.nvm_cb.project_id != patchfile_project_id) { /* Project IDs mismatch */ HAL_TRACE_DEBUG2( "Patch download skipped: Mismatched Project ID (NVM ProjId: 0x%04x, " "Patchfile ProjId: 0x%04x)", nfc_hal_cb.nvm_cb.project_id, patchfile_project_id); return_code = NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT; } /* Skip download if version of patchfile is equal to version in NVM */ /* and patches of the power modes are the same as the good patches in NVM */ else if ((nfc_hal_cb.nvm_cb.ver_major == patchfile_ver_major) && (nfc_hal_cb.nvm_cb.ver_minor == patchfile_ver_minor) && ((nvm_patch_present_mask | patchfile_patch_present_mask) == nvm_patch_present_mask)) /* if the NVM patch include all the patched in file */ { HAL_TRACE_DEBUG2( "Patch download skipped. NVM patch (version %i.%i) is the same than " "the patchfile ", nfc_hal_cb.nvm_cb.ver_major, nfc_hal_cb.nvm_cb.ver_minor); return_code = NFC_HAL_PRM_COMPLETE_EVT; } /* Remaining cases: Download all patches in the patchfile */ else { nfc_hal_cb.prm.spd_patch_needed_mask = patchfile_patch_present_mask; HAL_TRACE_DEBUG4( "Downloading patch version: %i.%i (previous version in NVM: " "%i.%i)...", patchfile_ver_major, patchfile_ver_minor, nfc_hal_cb.nvm_cb.ver_major, nfc_hal_cb.nvm_cb.ver_minor); } } else { /* Invalid patch file header */ HAL_TRACE_ERROR0("Invalid patch file header."); return_code = NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT; } /* If we need to download anything, get the first patch to download */ if (nfc_hal_cb.prm.spd_patch_needed_mask) { HAL_TRACE_ERROR4( "Downloading patch version: %i.%i (previous version in NVM: %i.%i)...", patchfile_ver_major, patchfile_ver_minor, nfc_hal_cb.nvm_cb.ver_major, nfc_hal_cb.nvm_cb.ver_minor); #if (NFC_HAL_PRE_I2C_PATCH_INCLUDED == TRUE) /* Check if I2C patch is needed: if */ /* - I2C patch file was provided using HAL_NfcPrmSetI2cPatch, and */ /* - current patch in NVM has ProjectID=0, or */ /* FPM is not present or corrupted, or */ /* or patchfile is major-ver 76+ */ /* or patchfile is not for B3 (always download for B4 onward) */ if ((nfc_hal_cb.prm_i2c.p_patch) && ((nfc_hal_cb.nvm_cb.project_id == 0) || (nfc_hal_cb.nvm_cb.fpm_size == 0) || (nfc_hal_cb.nvm_cb.flags & NFC_HAL_NVM_FLAGS_FPM_BAD) || (patchfile_ver_major >= 76) || (!(nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_BCM20791B3)))) { HAL_TRACE_DEBUG0("I2C patch fix required."); nfc_hal_cb.prm.flags |= NFC_HAL_PRM_FLAGS_I2C_FIX_REQUIRED; } #endif /* NFC_HAL_PRE_I2C_PATCH_INCLUDED */ nfc_hal_prm_spd_check_version_continue(); } else { static bool firstTime = true; if (firstTime) { HAL_TRACE_ERROR2("NVM patch version is %d.%d", nfc_hal_cb.nvm_cb.ver_major, nfc_hal_cb.nvm_cb.ver_minor); firstTime = false; } /* Download complete */ nfc_hal_prm_spd_handle_download_complete(return_code); } } #if (NFC_HAL_TRACE_VERBOSE == TRUE) /******************************************************************************* ** ** Function nfc_hal_prm_spd_status_str ** ** Description Return status string for a given spd status code ** ** Returns Status string ** *******************************************************************************/ uint8_t* nfc_hal_prm_spd_status_str(uint8_t spd_status_code) { char* p_str; switch (spd_status_code) { case NCI_STATUS_SPD_ERROR_DEST: p_str = "SPD_ERROR_DEST"; break; case NCI_STATUS_SPD_ERROR_PROJECTID: p_str = "SPD_ERROR_PROJECTID"; break; case NCI_STATUS_SPD_ERROR_CHIPVER: p_str = "SPD_ERROR_CHIPVER"; break; case NCI_STATUS_SPD_ERROR_MAJORVER: p_str = "SPD_ERROR_MAJORVER"; break; case NCI_STATUS_SPD_ERROR_INVALID_PARAM: p_str = "SPD_ERROR_INVALID_PARAM"; break; case NCI_STATUS_SPD_ERROR_INVALID_SIG: p_str = "SPD_ERROR_INVALID_SIG"; break; case NCI_STATUS_SPD_ERROR_NVM_CORRUPTED: p_str = "SPD_ERROR_NVM_CORRUPTED"; break; case NCI_STATUS_SPD_ERROR_PWR_MODE: p_str = "SPD_ERROR_PWR_MODE"; break; case NCI_STATUS_SPD_ERROR_MSG_LEN: p_str = "SPD_ERROR_MSG_LEN"; break; case NCI_STATUS_SPD_ERROR_PATCHSIZE: p_str = "SPD_ERROR_PATCHSIZE"; break; default: p_str = "Unspecified Error"; break; } return ((uint8_t*)p_str); } #endif /* (NFC_HAL_TRACE_VERBOSE == TRUE) */ /******************************************************************************* ** ** Function nfc_hal_prm_nvm_rw_cmd ** ** Description Non Volatile Read Write Command; for now only write zeros ** ** Returns TRUE if done. ** *******************************************************************************/ static bool nfc_hal_prm_nvm_rw_cmd(void) { tNFC_HAL_PRM_RM_RF* p_param = (tNFC_HAL_PRM_RM_RF*)(nfc_hal_cb.prm.p_param); uint8_t *p_buff, *p, *p_end; uint8_t len = 0; uint16_t cmd_len; if (p_param) len = p_param[nfc_hal_cb.prm.param_idx].len; HAL_TRACE_DEBUG2("nfc_hal_prm_nvm_rw_cmd: %d/%d", nfc_hal_cb.prm.param_idx, len); if (len == 0) { return true; } cmd_len = len + 7; p_buff = (uint8_t*)GKI_getbuf(cmd_len); if (p_buff == NULL) { HAL_TRACE_ERROR0("NVM No buffer"); nfc_hal_prm_spd_handle_download_complete(NFC_HAL_PRM_ABORT_EVT); return true; } p = p_buff; UINT8_TO_STREAM(p, (NCI_MTS_CMD | NCI_GID_PROP)); UINT8_TO_STREAM(p, NCI_MSG_EEPROM_RW); UINT8_TO_STREAM(p, (len + 4)); UINT8_TO_STREAM(p, 1); /* 1=write 0=read */ UINT16_TO_STREAM(p, p_param[nfc_hal_cb.prm.param_idx].offset); UINT8_TO_STREAM(p, len); memset(p, 0, len); /* Fill remaining bytes with zeros*/ nfc_hal_cb.prm.param_idx++; nfc_hal_dm_send_nci_cmd(p_buff, cmd_len, nfc_hal_prm_nci_command_complete_cback); GKI_freebuf(p_buff); return false; } /******************************************************************************* ** ** Function nfc_hal_prm_nci_command_complete_cback ** ** Description Callback for NCI vendor specific command complete ** (for secure patch download) ** ** Returns void ** *******************************************************************************/ void nfc_hal_prm_nci_command_complete_cback(tNFC_HAL_NCI_EVT event, uint16_t data_len, uint8_t* p_data) { uint8_t status, u8; uint8_t* p; uint32_t post_signature_delay; NFC_HAL_PRM_STATE("nfc_hal_prm_nci_command_complete_cback"); /* Stop the command-timeout timer */ nfc_hal_main_stop_quick_timer(&nfc_hal_cb.prm.timer); /* Skip over NCI header */ p = p_data + NCI_MSG_HDR_SIZE; /* Handle SECURE_PATCH_DOWNLOAD Rsp */ if (event == NFC_VS_SEC_PATCH_DOWNLOAD_EVT) { /* Status and error code */ STREAM_TO_UINT8(status, p); STREAM_TO_UINT8(u8, p); if (status != NCI_STATUS_OK) { #if (NFC_HAL_TRACE_VERBOSE == TRUE) HAL_TRACE_ERROR2("Patch download failed, reason code=0x%X (%s)", status, nfc_hal_prm_spd_status_str(status)); #else HAL_TRACE_ERROR1("Patch download failed, reason code=0x%X", status); #endif /* Notify application */ nfc_hal_prm_spd_handle_download_complete( NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT); return; } /* If last segment (SIGNATURE) sent */ if (nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_SIGNATURE_SENT) { /* Wait for authentication complete (SECURE_PATCH_DOWNLOAD NTF), including * time to commit to NVM (for BCM43341B0) */ nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_SPD_AUTHENTICATING; nfc_hal_main_start_quick_timer( &nfc_hal_cb.prm.timer, 0x00, (NFC_HAL_PRM_COMMIT_DELAY * QUICK_TIMER_TICKS_PER_SEC) / 1000); return; } /* Download next segment */ else if (nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_USE_PATCHRAM_BUF) { /* If patch is in a buffer, get next patch from buffer */ nfc_hal_prm_spd_send_next_segment(); } else { /* Notify adaptation layer to get next patch segment (via * HAL_NfcPrmDownloadContinue) */ (nfc_hal_cb.prm.p_cback)(NFC_HAL_PRM_CONTINUE_EVT); } } /* Handle SECURE_PATCH_DOWNLOAD NTF */ else if (event == NFC_VS_SEC_PATCH_AUTH_EVT) { HAL_TRACE_DEBUG1("prm flags:0x%x.", nfc_hal_cb.prm.flags); /* Status and error code */ STREAM_TO_UINT8(status, p); STREAM_TO_UINT8(u8, p); /* Sanity check - should only get this NTF while in AUTHENTICATING stage */ if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_AUTHENTICATING) { if (status != NCI_STATUS_OK) { HAL_TRACE_ERROR0("Patch authentication failed"); nfc_hal_prm_spd_handle_download_complete( NFC_HAL_PRM_ABORT_BAD_SIGNATURE_EVT); return; } #if (NFC_HAL_PRE_I2C_PATCH_INCLUDED == TRUE) if (nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_I2C_FIX_REQUIRED) { HAL_TRACE_DEBUG1( "PreI2C patch downloaded...waiting %i ms for NFCC to reboot.", nfc_hal_cb.prm_i2c.prei2c_delay); /* Restore pointers to patchfile */ nfc_hal_cb.prm.flags &= ~NFC_HAL_PRM_FLAGS_I2C_FIX_REQUIRED; nfc_hal_cb.prm.p_cur_patch_data = nfc_hal_cb.prm.p_spd_patch; nfc_hal_cb.prm.cur_patch_offset = nfc_hal_cb.prm.spd_patch_offset; nfc_hal_cb.prm.cur_patch_len_remaining = nfc_hal_cb.prm.spd_patch_len_remaining; /* Resume normal patch download */ nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_SPD_GET_PATCH_HEADER; nfc_hal_cb.prm.flags &= ~NFC_HAL_PRM_FLAGS_SIGNATURE_SENT; /* Post PreI2C delay */ nfc_hal_main_start_quick_timer( &nfc_hal_cb.prm.timer, 0x00, (nfc_hal_cb.prm_i2c.prei2c_delay * QUICK_TIMER_TICKS_PER_SEC) / 1000); return; } #endif /* NFC_HAL_PRE_I2C_PATCH_INCLUDED */ /* Wait for NFCC to save the patch to NVM */ if (!(nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_BCM20791B3)) { /* 20791B4 or newer - wait for RESET_NTF; including time to commit to * NVM (for BCM20791B4+) */ post_signature_delay = NFC_HAL_PRM_COMMIT_DELAY; HAL_TRACE_DEBUG1( "Patch downloaded and authenticated. Waiting %i ms for RESET " "NTF...", post_signature_delay); } else if (nfc_hal_cb.nvm_cb.flags & NFC_HAL_NVM_FLAGS_NO_NVM) { /* No NVM. Wait for NFCC to restart */ post_signature_delay = NFC_HAL_PRM_END_DELAY; HAL_TRACE_DEBUG1( "Patch downloaded and authenticated. Waiting %i ms for NFCC to " "restart...", post_signature_delay); } else { /* Wait for NFCC to save the patch to NVM (need about 1 ms per byte) */ post_signature_delay = nfc_hal_cb.prm.spd_patch_desc[nfc_hal_cb.prm.spd_cur_patch_idx].len; if (post_signature_delay < nfc_hal_cb.prm.patchram_delay) post_signature_delay = nfc_hal_cb.prm.patchram_delay; HAL_TRACE_DEBUG1( "Patch downloaded and authenticated. Waiting %i ms for NVM update " "to complete...", post_signature_delay); } nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_SPD_AUTH_DONE; nfc_hal_main_start_quick_timer( &nfc_hal_cb.prm.timer, 0x00, (post_signature_delay * QUICK_TIMER_TICKS_PER_SEC) / 1000); } else { HAL_TRACE_ERROR0("Got unexpected SECURE_PATCH_DOWNLOAD NTF"); nfc_hal_prm_spd_handle_download_complete(NFC_HAL_PRM_ABORT_EVT); } } /* Handle NCI_MSG_GET_PATCH_VERSION RSP */ else if (event == NFC_VS_GET_PATCH_VERSION_EVT) { nfc_hal_prm_spd_handle_download_complete(NFC_HAL_PRM_COMPLETE_EVT); } else if (event == NFC_VS_EEPROM_RW_EVT) { STREAM_TO_UINT8(status, p); if (status == NCI_STATUS_OK) { if (nfc_hal_prm_nvm_rw_cmd()) { nfc_hal_prm_spd_check_version_continue(); } } else { HAL_TRACE_ERROR0("NVM failed"); nfc_hal_prm_spd_handle_download_complete(NFC_HAL_PRM_ABORT_EVT); } } else { /* Invalid response from NFCC during patch download */ HAL_TRACE_ERROR1( "Invalid response from NFCC during patch download (opcode=0x%02X)", event); nfc_hal_prm_spd_handle_download_complete( NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT); } NFC_HAL_PRM_STATE("prm_nci_command_complete_cback"); } /******************************************************************************* ** ** Function nfc_hal_prm_nfcc_ready_to_continue ** ** Description Continue to download patch or notify application *completition ** ** Returns void ** *******************************************************************************/ void nfc_hal_prm_nfcc_ready_to_continue(void) { uint8_t get_patch_version_cmd[NCI_MSG_HDR_SIZE] = { NCI_MTS_CMD | NCI_GID_PROP, NCI_MSG_GET_PATCH_VERSION, 0x00}; /* Clear the bit for the patch we just downloaded */ nfc_hal_cb.prm.spd_patch_needed_mask &= ~((uint32_t)1 << nfc_hal_cb.prm .spd_patch_desc[nfc_hal_cb.prm.spd_cur_patch_idx] .power_mode); /* Check if another patch to download */ nfc_hal_cb.prm.spd_cur_patch_idx++; if ((nfc_hal_cb.prm.spd_patch_needed_mask) && (nfc_hal_cb.prm.spd_cur_patch_idx < nfc_hal_cb.prm.spd_patch_count)) { nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_SPD_GET_PATCH_HEADER; nfc_hal_cb.prm.flags &= ~NFC_HAL_PRM_FLAGS_SIGNATURE_SENT; if (nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_USE_PATCHRAM_BUF) { /* If patch is in a buffer, get next patch from buffer */ nfc_hal_prm_spd_handle_next_patch_start(); } else { /* Notify adaptation layer to get next patch header (via * HAL_NfcPrmDownloadContinue) */ (nfc_hal_cb.prm.p_cback)(NFC_HAL_PRM_SPD_GET_NEXT_PATCH); } } else { /* Done downloading */ HAL_TRACE_DEBUG0( "Patch downloaded and authenticated. Get new patch version."); /* add get patch info again to verify the effective FW version */ nfc_hal_dm_send_nci_cmd(get_patch_version_cmd, NCI_MSG_HDR_SIZE, nfc_hal_prm_nci_command_complete_cback); nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_W4_GET_VERSION; } } /******************************************************************************* ** ** Function nfc_hal_prm_spd_reset_ntf ** ** Description Received RESET NTF from NFCC, indicating it has completed ** reset after patch download. ** ** Returns void ** *******************************************************************************/ void nfc_hal_prm_spd_reset_ntf(uint8_t reset_reason, uint8_t reset_type) { /* Check if we were expecting a RESET NTF */ if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_AUTH_DONE) { HAL_TRACE_DEBUG2( "Received RESET NTF after patch download (reset_reason=%i, " "reset_type=%i)", reset_reason, reset_type); /* Stop waiting for RESET NTF */ nfc_hal_main_stop_quick_timer(&nfc_hal_cb.prm.timer); { /* Continue with patch download */ nfc_hal_prm_nfcc_ready_to_continue(); } } else if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_GET_PATCH_HEADER) { HAL_TRACE_DEBUG0( "Received RESET NTF after pre-I2C patch download. Proceeding with " "patch download..."); /* Stop waiting for RESET NTF */ nfc_hal_main_stop_quick_timer(&nfc_hal_cb.prm.timer); nfc_hal_prm_spd_handle_next_patch_start(); } else { HAL_TRACE_ERROR2( "Received unexpected RESET NTF (reset_reason=%i, reset_type=%i)", reset_reason, reset_type); } } /******************************************************************************* ** ** Function: nfc_post_final_baud_update ** ** Description: Called after baud rate udate ** ** Returns: Nothing ** *******************************************************************************/ void nfc_hal_prm_post_baud_update(tHAL_NFC_STATUS status) { NFC_HAL_PRM_STATE("nfc_hal_prm_post_baud_update"); if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_AUTH_DONE) { /* Proceed with next step of patch download sequence */ nfc_hal_prm_nfcc_ready_to_continue(); } } /******************************************************************************* ** ** Function nfc_hal_prm_process_timeout ** ** Description Process timer expireation for patch download ** ** Returns void ** *******************************************************************************/ void nfc_hal_prm_process_timeout(void* p_tle) { NFC_HAL_PRM_STATE("nfc_hal_prm_process_timeout"); if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_AUTH_DONE) { if (!(nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_BCM20791B3)) { /* Timeout waiting for RESET NTF after signature sent */ HAL_TRACE_ERROR0("Timeout waiting for RESET NTF after patch download"); nfc_hal_prm_spd_handle_download_complete(NFC_HAL_PRM_ABORT_EVT); } else { nfc_hal_prm_nfcc_ready_to_continue(); } } else if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_GET_PATCH_HEADER) { HAL_TRACE_DEBUG0( "Delay after PreI2C patch download...proceeding to download firmware " "patch"); nfc_hal_prm_spd_handle_next_patch_start(); } else if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_W4_GET_VERSION) { HAL_TRACE_DEBUG0("get patch version timeout???"); nfc_hal_prm_spd_handle_download_complete(NFC_HAL_PRM_COMPLETE_EVT); } else { HAL_TRACE_ERROR1("Patch download: command timeout (state=%i)", nfc_hal_cb.prm.state); nfc_hal_prm_spd_handle_download_complete(NFC_HAL_PRM_ABORT_EVT); } NFC_HAL_PRM_STATE("nfc_hal_prm_process_timeout"); } /******************************************************************************* ** ** Function HAL_NfcPrmDownloadStart ** ** Description Initiate patch download ** ** Input Params ** format_type patch format type ** (NFC_HAL_PRM_FORMAT_BIN, *NFC_HAL_PRM_FORMAT_HCD, or ** NFC_HAL_PRM_FORMAT_NCD) ** ** dest_address destination adderess (needed for BIN format *only) ** ** p_patchram_buf pointer to patchram buffer. If NULL, ** then app must call *HAL_NfcPrmDownloadContinue when ** NFC_HAL_PRM_CONTINUE_EVT is received, to *send the next ** segment of patchram ** ** patchram_len size of p_patchram_buf (if non-NULL) ** ** patchram_delay The delay after each patch. ** If the given value is less than the size of *the patchram, ** the size of patchram is used instead. ** ** p_cback callback for download status ** ** ** Returns TRUE if successful, otherwise FALSE ** ** *******************************************************************************/ bool HAL_NfcPrmDownloadStart(tNFC_HAL_PRM_FORMAT format_type, uint32_t dest_address, uint8_t* p_patchram_buf, uint32_t patchram_len, uint32_t patchram_delay, tNFC_HAL_PRM_CBACK* p_cback) { HAL_TRACE_API0("HAL_NfcPrmDownloadStart ()"); memset(&nfc_hal_cb.prm, 0, sizeof(tNFC_HAL_PRM_CB)); if (p_patchram_buf) { nfc_hal_cb.prm.p_cur_patch_data = p_patchram_buf; nfc_hal_cb.prm.cur_patch_offset = 0; nfc_hal_cb.prm.cur_patch_len_remaining = (uint16_t)patchram_len; nfc_hal_cb.prm.flags |= NFC_HAL_PRM_FLAGS_USE_PATCHRAM_BUF; if (patchram_len == 0) return false; } nfc_hal_cb.prm.p_cback = p_cback; nfc_hal_cb.prm.dest_ram = dest_address; nfc_hal_cb.prm.format = format_type; nfc_hal_cb.prm.patchram_delay = patchram_delay; nfc_hal_cb.prm.timer.p_cback = nfc_hal_prm_process_timeout; if (format_type == NFC_HAL_PRM_FORMAT_NCD) { /* Store patch buffer pointer and length */ nfc_hal_cb.prm.p_spd_patch = p_patchram_buf; nfc_hal_cb.prm.spd_patch_len_remaining = (uint16_t)patchram_len; nfc_hal_cb.prm.spd_patch_offset = 0; /* If patch download is required, but no NVM is available, then abort */ if ((p_nfc_hal_cfg->nfc_hal_prm_nvm_required) && (nfc_hal_cb.nvm_cb.flags & NFC_HAL_NVM_FLAGS_NO_NVM)) { HAL_TRACE_ERROR0( "This platform requires NVM and the NVM is not available - Abort"); nfc_hal_prm_spd_handle_download_complete(NFC_HAL_PRM_ABORT_NO_NVM_EVT); return false; } /* Compare patch version in NVM with version in patchfile */ nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_SPD_COMPARE_VERSION; if (nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_USE_PATCHRAM_BUF) { /* If patchfile is in a buffer, get patch version from buffer */ nfc_hal_prm_spd_check_version(); } else { /* If patchfile is not in a buffer, then request patchfile header from * adaptation layer. */ (nfc_hal_cb.prm.p_cback)(NFC_HAL_PRM_SPD_GET_PATCHFILE_HDR_EVT); } } else { HAL_TRACE_ERROR0("Unexpected patch format."); return false; } return true; } /******************************************************************************* ** ** Function HAL_NfcPrmDownloadContinue ** ** Description Send next segment of patchram to controller. Called when ** NFC_HAL_PRM_CONTINUE_EVT is received. ** ** Only needed if HAL_NfcPrmDownloadStart was called with ** p_patchram_buf=NULL ** ** Input Params p_patch_data pointer to patch data ** patch_data_len patch data len ** ** Returns TRUE if successful, otherwise FALSE ** *******************************************************************************/ bool HAL_NfcPrmDownloadContinue(uint8_t* p_patch_data, uint16_t patch_data_len) { HAL_TRACE_API2("HAL_NfcPrmDownloadContinue ():state = %d, patch_data_len=%d", nfc_hal_cb.prm.state, patch_data_len); /* Check if we are in a valid state for this API */ if ((nfc_hal_cb.prm.state != NFC_HAL_PRM_ST_SPD_COMPARE_VERSION) && (nfc_hal_cb.prm.state != NFC_HAL_PRM_ST_SPD_GET_PATCH_HEADER) && (nfc_hal_cb.prm.state != NFC_HAL_PRM_ST_SPD_DOWNLOADING)) return false; if (patch_data_len == 0) return false; nfc_hal_cb.prm.cur_patch_offset = 0; nfc_hal_cb.prm.p_cur_patch_data = p_patch_data; nfc_hal_cb.prm.cur_patch_len_remaining = patch_data_len; /* Call appropriate handler */ if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_COMPARE_VERSION) { nfc_hal_prm_spd_check_version(); } else if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_GET_PATCH_HEADER) { nfc_hal_prm_spd_handle_next_patch_start(); } else if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_DOWNLOADING) { nfc_hal_prm_spd_send_next_segment(); } else { HAL_TRACE_ERROR1("Unexpected patch state:%d.", nfc_hal_cb.prm.state); } return true; } /******************************************************************************* ** ** Function HAL_NfcPrmSetI2cPatch ** ** Description Specify patchfile for BCM20791B3 I2C fix. This fix ** must be downloaded prior to initial patch download for I2C ** transport ** ** Input Params p_i2c_patchfile_buf: pointer to patch for i2c fix ** i2c_patchfile_len: length of patch ** prei2c_delay: the delay before downloading main patch ** if 0 is given, NFC_HAL_PRM_POST_I2C_FIX_DELAY *is used instead. ** ** Returns Nothing ** ** *******************************************************************************/ void HAL_NfcPrmSetI2cPatch(uint8_t* p_i2c_patchfile_buf, uint16_t i2c_patchfile_len, uint32_t prei2c_delay) { #if (NFC_HAL_PRE_I2C_PATCH_INCLUDED == TRUE) HAL_TRACE_API0("HAL_NfcPrmSetI2cPatch ()"); nfc_hal_cb.prm_i2c.prei2c_delay = NFC_HAL_PRM_POST_I2C_FIX_DELAY; if (prei2c_delay) nfc_hal_cb.prm_i2c.prei2c_delay = prei2c_delay; nfc_hal_cb.prm_i2c.p_patch = p_i2c_patchfile_buf; nfc_hal_cb.prm_i2c.len = i2c_patchfile_len; #endif /* NFC_HAL_PRE_I2C_PATCH_INCLUDED */ } /******************************************************************************* ** ** Function HAL_NfcPrmSetSpdNciCmdPayloadSize ** ** Description Set Host-to-NFCC NCI message size for secure patch download ** ** This API must be called before calling *HAL_NfcPrmDownloadStart. ** If the API is not called, then PRM will use the default ** message size. ** ** Typically, this API is only called for platforms that have ** message-size limitations in the transport/driver. ** ** Valid message size range: *NFC_HAL_PRM_MIN_NCI_CMD_PAYLOAD_SIZE to 255. ** ** Returns HAL_NFC_STATUS_OK if successful ** HAL_NFC_STATUS_FAILED otherwise ** ** *******************************************************************************/ tHAL_NFC_STATUS HAL_NfcPrmSetSpdNciCmdPayloadSize(uint8_t max_payload_size) { /* Validate: minimum size is NFC_HAL_PRM_MIN_NCI_CMD_PAYLOAD_SIZE */ if (max_payload_size < NFC_HAL_PRM_MIN_NCI_CMD_PAYLOAD_SIZE) { HAL_TRACE_ERROR2( "HAL_NfcPrmSetSpdNciCmdPayloadSize: invalid size (%i). Must be between " "%i and 255", max_payload_size, NFC_HAL_PRM_MIN_NCI_CMD_PAYLOAD_SIZE); return (HAL_NFC_STATUS_FAILED); } else { HAL_TRACE_API1( "HAL_NfcPrmSetSpdNciCmdPayloadSize: new message size during download: " "%i", max_payload_size); nfc_hal_cb.ncit_cb.nci_ctrl_size = max_payload_size; return (HAL_NFC_STATUS_OK); } }