Files
examples/cc25xx-example/ti/BLE-CC254x/Projects/ble/Profiles/OAD/oad_target.c
2017-06-10 17:57:47 +08:00

697 lines
21 KiB
C

/**************************************************************************************************
Filename: oad_target.c
Revised: $Date: 2012-11-16 18:39:26 -0800 (Fri, 16 Nov 2012) $
Revision: $Revision: 32218 $
Description: This file contains OAD Target implementation.
Copyright 2012 Texas Instruments Incorporated. All rights reserved.
IMPORTANT: Your use of this Software is limited to those specific rights
granted under the terms of a software license agreement between the user
who downloaded the software, his/her employer (which must be your employer)
and Texas Instruments Incorporated (the "License"). You may not use this
Software unless you agree to abide by the terms of the License. The License
limits your use, and you acknowledge, that the Software may not be modified,
copied or distributed unless embedded on a Texas Instruments microcontroller
or used solely and exclusively in conjunction with a Texas Instruments radio
frequency transceiver, which is integrated into your product. Other than for
the foregoing purpose, you may not use, reproduce, copy, prepare derivative
works of, modify, distribute, perform, display or sell this Software and/or
its documentation for any purpose.
YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE
PROVIDED “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,
INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE,
NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL
TEXAS INSTRUMENTS OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER CONTRACT,
NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER
LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE
OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT
OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
(INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
Should you have any questions regarding your right to use this Software,
contact Texas Instruments Incorporated at www.TI.com.
**************************************************************************************************/
/*********************************************************************
* INCLUDES
*/
#include "bcomdef.h"
#include "att.h"
#include "gatt.h"
#include "gatt_uuid.h"
#include "gattservapp.h"
#include "hal_aes.h"
#include "hal_crc.h"
#include "hal_flash.h"
#if (defined HAL_LCD) && (HAL_LCD == TRUE)
#include "hal_lcd.h"
#endif
#include "hal_types.h"
#include "linkdb.h"
#include "oad.h"
#include "oad_target.h"
#include "OSAL.h"
/*********************************************************************
* CONSTANTS
*/
#define OAD_FLASH_PAGE_MULT ((uint16)(HAL_FLASH_PAGE_SIZE / HAL_FLASH_WORD_SIZE))
#if defined (FEATURE_OAD_SECURE) && defined (HAL_IMAGE_A)
// Enabled to ONLY build a BOOTSTRAP Encrypted Image-A (for programming over
// BEM, not BIM). Comment line below to build a non-bootstrap Encrypted Image-A.
#define BOOTP_E_IMAGE_A
#endif
#if !defined (OAD_IMAGE_VERSION)
#define OAD_IMAGE_VERSION 0x0000
#endif
#if !defined (OAD_IMAGE_A_USER_ID)
#define OAD_IMAGE_A_USER_ID {'A', 'A', 'A', 'A'}
#endif
#if !defined (OAD_IMAGE_B_USER_ID)
#define OAD_IMAGE_B_USER_ID {'B', 'B', 'B', 'B'}
#endif
/*********************************************************************
* MACROS
*/
/*********************************************************************
* GLOBAL VARIABLES
*/
// OAD Service UUID
static CONST uint8 oadServUUID[ATT_UUID_SIZE] =
{
TI_BASE_UUID_128( OAD_SERVICE_UUID )
};
static CONST uint8 oadCharUUID[OAD_CHAR_CNT][ATT_UUID_SIZE] =
{
// OAD Image Identify UUID
TI_BASE_UUID_128( OAD_IMG_IDENTIFY_UUID ),
// OAD Image Block Request/Response UUID
TI_BASE_UUID_128( OAD_IMG_BLOCK_UUID )
};
/*********************************************************************
* Profile Attributes - variables
*/
// OAD Service attribute
static CONST gattAttrType_t oadService = { ATT_UUID_SIZE, oadServUUID };
// Place holders for the GATT Server App to be able to lookup handles.
static uint8 oadCharVals[OAD_CHAR_CNT];
// OAD Characteristic Properties
static uint8 oadCharProps = GATT_PROP_WRITE_NO_RSP | GATT_PROP_WRITE | GATT_PROP_NOTIFY;
// OAD Client Characteristic Configs
static gattCharCfg_t oadImgIdentifyConfig[GATT_MAX_NUM_CONN];
static gattCharCfg_t oadImgBlockConfig[GATT_MAX_NUM_CONN];
// OAD Characteristic user descriptions
static CONST uint8 oadImgIdentifyDesc[] = "Img Identify";
static CONST uint8 oadImgBlockDesc[] = "Img Block";
/*********************************************************************
* Profile Attributes - Table
*/
static gattAttribute_t oadAttrTbl[] =
{
// OAD Service
{
{ ATT_BT_UUID_SIZE, primaryServiceUUID },
GATT_PERMIT_READ,
0,
(uint8 *)&oadService
},
// OAD Image Identify Characteristic Declaration
{
{ ATT_BT_UUID_SIZE, characterUUID },
GATT_PERMIT_READ,
0,
&oadCharProps
},
// OAD Image Identify Characteristic Value
{
{ ATT_UUID_SIZE, oadCharUUID[0] },
GATT_PERMIT_WRITE,
0,
oadCharVals+0
},
// Characteristic configuration
{
{ ATT_BT_UUID_SIZE, clientCharCfgUUID },
GATT_PERMIT_READ | GATT_PERMIT_WRITE,
0,
(uint8 *)oadImgIdentifyConfig
},
// OAD Image Identify User Description
{
{ ATT_BT_UUID_SIZE, charUserDescUUID },
GATT_PERMIT_READ,
0,
(uint8 *)oadImgIdentifyDesc
},
// OAD Image Block Request/Response Characteristic Declaration
{
{ ATT_BT_UUID_SIZE, characterUUID },
GATT_PERMIT_READ,
0,
&oadCharProps
},
// OAD Image Block Request/Response Characteristic Value
{
{ ATT_UUID_SIZE, oadCharUUID[1] },
GATT_PERMIT_WRITE,
0,
oadCharVals+1
},
// Characteristic configuration
{
{ ATT_BT_UUID_SIZE, clientCharCfgUUID },
GATT_PERMIT_READ | GATT_PERMIT_WRITE,
0,
(uint8 *)oadImgBlockConfig
},
// OAD Image Block Request/Response User Description
{
{ ATT_BT_UUID_SIZE, charUserDescUUID },
GATT_PERMIT_READ,
0,
(uint8 *)oadImgBlockDesc
}
};
#pragma location="IMAGE_HEADER"
const __code img_hdr_t _imgHdr = {
#if defined FEATURE_OAD_SECURE
2012, // CRC must not be 0x0000 or 0xFFFF.
#endif
#if defined (BOOTP_E_IMAGE_A)
#warning "Forcing a CRC-shadow match with the BOOTP_E_IMAGE_A flag - is this bootstrap code?"
2012, // CRC-shadow forced to match CRC for a bootstrap Encrypted Image-A
#else
0xFFFF, // CRC-shadow must be 0xFFFF for everything else
#endif
OAD_IMG_VER( OAD_IMAGE_VERSION ), // 15-bit Version #, left-shifted 1; OR with Image-B/Not-A bit.
OAD_IMG_R_AREA * OAD_FLASH_PAGE_MULT,
#if defined HAL_IMAGE_A
OAD_IMAGE_A_USER_ID, // User-Id
#else
OAD_IMAGE_B_USER_ID, // User-Id
#endif
{ 0xFF, 0xFF, 0xFF, 0xFF } // Reserved
};
#pragma required=_imgHdr
#pragma location="AES_HEADER"
static const __code aes_hdr_t _aesHdr = {
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B }, // Dummy Nonce
{ 0xFF, 0xFF, 0xFF, 0xFF } // Spare
};
#pragma required=_aesHdr
/*********************************************************************
* LOCAL VARIABLES
*/
static uint16 oadBlkNum = 0, oadBlkTot = 0xFFFF;
/*********************************************************************
* LOCAL FUNCTIONS
*/
static uint8 oadReadAttrCB(uint16 connHandle, gattAttribute_t *pAttr,
uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen);
static bStatus_t oadWriteAttrCB(uint16 connHandle, gattAttribute_t *pAttr,
uint8 *pValue, uint8 len, uint16 offset);
CONST gattServiceCBs_t oadCBs =
{
oadReadAttrCB, // Read callback function pointer.
oadWriteAttrCB, // Write callback function pointer.
NULL // Authorization callback function pointer.
};
static void oadImgBlockReq(uint16 connHandle, uint16 blkNum);
static void oadImgIdentifyReq(uint16 connHandle, img_hdr_t *pImgHdr);
static bStatus_t oadImgIdentifyWrite( uint16 connHandle, uint8 *pValue );
static bStatus_t oadImgBlockWrite( uint16 connHandle, uint8 *pValue );
static void oadHandleConnStatusCB( uint16 connHandle, uint8 changeType );
#if !defined FEATURE_OAD_SECURE
static uint8 checkDL(void);
#endif
/*********************************************************************
* @fn OADTarget_AddService
*
* @brief Initializes the OAD Service by registering GATT attributes
* with the GATT server. Only call this function once.
*
* @return The return value of GATTServApp_RegisterForMsg().
*/
bStatus_t OADTarget_AddService(void)
{
// Initialize Client Characteristic Configuration attributes
GATTServApp_InitCharCfg( INVALID_CONNHANDLE, oadImgIdentifyConfig );
GATTServApp_InitCharCfg( INVALID_CONNHANDLE, oadImgBlockConfig );
// Register with Link DB to receive link status change callback
VOID linkDB_Register( oadHandleConnStatusCB );
return GATTServApp_RegisterService(oadAttrTbl, GATT_NUM_ATTRS(oadAttrTbl), &oadCBs);
}
/*********************************************************************
* @fn oadReadAttrCB
*
* @brief Read an attribute.
*
* @param connHandle - connection message was received on
* @param pAttr - pointer to attribute
* @param pValue - pointer to data to be read
* @param pLen - length of data to be read
* @param offset - offset of the first octet to be read
* @param maxLen - maximum length of data to be read
*
* @return Success or Failure
*/
static uint8 oadReadAttrCB(uint16 connHandle, gattAttribute_t *pAttr,
uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen)
{
bStatus_t status = SUCCESS;
// TBD: is there any use for supporting reads
*pLen = 0;
status = ATT_ERR_INVALID_HANDLE;
return status;
}
/*********************************************************************
* @fn oadWriteAttrCB
*
* @brief Validate and Write attribute data
*
* @param connHandle - connection message was received on
* @param pAttr - pointer to attribute
* @param pValue - pointer to data to be written
* @param len - length of data
* @param offset - offset of the first octet to be written
*
* @return Success or Failure
*/
static bStatus_t oadWriteAttrCB(uint16 connHandle, gattAttribute_t *pAttr,
uint8 *pValue, uint8 len, uint16 offset)
{
bStatus_t status = SUCCESS;
if ( pAttr->type.len == ATT_BT_UUID_SIZE )
{
// 16-bit UUID
uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
if ( uuid == GATT_CLIENT_CHAR_CFG_UUID)
{
status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
offset, GATT_CLIENT_CFG_NOTIFY );
}
else
{
status = ATT_ERR_ATTR_NOT_FOUND; // Should never get here!
}
}
else
{
// 128-bit UUID
if (osal_memcmp(pAttr->type.uuid, oadCharUUID[OAD_CHAR_IMG_IDENTIFY], ATT_UUID_SIZE))
{
status = oadImgIdentifyWrite( connHandle, pValue );
}
else if (osal_memcmp(pAttr->type.uuid, oadCharUUID[OAD_CHAR_IMG_BLOCK], ATT_UUID_SIZE))
{
status = oadImgBlockWrite( connHandle, pValue );
}
else
{
status = ATT_ERR_ATTR_NOT_FOUND; // Should never get here!
}
}
return status;
}
/*********************************************************************
* @fn oadImgIdentifyWrite
*
* @brief Process the Image Identify Write.
*
* @param connHandle - connection message was received on
* @param pValue - pointer to data to be written
*
* @return status
*/
static bStatus_t oadImgIdentifyWrite( uint16 connHandle, uint8 *pValue )
{
img_hdr_t rxHdr;
img_hdr_t ImgHdr;
rxHdr.ver = BUILD_UINT16( pValue[0], pValue[1] );
rxHdr.len = BUILD_UINT16( pValue[2], pValue[3] );
(void)osal_memcpy(rxHdr.uid, pValue+4, sizeof(rxHdr.uid));
HalFlashRead(OAD_IMG_R_PAGE, OAD_IMG_HDR_OSET, (uint8 *)&ImgHdr, sizeof(img_hdr_t));
oadBlkTot = rxHdr.len / (OAD_BLOCK_SIZE / HAL_FLASH_WORD_SIZE);
if ( (OAD_IMG_ID( ImgHdr.ver ) != OAD_IMG_ID( rxHdr.ver )) && // TBD: add customer criteria for initiating OAD here.
(oadBlkTot <= OAD_BLOCK_MAX) &&
(oadBlkTot != 0) )
{
oadBlkNum = 0;
oadImgBlockReq(connHandle, 0);
}
else
{
oadImgIdentifyReq(connHandle, &ImgHdr);
}
return ( SUCCESS );
}
/*********************************************************************
* @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 oadImgIdentifyReq
*
* @brief Process the Image Identify Request.
*
* @param connHandle - connection message was received on
* @param pImgHdr - Pointer to the img_hdr_t data to send.
*
* @return None
*/
static void oadImgBlockReq(uint16 connHandle, uint16 blkNum)
{
uint16 value = GATTServApp_ReadCharCfg( connHandle, oadImgBlockConfig );
// If notifications enabled
if ( value & GATT_CLIENT_CFG_NOTIFY )
{
attHandleValueNoti_t noti;
gattAttribute_t *pAttr = GATTServApp_FindAttr(oadAttrTbl, GATT_NUM_ATTRS(oadAttrTbl),
oadCharVals+OAD_CHAR_IMG_BLOCK);
noti.handle = pAttr->handle;
noti.len = 2;
noti.value[0] = LO_UINT16(blkNum);
noti.value[1] = HI_UINT16(blkNum);
VOID GATT_Notification(connHandle, &noti, FALSE);
}
}
/*********************************************************************
* @fn oadImgIdentifyReq
*
* @brief Process the Image Identify Request.
*
* @param connHandle - connection message was received on
* @param pImgHdr - Pointer to the img_hdr_t data to send.
*
* @return None
*/
static void oadImgIdentifyReq(uint16 connHandle, img_hdr_t *pImgHdr)
{
uint16 value = GATTServApp_ReadCharCfg( connHandle, oadImgIdentifyConfig );
// If notifications enabled
if ( value & GATT_CLIENT_CFG_NOTIFY )
{
attHandleValueNoti_t noti;
gattAttribute_t *pAttr = GATTServApp_FindAttr(oadAttrTbl, GATT_NUM_ATTRS(oadAttrTbl),
oadCharVals+OAD_CHAR_IMG_IDENTIFY);
noti.handle = pAttr->handle;
noti.len = OAD_IMG_HDR_SIZE;
noti.value[0] = LO_UINT16(pImgHdr->ver);
noti.value[1] = HI_UINT16(pImgHdr->ver);
noti.value[2] = LO_UINT16(pImgHdr->len);
noti.value[3] = HI_UINT16(pImgHdr->len);
(void)osal_memcpy(noti.value+4, pImgHdr->uid, sizeof(pImgHdr->uid));
VOID GATT_Notification(connHandle, &noti, FALSE);
}
}
#if !defined FEATURE_OAD_SECURE
/**************************************************************************************************
* @fn crcCalcDL
*
* @brief Run the CRC16 Polynomial calculation over the DL image.
*
* input parameters
*
* None.
*
* output parameters
*
* None.
*
* @return The CRC16 calculated.
**************************************************************************************************
*/
static uint16 crcCalcDL(void)
{
uint8 pageEnd = oadBlkTot / OAD_BLOCKS_PER_PAGE;
uint16 osetEnd = (oadBlkTot - (pageEnd * OAD_BLOCKS_PER_PAGE)) * OAD_BLOCK_SIZE;
#if defined HAL_IMAGE_B
pageEnd += OAD_IMG_D_PAGE + OAD_IMG_B_AREA;
#else
pageEnd += OAD_IMG_D_PAGE;
#endif
HalCRCInit(0x0000); // Seed thd CRC calculation with zero.
for (uint8 page = OAD_IMG_D_PAGE; ; page++)
{
#if defined HAL_IMAGE_B
// Skip the Image-B area which lies between the lower & upper Image-A parts.
if (page == OAD_IMG_B_PAGE)
{
page += OAD_IMG_B_AREA;
}
#endif
for (uint16 oset = 0; oset < HAL_FLASH_PAGE_SIZE; oset += HAL_FLASH_WORD_SIZE)
{
if ((page == OAD_IMG_D_PAGE) && (oset == OAD_IMG_CRC_OSET))
{
continue; // Skip the CRC and shadow.
}
else if ((page == pageEnd) && (oset == osetEnd))
{
return HalCRCCalc();
}
else
{
uint8 buf[HAL_FLASH_WORD_SIZE];
HalFlashRead(page, oset, buf, HAL_FLASH_WORD_SIZE);
for (uint8 idx = 0; idx < HAL_FLASH_WORD_SIZE; idx++)
{
HalCRCExec(buf[idx]);
}
}
}
}
}
/**************************************************************************************************
* @fn checkDL
*
* @brief Check validity of the downloaded image.
*
* input parameters
*
* None.
*
* output parameters
*
* None.
*
* @return TRUE or FALSE for image valid.
**************************************************************************************************
*/
static uint8 checkDL(void)
{
uint16 crc[2];
HalFlashRead(OAD_IMG_D_PAGE, OAD_IMG_CRC_OSET, (uint8 *)crc, sizeof(crc));
if ((crc[0] == 0xFFFF) || (crc[0] == 0x0000))
{
return FALSE;
}
if (crc[1] == 0xFFFF)
{
crc[1] = crcCalcDL();
#if defined FEATURE_OAD_BIM // If download image is made to run in-place, enable it here.
uint16 addr = OAD_IMG_D_PAGE * OAD_FLASH_PAGE_MULT + OAD_IMG_CRC_OSET / HAL_FLASH_WORD_SIZE;
crc[0] = 0xFFFF;
HalFlashWrite(addr, (uint8 *)crc, 1);
HalFlashRead(OAD_IMG_D_PAGE, OAD_IMG_CRC_OSET, (uint8 *)crc, sizeof(crc));
#endif
}
return (crc[0] == crc[1]);
}
#endif
/*********************************************************************
* @fn oadHandleConnStatusCB
*
* @brief OAD Service link status change handler function.
*
* @param connHandle - connection handle
* @param changeType - type of change
*
* @return none
*/
static void oadHandleConnStatusCB( uint16 connHandle, uint8 changeType )
{
// Make sure this is not loopback connection
if ( connHandle != LOOPBACK_CONNHANDLE )
{
// Reset Client Char Config if connection has dropped
if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED ) ||
( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) &&
( !linkDB_Up( connHandle ) ) ) )
{
GATTServApp_InitCharCfg( connHandle, oadImgIdentifyConfig );
GATTServApp_InitCharCfg( connHandle, oadImgBlockConfig );
}
}
}
/*********************************************************************
*********************************************************************/