void zclHomelink_zigbeeReset(void) { uint16 clusters[2]; zAddrType_t tempAddr = {{0}, (afAddrMode_t)AddrGroup}; aps_Group_t tempGroup = {0, "SwitchX"}; int i; for (i = HAL_NV_PAGE_BEG; i <= (HAL_NV_PAGE_BEG + HAL_NV_PAGE_CNT); i++) { HalFlashErase(i); } clusters[0] = ZCL_CLUSTER_ID_GEN_ON_OFF; clusters[1] = ZCL_CLUSTER_ID_GEN_LEVEL_CONTROL; BindSetDefaultNV(); aps_GroupsSetDefaultNV(); // Initialize default groups. Each button has a group ID built from the MAC. // The top 2 bits are for the endpoints. for(i = 0; i < NUM_BUTTONS; i ++) { tempGroup.ID = i << 14 | (aExtendedAddress[1] & 0x3F) << 8 | aExtendedAddress[0]; tempGroup.name[6] = '1' + i; tempAddr.addr.shortAddr = tempGroup.ID; bindAddEntry(ENDPOINT+i, &tempAddr, 0, 2, clusters); aps_AddGroup(ENDPOINT+i, &tempGroup); } BindWriteNV(); aps_GroupsWriteNV(); Onboard_soft_reset(); }
/********************************************************************* * @fn HalOADWrite * * @brief Write to the storage medium according to the image type. * * NOTE: Destructive write on page boundary! When writing to the first flash word * of a page boundary, the page is erased without saving/restoring the bytes not written. * Writes anywhere else on a page assume that the location written to has been erased. * * @param oset - Offset into the monolithic image, aligned to HAL_FLASH_WORD_SIZE. * @param pBuf - Pointer to the buffer in from which to write. * @param len - Number of bytes to write. If not an even multiple of HAL_FLASH_WORD_SIZE, * remainder bytes are overwritten with garbage. * @param type - Which image: HAL_OAD_RC or HAL_OAD_DL. * * @return None. *********************************************************************/ void HalOADWrite(uint32 oset, uint8 *pBuf, uint16 len, image_t type) { if (HAL_OAD_RC != type) { #if HAL_OAD_XNV_IS_INT preamble_t preamble; HalOADRead(PREAMBLE_OFFSET, (uint8 *)&preamble, sizeof(preamble_t), HAL_OAD_RC); //oset += HAL_OAD_RC_START + preamble.len; oset += HAL_OAD_RC_START + HAL_OAD_DL_OSET; #elif HAL_OAD_XNV_IS_SPI oset += HAL_OAD_DL_OSET; HalSPIWrite(oset, pBuf, len); return; #endif } else { oset += HAL_OAD_RC_START; } if ((oset % HAL_FLASH_PAGE_SIZE) == 0) { HalFlashErase(oset / HAL_FLASH_PAGE_SIZE); } HalFlashWrite(oset / HAL_FLASH_WORD_SIZE, pBuf, len / HAL_FLASH_WORD_SIZE); }
/********************************************************************* * @fn erasePage * * @brief Erases a page in Flash. * * @param pg - Valid NV page to erase. * * @return none */ static void erasePage( uint8 pg ) { if ( !OSAL_NV_CHECK_BUS_VOLTAGE || failF) { failF = TRUE; return; } HalFlashErase(pg); { // Verify the erase operation uint16 offset; uint8 tmp; for (offset = 0; offset < OSAL_NV_PAGE_SIZE; offset ++) { HalFlashRead(pg, offset, &tmp, 1); if (tmp != OSAL_NV_ERASED) { failF = TRUE; break; } } } }
/********************************************************************* * @fn erasePage * * @brief Erases a page in Flash. * * @param pg - Valid NV page to erase. * * @return none */ static void erasePage( uint8 pg ) { HalFlashErase(pg); pgOff[pg - OSAL_NV_PAGE_BEG] = OSAL_NV_PAGE_HDR_SIZE; pgLost[pg - OSAL_NV_PAGE_BEG] = 0; }
// Max length of device name: 20 (excluding tailing null) // This function prepare contents for the backup page void persistent_flash_backup_prepare(uint16_t offset, uint8_t *buf, uint16_t len) { uint32_t buf32[64]; uint8_t i, j; HalFlashErase(pidx + 1); for (i = 0; i < 4; i++) { HalFlashRead(pidx, i * 256, (uint8_t *)buf32, 256); if (i == 0) { buf32[0] = 0xFFFFADDE; } // Cleanup old power mgmt data if (offset == 520) { if (i == 2) { for (j = 2; j < 64; j++) { buf32[j] = 0xFFFFFFFF; } } else if (i == 3) { for (j = 0; j < 64; j++) { buf32[j] = 0xFFFFFFFF; } } } if ((offset / 256) == (uint16_t)i) { memcpy((((uint8_t *)buf32) + (uint32_t)(offset % 256)), buf, len); } HalFlashWrite((pidx + 1), i * 256, (uint8_t *)buf32, 256); } buf32[0] = 0xEFBEADDE; HalFlashWrite((pidx + 1), 0, (uint8_t *)buf32, 4); }
// This function does the persistent page overwrite void persistent_flash_backup_finish(void) { uint32_t buf32[64]; uint8_t i; HalFlashErase(pidx); for (i = 0; i < 4; i++) { HalFlashRead((pidx + 1), i * 256, (uint8_t *)buf32, 256); if (i == 0) { buf32[0] = 0xFFFFADDE; } HalFlashWrite(pidx, i * 256, (uint8_t *)buf32, 256); } buf32[0] = 0xFFBEADDE; HalFlashWrite(pidx, 0, (uint8_t *)buf32, 4); HalFlashErase(pidx + 1); buf32[0] = 0xEFBEADDE; HalFlashWrite(pidx, 0, (uint8_t *)buf32, 4); }
/********************************************************************* * @fn erasePage * * @brief Erases a page in Flash. * * @param pg - Valid NV page to erase. * * @return none */ static void erasePage( uint8 pg ) { if ( !OSAL_NV_CHECK_BUS_VOLTAGE ) { failF = TRUE; return; } HalFlashErase(pg); pgOff[pg - OSAL_NV_PAGE_BEG] = OSAL_NV_PAGE_HDR_SIZE; pgLost[pg - OSAL_NV_PAGE_BEG] = 0; }
// Input buffer at least 21 bytes (couting tailing zero) void custom_set_dn(uint8 *dn) { uint8 idx = 0; uint8 buf[CUSTOM_DN_MAGIC_LEN + CUSTOM_DN_LEN] = {0}; for (idx = 0; idx < (CUSTOM_DN_MAGIC_LEN + CUSTOM_DN_LEN); idx++) { buf[idx] = 0; } dn[CUSTOM_DN_LEN] = 0; HalFlashErase(CUSTOM_DN_PAGE_IDX); osal_memcpy(buf, dn_magic, CUSTOM_DN_MAGIC_LEN); osal_memcpy((buf + CUSTOM_DN_MAGIC_LEN), dn, osal_strlen((char *)dn)); HalFlashWrite((((uint32)CUSTOM_DN_PAGE_IDX * (uint32)2048 + CUSTOM_DN_MGIC_OFFSET) / 4), buf, (CUSTOM_DN_MAGIC_LEN + CUSTOM_DN_LEN) / 4); }
// FIXME: currently only device name void custom_init(void) { uint8 buf[CUSTOM_DN_MAGIC_LEN + CUSTOM_DN_LEN]; uint8 idx = 0; HalFlashRead(CUSTOM_DN_PAGE_IDX, CUSTOM_DN_MGIC_OFFSET, buf, (CUSTOM_DN_MAGIC_LEN + CUSTOM_DN_LEN)); if (osal_memcmp(buf, dn_magic, CUSTOM_DN_MAGIC_LEN) != TRUE) { HalFlashErase(CUSTOM_DN_PAGE_IDX); for (idx = 0; idx < (CUSTOM_DN_MAGIC_LEN + CUSTOM_DN_LEN); idx++) { buf[idx] = 0; } osal_memcpy(buf, dn_magic, CUSTOM_DN_MAGIC_LEN); osal_memcpy((buf + CUSTOM_DN_MAGIC_LEN), dn_default, 3); HalFlashWrite((((uint32)CUSTOM_DN_PAGE_IDX * (uint32)2048 + CUSTOM_DN_MGIC_OFFSET) / 4), buf, (CUSTOM_DN_MAGIC_LEN + CUSTOM_DN_LEN) / 4); } }
/************************************************************************************************** * @fn sblExec * * @brief Act on the SB command and received buffer. * * @param pBuf - A pointer to the RPC command buffer received. * * None. * * output parameters * * None. * * @return None. ************************************************************************************************** */ static void sblExec(uint8 *pBuf) { uint16 t16 = BUILD_UINT16(pBuf[SBL_REQ_ADDR_LSB],pBuf[SBL_REQ_ADDR_MSB]) + OAD_DONGLE_SBL_IMG_BEG; uint8 len = 1; uint8 rsp = SBL_SUCCESS; switch (pBuf[RPC_POS_CMD1]) { case SBL_WRITE_CMD: if ((t16 >= OAD_DONGLE_SBL_IMG_END) || (t16 < OAD_DONGLE_SBL_IMG_BEG)) { rsp = SBL_FAILURE; break; } if ((t16 % SBL_PAGE_SIZE) == 0) { HalFlashErase(t16 / SBL_PAGE_SIZE); } HalFlashWrite(t16, (pBuf + SBL_REQ_DAT0), (SBL_RW_BUF_LEN / HAL_FLASH_WORD_SIZE)); break; case SBL_READ_CMD: len = SBL_RW_BUF_LEN + SBL_READ_HDR_LEN; pBuf[SBL_RSP_ADDR_MSB] = pBuf[SBL_REQ_ADDR_MSB]; pBuf[SBL_RSP_ADDR_LSB] = pBuf[SBL_REQ_ADDR_LSB]; HalFlashRead(t16 / SBL_PAGE_SIZE, (t16 % SBL_PAGE_SIZE) << 2, (pBuf + SBL_RSP_DAT0), SBL_RW_BUF_LEN); break; case SBL_ENABLE_CMD: // Bootload master must verify download by read back - no room for CRC checking code in dongle. break; case SBL_HANDSHAKE_CMD: break; default: rsp = SBL_FAILURE; break; } pBuf[RPC_POS_LEN] = len; pBuf[RPC_POS_CMD1] |= SBL_RSP_MASK; pBuf[RPC_POS_DAT0] = rsp; }
/********************************************************************* * @fn dl2rc * * @brief Copy the DL image to the RC image location. * * NOTE: Assumes that DL image ends on a flash word boundary. * * @param None. * * @return None. *********************************************************************/ static void dl2rc(void) { preamble_t preamble; uint32 oset; uint16 addr = HAL_OAD_RC_START / HAL_FLASH_WORD_SIZE; uint8 buf[4]; HalOADRead(PREAMBLE_OFFSET, (uint8 *)&preamble, sizeof(preamble_t), HAL_OAD_DL); for (oset = 0; oset < preamble.len; oset += HAL_FLASH_WORD_SIZE) { HalOADRead(oset, buf, HAL_FLASH_WORD_SIZE, HAL_OAD_DL); if ((addr % (HAL_FLASH_PAGE_SIZE / HAL_FLASH_WORD_SIZE)) == 0) { HalFlashErase(addr / (HAL_FLASH_PAGE_SIZE / HAL_FLASH_WORD_SIZE)); } HalFlashWrite(addr++, buf, 1); } }
/****************************************************************************** * @fn dl2rc * * @brief Copy the DL image to the RC image location. * * NOTE: Assumes that DL image ends on a flash word boundary. * * @param None. * * @return None. */ static void dl2rc(void) { uint32 oset; OTA_SubElementHdr_t subElement; OTA_ImageHeader_t header; uint16 addr = HAL_OTA_RC_START / HAL_FLASH_WORD_SIZE; uint8 buf[4]; // Determine the length and starting point of the upgrade image HalOTARead(0, (uint8 *)&header, sizeof(OTA_ImageHeader_t), HAL_OTA_DL); HalOTARead(header.headerLength, (uint8*)&subElement, OTA_SUB_ELEMENT_HDR_LEN, HAL_OTA_DL); for (oset = 0; oset < subElement.length; oset += HAL_FLASH_WORD_SIZE) { HalOTARead(oset + header.headerLength + OTA_SUB_ELEMENT_HDR_LEN, buf, HAL_FLASH_WORD_SIZE, HAL_OTA_DL); if ((addr % (HAL_FLASH_PAGE_SIZE / HAL_FLASH_WORD_SIZE)) == 0) { HalFlashErase(addr / (HAL_FLASH_PAGE_SIZE / HAL_FLASH_WORD_SIZE)); } HalFlashWrite(addr++, buf, 1); } }
/************************************************************************************************** * @fn imgCrypt * * @brief Run the AES CTR decryption over the image area specified. * * input parameters * * @param imgSel - Image select: 0 for Image-A and 1 for Image-B. * @param imgHdr - Pointer to the Image Header corresponding to the image select. * * output parameters * * None. * * @return None. */ static void imgCrypt(uint8 imgSel, img_hdr_t *imgHdr) { aesLoadKey(); uint8 pgEnd = imgHdr->res[0] + ImgPageBeg[imgSel]; if (imgSel == 0) { pgEnd += ImgPageLen[1]; } for (uint8 pgNum = ImgPageBeg[imgSel]; pgNum < pgEnd; ) { BEM_NVM_GET(pgNum, pageBuf, HAL_FLASH_PAGE_SIZE); // EBL is used to encrypt the image in SBL_RW_BUF_LEN blocks, so it must be decrypted likewise. for (uint8 blk = 0; blk < (HAL_FLASH_PAGE_SIZE / SBL_RW_BUF_LEN); blk++) { if ((pgNum == ImgPageBeg[imgSel]) && (blk == 0)) { aesCrypt(1, pageBuf + ((uint16)SBL_RW_BUF_LEN * blk)); } else { aesCrypt(0, pageBuf + ((uint16)SBL_RW_BUF_LEN * blk)); } } HalFlashErase(pgNum); BEM_NVM_SET(pgNum, pageBuf, HAL_FLASH_PAGE_SIZE); pgNum++; if ((imgSel == 0) && (pgNum == ImgPageBeg[1])) { pgNum += ImgPageLen[1]; } } }
/****************************************************************************** * @fn HalOTAWrite * * @brief Write to the storage medium according to the image type. * * NOTE: Destructive write on page boundary! When writing to the first flash word * of a page boundary, the page is erased without saving/restoring the bytes not written. * Writes anywhere else on a page assume that the location written to has been erased. * * @param oset - Offset into the monolithic image, aligned to HAL_FLASH_WORD_SIZE. * @param pBuf - Pointer to the buffer in from which to write. * @param len - Number of bytes to write. If not an even multiple of HAL_FLASH_WORD_SIZE, * remainder bytes are overwritten with garbage. * @param type - Which image: HAL_OTA_RC or HAL_OTA_DL. * * @return None. */ void HalOTAWrite(uint32 oset, uint8 *pBuf, uint16 len, image_t type) { if (HAL_OTA_RC != type) { #if HAL_OTA_XNV_IS_INT oset += HAL_OTA_RC_START + HAL_OTA_DL_OSET; #elif HAL_OTA_XNV_IS_SPI oset += HAL_OTA_DL_OSET; HalSPIWrite(oset, pBuf, len); return; #endif } else { oset += HAL_OTA_RC_START; } if ((oset % HAL_FLASH_PAGE_SIZE) == 0) { HalFlashErase(oset / HAL_FLASH_PAGE_SIZE); } HalFlashWrite(oset / HAL_FLASH_WORD_SIZE, pBuf, len / HAL_FLASH_WORD_SIZE); }
/************************************************************************************************** * @fn sblProc * * @brief Process the SB command and received buffer. * * input parameters * * None. * * output parameters * * None. * * @return None. */ static void sblProc(void) { uint16 t16 = BUILD_UINT16(sbBuf[SBL_REQ_ADDR_LSB], sbBuf[SBL_REQ_ADDR_MSB]) + SBL_ADDR_BEG; uint8 len = 1, rsp = SBL_SUCCESS; switch (sbBuf[RPC_POS_CMD1]) { case SBL_WRITE_CMD: if ((t16 >= SBL_ADDR_BEG) && (t16 <= SBL_ADDR_END)) { if ((t16 % SBL_PAGE_LEN) == 0) { HalFlashErase(t16 / SBL_PAGE_LEN); } if (SBL_SECURE) { if (t16 == SBL_ADDR_IMG_HDR) { if (!imgHdrCheck(sbBuf + SBL_REQ_DAT0)) { rsp = SBL_FAILURE; break; } aesCrypt(1, sbBuf + SBL_REQ_DAT0); } else { aesCrypt(0, sbBuf + SBL_REQ_DAT0); } } SBL_NVM_SET(t16, (sbBuf + SBL_REQ_DAT0), SBL_RW_BUF_LEN); // Immediately read back what was written to keep the 'imgHdr' variable in sync with flash. if (t16 == SBL_ADDR_IMG_HDR) { SBL_READ_IMG_HDR(); } } else { rsp = SBL_FAILURE; } break; case SBL_READ_CMD: if ((t16 >= SBL_ADDR_BEG) && (t16 <= SBL_ADDR_END)) { len = SBL_RW_BUF_LEN + SBL_READ_HDR_LEN; sbBuf[SBL_RSP_ADDR_MSB] = sbBuf[SBL_REQ_ADDR_MSB]; sbBuf[SBL_RSP_ADDR_LSB] = sbBuf[SBL_REQ_ADDR_LSB]; SBL_NVM_GET(t16, (sbBuf + SBL_RSP_DAT0), SBL_RW_BUF_LEN); if (SBL_SECURE || (SBL_SIGNER && signMode)) { if (t16 == SBL_ADDR_IMG_HDR) { aesCrypt(1, sbBuf + SBL_RSP_DAT0); } else { aesCrypt(0, sbBuf + SBL_RSP_DAT0); } } } else { rsp = SBL_FAILURE; } break; case SBL_ENABLE_CMD: if (SBL_SIGNER) // A Signer must never enable the image for clean read back with crc[1]=0xFFFF. { signMode = FALSE; // PC Tool read back must be un-encrypted after downloading a new image. } else if (!SBL_SECURE) { imgHdr.crc[1] = imgHdr.crc[0]; imgHdr.crc[0] = 0xFFFF; SBL_NVM_SET(SBL_ADDR_CRC, imgHdr.crc, sizeof(imgHdr.crc)); SBL_READ_IMG_HDR(); } else if (!checkRC()) { rsp = SBL_VALIDATE_FAILED; } break; case SBL_HANDSHAKE_CMD: break; case SBL_SIGNATURE_CMD: len = ((rsp = procSignatureCmd((sbBuf + RPC_POS_DAT0 + 1))) == SBL_SUCCESS) ? 17 : 1; break; default: rsp = SBL_FAILURE; break; } sbBuf[RPC_POS_LEN] = len; sbBuf[RPC_POS_CMD1] |= SBL_RSP_MASK; sbBuf[RPC_POS_DAT0] = rsp; }
/********************************************************************* * @fn oadImgBlockWrite * * @brief Process the Image Block Write. * * @param connHandle - connection message was received on * @param pValue - pointer to data to be written * * @return status */ static bStatus_t oadImgBlockWrite( uint16 connHandle, uint8 *pValue ) { uint16 blkNum = BUILD_UINT16( pValue[0], pValue[1] ); // make sure this is the image we're expecting if ( blkNum == 0 ) { img_hdr_t ImgHdr; uint16 ver = BUILD_UINT16( pValue[6], pValue[7] ); uint16 blkTot = BUILD_UINT16( pValue[8], pValue[9] ) / (OAD_BLOCK_SIZE / HAL_FLASH_WORD_SIZE); HalFlashRead(OAD_IMG_R_PAGE, OAD_IMG_HDR_OSET, (uint8 *)&ImgHdr, sizeof(img_hdr_t)); if ( ( oadBlkNum != blkNum ) || ( oadBlkTot != blkTot ) || ( OAD_IMG_ID( ImgHdr.ver ) == OAD_IMG_ID( ver ) ) ) { return ( ATT_ERR_WRITE_NOT_PERMITTED ); } } if (oadBlkNum == blkNum) { uint16 addr = oadBlkNum * (OAD_BLOCK_SIZE / HAL_FLASH_WORD_SIZE) + (OAD_IMG_D_PAGE * OAD_FLASH_PAGE_MULT); oadBlkNum++; #if defined FEATURE_OAD_SECURE if (blkNum == 0) { // Stop attack with crc0==crc1 by forcing crc1=0xffff. pValue[4] = 0xFF; pValue[5] = 0xFF; } #endif #if defined HAL_IMAGE_B // Skip the Image-B area which lies between the lower & upper Image-A parts. if (addr >= (OAD_IMG_B_PAGE * OAD_FLASH_PAGE_MULT)) { addr += OAD_IMG_B_AREA * OAD_FLASH_PAGE_MULT; } #endif if ((addr % OAD_FLASH_PAGE_MULT) == 0) { HalFlashErase(addr / OAD_FLASH_PAGE_MULT); } HalFlashWrite(addr, pValue+2, (OAD_BLOCK_SIZE / HAL_FLASH_WORD_SIZE)); } if (oadBlkNum == oadBlkTot) // If the OAD Image is complete. { #if defined FEATURE_OAD_SECURE HAL_SYSTEM_RESET(); // Only the secure OAD boot loader has the security key to decrypt. #else if (checkDL()) { #if !defined HAL_IMAGE_A // The BIM always checks for a valid Image-B before Image-A, // so Image-A never has to invalidate itself. uint16 crc[2] = { 0x0000, 0xFFFF }; uint16 addr = OAD_IMG_R_PAGE * OAD_FLASH_PAGE_MULT + OAD_IMG_CRC_OSET / HAL_FLASH_WORD_SIZE; HalFlashWrite(addr, (uint8 *)crc, 1); #endif HAL_SYSTEM_RESET(); } #endif } else // Request the next OAD Image block. { oadImgBlockReq(connHandle, oadBlkNum); } return ( SUCCESS ); }
/************************************************************************************************** * @fn sbCmnd * * @brief Act on the SB command and received buffer. * * input parameters * * None. * * output parameters * * None. * * @return TRUE to indicate that the SB_ENABLE_CMD command was successful; FALSE otherwise. ************************************************************************************************** */ static uint8 sbCmnd(void) { uint16 tmp = BUILD_UINT16(sbBuf[SB_DATA_STATE], sbBuf[SB_DATA_STATE+1]) + SB_IMG_OSET; uint16 crc[2]; uint8 len = 1; uint8 rsp = SB_SUCCESS; uint8 rtrn = FALSE; switch (sbCmd2) { case SB_HANDSHAKE_CMD: break; case SB_WRITE_CMD: if ((tmp % SB_WPG_SIZE) == 0) { HalFlashErase(tmp / SB_WPG_SIZE); } HalFlashWrite(tmp, sbBuf+SB_DATA_STATE+2, SB_RW_BUF_LEN / HAL_FLASH_WORD_SIZE); break; case SB_READ_CMD: #if !MT_SYS_OSAL_NV_READ_CERTIFICATE_DATA if ((tmp / (HAL_FLASH_PAGE_SIZE / 4)) >= HAL_NV_PAGE_BEG) { rsp = SB_FAILURE; break; } #endif HalFlashRead(tmp / (HAL_FLASH_PAGE_SIZE / 4), (tmp % (HAL_FLASH_PAGE_SIZE / 4)) << 2, sbBuf + SB_DATA_STATE + 3, SB_RW_BUF_LEN); sbBuf[SB_DATA_STATE+2] = sbBuf[SB_DATA_STATE+1]; sbBuf[SB_DATA_STATE+1] = sbBuf[SB_DATA_STATE]; len = SB_RW_BUF_LEN + 3; break; case SB_ENABLE_CMD: HalFlashRead(HAL_SB_CRC_ADDR / HAL_FLASH_PAGE_SIZE, HAL_SB_CRC_ADDR % HAL_FLASH_PAGE_SIZE, (uint8 *)crc, sizeof(crc)); // Bootload master must have verified extra checks to be issuing the SB_ENABLE_CMD. //if ((crc[0] != crc[1]) && (crc[0] != 0xFFFF) && (crc[0] != 0x0000)) if (crc[1] != crc[0]) { crc[1] = crc[0]; HalFlashWrite((HAL_SB_CRC_ADDR / HAL_FLASH_WORD_SIZE), (uint8 *)crc, 1); HalFlashRead( HAL_SB_CRC_ADDR / HAL_FLASH_PAGE_SIZE, HAL_SB_CRC_ADDR % HAL_FLASH_PAGE_SIZE, (uint8 *)crc, sizeof(crc)); } // Bootload master must have verified extra checks to be issuing the SB_ENABLE_CMD. //if ((crc[0] == crc[1]) && (crc[0] != 0xFFFF) && (crc[0] != 0x0000)) if (crc[0] == crc[1]) { rtrn = TRUE; } else { rsp = SB_VALIDATE_FAILED; } break; default: break; } sbResp(rsp, len); return rtrn; }