initial commit
This commit is contained in:
+552
@@ -0,0 +1,552 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_math.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* indexes into base UUID for 16-to-128 bit UUID conversion */
|
||||
#define ATT_BASE_UUID_POS_0 12
|
||||
#define ATT_BASE_UUID_POS_1 13
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
static uint8_t attBaseUuid[] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
|
||||
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Default component function inteface */
|
||||
const attFcnIf_t attFcnDefault =
|
||||
{
|
||||
attEmptyDataCback,
|
||||
(l2cCtrlCback_t) attEmptyHandler,
|
||||
(attMsgHandler_t) attEmptyHandler,
|
||||
attEmptyConnCback
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
attCb_t attCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2C data callback for ATT.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attL2cDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t pduType;
|
||||
|
||||
/* parse PDU type */
|
||||
pduType = *(pPacket + L2C_PAYLOAD_START);
|
||||
|
||||
/* if from server */
|
||||
if ((pduType & ATT_PDU_MASK_SERVER) != 0)
|
||||
{
|
||||
/* call client data callback */
|
||||
(*attCb.pClient->dataCback)(handle, len, pPacket);
|
||||
}
|
||||
/* else from client */
|
||||
else
|
||||
{
|
||||
/* call server data callback */
|
||||
(*attCb.pServer->dataCback)(handle, len, pPacket);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2C control callback for ATT.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attL2cCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
attCcb_t *pCcb;
|
||||
|
||||
/* get connection control block */
|
||||
pCcb = attCcbByConnId((dmConnId_t) pMsg->param);
|
||||
|
||||
/* verify connection is open */
|
||||
if (pCcb->connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
if (pMsg->event == L2C_CTRL_FLOW_DISABLE_IND)
|
||||
{
|
||||
/* flow disabled */
|
||||
pCcb->control |= ATT_CCB_STATUS_FLOW_DISABLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* flow enabled */
|
||||
pCcb->control &= ~ATT_CCB_STATUS_FLOW_DISABLED;
|
||||
|
||||
/* call server control callback */
|
||||
(*attCb.pServer->ctrlCback)(pMsg);
|
||||
|
||||
/* check flow again; could be changed recursively */
|
||||
if (!(pCcb->control & ATT_CCB_STATUS_FLOW_DISABLED))
|
||||
{
|
||||
/* call client control callback */
|
||||
(*attCb.pClient->ctrlCback)(pMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM connection callback for ATT.
|
||||
*
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attDmConnCback(dmEvt_t *pDmEvt)
|
||||
{
|
||||
attCcb_t *pCcb;
|
||||
|
||||
pCcb = attCcbByConnId((dmConnId_t) pDmEvt->hdr.param);
|
||||
|
||||
/* if new connection created */
|
||||
if (pDmEvt->hdr.event == DM_CONN_OPEN_IND)
|
||||
{
|
||||
/* initialize control block before handling event */
|
||||
pCcb->handle = pDmEvt->connOpen.handle;
|
||||
pCcb->mtu = ATT_DEFAULT_MTU;
|
||||
pCcb->connId = (dmConnId_t) pDmEvt->hdr.param;
|
||||
pCcb->control = 0;
|
||||
pCcb->pPendDbHashRsp = NULL;
|
||||
}
|
||||
|
||||
/* if connection has been opened */
|
||||
if (pCcb->connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
/* pass event to server */
|
||||
(*attCb.pServer->connCback)(pCcb, pDmEvt);
|
||||
|
||||
/* pass event to client */
|
||||
(*attCb.pClient->connCback)(pCcb, pDmEvt);
|
||||
|
||||
/* if connection closed */
|
||||
if (pDmEvt->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* clear control block after handling event */
|
||||
pCcb->connId = DM_CONN_ID_NONE;
|
||||
|
||||
if (pCcb->pPendDbHashRsp)
|
||||
{
|
||||
WsfBufFree(pCcb->pPendDbHashRsp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* execute ATT connection callback */
|
||||
if (attCb.connCback != NULL)
|
||||
{
|
||||
(*attCb.connCback)(pDmEvt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief ATT empty event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attEmptyHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Empty connection callback for ATT.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attEmptyConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Empty data callback for ATT.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attEmptyDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the connection control block for the given handle.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
*
|
||||
* \return Pointer to connection control block or NULL if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
attCcb_t *attCcbByHandle(uint16_t handle)
|
||||
{
|
||||
dmConnId_t connId;
|
||||
|
||||
if ((connId = DmConnIdByHandle(handle)) != DM_CONN_ID_NONE)
|
||||
{
|
||||
return &attCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the connection control block for the connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to connection control block.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
attCcb_t *attCcbByConnId(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return &attCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Compare a 16 bit UUID to a 128 bit UUID.
|
||||
*
|
||||
* \param pUuid16 Pointer to 16 bit UUID.
|
||||
* \param pUuid128 Pointer to 128 bit UUID.
|
||||
*
|
||||
* \return TRUE of UUIDs match, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t attUuidCmp16to128(const uint8_t *pUuid16, const uint8_t *pUuid128)
|
||||
{
|
||||
attBaseUuid[ATT_BASE_UUID_POS_0] = pUuid16[0];
|
||||
attBaseUuid[ATT_BASE_UUID_POS_1] = pUuid16[1];
|
||||
|
||||
return (memcmp(attBaseUuid, pUuid128, ATT_128_UUID_LEN) == 0);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the attribute protocol MTU of a connection.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param peerMtu Peer MTU.
|
||||
* \param localMtu Local MTU.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attSetMtu(attCcb_t *pCcb, uint16_t peerMtu, uint16_t localMtu)
|
||||
{
|
||||
uint16_t mtu;
|
||||
|
||||
/* set negotiated mtu for the connection to the lesser of ours and theirs */
|
||||
mtu = WSF_MIN(peerMtu, localMtu);
|
||||
|
||||
/* if current mtu is not the same as the negotiated value */
|
||||
if (pCcb->mtu != mtu)
|
||||
{
|
||||
/* set mtu to the new value */
|
||||
pCcb->mtu = mtu;
|
||||
|
||||
/* notify app about the new value */
|
||||
attExecCallback(pCcb->connId, ATT_MTU_UPDATE_IND, 0, ATT_SUCCESS, mtu);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute application callback function.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param event Callback event ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param status Callback event status.
|
||||
* \param mtu Negotiated MTU value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attExecCallback(dmConnId_t connId, uint8_t event, uint16_t handle, uint8_t status, uint16_t mtu)
|
||||
{
|
||||
if (attCb.cback)
|
||||
{
|
||||
attEvt_t evt;
|
||||
|
||||
evt.hdr.param = connId;
|
||||
evt.hdr.event = event;
|
||||
evt.hdr.status = status;
|
||||
evt.valueLen = 0;
|
||||
evt.handle = handle;
|
||||
evt.continuing = 0;
|
||||
evt.mtu = mtu;
|
||||
|
||||
(*attCb.cback)(&evt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate an ATT data message buffer to be used for the ATT attribute protocol messages.
|
||||
*
|
||||
* \param len Message length in bytes.
|
||||
*
|
||||
* \return Pointer to data message buffer or NULL if allocation failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void *attMsgAlloc(uint16_t len)
|
||||
{
|
||||
return WsfMsgDataAlloc(len, HCI_TX_DATA_TAILROOM);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief ATT handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for ATT.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
/* store handler ID */
|
||||
attCb.handlerId = handlerId;
|
||||
|
||||
/* initialize control block */
|
||||
attCb.pClient = &attFcnDefault;
|
||||
attCb.pServer = &attFcnDefault;
|
||||
|
||||
/* Register with L2C */
|
||||
L2cRegister(L2C_CID_ATT, attL2cDataCback, attL2cCtrlCback);
|
||||
|
||||
/* Register with DM */
|
||||
DmConnRegister(DM_CLIENT_ID_ATT, attDmConnCback);
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for ATT.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* Handle message */
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
if (pMsg->event >= ATTS_MSG_START)
|
||||
{
|
||||
/* pass event to server */
|
||||
(*attCb.pServer->msgCback)(pMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* pass event to client */
|
||||
(*attCb.pClient->msgCback)(pMsg);
|
||||
}
|
||||
}
|
||||
/* Handle events */
|
||||
else if (event)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback with ATT.
|
||||
*
|
||||
* \param cback Client callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttRegister(attCback_t cback)
|
||||
{
|
||||
attCb.cback = cback;
|
||||
|
||||
/* if configured MTU size is larger than maximum RX PDU length */
|
||||
if (pAttCfg->mtu > (HciGetMaxRxAclLen() - L2C_HDR_LEN))
|
||||
{
|
||||
/* notify app about MTU misconfiguration */
|
||||
attExecCallback(0, DM_ERROR_IND, 0, DM_ERR_ATT_RX_PDU_LEN_EXCEEDED, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a connection callback with ATT. The callback is typically used to
|
||||
* manage the attribute server database.
|
||||
*
|
||||
* \param cback Client callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttConnRegister(dmCback_t cback)
|
||||
{
|
||||
attCb.connCback = cback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the attribute protocol MTU of a connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return MTU of the connection.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t AttGetMtu(dmConnId_t connId)
|
||||
{
|
||||
return (attCcbByConnId(connId)->mtu);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate an ATT message buffer to be sent with the ATT attribute protocol
|
||||
* zero-copy APIs.
|
||||
*
|
||||
* \param len Message length in bytes.
|
||||
* \param opcode Opcode for ATT message.
|
||||
*
|
||||
* \return Pointer to message buffer or NULL if allocation failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void *AttMsgAlloc(uint16_t len, uint8_t opcode)
|
||||
{
|
||||
uint8_t *pMsg;
|
||||
uint8_t hdrLen;
|
||||
|
||||
WSF_ASSERT((opcode == ATT_PDU_VALUE_IND) || (opcode == ATT_PDU_VALUE_NTF));
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case ATT_PDU_VALUE_IND:
|
||||
case ATT_PDU_VALUE_NTF:
|
||||
hdrLen = ATT_VALUE_IND_NTF_BUF_LEN;
|
||||
break;
|
||||
|
||||
default:
|
||||
hdrLen = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hdrLen > 0)
|
||||
{
|
||||
pMsg = attMsgAlloc(hdrLen + len);
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
/* return pointer to attribute value buffer */
|
||||
return (pMsg + hdrLen);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Free an ATT message buffer allocated with AttMsgAlloc().
|
||||
*
|
||||
* \param pMsg Pointer to message buffer.
|
||||
* \param opcode Opcode for ATT message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttMsgFree(void *pMsg, uint8_t opcode)
|
||||
{
|
||||
uint8_t hdrLen;
|
||||
|
||||
WSF_ASSERT((opcode == ATT_PDU_VALUE_IND) || (opcode == ATT_PDU_VALUE_NTF));
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case ATT_PDU_VALUE_IND:
|
||||
case ATT_PDU_VALUE_NTF:
|
||||
hdrLen = ATT_VALUE_IND_NTF_BUF_LEN;
|
||||
break;
|
||||
|
||||
default:
|
||||
hdrLen = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
WsfMsgFree(((uint8_t *)pMsg) - hdrLen);
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef ATT_MAIN_H
|
||||
#define ATT_MAIN_H
|
||||
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "l2c_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* ATT protocol methods */
|
||||
#define ATT_METHOD_ERR 0 /* Error response */
|
||||
#define ATT_METHOD_MTU 1 /* Exchange mtu */
|
||||
#define ATT_METHOD_FIND_INFO 2 /* Find information */
|
||||
#define ATT_METHOD_FIND_TYPE 3 /* Find by type value */
|
||||
#define ATT_METHOD_READ_TYPE 4 /* Read by type value */
|
||||
#define ATT_METHOD_READ 5 /* Read */
|
||||
#define ATT_METHOD_READ_BLOB 6 /* Read long */
|
||||
#define ATT_METHOD_READ_MULTIPLE 7 /* Read multiple */
|
||||
#define ATT_METHOD_READ_GROUP_TYPE 8 /* Read group type */
|
||||
#define ATT_METHOD_WRITE 9 /* Write */
|
||||
#define ATT_METHOD_WRITE_CMD 10 /* Write command */
|
||||
#define ATT_METHOD_PREPARE_WRITE 11 /* Prepare write */
|
||||
#define ATT_METHOD_EXECUTE_WRITE 12 /* Execute write */
|
||||
#define ATT_METHOD_VALUE_NTF 13 /* Handle value notification */
|
||||
#define ATT_METHOD_VALUE_IND 14 /* Handle value indication */
|
||||
#define ATT_METHOD_VALUE_CNF 15 /* Handle value confirm */
|
||||
#define ATT_METHOD_SIGNED_WRITE_CMD 16 /* Signed write command */
|
||||
|
||||
/* Convert opcode to method */
|
||||
#define ATT_OPCODE_2_METHOD(op) (((op) & ~ATT_PDU_MASK_SERVER) / 2)
|
||||
|
||||
/* Client and server message macros */
|
||||
#define ATTC_MSG_START 0x00
|
||||
#define ATTS_MSG_START 0x20
|
||||
#define ATT_MSG_MASK(msg) ((msg) & 0x1F)
|
||||
|
||||
/* Buffer lengths for messages */
|
||||
#define ATT_VALUE_IND_NTF_BUF_LEN (ATT_VALUE_NTF_LEN + L2C_PAYLOAD_START)
|
||||
|
||||
/* attCcb_t control bits */
|
||||
#define ATT_CCB_STATUS_MTU_SENT (1<<0) /* MTU req or rsp sent */
|
||||
#define ATT_CCB_STATUS_FLOW_DISABLED (1<<1) /* Data flow disabled */
|
||||
#define ATT_CCB_STATUS_TX_TIMEOUT (1<<2) /* ATT transaction timed out */
|
||||
#define ATT_CCB_STATUS_RSP_PENDING (1<<3) /* ATTS write rsp pending */
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t startHandle; /* Start handle of the requested operation. Used if an Error Response is sent. */
|
||||
uint16_t handle; /* Attribute handle of pending response. */
|
||||
} attPendDbHashRsp_t;
|
||||
|
||||
/* Connection control block */
|
||||
typedef struct
|
||||
{
|
||||
wsfQueue_t prepWriteQueue; /* prepare write queue */
|
||||
wsfTimer_t idleTimer; /* service discovery idle timer */
|
||||
uint16_t handle; /* connection handle */
|
||||
uint16_t mtu; /* connection mtu */
|
||||
dmConnId_t connId; /* DM connection ID */
|
||||
uint8_t control; /* Control bitfield */
|
||||
attPendDbHashRsp_t *pPendDbHashRsp; /* Pending ATT Response information. */
|
||||
} attCcb_t;
|
||||
|
||||
/* ATT message handling function type */
|
||||
typedef void (*attMsgHandler_t)(void *pMsg);
|
||||
|
||||
/* ATT connection callback type */
|
||||
typedef void (*attConnCback_t)(attCcb_t *pCcb, dmEvt_t *pDmEvt);
|
||||
|
||||
/* Callback interface for client and server */
|
||||
typedef struct
|
||||
{
|
||||
l2cDataCback_t dataCback; /* Data callback */
|
||||
l2cCtrlCback_t ctrlCback; /* Control callback */
|
||||
attMsgHandler_t msgCback; /* Message handling callback */
|
||||
attConnCback_t connCback; /* Connection callback */
|
||||
} attFcnIf_t;
|
||||
|
||||
/* Main control block of the ATT subsystem */
|
||||
typedef struct
|
||||
{
|
||||
attCcb_t ccb[DM_CONN_MAX]; /* Connection control blocks */
|
||||
attFcnIf_t const *pClient; /* Client callback interface */
|
||||
attFcnIf_t const *pServer; /* Server callback interface */
|
||||
attCback_t cback; /* ATT callback function */
|
||||
dmCback_t connCback; /* ATT connection callback function */
|
||||
wsfHandlerId_t handlerId; /* WSF handler ID */
|
||||
uint8_t errTest; /* Status code for error testing */
|
||||
} attCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Default component function inteface */
|
||||
extern const attFcnIf_t attFcnDefault;
|
||||
|
||||
/* Control block */
|
||||
extern attCb_t attCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
void attEmptyHandler(wsfMsgHdr_t *pMsg);
|
||||
void attEmptyDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket);
|
||||
void attEmptyConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt);
|
||||
|
||||
attCcb_t *attCcbByHandle(uint16_t handle);
|
||||
attCcb_t *attCcbByConnId(dmConnId_t connId);
|
||||
|
||||
bool_t attUuidCmp16to128(const uint8_t *pUuid16, const uint8_t *pUuid128);
|
||||
void attSetMtu(attCcb_t *pCcb, uint16_t peerMtu, uint16_t localMtu);
|
||||
void attExecCallback(dmConnId_t connId, uint8_t event, uint16_t handle, uint8_t status, uint16_t mtu);
|
||||
void *attMsgAlloc(uint16_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* ATT_MAIN_H */
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT optional signed PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef ATT_SIGN_H
|
||||
#define ATT_SIGN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* CMAC algorithm subkey length */
|
||||
#define ATT_CMAC_SUBKEY_LEN 16
|
||||
|
||||
/* CMAC algorithm block length */
|
||||
#define ATT_CMAC_BLOCK_LEN 16
|
||||
|
||||
/* CMAC constant Rb */
|
||||
#define ATT_CMAC_RB 0x87
|
||||
|
||||
/* CMAC signature result length */
|
||||
#define ATT_CMAC_RESULT_LEN 8
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* ATT_SIGN_H */
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT UUID constants.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_uuid.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Service UUIDs */
|
||||
const uint8_t attGapSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GAP_SERVICE)};
|
||||
const uint8_t attGattSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GATT_SERVICE)};
|
||||
const uint8_t attIasSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_IMMEDIATE_ALERT_SERVICE)};
|
||||
const uint8_t attLlsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_LINK_LOSS_SERVICE)};
|
||||
const uint8_t attTpsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TX_POWER_SERVICE)};
|
||||
const uint8_t attCtsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CURRENT_TIME_SERVICE)};
|
||||
const uint8_t attRtusSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_REF_TIME_UPDATE_SERVICE)};
|
||||
const uint8_t attNdcsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DST_CHANGE_SERVICE)};
|
||||
const uint8_t attGlsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GLUCOSE_SERVICE)};
|
||||
const uint8_t attHtsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HEALTH_THERM_SERVICE)};
|
||||
const uint8_t attDisSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)};
|
||||
const uint8_t attNwaSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_NETWORK_AVAIL_SERVICE)};
|
||||
const uint8_t attWdsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_WATCHDOG_SERVICE)};
|
||||
const uint8_t attHrsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HEART_RATE_SERVICE)};
|
||||
const uint8_t attPassSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PHONE_ALERT_SERVICE)};
|
||||
const uint8_t attBasSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BATTERY_SERVICE)};
|
||||
const uint8_t attBpsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BLOOD_PRESSURE_SERVICE)};
|
||||
const uint8_t attAnsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_NOTIF_SERVICE)};
|
||||
const uint8_t attHidSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_SERVICE)};
|
||||
const uint8_t attSpsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SCAN_PARAM_SERVICE)};
|
||||
const uint8_t attPlxsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PULSE_OXIMITER_SERVICE)};
|
||||
const uint8_t attUdsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_USER_DATA_SERVICE)};
|
||||
const uint8_t attMprvSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PRV_SERVICE)};
|
||||
const uint8_t attMprxSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PROXY_SERVICE)};
|
||||
|
||||
/*! GATT UUIDs */
|
||||
const uint8_t attPrimSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PRIMARY_SERVICE)};
|
||||
const uint8_t attSecSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SECONDARY_SERVICE)};
|
||||
const uint8_t attIncUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_INCLUDE)};
|
||||
const uint8_t attChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CHARACTERISTIC)};
|
||||
|
||||
/*! Descriptor UUIDs */
|
||||
const uint8_t attChExtUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CHARACTERISTIC_EXT)};
|
||||
const uint8_t attChUserDescUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CHAR_USER_DESC)};
|
||||
const uint8_t attCliChCfgUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CLIENT_CHAR_CONFIG)};
|
||||
const uint8_t attSrvChCfgUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SERVER_CHAR_CONFIG)};
|
||||
const uint8_t attChPresFmtUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CHAR_PRES_FORMAT)};
|
||||
const uint8_t attAggFmtUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_AGGREGATE_FORMAT)};
|
||||
const uint8_t attValRangeUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_VALID_RANGE)};
|
||||
const uint8_t attHidErmUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_EXT_REPORT_MAPPING)};
|
||||
const uint8_t attHidRimUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_REPORT_ID_MAPPING)};
|
||||
|
||||
/*! Characteristic UUIDs */
|
||||
const uint8_t attDnChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DEVICE_NAME)};
|
||||
const uint8_t attApChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_APPEARANCE)};
|
||||
const uint8_t attPpfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PERIPH_PRIVACY_FLAG)};
|
||||
const uint8_t attRaChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RECONN_ADDR)};
|
||||
const uint8_t attPpcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PREF_CONN_PARAM)};
|
||||
const uint8_t attScChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SERVICE_CHANGED)};
|
||||
const uint8_t attAlChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_LEVEL)};
|
||||
const uint8_t attTxpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TX_POWER_LEVEL)};
|
||||
const uint8_t attDtChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DATE_TIME)};
|
||||
const uint8_t attDwChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DAY_OF_WEEK)};
|
||||
const uint8_t attDdtChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DAY_DATE_TIME)};
|
||||
const uint8_t attEt100ChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_EXACT_TIME_100)};
|
||||
const uint8_t attEt256ChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_EXACT_TIME_256)};
|
||||
const uint8_t attDstoChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DST_OFFSET)};
|
||||
const uint8_t attTzChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_ZONE)};
|
||||
const uint8_t attLtiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_LOCAL_TIME_INFO)};
|
||||
const uint8_t attStzChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SECONDARY_TIME_ZONE)};
|
||||
const uint8_t attTdstChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_WITH_DST)};
|
||||
const uint8_t attTaChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_ACCURACY)};
|
||||
const uint8_t attTsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_SOURCE)};
|
||||
const uint8_t attRtiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_REFERENCE_TIME_INFO)};
|
||||
const uint8_t attTbChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_BROADCAST)};
|
||||
const uint8_t attTucpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_UPDATE_CP)};
|
||||
const uint8_t attTusChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_UPDATE_STATE)};
|
||||
const uint8_t attGlmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GLUCOSE_MEAS)};
|
||||
const uint8_t attBlChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BATTERY_LEVEL)};
|
||||
const uint8_t attBpsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BATTERY_POWER_STATE)};
|
||||
const uint8_t attBlsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BATTERY_LEVEL_STATE)};
|
||||
const uint8_t attTmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TEMP_MEAS)};
|
||||
const uint8_t attTtChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TEMP_TYPE)};
|
||||
const uint8_t attItChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_INTERMEDIATE_TEMP)};
|
||||
const uint8_t attTcelChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TEMP_C)};
|
||||
const uint8_t attTfahChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TEMP_F)};
|
||||
const uint8_t attSidChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SYSTEM_ID)};
|
||||
const uint8_t attMnsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MODEL_NUMBER)};
|
||||
const uint8_t attSnsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SERIAL_NUMBER)};
|
||||
const uint8_t attFrsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_FIRMWARE_REV)};
|
||||
const uint8_t attHrsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HARDWARE_REV)};
|
||||
const uint8_t attSrsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SOFTWARE_REV)};
|
||||
const uint8_t attMfnsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MANUFACTURER_NAME)};
|
||||
const uint8_t attIeeeChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_11073_CERT_DATA)};
|
||||
const uint8_t attCtChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CURRENT_TIME)};
|
||||
const uint8_t attElChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ELEVATION)};
|
||||
const uint8_t attLatChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_LATITUDE)};
|
||||
const uint8_t attLongChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_LONGITUDE)};
|
||||
const uint8_t attP2dChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_POSITION_2D)};
|
||||
const uint8_t attP3dChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_POSITION_3D)};
|
||||
const uint8_t attVidChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_VENDOR_ID)};
|
||||
const uint8_t attHbmiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_BOOT_MOUSE_IN)};
|
||||
const uint8_t attGlmcChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GLUCOSE_MEAS_CONTEXT)};
|
||||
const uint8_t attBpmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BP_MEAS)};
|
||||
const uint8_t attIcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_INTERMEDIATE_BP)};
|
||||
const uint8_t attHrmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HR_MEAS)};
|
||||
const uint8_t attBslChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HR_SENSOR_LOC)};
|
||||
const uint8_t attHrcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HR_CP)};
|
||||
const uint8_t attRemChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_REMOVABLE)};
|
||||
const uint8_t attSrChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SERVICE_REQ)};
|
||||
const uint8_t attStcChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SCI_TEMP_C)};
|
||||
const uint8_t attStrChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_STRING)};
|
||||
const uint8_t attNwaChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_NETWORK_AVAIL)};
|
||||
const uint8_t attAsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_STATUS)};
|
||||
const uint8_t attRcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RINGER_CP)};
|
||||
const uint8_t attRsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RINGER_SETTING)};
|
||||
const uint8_t attAcbmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_CAT_ID_MASK)};
|
||||
const uint8_t attAcChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_CAT_ID)};
|
||||
const uint8_t attAncpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_NOTIF_CP)};
|
||||
const uint8_t attUasChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_UNREAD_ALERT_STATUS)};
|
||||
const uint8_t attNaChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_NEW_ALERT)};
|
||||
const uint8_t attSnacChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SUP_NEW_ALERT_CAT)};
|
||||
const uint8_t attSuacChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SUP_UNREAD_ALERT_CAT)};
|
||||
const uint8_t attBpfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BP_FEATURE)};
|
||||
const uint8_t attHidBmiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MEAS_INTERVAL)};
|
||||
const uint8_t attHidBkiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_BOOT_KEYBOARD_IN)};
|
||||
const uint8_t attHidBkoChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_BOOT_KEYBOARD_OUT)};
|
||||
const uint8_t attHidiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_INFORMATION)};
|
||||
const uint8_t attHidRmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_REPORT_MAP)};
|
||||
const uint8_t attHidcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_CONTROL_POINT)};
|
||||
const uint8_t attHidRepChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_REPORT)};
|
||||
const uint8_t attHidPmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_PROTOCOL_MODE)};
|
||||
const uint8_t attSiwChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SCAN_INT_WIND)};
|
||||
const uint8_t attPnpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PNP_ID)};
|
||||
const uint8_t attGlfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GLUCOSE_FEATURE)};
|
||||
const uint8_t attRacpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RACP)};
|
||||
const uint8_t attCarChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CAR)};
|
||||
const uint8_t attRsfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RUNNING_SPEED_FEATURE)};
|
||||
const uint8_t attRsmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RUNNING_SPEED_MEASUREMENT)};
|
||||
const uint8_t attCpfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CYCLING_POWER_FEATURE)};
|
||||
const uint8_t attCpmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CYCLING_POWER_MEASUREMENT)};
|
||||
const uint8_t attCsfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CYCLING_SPEED_FEATURE)};
|
||||
const uint8_t attCsmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CYCLING_SPEED_MEASUREMENT)};
|
||||
const uint8_t attSlChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SENSOR_LOCATION)};
|
||||
const uint8_t attPlxfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PULSE_OX_FEATURES)};
|
||||
const uint8_t attPlxscmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PULSE_OX_SPOT_CHECK)};
|
||||
const uint8_t attPlxcmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PULSE_OX_CONTINUOUS)};
|
||||
const uint8_t attRpaoChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RPAO)};
|
||||
const uint8_t attDbciChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DB_CHANGE_INCREMENT)};
|
||||
const uint8_t attUiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_USER_INDEX)};
|
||||
const uint8_t attUcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_USER_CONTROL_POINT)};
|
||||
const uint8_t attMprvDinChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PRV_DATA_IN)};
|
||||
const uint8_t attMprvDoutChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PRV_DATA_OUT)};
|
||||
const uint8_t attMprxDinChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PROXY_DATA_IN)};
|
||||
const uint8_t attMprxDoutChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PROXY_DATA_OUT)};
|
||||
const uint8_t attWssSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_WEIGHT_SCALE_SERVICE)};
|
||||
const uint8_t attWmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_WEIGHT_MEAS)};
|
||||
const uint8_t attWsfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_WEIGHT_SCALE_FEATURE)};
|
||||
const uint8_t attGattCsfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CLIENT_SUPPORTED_FEATURES)};
|
||||
const uint8_t attGattDbhChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DATABASE_HASH)};
|
||||
const uint8_t attCteSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CONSTANT_TONE_SERVICE)};
|
||||
const uint8_t attCteEnChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CTE_ENABLE)};
|
||||
const uint8_t attCteMinLenChUuid[ATT_16_UUID_LEN] ={UINT16_TO_BYTES(ATT_UUID_CTE_MIN_LEN)};
|
||||
const uint8_t attCteTxCntChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CTE_TX_CNT)};
|
||||
const uint8_t attCteTxDurChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CTE_TX_DURATION)};
|
||||
const uint8_t attCteIntChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CTE_INTERVAL)};
|
||||
const uint8_t attCtePhyChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CTE_PHY)};
|
||||
Vendored
+706
@@ -0,0 +1,706 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client service and characteristic utility functions.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_uuid.h"
|
||||
#include "att_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
#define ATT_DISC_HDL_IDX_NONE 0xFF
|
||||
|
||||
#define ATT_SET_UUID_MASK ATTC_SET_UUID_128
|
||||
|
||||
/* Characteristic declaration lengths */
|
||||
#define ATT_CHAR_DECL_LEN_UUID16 5
|
||||
#define ATT_CHAR_DECL_LEN_UUID128 19
|
||||
|
||||
/* Read by type response lengths for characteristic discovery */
|
||||
#define ATT_READ_RSP_LEN_UUID16 (ATT_CHAR_DECL_LEN_UUID16 + 2)
|
||||
#define ATT_READ_RSP_LEN_UUID128 (ATT_CHAR_DECL_LEN_UUID128 + 2)
|
||||
|
||||
/* Find info response lengths */
|
||||
#define ATT_FIND_RSP_LEN_UUID16 (ATT_16_UUID_LEN + 2)
|
||||
#define ATT_FIND_RSP_LEN_UUID128 (ATT_128_UUID_LEN + 2)
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Compare two UUIDs.
|
||||
*
|
||||
* \param pChar Characteristic that we are looking for.
|
||||
* \param pUuid Pointer to peer UUID data.
|
||||
* \param settings Indicates 16 or 128 bit UUID.
|
||||
*
|
||||
* \return TRUE if UUIDs match.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t attcUuidCmp(attcDiscChar_t *pChar, uint8_t *pUuid, uint8_t settings)
|
||||
{
|
||||
/* if both uuids are the same length */
|
||||
if ((pChar->settings & ATT_SET_UUID_MASK) == settings)
|
||||
{
|
||||
/* simply compare the data */
|
||||
return (memcmp(pChar->pUuid, pUuid, (settings == 0) ? ATT_16_UUID_LEN : ATT_128_UUID_LEN) == 0);
|
||||
}
|
||||
/* if discovered UUID is 128 bit and our UUID is 16 bit */
|
||||
else if ((settings == ATTC_SET_UUID_128) && ((pChar->settings & ATTC_SET_UUID_128) == 0))
|
||||
{
|
||||
/* convert our UUID to 128 bit and compare */
|
||||
return attUuidCmp16to128(pChar->pUuid, pUuid);
|
||||
}
|
||||
/* else discovered UUID is 16 bit and our UUID is 128 bit */
|
||||
else
|
||||
{
|
||||
/* no match */
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Verify that required characterstics and descriptors have been discovered.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
*
|
||||
* \return ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attcDiscVerify(attcDiscCb_t *pCb)
|
||||
{
|
||||
attcDiscChar_t **pChar;
|
||||
uint8_t i;
|
||||
|
||||
/* for each characteristic */
|
||||
for (i = 0, pChar = pCb->pCharList; i < pCb->charListLen; i++, pChar++)
|
||||
{
|
||||
/* if characteristic required */
|
||||
if (((*pChar)->settings & ATTC_SET_REQUIRED) != 0)
|
||||
{
|
||||
/* verify handle was discovered */
|
||||
if (pCb->pHdlList[i] == ATT_HANDLE_NONE)
|
||||
{
|
||||
return ATT_ERR_REQ_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find next characteristic that requires descriptor discovery. If none found,
|
||||
* discovery is complete; verify that required characterstics have been discovered.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attcDiscDescriptors(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
attcDiscChar_t **pChar;
|
||||
uint16_t startHdl = ATT_HANDLE_NONE;
|
||||
uint16_t endHdl = ATT_HANDLE_NONE;
|
||||
|
||||
/* find next descriptor in list */
|
||||
pChar = pCb->pCharList + pCb->charListIdx;
|
||||
while (pCb->charListIdx < pCb->charListLen)
|
||||
{
|
||||
/* if this is a descriptor */
|
||||
if (((*pChar)->settings & ATTC_SET_DESCRIPTOR) != 0)
|
||||
{
|
||||
/* start handle is one greater than characteristic value handle,
|
||||
* which is stored in the previous entry in the list;
|
||||
* end handle is stored at current entry in the list
|
||||
*/
|
||||
startHdl = pCb->pHdlList[pCb->charListIdx - 1] + 1;
|
||||
endHdl = pCb->pHdlList[pCb->charListIdx];
|
||||
|
||||
/* clear temp end handle */
|
||||
pCb->pHdlList[pCb->charListIdx] = ATT_HANDLE_NONE;
|
||||
|
||||
/* if there are descriptors */
|
||||
if (startHdl <= endHdl)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we are looking for descriptors for this characteristic but
|
||||
* there aren't any;
|
||||
* skip over any other descriptors that follow in our list
|
||||
*/
|
||||
while (++pCb->charListIdx < pCb->charListLen)
|
||||
{
|
||||
pChar++;
|
||||
if ((*pChar)->settings & ATTC_SET_DESCRIPTOR)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* go to next in list */
|
||||
pChar++;
|
||||
pCb->charListIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
/* if no more descriptors to be discovered */
|
||||
if (pCb->charListIdx == pCb->charListLen)
|
||||
{
|
||||
/* we're done; verify required characteristics and descriptors were discovered */
|
||||
return attcDiscVerify(pCb);
|
||||
}
|
||||
/* else initiate characteristic descriptor discovery */
|
||||
else
|
||||
{
|
||||
AttcFindInfoReq(connId, startHdl, endHdl, TRUE);
|
||||
return ATT_CONTINUING;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a descriptor handle/UUID pair.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param settings Indicates 16 or 128 bit UUID.
|
||||
* \param pPair Pointer to handle/UUID pair.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcDiscProcDescPair(attcDiscCb_t *pCb, uint8_t settings, uint8_t *pPair)
|
||||
{
|
||||
attcDiscChar_t **pDesc;
|
||||
uint16_t hdl;
|
||||
uint8_t i;
|
||||
|
||||
/* parse handle */
|
||||
BSTREAM_TO_UINT16(hdl, pPair);
|
||||
|
||||
/* now pPair points to UUID; find descriptor with matching UUID */
|
||||
pDesc = &pCb->pCharList[pCb->charListIdx];
|
||||
for (i = pCb->charListIdx;
|
||||
(i < pCb->charListLen) && (((*pDesc)->settings & ATTC_SET_DESCRIPTOR) != 0);
|
||||
i++, pDesc++)
|
||||
{
|
||||
/* if characteristic not already found */
|
||||
if (pCb->pHdlList[i] == 0)
|
||||
{
|
||||
/* if UUIDs match */
|
||||
if (attcUuidCmp(*pDesc, pPair, settings))
|
||||
{
|
||||
/* match found; store handle */
|
||||
pCb->pHdlList[i] = hdl;
|
||||
|
||||
ATT_TRACE_INFO1("descriptor found handle:0x%x", hdl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a ATTC_FIND_INFO_RSP received while performing characteristic discovery.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param pMsg ATT callback event message.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attcDiscProcDesc(attcDiscCb_t *pCb, attEvt_t *pMsg)
|
||||
{
|
||||
attcDiscChar_t **pChar;
|
||||
uint8_t *p;
|
||||
uint8_t *pEnd;
|
||||
uint8_t format;
|
||||
uint8_t pairLen;
|
||||
uint8_t settings;
|
||||
|
||||
/* if find info successful */
|
||||
if (pMsg->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
p = pMsg->pValue;
|
||||
pEnd = pMsg->pValue + pMsg->valueLen;
|
||||
|
||||
/* determine UUID format */
|
||||
BSTREAM_TO_UINT8(format, p);
|
||||
if (format == ATT_FIND_HANDLE_16_UUID)
|
||||
{
|
||||
settings = 0;
|
||||
pairLen = ATT_FIND_RSP_LEN_UUID16;
|
||||
}
|
||||
else if (format == ATT_FIND_HANDLE_128_UUID)
|
||||
{
|
||||
settings = ATTC_SET_UUID_128;
|
||||
pairLen = ATT_FIND_RSP_LEN_UUID128;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATT_ERR_INVALID_RSP;
|
||||
}
|
||||
|
||||
/* for each handle/UUID pair */
|
||||
while (p < pEnd)
|
||||
{
|
||||
/* process descriptor handle/UUID pair */
|
||||
attcDiscProcDescPair(pCb, settings, p);
|
||||
|
||||
/* go to next */
|
||||
p += pairLen;
|
||||
}
|
||||
}
|
||||
|
||||
/* if descriptor discovery complete for this characteristic */
|
||||
if (pMsg->hdr.status != ATT_SUCCESS || pMsg->continuing == FALSE)
|
||||
{
|
||||
/* go to next entry in list */
|
||||
pChar = &pCb->pCharList[pCb->charListIdx];
|
||||
do
|
||||
{
|
||||
/* check if at end of list */
|
||||
pCb->charListIdx++;
|
||||
if (pCb->charListIdx == pCb->charListLen)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* skip over descriptors that follow current characteristic */
|
||||
pChar++;
|
||||
} while ((*pChar)->settings & ATTC_SET_DESCRIPTOR);
|
||||
|
||||
/* proceed with descriptor discovery for the next characteristic */
|
||||
return attcDiscDescriptors((dmConnId_t) pMsg->hdr.param, pCb);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* still more to do */
|
||||
return ATT_CONTINUING;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a characteristic declaration.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param settings Indicates 16 or 128 bit UUID.
|
||||
* \param pDecl Pointer to declaration.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcDiscProcCharDecl(attcDiscCb_t *pCb, uint8_t settings, uint8_t *pDecl)
|
||||
{
|
||||
attcDiscChar_t **pChar;
|
||||
uint16_t hdl;
|
||||
uint16_t declHdl;
|
||||
uint8_t i;
|
||||
|
||||
/* parse it */
|
||||
BSTREAM_TO_UINT16(declHdl, pDecl);
|
||||
pDecl++; /* skip properties field */
|
||||
BSTREAM_TO_UINT16(hdl, pDecl);
|
||||
|
||||
/* if looking for end handle of previous characteristic */
|
||||
if (pCb->endHdlIdx != ATT_DISC_HDL_IDX_NONE)
|
||||
{
|
||||
/* end handle of previous characteristic is one less than
|
||||
* the handle of the current characteristic declaration
|
||||
*/
|
||||
pCb->pHdlList[pCb->endHdlIdx] = declHdl - 1;
|
||||
pCb->endHdlIdx = ATT_DISC_HDL_IDX_NONE;
|
||||
}
|
||||
|
||||
/* check handle */
|
||||
if (hdl > declHdl && hdl <= pCb->svcEndHdl)
|
||||
{
|
||||
/* now pDecl points to UUID; search for UUID in characteristic list */
|
||||
for (i = 0, pChar = pCb->pCharList; i < pCb->charListLen; i++, pChar++)
|
||||
{
|
||||
/* if characteristic not already found */
|
||||
if (pCb->pHdlList[i] == 0)
|
||||
{
|
||||
/* if UUIDs match */
|
||||
if (attcUuidCmp(*pChar, pDecl, settings))
|
||||
{
|
||||
/* match found; store handle */
|
||||
pCb->pHdlList[i] = hdl;
|
||||
|
||||
/* if not at end of list and next in list is a descriptor */
|
||||
if (i < (pCb->charListLen - 1) &&
|
||||
((*(pChar + 1))->settings & ATTC_SET_DESCRIPTOR) == ATTC_SET_DESCRIPTOR)
|
||||
{
|
||||
/* characteristic has descriptors, we need to find end handle
|
||||
* store end handle temporarily in handle list location
|
||||
* for the first descriptor
|
||||
*/
|
||||
pCb->endHdlIdx = i + 1;
|
||||
}
|
||||
|
||||
ATT_TRACE_INFO1("characteristic found handle:0x%x", hdl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* invalid handle; skip this declaration */
|
||||
ATT_TRACE_WARN1("invalid handle:0x%x", hdl);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a ATTC_READ_BY_TYPE_RSP received while performing characteristic discovery.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param pMsg ATT callback event message.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attcDiscProcChar(attcDiscCb_t *pCb, attEvt_t *pMsg)
|
||||
{
|
||||
uint8_t *p;
|
||||
uint8_t *pEnd;
|
||||
uint8_t pairLen;
|
||||
uint8_t settings;
|
||||
|
||||
/* if read by type successful */
|
||||
if (pMsg->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
p = pMsg->pValue;
|
||||
pEnd = pMsg->pValue + pMsg->valueLen;
|
||||
|
||||
/* verify attribute-handle pair length and determine UUID length */
|
||||
BSTREAM_TO_UINT8(pairLen, p);
|
||||
if (pairLen == ATT_READ_RSP_LEN_UUID16)
|
||||
{
|
||||
settings = 0;
|
||||
}
|
||||
else if (pairLen == ATT_READ_RSP_LEN_UUID128)
|
||||
{
|
||||
settings = ATTC_SET_UUID_128;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATT_ERR_INVALID_RSP;
|
||||
}
|
||||
|
||||
/* Note that ATT already verifies response length and handle values */
|
||||
|
||||
/* for each characteristic declaration */
|
||||
while (p < pEnd)
|
||||
{
|
||||
/* process characteristic declaration */
|
||||
attcDiscProcCharDecl(pCb, settings, p);
|
||||
|
||||
/* go to next */
|
||||
p += pairLen;
|
||||
}
|
||||
}
|
||||
|
||||
/* if characteristic discovery complete */
|
||||
if (pMsg->hdr.status != ATT_SUCCESS || pMsg->continuing == FALSE)
|
||||
{
|
||||
/* check if characteristic end handle needs to be set */
|
||||
if (pCb->endHdlIdx != ATT_DISC_HDL_IDX_NONE)
|
||||
{
|
||||
/* end handle of characteristic declaration is end handle of service */
|
||||
pCb->pHdlList[pCb->endHdlIdx] = pCb->svcEndHdl;
|
||||
}
|
||||
|
||||
/* proceed with descriptor discovery */
|
||||
pCb->charListIdx = 0;
|
||||
return attcDiscDescriptors((dmConnId_t) pMsg->hdr.param, pCb);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* still more to do */
|
||||
return ATT_CONTINUING;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiates the next characteristic configuration procedure.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param pMsg ATT callback event message.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and configuration procedure is continuing.
|
||||
* ATT_SUCCESS if configuration procedure completed successfully.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attcDiscConfigNext(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
attcDiscCfg_t *pCfg;
|
||||
|
||||
pCfg = pCb->pCfgList + pCb->charListIdx;
|
||||
|
||||
/* iterate over list */
|
||||
while (pCb->charListIdx < pCb->cfgListLen)
|
||||
{
|
||||
/* if handle was discovered */
|
||||
if (pCb->pHdlList[pCfg->hdlIdx] != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* if value present do write req */
|
||||
if (pCfg->valueLen != 0)
|
||||
{
|
||||
AttcWriteReq(connId, pCb->pHdlList[pCfg->hdlIdx], pCfg->valueLen, (uint8_t *) pCfg->pValue);
|
||||
}
|
||||
/* else do read */
|
||||
else
|
||||
{
|
||||
AttcReadReq(connId, pCb->pHdlList[pCfg->hdlIdx]);
|
||||
}
|
||||
|
||||
/* done for now */
|
||||
return ATT_CONTINUING;
|
||||
}
|
||||
|
||||
/* next in list */
|
||||
pCb->charListIdx++;
|
||||
pCfg++;
|
||||
}
|
||||
|
||||
/* nothing left to configure; we're done */
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function discovers the given service on a peer device. Function
|
||||
* AttcFindByTypeValueReq() is called to initiate the discovery procedure.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param uuidLen Length of UUID (2 or 16).
|
||||
* \param pUuid Pointer to UUID data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcDiscService(dmConnId_t connId, attcDiscCb_t *pCb, uint8_t uuidLen, uint8_t *pUuid)
|
||||
{
|
||||
AttcFindByTypeValueReq(connId, ATT_HANDLE_START, ATT_HANDLE_MAX, ATT_UUID_PRIMARY_SERVICE,
|
||||
uuidLen, pUuid, FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function processes a service discovery result. It should be called
|
||||
* when an ATTC_FIND_BY_TYPE_VALUE_RSP callback event is received after service
|
||||
* discovery is initiated by calling AttcDiscService().
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param pMsg ATT callback event message.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttcDiscServiceCmpl(attcDiscCb_t *pCb, attEvt_t *pMsg)
|
||||
{
|
||||
uint8_t *p;
|
||||
|
||||
/* verify callback event */
|
||||
if (pMsg->hdr.event != ATTC_FIND_BY_TYPE_VALUE_RSP)
|
||||
{
|
||||
ATT_TRACE_WARN1("unexpected callback event %d", pMsg->hdr.event);
|
||||
return ATT_ERR_UNDEFINED;
|
||||
}
|
||||
/* verify status */
|
||||
else if (pMsg->hdr.status != ATT_SUCCESS)
|
||||
{
|
||||
return pMsg->hdr.status;
|
||||
}
|
||||
/* verify result was found */
|
||||
else if (pMsg->valueLen == 0)
|
||||
{
|
||||
return ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* get handles of first returned service only; ATT has already performed error checking */
|
||||
p = pMsg->pValue;
|
||||
BSTREAM_TO_UINT16(pCb->svcStartHdl, p);
|
||||
BSTREAM_TO_UINT16(pCb->svcEndHdl, p);
|
||||
|
||||
ATT_TRACE_INFO2("found service startHdl=0x%x endHdl=0x%x", pCb->svcStartHdl, pCb->svcEndHdl);
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function starts characteristic and characteristic descriptor
|
||||
* discovery for a service on a peer device. The service must have been previously
|
||||
* discovered by calling AttcDiscService() and AttcDiscServiceCmpl().
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcDiscCharStart(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
/* initialize control block */
|
||||
pCb->charListIdx = 0;
|
||||
pCb->endHdlIdx = ATT_DISC_HDL_IDX_NONE;
|
||||
|
||||
AttcReadByTypeReq(connId, pCb->svcStartHdl, pCb->svcEndHdl, ATT_16_UUID_LEN,
|
||||
(uint8_t *) attChUuid, TRUE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function processes a characteristic discovery result. It should be
|
||||
* called when an ATTC_READ_BY_TYPE_RSP or ATTC_FIND_INFO_RSP callback event is
|
||||
* received after characteristic discovery is initiated by calling AttcDiscCharStart().
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param pMsg ATT callback event message.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttcDiscCharCmpl(attcDiscCb_t *pCb, attEvt_t *pMsg)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* verify callback event */
|
||||
if (pMsg->hdr.event != ATTC_READ_BY_TYPE_RSP &&
|
||||
pMsg->hdr.event != ATTC_FIND_INFO_RSP)
|
||||
{
|
||||
ATT_TRACE_WARN1("unexpected callback event %d", pMsg->hdr.event);
|
||||
return ATT_ERR_UNDEFINED;
|
||||
}
|
||||
|
||||
/* if read by type (characteristic discovery) */
|
||||
if (pMsg->hdr.event == ATTC_READ_BY_TYPE_RSP)
|
||||
{
|
||||
status = attcDiscProcChar(pCb, pMsg);
|
||||
}
|
||||
/* else if find info (descriptor discovery) */
|
||||
else
|
||||
{
|
||||
status = attcDiscProcDesc(pCb, pMsg);
|
||||
}
|
||||
|
||||
/* if characteristic discovery failed clear any handles */
|
||||
if (status != ATT_SUCCESS && status != ATT_CONTINUING)
|
||||
{
|
||||
memset(pCb->pHdlList, 0, (pCb->charListLen * sizeof(uint16_t)));
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function starts characteristic configuration for characteristics on a
|
||||
* peer device. The characteristics must have been previously discovered by calling
|
||||
* AttcDiscCharStart() and AttcDisccharCont().
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and configuration procedure is continuing.
|
||||
* ATT_SUCCESS if nothing to configure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttcDiscConfigStart(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
/* use char list index to iterate over config list */
|
||||
pCb->charListIdx = 0;
|
||||
|
||||
return attcDiscConfigNext(connId, pCb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function initiates the next characteristic configuration procedure.
|
||||
* It should be called when an ATTC_READ_RSP or ATTC_WRITE_RSP callback event is received
|
||||
* after characteristic configuration is initiated by calling AttcDiscConfigStart().
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to discovery control block.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and configuration procedure is continuing.
|
||||
* ATT_SUCCESS if configuration procedure completed successfully.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttcDiscConfigCmpl(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
/* go to next in list */
|
||||
pCb->charListIdx++;
|
||||
|
||||
return attcDiscConfigNext(connId, pCb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function resumes the characteristic configuration procedure. It can
|
||||
* be called when an ATTC_READ_RSP or ATTC_WRITE_RSP callback event is received
|
||||
* with failure status to attempt the read or write procedure again.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to discovery control block.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and configuration procedure is continuing.
|
||||
* ATT_SUCCESS if configuration procedure completed successfully.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttcDiscConfigResume(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
return attcDiscConfigNext(connId, pCb);
|
||||
}
|
||||
Vendored
+840
@@ -0,0 +1,840 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_math.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "attc_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* type for send request function */
|
||||
typedef void (*attcSendReq_t)(attcCcb_t *pCcb);
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
static void attcDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket);
|
||||
static void attcCtrlCback(wsfMsgHdr_t *pMsg);
|
||||
static void attcConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt);
|
||||
static void attcMsgCback(attcApiMsg_t *pMsg);
|
||||
|
||||
static void attcSendSimpleReq(attcCcb_t *pCcb);
|
||||
static void attcSendContinuingReq(attcCcb_t *pCcb);
|
||||
static void attcSendMtuReq(attcCcb_t *pCcb);
|
||||
static void attcSendWriteCmd(attcCcb_t *pCcb);
|
||||
static void attcSendPrepWriteReq(attcCcb_t *pCcb);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Interface to ATT */
|
||||
static const attFcnIf_t attcFcnIf =
|
||||
{
|
||||
attcDataCback,
|
||||
attcCtrlCback,
|
||||
(attMsgHandler_t) attcMsgCback,
|
||||
attcConnCback
|
||||
};
|
||||
|
||||
/* Table of send request functions */
|
||||
static const attcSendReq_t attcSendReqTbl[] =
|
||||
{
|
||||
NULL, /* ATTC_MSG_API_NONE (unused) */
|
||||
attcSendMtuReq, /* ATTC_MSG_API_MTU */
|
||||
attcSendContinuingReq, /* ATTC_MSG_API_FIND_INFO */
|
||||
attcSendContinuingReq, /* ATTC_MSG_API_FIND_BY_TYPE_VALUE */
|
||||
attcSendContinuingReq, /* ATTC_MSG_API_READ_BY_TYPE */
|
||||
attcSendSimpleReq, /* ATTC_MSG_API_READ */
|
||||
attcSendContinuingReq, /* ATTC_MSG_API_READ_LONG */
|
||||
attcSendSimpleReq, /* ATTC_MSG_API_READ_MULTIPLE */
|
||||
attcSendContinuingReq, /* ATTC_MSG_API_READ_BY_GROUP_TYPE */
|
||||
attcSendSimpleReq, /* ATTC_MSG_API_WRITE */
|
||||
attcSendWriteCmd, /* ATTC_MSG_API_WRITE_CMD */
|
||||
attcSendPrepWriteReq, /* ATTC_MSG_API_PREP_WRITE */
|
||||
attcSendSimpleReq /* ATTC_MSG_API_EXEC_WRITE */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
attcCb_t attcCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if application callback is pending for a given write command, or the maximum
|
||||
* number of simultaneous write commands has been reached.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
* \param pMsg ATTC message.
|
||||
*
|
||||
* \return TRUE if app callback's pending or maximum number of simultaneous write commands reached.
|
||||
* FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t attcPendWriteCmd(attcCcb_t *pCcb, attcApiMsg_t *pMsg)
|
||||
{
|
||||
uint8_t pendRsp;
|
||||
uint8_t i;
|
||||
|
||||
/* initialize number of response callbacks pending */
|
||||
pendRsp = 0;
|
||||
|
||||
for (i = 0; i < ATT_NUM_SIMUL_WRITE_CMD; i++)
|
||||
{
|
||||
/* if callback pending for write command */
|
||||
if (pCcb->pendWriteCmdHandle[i] != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* if callback pending for this handle */
|
||||
if (pCcb->pendWriteCmdHandle[i] == pMsg->handle)
|
||||
{
|
||||
/* callback pending for this write command */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
pendRsp++;
|
||||
}
|
||||
}
|
||||
|
||||
/* no callback is pending for this write command but see if the maximum number of simultaneous
|
||||
write commands has been reached */
|
||||
return (pendRsp < ATT_NUM_SIMUL_WRITE_CMD) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set pending write command response callback for a given attribute handle.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSetPendWriteCmd(attcCcb_t *pCcb)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < ATT_NUM_SIMUL_WRITE_CMD; i++)
|
||||
{
|
||||
/* if entry free */
|
||||
if (pCcb->pendWriteCmdHandle[i] == ATT_HANDLE_NONE)
|
||||
{
|
||||
/* set pending write command handle */
|
||||
pCcb->pendWriteCmdHandle[i] = pCcb->outReq.handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Call pending write command response application callback.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCcb ATTC control block.
|
||||
* \param status Callback event status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcWriteCmdCallback(dmConnId_t connId, attcCcb_t *pCcb, uint8_t status)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* if any pending write command callback */
|
||||
for (i = 0; i < ATT_NUM_SIMUL_WRITE_CMD; i++)
|
||||
{
|
||||
if (pCcb->pendWriteCmdHandle[i] != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* call callback with status */
|
||||
attcExecCallback(connId, ATTC_WRITE_CMD_RSP, pCcb->pendWriteCmdHandle[i], status);
|
||||
pCcb->pendWriteCmdHandle[i] = ATT_HANDLE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send attribute client request that has already been built.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSendSimpleReq(attcCcb_t *pCcb)
|
||||
{
|
||||
attcPktParam_t *pPkt = pCcb->outReq.pPkt;
|
||||
|
||||
/* clear stored packet pointer */
|
||||
pCcb->outReq.pPkt = NULL;
|
||||
|
||||
/* start request timer (except for write command) */
|
||||
if (pCcb->outReq.hdr.event != ATTC_MSG_API_WRITE_CMD)
|
||||
{
|
||||
pCcb->outReqTimer.msg.event = ATTC_MSG_REQ_TIMEOUT;
|
||||
WsfTimerStartSec(&pCcb->outReqTimer, pAttCfg->transTimeout);
|
||||
}
|
||||
|
||||
/* send packet to L2CAP */
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, pPkt->len, (uint8_t *) pPkt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a request that may be continuing.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSendContinuingReq(attcCcb_t *pCcb)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* allocate new buffer */
|
||||
if ((pPkt = attMsgAlloc(pCcb->outReq.pPkt->len + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* copy stored packet to new */
|
||||
memcpy(pPkt, pCcb->outReq.pPkt, pCcb->outReq.pPkt->len + L2C_PAYLOAD_START);
|
||||
}
|
||||
/* else handle error case of allocation failure */
|
||||
else
|
||||
{
|
||||
/* free stored packet and call callback with failure status */
|
||||
attcReqClear(pCcb, &pCcb->outReq, ATT_ERR_MEMORY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* else not continuing */
|
||||
else
|
||||
{
|
||||
/* send the stored packet */
|
||||
pPkt = pCcb->outReq.pPkt;
|
||||
|
||||
/* clear stored packet pointer */
|
||||
pCcb->outReq.pPkt = NULL;
|
||||
}
|
||||
|
||||
/* build remaining fields of packet from stored parameters */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_READ_LONG)
|
||||
{
|
||||
/* build offset field */
|
||||
p += sizeof(uint16_t);
|
||||
UINT16_TO_BSTREAM(p, pCcb->outReqParams.o.offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Find Info, Find By Type Value, Read By Type, and Read By Group Type
|
||||
* request packets all begin with a start handle and end handle.
|
||||
*/
|
||||
UINT16_TO_BSTREAM(p, pCcb->outReqParams.h.startHandle);
|
||||
UINT16_TO_BSTREAM(p, pCcb->outReqParams.h.endHandle);
|
||||
}
|
||||
|
||||
/* start request timer */
|
||||
pCcb->outReqTimer.msg.event = ATTC_MSG_REQ_TIMEOUT;
|
||||
WsfTimerStartSec(&pCcb->outReqTimer, pAttCfg->transTimeout);
|
||||
|
||||
/* send packet to L2CAP */
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, pPkt->len, (uint8_t *) pPkt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send MTU request.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSendMtuReq(attcCcb_t *pCcb)
|
||||
{
|
||||
/* if MTU already exchanged */
|
||||
if (pCcb->pMainCcb->control & ATT_CCB_STATUS_MTU_SENT)
|
||||
{
|
||||
/* discard request */
|
||||
attcFreePkt(&pCcb->outReq);
|
||||
|
||||
/* clear out req */
|
||||
pCcb->outReq.hdr.event = ATTC_MSG_API_NONE;
|
||||
|
||||
ATT_TRACE_WARN0("MTU req discarded");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set MTU sent */
|
||||
pCcb->pMainCcb->control |= ATT_CCB_STATUS_MTU_SENT;
|
||||
|
||||
/* send packet */
|
||||
attcSendSimpleReq(pCcb);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send attribute client Write command.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSendWriteCmd(attcCcb_t *pCcb)
|
||||
{
|
||||
/* send packet */
|
||||
attcSendSimpleReq(pCcb);
|
||||
|
||||
/* if flow not disabled */
|
||||
if (!(pCcb->pMainCcb->control & ATT_CCB_STATUS_FLOW_DISABLED))
|
||||
{
|
||||
/* call callback */
|
||||
attcExecCallback(pCcb->pMainCcb->connId, ATTC_WRITE_CMD_RSP, pCcb->outReq.handle, ATT_SUCCESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set pending write command callback for this handle */
|
||||
attcSetPendWriteCmd(pCcb);
|
||||
}
|
||||
|
||||
/* clear out req */
|
||||
pCcb->outReq.hdr.event = ATTC_MSG_API_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send attribute client Prepare Write request.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSendPrepWriteReq(attcCcb_t *pCcb)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
uint16_t dataLen;
|
||||
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* determine size of buffer to allocate */
|
||||
if (pCcb->outReqParams.w.len < (pCcb->pMainCcb->mtu - ATT_PREP_WRITE_REQ_LEN))
|
||||
{
|
||||
dataLen = pCcb->outReqParams.w.len;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataLen = pCcb->pMainCcb->mtu - ATT_PREP_WRITE_REQ_LEN;
|
||||
}
|
||||
|
||||
/* allocate new buffer */
|
||||
if ((pPkt = attMsgAlloc(dataLen + ATT_PREP_WRITE_REQ_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* copy fixed fields */
|
||||
memcpy(pPkt, pCcb->outReq.pPkt, ATT_PREP_WRITE_REQ_LEN + L2C_PAYLOAD_START);
|
||||
|
||||
/* copy data */
|
||||
memcpy(((uint8_t *) pPkt + L2C_PAYLOAD_START + ATT_PREP_WRITE_REQ_LEN),
|
||||
pCcb->outReqParams.w.pValue, dataLen);
|
||||
|
||||
/* update length and data pointer */
|
||||
pCcb->outReqParams.w.pValue += dataLen;
|
||||
pCcb->outReqParams.w.len -= dataLen;
|
||||
}
|
||||
/* else handle error case of allocation failure */
|
||||
else
|
||||
{
|
||||
/* free stored packet and call callback with failure status */
|
||||
attcReqClear(pCcb, &pCcb->outReq, ATT_ERR_MEMORY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* else not continuing */
|
||||
else
|
||||
{
|
||||
dataLen = pCcb->outReqParams.w.len;
|
||||
|
||||
/* send the stored packet */
|
||||
pPkt = pCcb->outReq.pPkt;
|
||||
|
||||
/* clear stored packet pointer */
|
||||
pCcb->outReq.pPkt = NULL;
|
||||
}
|
||||
|
||||
/* build remaining fields of packet from stored parameters */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START + ATT_HDR_LEN + sizeof(uint16_t);
|
||||
UINT16_TO_BSTREAM(p, pCcb->outReqParams.w.offset);
|
||||
|
||||
/* update offset after building packet */
|
||||
pCcb->outReqParams.w.offset += dataLen;
|
||||
|
||||
/* start request timer */
|
||||
pCcb->outReqTimer.msg.event = ATTC_MSG_REQ_TIMEOUT;
|
||||
WsfTimerStartSec(&pCcb->outReqTimer, pAttCfg->transTimeout);
|
||||
|
||||
/* send packet to L2CAP */
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, dataLen + ATT_PREP_WRITE_REQ_LEN, (uint8_t *) pPkt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Lookup and execute function to build and send request.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcSendReq(attcCcb_t *pCcb)
|
||||
{
|
||||
(*attcSendReqTbl[pCcb->outReq.hdr.event])(pCcb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up and send an attribute client request.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
* \param pMsg ATTC message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcSetupReq(attcCcb_t *pCcb, attcApiMsg_t *pMsg)
|
||||
{
|
||||
/* set out req to api message */
|
||||
pCcb->outReq = *pMsg;
|
||||
|
||||
/* store parameters */
|
||||
pCcb->outReqParams = *(pMsg->pPkt);
|
||||
|
||||
/* build and send request */
|
||||
attcSendReq(pCcb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Data callback for ATTC.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t opcode;
|
||||
attcCcb_t *pCcb;
|
||||
|
||||
/* get connection control block for this handle, ignore packet if not found */
|
||||
if ((pCcb = attcCcbByHandle(handle)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse opcode */
|
||||
opcode = *(pPacket + L2C_PAYLOAD_START);
|
||||
|
||||
/* if response */
|
||||
if (opcode <= ATT_PDU_EXEC_WRITE_RSP)
|
||||
{
|
||||
attcProcRsp(pCcb, len, pPacket);
|
||||
}
|
||||
/* else if indication or notification */
|
||||
else if ((opcode == ATT_PDU_VALUE_NTF) || (opcode == ATT_PDU_VALUE_IND))
|
||||
{
|
||||
attcProcInd(pCcb, len, pPacket);
|
||||
}
|
||||
/* else unknown opcode */
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN1("attc unknown opcode 0x%02x", opcode);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2CAP control callback.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
attcCcb_t *pCcb;
|
||||
|
||||
/* note this function is currently only called when flow is enabled */
|
||||
|
||||
/* get CCB */
|
||||
if ((pCcb = attcCcbByConnId((dmConnId_t) pMsg->param)) != NULL)
|
||||
{
|
||||
/* if confirmation pending try sending now */
|
||||
AttcIndConfirm((dmConnId_t) pMsg->param);
|
||||
|
||||
/* call pending write command callback */
|
||||
attcWriteCmdCallback((dmConnId_t) pMsg->param, pCcb, ATT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection callback for ATTC.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt)
|
||||
{
|
||||
attcCcb_t *pClient;
|
||||
uint16_t localMtu;
|
||||
uint8_t status;
|
||||
|
||||
/* if connection opened */
|
||||
if (pDmEvt->hdr.event == DM_CONN_OPEN_IND)
|
||||
{
|
||||
/* if we initiated connection send MTU request */
|
||||
if (DmConnRole(pCcb->connId) == DM_ROLE_MASTER)
|
||||
{
|
||||
localMtu = WSF_MIN(pAttCfg->mtu, (HciGetMaxRxAclLen() - L2C_HDR_LEN));
|
||||
|
||||
/* if desired MTU is not the default */
|
||||
if (localMtu != ATT_DEFAULT_MTU)
|
||||
{
|
||||
AttcMtuReq(pCcb->connId, localMtu);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* if connection closed */
|
||||
else if (pDmEvt->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* set status */
|
||||
if (pDmEvt->connClose.hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
status = pDmEvt->connClose.reason + ATT_HCI_ERR_BASE;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = pDmEvt->connClose.hdr.status + ATT_HCI_ERR_BASE;
|
||||
}
|
||||
|
||||
/* get client control block directly */
|
||||
pClient = &attcCb.ccb[pCcb->connId - 1];
|
||||
|
||||
/* free any out req */
|
||||
if (pClient->outReq.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
WsfTimerStop(&pClient->outReqTimer);
|
||||
attcReqClear(pClient, &pClient->outReq, status);
|
||||
}
|
||||
|
||||
/* free any req on deck */
|
||||
if (pClient->onDeck.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
attcReqClear(pClient, &pClient->onDeck, status);
|
||||
}
|
||||
|
||||
/* initialize other control block variables */
|
||||
pClient->flowDisabled = FALSE;
|
||||
pClient->cnfPending = FALSE;
|
||||
|
||||
/* pass to connection close callback for signed data */
|
||||
if (attcCb.pSign != NULL)
|
||||
{
|
||||
(*attcCb.pSign->closeCback)(pClient, status);
|
||||
}
|
||||
|
||||
/* call pending write command callback */
|
||||
attcWriteCmdCallback(pCcb->connId, pClient, status);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Message handler callback for ATTC.
|
||||
*
|
||||
* \param pMsg ATTC message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcMsgCback(attcApiMsg_t *pMsg)
|
||||
{
|
||||
attcCcb_t *pCcb;
|
||||
|
||||
/* get CCB and verify connection still in use */
|
||||
if ((pCcb = attcCcbByConnId((dmConnId_t) pMsg->hdr.param)) == NULL)
|
||||
{
|
||||
/* if message has a packet buffer */
|
||||
if (pMsg->hdr.event >= ATTC_MSG_API_MTU &&
|
||||
pMsg->hdr.event <= ATTC_MSG_API_SIGNED_WRITE_CMD)
|
||||
{
|
||||
/* free packet buffer */
|
||||
attcFreePkt(pMsg);
|
||||
}
|
||||
|
||||
/* ignore if connection not in use */
|
||||
return;
|
||||
}
|
||||
|
||||
/* if an API request to send packet (non-signed) */
|
||||
if (pMsg->hdr.event <= ATTC_MSG_API_EXEC_WRITE)
|
||||
{
|
||||
/* verify no API request already waiting on deck, in progress, or no pending write command
|
||||
already for this handle */
|
||||
if ((pCcb->onDeck.hdr.event != ATTC_MSG_API_NONE) ||
|
||||
(pCcb->outReq.hdr.event > ATTC_MSG_API_MTU) ||
|
||||
((pMsg->hdr.event == ATTC_MSG_API_WRITE_CMD) &&
|
||||
attcPendWriteCmd(pCcb, pMsg)))
|
||||
{
|
||||
/* free request and call callback with failure status */
|
||||
attcReqClear(pCcb, pMsg, ATT_ERR_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
|
||||
/* if MTU request in progress or flow controlled */
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_MTU || pCcb->flowDisabled)
|
||||
{
|
||||
/* put request "on deck" for processing later */
|
||||
pCcb->onDeck = *pMsg;
|
||||
}
|
||||
/* otherwise ready to send; set up request */
|
||||
else
|
||||
{
|
||||
attcSetupReq(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
/* else if signed data event */
|
||||
else if (pMsg->hdr.event >= ATTC_MSG_API_SIGNED_WRITE_CMD &&
|
||||
pMsg->hdr.event <= ATTC_MSG_CMAC_CMPL)
|
||||
{
|
||||
/* pass to message callback for signed data */
|
||||
if (attcCb.pSign != NULL)
|
||||
{
|
||||
(*attcCb.pSign->msgCback)(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
/* else if cancel request */
|
||||
else if (pMsg->hdr.event == ATTC_MSG_API_CANCEL)
|
||||
{
|
||||
/* free any out req (except mtu req) */
|
||||
if (pCcb->outReq.hdr.event != ATTC_MSG_API_NONE &&
|
||||
pCcb->outReq.hdr.event != ATTC_MSG_API_MTU)
|
||||
{
|
||||
WsfTimerStop(&pCcb->outReqTimer);
|
||||
attcReqClear(pCcb, &pCcb->outReq, ATT_ERR_CANCELLED);
|
||||
}
|
||||
/* else free any req on deck */
|
||||
else if (pCcb->onDeck.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
attcReqClear(pCcb, &pCcb->onDeck, ATT_ERR_CANCELLED);
|
||||
}
|
||||
}
|
||||
/* else if timeout */
|
||||
else if (pMsg->hdr.event == ATTC_MSG_REQ_TIMEOUT)
|
||||
{
|
||||
/* free any out req */
|
||||
if (pCcb->outReq.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
attcReqClear(pCcb, &pCcb->outReq, ATT_ERR_TIMEOUT);
|
||||
pCcb->pMainCcb->control |= ATT_CCB_STATUS_TX_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the ATTC connection control block connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to connection control block or NULL if not in use.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
attcCcb_t *attcCcbByConnId(dmConnId_t connId)
|
||||
{
|
||||
if (DmConnInUse(connId))
|
||||
{
|
||||
return &attcCb.ccb[connId - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN1("attc ccb not in use: %d", connId);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the connection control block for the given handle.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
*
|
||||
* \return Pointer to connection control block or NULL if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
attcCcb_t *attcCcbByHandle(uint16_t handle)
|
||||
{
|
||||
dmConnId_t connId;
|
||||
|
||||
if ((connId = DmConnIdByHandle(handle)) != DM_CONN_ID_NONE)
|
||||
{
|
||||
return &attcCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Free the packet buffer of an API message structure.
|
||||
*
|
||||
* \param pMsg Pointer to API message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcFreePkt(attcApiMsg_t *pMsg)
|
||||
{
|
||||
if (pMsg->pPkt != NULL)
|
||||
{
|
||||
WsfMsgFree(pMsg->pPkt);
|
||||
pMsg->pPkt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute application callback function.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param event Callback event ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param status Callback event status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcExecCallback(dmConnId_t connId, uint8_t event, uint16_t handle, uint8_t status)
|
||||
{
|
||||
if (event != ATT_METHOD_MTU)
|
||||
{
|
||||
attExecCallback(connId, event, handle, status, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear an outstanding request and execute the callback.
|
||||
*
|
||||
* \param pCcb Pointer to control block.
|
||||
* \param pMsg API message.
|
||||
* \param status Callback event status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcReqClear(attcCcb_t *pCcb, attcApiMsg_t *pMsg, uint8_t status)
|
||||
{
|
||||
attcFreePkt(pMsg);
|
||||
attcExecCallback(pCcb->pMainCcb->connId, pMsg->hdr.event, pMsg->handle, status);
|
||||
pMsg->hdr.event = ATTC_MSG_API_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set automatic Indication Confirmations sent from this ATT Client.
|
||||
*
|
||||
* \param enable \ref TRUE to enable automatic confirmations (default), \ref FALSE to disable.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcSetAutoConfirm(bool_t enable)
|
||||
{
|
||||
attcCb.autoCnf = enable;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATT client.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcInit(void)
|
||||
{
|
||||
uint8_t i;
|
||||
attcCcb_t *pCcb;
|
||||
|
||||
/* Initialize control block */
|
||||
attcCb.pSign = NULL;
|
||||
attcCb.autoCnf = TRUE;
|
||||
|
||||
/* Initialize control block CCBs */
|
||||
for (i = 0, pCcb = attcCb.ccb; i < DM_CONN_MAX; i++, pCcb++)
|
||||
{
|
||||
/* set pointer to main CCB */
|
||||
pCcb->pMainCcb = &attCb.ccb[i];
|
||||
|
||||
/* initialize timer */
|
||||
pCcb->outReqTimer.handlerId = attCb.handlerId;
|
||||
pCcb->outReqTimer.msg.param = i + 1; /* param stores the conn id */
|
||||
}
|
||||
|
||||
/* set up callback interface */
|
||||
attCb.pClient = &attcFcnIf;
|
||||
}
|
||||
Vendored
+208
@@ -0,0 +1,208 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef ATTC_MAIN_H
|
||||
#define ATTC_MAIN_H
|
||||
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "att_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Buffer lengths for requests */
|
||||
#define ATT_MTU_REQ_BUF_LEN (ATT_MTU_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_FIND_INFO_REQ_BUF_LEN (ATT_FIND_INFO_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_FIND_TYPE_REQ_BUF_LEN (ATT_FIND_TYPE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_READ_TYPE_REQ_BUF_LEN (ATT_READ_TYPE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_READ_REQ_BUF_LEN (ATT_READ_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_READ_BLOB_REQ_BUF_LEN (ATT_READ_BLOB_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_READ_MULT_REQ_BUF_LEN (ATT_READ_MULT_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_READ_GROUP_TYPE_REQ_BUF_LEN (ATT_READ_GROUP_TYPE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_WRITE_REQ_BUF_LEN (ATT_WRITE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_WRITE_CMD_BUF_LEN (ATT_WRITE_CMD_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_SIGNED_WRITE_CMD_BUF_LEN (ATT_SIGNED_WRITE_CMD_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_PREP_WRITE_REQ_BUF_LEN (ATT_PREP_WRITE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_EXEC_WRITE_REQ_BUF_LEN (ATT_EXEC_WRITE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
|
||||
/* values for 'continuing' flag */
|
||||
#define ATTC_CONTINUING TRUE
|
||||
#define ATTC_NOT_CONTINUING FALSE
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* ATTC event handler messages */
|
||||
enum
|
||||
{
|
||||
/* messages from API; note these match method values */
|
||||
ATTC_MSG_API_NONE = ATTC_MSG_START,
|
||||
ATTC_MSG_API_MTU = ATT_METHOD_MTU,
|
||||
ATTC_MSG_API_FIND_INFO = ATT_METHOD_FIND_INFO,
|
||||
ATTC_MSG_API_FIND_BY_TYPE_VALUE = ATT_METHOD_FIND_TYPE,
|
||||
ATTC_MSG_API_READ_BY_TYPE = ATT_METHOD_READ_TYPE,
|
||||
ATTC_MSG_API_READ = ATT_METHOD_READ,
|
||||
ATTC_MSG_API_READ_LONG = ATT_METHOD_READ_BLOB,
|
||||
ATTC_MSG_API_READ_MULTIPLE = ATT_METHOD_READ_MULTIPLE,
|
||||
ATTC_MSG_API_READ_BY_GROUP_TYPE = ATT_METHOD_READ_GROUP_TYPE,
|
||||
ATTC_MSG_API_WRITE = ATT_METHOD_WRITE,
|
||||
ATTC_MSG_API_WRITE_CMD = ATT_METHOD_WRITE_CMD,
|
||||
ATTC_MSG_API_PREP_WRITE = ATT_METHOD_PREPARE_WRITE,
|
||||
ATTC_MSG_API_EXEC_WRITE = ATT_METHOD_EXECUTE_WRITE,
|
||||
ATTC_MSG_API_SIGNED_WRITE_CMD,
|
||||
ATTC_MSG_CMAC_CMPL,
|
||||
ATTC_MSG_API_CANCEL,
|
||||
ATTC_MSG_REQ_TIMEOUT
|
||||
};
|
||||
|
||||
/*!
|
||||
* Data buffer format for API request messages:
|
||||
*
|
||||
* | attcPktParam_t | ATT request data |
|
||||
* | bytes 0 to 7 | bytes 8 - |
|
||||
*/
|
||||
|
||||
/* Structure for API with offset parameter */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t len;
|
||||
uint16_t offset;
|
||||
} attcPktParamOffset_t;
|
||||
|
||||
/* Structure for API with start and end handle parameters */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t len;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
} attcPktParamHandles_t;
|
||||
|
||||
/* Structure for API with offset and value parameters */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t len;
|
||||
uint16_t offset;
|
||||
uint8_t *pValue;
|
||||
} attcPktParamPrepWrite_t;
|
||||
|
||||
/* union of API parameter types */
|
||||
typedef union
|
||||
{
|
||||
uint16_t len;
|
||||
attcPktParamOffset_t o;
|
||||
attcPktParamHandles_t h;
|
||||
attcPktParamPrepWrite_t w;
|
||||
} attcPktParam_t;
|
||||
|
||||
/* verify attcPktParam_t will work in data buffer format described above */
|
||||
WSF_CT_ASSERT(sizeof(attcPktParam_t) <= L2C_PAYLOAD_START);
|
||||
|
||||
/* API message structure */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
attcPktParam_t *pPkt;
|
||||
uint16_t handle;
|
||||
} attcApiMsg_t;
|
||||
|
||||
/* ATTC connection control block */
|
||||
typedef struct
|
||||
{
|
||||
attCcb_t *pMainCcb; /* Pointer to ATT main CCB */
|
||||
attcApiMsg_t onDeck; /* API message "on deck" waiting to be sent */
|
||||
attcApiMsg_t outReq; /* Outstanding request waiting for response */
|
||||
attcPktParam_t outReqParams; /* Parameters associated with outstanding request */
|
||||
wsfTimer_t outReqTimer; /* Outstanding request timer */
|
||||
bool_t flowDisabled; /* Data flow disabled */
|
||||
bool_t cnfPending; /* Handle value confirm packet waiting to be sent */
|
||||
uint16_t pendWriteCmdHandle[ATT_NUM_SIMUL_WRITE_CMD]; /* Callback to app pending for this write cmd handle */
|
||||
} attcCcb_t;
|
||||
|
||||
/* Signed data callbacks */
|
||||
typedef void (*attcSignMsgCback_t)(attcCcb_t *pCcb, attcApiMsg_t *pMsg);
|
||||
typedef void (*attcCloseCback_t)(attcCcb_t *pCcb, uint8_t status);
|
||||
|
||||
/* Signed data callback interface */
|
||||
typedef struct
|
||||
{
|
||||
attcSignMsgCback_t msgCback; /* Message handling callback */
|
||||
attcCloseCback_t closeCback; /* Connection close callback */
|
||||
} attcSignFcnIf_t;
|
||||
|
||||
/* Main control block of the ATTC subsystem */
|
||||
typedef struct
|
||||
{
|
||||
attcCcb_t ccb[DM_CONN_MAX];
|
||||
attcSignFcnIf_t const *pSign;
|
||||
bool_t autoCnf;
|
||||
} attcCb_t;
|
||||
|
||||
/* type for response processing functions */
|
||||
typedef void (*attcProcRsp_t)(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
extern attcCb_t attcCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
attcCcb_t *attcCcbByConnId(dmConnId_t connId);
|
||||
attcCcb_t *attcCcbByHandle(uint16_t handle);
|
||||
void attcFreePkt(attcApiMsg_t *pMsg);
|
||||
void attcExecCallback(dmConnId_t connId, uint8_t event, uint16_t handle, uint8_t status);
|
||||
void attcReqClear(attcCcb_t *pCcb, attcApiMsg_t *pMsg, uint8_t status);
|
||||
|
||||
void attcSetupReq(attcCcb_t *pCcb, attcApiMsg_t *pMsg);
|
||||
void attcSendReq(attcCcb_t *pCcb);
|
||||
void attcSendMsg(dmConnId_t connId, uint16_t handle, uint8_t msgId, attcPktParam_t *pPkt, bool_t continuing);
|
||||
|
||||
void attcProcRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attcProcInd(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
|
||||
void attcProcErrRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcMtuRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcFindOrReadRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcFindByTypeRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcReadRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcReadLongRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcWriteRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcPrepWriteRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* ATTC_MAIN_H */
|
||||
Vendored
+699
@@ -0,0 +1,699 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client mandatory PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_math.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "attc_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Table of response processing functions */
|
||||
static const attcProcRsp_t attcProcRspTbl[] =
|
||||
{
|
||||
attcProcErrRsp, /* ATT_METHOD_ERR */
|
||||
attcProcMtuRsp, /* ATT_METHOD_MTU */
|
||||
attcProcFindOrReadRsp, /* ATT_METHOD_FIND_INFO */
|
||||
attcProcFindByTypeRsp, /* ATT_METHOD_FIND_TYPE */
|
||||
attcProcFindOrReadRsp, /* ATT_METHOD_READ_TYPE */
|
||||
attcProcReadRsp, /* ATT_METHOD_READ */
|
||||
attcProcReadLongRsp, /* ATT_METHOD_READ_BLOB */
|
||||
attcProcReadRsp, /* ATT_METHOD_READ_MULTIPLE */
|
||||
attcProcFindOrReadRsp, /* ATT_METHOD_READ_GROUP_TYPE */
|
||||
attcProcWriteRsp, /* ATT_METHOD_WRITE */
|
||||
NULL, /* ATT_METHOD_WRITE_CMD */
|
||||
attcProcPrepWriteRsp, /* ATT_METHOD_PREPARE_WRITE */
|
||||
attcProcWriteRsp /* ATT_METHOD_EXECUTE_WRITE */
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Error response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcErrRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
uint8_t *p;
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
|
||||
/* set callback event from stored method */
|
||||
pEvt->hdr.event = pCcb->outReq.hdr.event;
|
||||
|
||||
/* ignore request opcode in the error response */
|
||||
p++;
|
||||
|
||||
/* if request was a read or write with a specific handle */
|
||||
if (pEvt->hdr.event == ATTC_READ_RSP || pEvt->hdr.event == ATTC_READ_LONG_RSP ||
|
||||
pEvt->hdr.event == ATTC_WRITE_RSP || pEvt->hdr.event == ATTC_PREPARE_WRITE_RSP)
|
||||
{
|
||||
/* ignore handle in the error response; callback will use stored handle from request */
|
||||
p += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set handle from packet */
|
||||
BSTREAM_TO_UINT16(pEvt->handle, p);
|
||||
}
|
||||
|
||||
/* set status from error code in packet, but verify it's not 'success' */
|
||||
BSTREAM_TO_UINT8(pEvt->hdr.status, p);
|
||||
if (pEvt->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_UNDEFINED;
|
||||
}
|
||||
|
||||
/* no parameters so clear length */
|
||||
pEvt->valueLen = 0;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received MTU response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcMtuRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
uint16_t mtu;
|
||||
|
||||
BYTES_TO_UINT16(mtu, pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN);
|
||||
|
||||
/* verify */
|
||||
if (mtu < ATT_DEFAULT_MTU)
|
||||
{
|
||||
mtu = ATT_DEFAULT_MTU;
|
||||
}
|
||||
|
||||
/* set mtu for the connection */
|
||||
attSetMtu(pCcb->pMainCcb, mtu, WSF_MIN(pAttCfg->mtu, (HciGetMaxRxAclLen() - L2C_HDR_LEN)));
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Find Information, Read By Type, or Read By Group Type
|
||||
* response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcFindOrReadRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
uint8_t *p;
|
||||
uint8_t *pEnd;
|
||||
uint16_t handle;
|
||||
uint16_t nextHandle;
|
||||
uint16_t prevHandle;
|
||||
uint8_t paramLen;
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
pEnd = pPacket + L2C_PAYLOAD_START + len;
|
||||
|
||||
/* parameter length depends on packet type */
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_FIND_INFO)
|
||||
{
|
||||
/* length in find info response is coded by UUID */
|
||||
if (*p++ == ATT_FIND_HANDLE_16_UUID)
|
||||
{
|
||||
paramLen = ATT_16_UUID_LEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
paramLen = ATT_128_UUID_LEN;
|
||||
}
|
||||
}
|
||||
else if (pCcb->outReq.hdr.event == ATTC_MSG_API_READ_BY_TYPE)
|
||||
{
|
||||
/* length in read by type response is handle plus parameter length */
|
||||
paramLen = *p++ - sizeof(uint16_t);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* length in read by group type response is two handles plus parameter length */
|
||||
paramLen = *p++ - (2 * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
/* get and verify all handles */
|
||||
nextHandle = pCcb->outReqParams.h.startHandle;
|
||||
while (p < pEnd)
|
||||
{
|
||||
/* get and compare handle */
|
||||
BSTREAM_TO_UINT16(handle, p);
|
||||
if (handle == 0 || nextHandle == 0 || handle < nextHandle ||
|
||||
handle > pCcb->outReqParams.h.endHandle)
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_INVALID_RSP;
|
||||
break;
|
||||
}
|
||||
|
||||
/* if read by group type response get second handle */
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_READ_BY_GROUP_TYPE)
|
||||
{
|
||||
prevHandle = handle;
|
||||
BSTREAM_TO_UINT16(handle, p);
|
||||
if (handle == 0 || handle < prevHandle || handle < nextHandle ||
|
||||
handle > pCcb->outReqParams.h.endHandle)
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_INVALID_RSP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* set next expected handle, with special case for max handle */
|
||||
if (handle == ATT_HANDLE_MAX)
|
||||
{
|
||||
nextHandle = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextHandle = handle + 1;
|
||||
}
|
||||
|
||||
/* skip over parameter */
|
||||
p += paramLen;
|
||||
|
||||
/* check for truncated response */
|
||||
if (p > pEnd)
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_INVALID_RSP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if response was correct */
|
||||
if (pEvt->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* if all handles read */
|
||||
if (nextHandle == 0 || nextHandle == (pCcb->outReqParams.h.endHandle + 1))
|
||||
{
|
||||
/* we're done */
|
||||
pCcb->outReq.hdr.status = ATTC_NOT_CONTINUING;
|
||||
}
|
||||
/* else set up for next request */
|
||||
else
|
||||
{
|
||||
pCcb->outReqParams.h.startHandle = nextHandle;
|
||||
pCcb->outReq.handle = nextHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Read response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcReadRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
/* nothing to process */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Write response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcWriteRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
/* no parameters so clear length */
|
||||
pEvt->valueLen = 0;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
attEvt_t evt;
|
||||
|
||||
/* if no request in progress ignore response */
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* get method */
|
||||
evt.hdr.event = ATT_OPCODE_2_METHOD(*(pPacket + L2C_PAYLOAD_START));
|
||||
|
||||
/* if response method is not error and does not match stored method ignore response */
|
||||
if ((evt.hdr.event != ATT_METHOD_ERR) && (evt.hdr.event != pCcb->outReq.hdr.event))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* stop request timer */
|
||||
WsfTimerStop(&pCcb->outReqTimer);
|
||||
|
||||
/* initialize event structure then process response */
|
||||
evt.pValue = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
evt.valueLen = len - ATT_HDR_LEN;
|
||||
evt.handle = pCcb->outReq.handle;
|
||||
evt.hdr.status = ATT_SUCCESS;
|
||||
(*attcProcRspTbl[evt.hdr.event])(pCcb, len, pPacket, &evt);
|
||||
|
||||
/* if not continuing or status is not success */
|
||||
if ((pCcb->outReq.hdr.status == ATTC_NOT_CONTINUING) || (evt.hdr.status != ATT_SUCCESS))
|
||||
{
|
||||
/* we're not sending another request so clear the out req */
|
||||
pCcb->outReq.hdr.event = ATTC_MSG_API_NONE;
|
||||
attcFreePkt(&pCcb->outReq);
|
||||
}
|
||||
|
||||
/* call callback (if not mtu rsp) */
|
||||
if ((evt.hdr.event != ATT_METHOD_MTU) && attCb.cback)
|
||||
{
|
||||
/* set additional parameters and call callback */
|
||||
evt.continuing = pCcb->outReq.hdr.status; /* continuing flag */
|
||||
evt.hdr.param = pCcb->outReq.hdr.param; /* connId */
|
||||
(*attCb.cback)(&evt);
|
||||
}
|
||||
|
||||
/* if no flow control */
|
||||
if (!pCcb->flowDisabled)
|
||||
{
|
||||
/* if out req ready */
|
||||
if (pCcb->outReq.pPkt != NULL)
|
||||
{
|
||||
/* build and send request */
|
||||
attcSendReq(pCcb);
|
||||
}
|
||||
/* else if api is on deck */
|
||||
else if (pCcb->onDeck.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
/* set up and send request */
|
||||
attcSetupReq(pCcb, &pCcb->onDeck);
|
||||
|
||||
/* clear on deck */
|
||||
pCcb->onDeck.hdr.event = ATTC_MSG_API_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received indication or notification packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcInd(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
attEvt_t evt;
|
||||
uint8_t *p;
|
||||
uint8_t *pPkt;
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START;
|
||||
|
||||
/* parse packet and set callback event struct */
|
||||
evt.hdr.event = ATT_OPCODE_2_METHOD(*p++);
|
||||
BSTREAM_TO_UINT16(evt.handle, p);
|
||||
evt.pValue = p;
|
||||
evt.valueLen = len - ATT_HDR_LEN - sizeof(uint16_t);
|
||||
evt.hdr.param = pCcb->pMainCcb->connId;
|
||||
evt.hdr.status = ATT_SUCCESS;
|
||||
evt.continuing = FALSE;
|
||||
|
||||
/* verify handle and call callback */
|
||||
if ((evt.handle != 0) && attCb.cback)
|
||||
{
|
||||
(*attCb.cback)(&evt);
|
||||
}
|
||||
|
||||
/* if indication send confirm */
|
||||
if (attcCb.autoCnf && (evt.hdr.event == ATT_METHOD_VALUE_IND))
|
||||
{
|
||||
if (!pCcb->flowDisabled)
|
||||
{
|
||||
if ((pPkt = attMsgAlloc(ATT_VALUE_CNF_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
*(pPkt + L2C_PAYLOAD_START) = ATT_PDU_VALUE_CNF;
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, ATT_VALUE_CNF_LEN, pPkt);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* mark confirm as pending; will be sent when flow enabled or application sends it. */
|
||||
pCcb->cnfPending = TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Build and send a WSF message to ATTC.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param msgId Message ID.
|
||||
* \param pPkt Packet parameters.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcSendMsg(dmConnId_t connId, uint16_t handle, uint8_t msgId, attcPktParam_t *pPkt, bool_t continuing)
|
||||
{
|
||||
attcCcb_t *pCcb;
|
||||
uint16_t mtu;
|
||||
bool_t transTimedOut;
|
||||
|
||||
WsfTaskLock();
|
||||
|
||||
/* get CCB and verify connection still in use */
|
||||
if ((pCcb = attcCcbByConnId(connId)) != NULL)
|
||||
{
|
||||
/* get MTU size */
|
||||
mtu = pCcb->pMainCcb->mtu;
|
||||
transTimedOut = !!(pCcb->pMainCcb->control & ATT_CCB_STATUS_TX_TIMEOUT);
|
||||
}
|
||||
/* else connection not in use */
|
||||
else
|
||||
{
|
||||
/* MTU size unknown */
|
||||
mtu = 0;
|
||||
transTimedOut = FALSE;
|
||||
}
|
||||
|
||||
WsfTaskUnlock();
|
||||
|
||||
/* if MTU size known for connection */
|
||||
if (mtu > 0)
|
||||
{
|
||||
/* if no transaction's timed out */
|
||||
if (!transTimedOut)
|
||||
{
|
||||
uint16_t dataLen = 0;
|
||||
|
||||
/* if packet is not null then find out its length */
|
||||
if (pPkt != NULL)
|
||||
{
|
||||
/* if not prepare write request */
|
||||
if (msgId != ATTC_MSG_API_PREP_WRITE)
|
||||
{
|
||||
dataLen = pPkt->len;
|
||||
}
|
||||
/* else prepare write request */
|
||||
else
|
||||
{
|
||||
/* if not continuing */
|
||||
if (!continuing)
|
||||
{
|
||||
/* single prepare write request */
|
||||
dataLen = ATT_PREP_WRITE_REQ_LEN + pPkt->w.len;
|
||||
}
|
||||
/* else will be sent as multiple prepare write requests */
|
||||
}
|
||||
}
|
||||
|
||||
/* if packet length is less than or equal to negotiated MTU */
|
||||
if (dataLen <= mtu)
|
||||
{
|
||||
attcApiMsg_t *pMsg;
|
||||
|
||||
/* allocate message buffer */
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(attcApiMsg_t))) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->hdr.status = continuing;
|
||||
pMsg->hdr.event = msgId;
|
||||
pMsg->pPkt = pPkt;
|
||||
pMsg->handle = handle;
|
||||
|
||||
/* send message */
|
||||
WsfMsgSend(attCb.handlerId, pMsg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* else packet length exceeds MTU size */
|
||||
else
|
||||
{
|
||||
/* call callback with failure status */
|
||||
attcExecCallback(connId, msgId, handle, ATT_ERR_MTU_EXCEEDED);
|
||||
}
|
||||
}
|
||||
else
|
||||
/* transaction's timed out */
|
||||
{
|
||||
/* call callback with failure status */
|
||||
attcExecCallback(connId, msgId, handle, ATT_ERR_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/* alloc failed, transaction's timed out or packet length exceeded MTU size; free packet buffer */
|
||||
if (pPkt != NULL)
|
||||
{
|
||||
WsfMsgFree(pPkt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Find Information Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param startHandle Attribute start handle.
|
||||
* \param endHandle Attribute end handle.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcFindInfoReq(dmConnId_t connId, uint16_t startHandle, uint16_t endHandle, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_FIND_INFO_REQ_BUF_LEN)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->len = ATT_FIND_INFO_REQ_LEN;
|
||||
pPkt->h.startHandle = startHandle;
|
||||
pPkt->h.endHandle = endHandle;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_FIND_INFO_REQ);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, startHandle, ATTC_MSG_API_FIND_INFO, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Read Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcReadReq(dmConnId_t connId, uint16_t handle)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_READ_REQ_BUF_LEN)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_READ_REQ_LEN;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_REQ);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_READ, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Write Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcWriteReq(dmConnId_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_WRITE_REQ_BUF_LEN + valueLen)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_WRITE_REQ_LEN + valueLen;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_WRITE_REQ);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pValue, valueLen);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_WRITE, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Cancel an attribute protocol request in progress.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcCancelReq(dmConnId_t connId)
|
||||
{
|
||||
attcSendMsg(connId, 0, ATTC_MSG_API_CANCEL, NULL, FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For internal use only.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param mtu Attribute protocol MTU.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcMtuReq(dmConnId_t connId, uint16_t mtu)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_MTU_REQ_BUF_LEN)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_MTU_REQ_LEN;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_MTU_REQ);
|
||||
UINT16_TO_BSTREAM(p, mtu);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, 0, ATTC_MSG_API_MTU, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol indication confirmation.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcIndConfirm(dmConnId_t connId)
|
||||
{
|
||||
attcCcb_t *pCcb;
|
||||
uint8_t *pPkt;
|
||||
|
||||
pCcb = attcCcbByHandle(connId - 1);
|
||||
|
||||
/* If confirmation is pending */
|
||||
if (pCcb && pCcb->cnfPending && !pCcb->flowDisabled)
|
||||
{
|
||||
if ((pPkt = attMsgAlloc(ATT_VALUE_CNF_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
pCcb->cnfPending = FALSE;
|
||||
|
||||
*(pPkt + L2C_PAYLOAD_START) = ATT_PDU_VALUE_CNF;
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, ATT_VALUE_CNF_LEN, pPkt);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+347
@@ -0,0 +1,347 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client optional read PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "attc_main.h"
|
||||
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Find By Type response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcFindByTypeRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
uint8_t *p;
|
||||
uint8_t *pEnd;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
uint16_t nextHandle;
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
pEnd = pPacket + L2C_PAYLOAD_START + len;
|
||||
|
||||
/* get and verify all handles */
|
||||
nextHandle = pCcb->outReqParams.h.startHandle;
|
||||
while (p < pEnd)
|
||||
{
|
||||
/* get handle pair */
|
||||
BSTREAM_TO_UINT16(startHandle, p);
|
||||
BSTREAM_TO_UINT16(endHandle, p);
|
||||
|
||||
/*
|
||||
* start handle of handle pair must be:
|
||||
* not greater than end handle of handle pair
|
||||
* not less than than start handle of request or end handle of previous handle pair
|
||||
* not greater than end handle of request
|
||||
* and no additional handle pairs following end handle = 0xFFFF
|
||||
*/
|
||||
if ((startHandle > endHandle) || (startHandle < nextHandle) ||
|
||||
(startHandle > pCcb->outReqParams.h.endHandle) || (nextHandle == 0))
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_INVALID_RSP;
|
||||
break;
|
||||
}
|
||||
|
||||
/* set next expected handle, with special case for max handle */
|
||||
if (endHandle == ATT_HANDLE_MAX)
|
||||
{
|
||||
nextHandle = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextHandle = endHandle + 1;
|
||||
}
|
||||
|
||||
/* check for truncated response */
|
||||
if (p > pEnd)
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_INVALID_RSP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if response was correct */
|
||||
if (pEvt->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* if all handles read */
|
||||
if (nextHandle == 0 || nextHandle > pCcb->outReqParams.h.endHandle)
|
||||
{
|
||||
/* we're done */
|
||||
pCcb->outReq.hdr.status = ATTC_NOT_CONTINUING;
|
||||
}
|
||||
/* else set up for next request */
|
||||
else
|
||||
{
|
||||
pCcb->outReqParams.h.startHandle = nextHandle;
|
||||
pCcb->outReq.handle = nextHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Read Long response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcReadLongRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* length of response is less than mtu */
|
||||
if (len < pCcb->pMainCcb->mtu)
|
||||
{
|
||||
/* we're done */
|
||||
pCcb->outReq.hdr.status = ATTC_NOT_CONTINUING;
|
||||
}
|
||||
/* else set up for next request */
|
||||
else
|
||||
{
|
||||
pCcb->outReqParams.o.offset += pEvt->valueLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Find By Type Value Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param startHandle Attribute start handle.
|
||||
* \param endHandle Attribute end handle.
|
||||
* \param uuid16 16-bit UUID to find.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcFindByTypeValueReq(dmConnId_t connId, uint16_t startHandle, uint16_t endHandle,
|
||||
uint16_t uuid16, uint16_t valueLen, uint8_t *pValue, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_FIND_TYPE_REQ_BUF_LEN + valueLen)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->len = ATT_FIND_TYPE_REQ_LEN + valueLen;
|
||||
pPkt->h.startHandle = startHandle;
|
||||
pPkt->h.endHandle = endHandle;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_FIND_TYPE_REQ);
|
||||
/* skip start and end handle fields */
|
||||
p += (2 * sizeof(uint16_t));
|
||||
UINT16_TO_BSTREAM(p, uuid16);
|
||||
memcpy(p, pValue, valueLen);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, startHandle, ATTC_MSG_API_FIND_BY_TYPE_VALUE, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Read By Type Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param startHandle Attribute start handle.
|
||||
* \param endHandle Attribute end handle.
|
||||
* \param uuidLen Length of UUID (2 or 16).
|
||||
* \param pUuid Pointer to UUID data.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcReadByTypeReq(dmConnId_t connId, uint16_t startHandle, uint16_t endHandle,
|
||||
uint8_t uuidLen, uint8_t *pUuid, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_READ_TYPE_REQ_BUF_LEN + uuidLen)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->len = ATT_READ_TYPE_REQ_LEN + uuidLen;
|
||||
pPkt->h.startHandle = startHandle;
|
||||
pPkt->h.endHandle = endHandle;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_TYPE_REQ);
|
||||
/* skip start and end handle fields */
|
||||
p += (2 * sizeof(uint16_t));
|
||||
memcpy(p, pUuid, uuidLen);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, startHandle, ATTC_MSG_API_READ_BY_TYPE, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Read Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param offset Read attribute data starting at this offset.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcReadLongReq(dmConnId_t connId, uint16_t handle, uint16_t offset, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_READ_BLOB_REQ_BUF_LEN)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->len = ATT_READ_BLOB_REQ_LEN;
|
||||
pPkt->o.offset = offset;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_BLOB_REQ);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_READ_LONG, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Read Multiple Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param numHandles Number of handles in attribute handle list.
|
||||
* \param pHandles List of attribute handles.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcReadMultipleReq(dmConnId_t connId, uint8_t numHandles, uint16_t *pHandles)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
uint16_t handle;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_READ_MULT_REQ_BUF_LEN + (numHandles * sizeof(uint16_t)))) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_READ_MULT_REQ_LEN + (numHandles * sizeof(uint16_t));
|
||||
|
||||
/* save first handle */
|
||||
handle = pHandles[0];
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_MULT_REQ);
|
||||
while (numHandles--)
|
||||
{
|
||||
UINT16_TO_BSTREAM(p, *pHandles);
|
||||
pHandles++;
|
||||
}
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_READ_MULTIPLE, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Read By Group Type Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param startHandle Attribute start handle.
|
||||
* \param endHandle Attribute end handle.
|
||||
* \param uuidLen Length of UUID (2 or 16).
|
||||
* \param pUuid Pointer to UUID data.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcReadByGroupTypeReq(dmConnId_t connId, uint16_t startHandle, uint16_t endHandle,
|
||||
uint8_t uuidLen, uint8_t *pUuid, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_READ_GROUP_TYPE_REQ_BUF_LEN + uuidLen)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->len = ATT_READ_GROUP_TYPE_REQ_LEN + uuidLen;
|
||||
pPkt->h.startHandle = startHandle;
|
||||
pPkt->h.endHandle = endHandle;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_GROUP_TYPE_REQ);
|
||||
/* skip start and end handle fields */
|
||||
p += (2 * sizeof(uint16_t));
|
||||
memcpy(p, pUuid, uuidLen);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, startHandle, ATTC_MSG_API_READ_BY_GROUP_TYPE, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
Vendored
+303
@@ -0,0 +1,303 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client optional signed PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "sec_api.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/calc128.h"
|
||||
#include "util/wstr.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "attc_main.h"
|
||||
#include "att_sign.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* ATTC signed PDU control block */
|
||||
typedef struct
|
||||
{
|
||||
attcApiMsg_t msg;
|
||||
} attcSignCb_t;
|
||||
|
||||
/* Message parameters */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
attcApiMsg_t api;
|
||||
} attcSignMsg_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
static void attcSignCloseCback(attcCcb_t *pCcb, uint8_t status);
|
||||
static void attcSignMsgCback(attcCcb_t *pCcb, attcSignMsg_t *pMsg);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Interface to ATT */
|
||||
static const attcSignFcnIf_t attcSignFcnIf =
|
||||
{
|
||||
(attcSignMsgCback_t) attcSignMsgCback,
|
||||
attcSignCloseCback
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
static attcSignCb_t *pAttcSignCb[DM_CONN_MAX];
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate a signing control block.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to control block.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static attcSignCb_t *attcSignCbAlloc(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
WSF_ASSERT(pAttcSignCb[connId - 1] == NULL);
|
||||
|
||||
return (pAttcSignCb[connId - 1] = WsfBufAlloc(sizeof(attcSignCb_t)));
|
||||
}
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Free a signing control block.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSignCbFree(dmConnId_t connId)
|
||||
{
|
||||
WsfBufFree(pAttcSignCb[connId - 1]);
|
||||
pAttcSignCb[connId - 1] = NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the signing control block for the connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to control block.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static attcSignCb_t *attcSignCbByConnId(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return pAttcSignCb[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection close callback for ATTC signed PDU processing.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param status Connection close status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSignCloseCback(attcCcb_t *pCcb, uint8_t status)
|
||||
{
|
||||
attcSignCb_t *pCb;
|
||||
|
||||
/* if connection closed while processing in progress */
|
||||
if ((pCb = attcSignCbByConnId(pCcb->pMainCcb->connId)) != NULL)
|
||||
{
|
||||
|
||||
/* message free buffer */
|
||||
if (pCb->msg.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
attcReqClear(pCcb, &pCb->msg, status);
|
||||
}
|
||||
|
||||
attcSignCbFree(pCcb->pMainCcb->connId);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Message handler callback for ATTC signed PDU processing.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param pMsg ATTC message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSignMsgCback(attcCcb_t *pCcb, attcSignMsg_t *pMsg)
|
||||
{
|
||||
attcSignCb_t *pCb;
|
||||
|
||||
if (pMsg->hdr.event == ATTC_MSG_API_SIGNED_WRITE_CMD)
|
||||
{
|
||||
/* adjust msg event id for later use with app callback */
|
||||
pMsg->hdr.event = ATTC_MSG_API_WRITE_CMD;
|
||||
|
||||
/* verify no API request already waiting on deck or in progress,
|
||||
* and no signed write already in progress
|
||||
*/
|
||||
if ((pCcb->onDeck.hdr.event != ATTC_MSG_API_NONE) ||
|
||||
(pCcb->outReq.hdr.event > ATTC_MSG_API_MTU) ||
|
||||
(attcSignCbByConnId((dmConnId_t) pMsg->hdr.param) != NULL))
|
||||
{
|
||||
/* free request and call callback with failure status */
|
||||
attcReqClear(pCcb, &pMsg->api, ATT_ERR_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate signing control block */
|
||||
if ((pCb = attcSignCbAlloc((dmConnId_t) pMsg->hdr.param)) != NULL)
|
||||
{
|
||||
/* store message */
|
||||
pCb->msg = pMsg->api;
|
||||
|
||||
uint16_t cmacTxtLen = pCb->msg.pPkt->len - ATT_AUTH_SIG_LEN + sizeof(uint32_t);
|
||||
uint8_t *pCmacText = WsfBufAlloc(cmacTxtLen);
|
||||
|
||||
if (pCmacText)
|
||||
{
|
||||
uint8_t revLocalCsrk[SEC_CMAC_KEY_LEN] = {0};
|
||||
|
||||
WStrReverseCpy(revLocalCsrk, DmSecGetLocalCsrk(), SEC_CMAC_KEY_LEN);
|
||||
WStrReverseCpy(pCmacText, (uint8_t *)pCb->msg.pPkt + L2C_PAYLOAD_START, cmacTxtLen);
|
||||
|
||||
if (SecCmac(revLocalCsrk, pCmacText, cmacTxtLen, attCb.handlerId,
|
||||
(dmConnId_t) pMsg->hdr.param, ATTC_MSG_CMAC_CMPL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
WsfBufFree(pCmacText);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* allocation failure */
|
||||
attcReqClear(pCcb, &pMsg->api, ATT_ERR_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (pMsg->hdr.event == ATTC_MSG_CMAC_CMPL)
|
||||
{
|
||||
secCmacMsg_t *pCmacMsg = (secCmacMsg_t*) pMsg;
|
||||
WsfBufFree(pCmacMsg->pPlainText);
|
||||
|
||||
if ((pCb = attcSignCbByConnId((dmConnId_t) pMsg->hdr.param)) != NULL)
|
||||
{
|
||||
/* append signature to packet */
|
||||
WStrReverseCpy((uint8_t *) pCb->msg.pPkt +
|
||||
(L2C_PAYLOAD_START + pCb->msg.pPkt->len - ATT_CMAC_RESULT_LEN),
|
||||
pCmacMsg->pCiphertext, ATT_CMAC_RESULT_LEN);
|
||||
|
||||
/* if MTU request in progress or flow controlled */
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_MTU || pCcb->flowDisabled)
|
||||
{
|
||||
/* put request "on deck" for processing later */
|
||||
pCcb->onDeck = pCb->msg;
|
||||
}
|
||||
/* otherwise ready to send */
|
||||
else
|
||||
{
|
||||
attcSetupReq(pCcb, &pCb->msg);
|
||||
}
|
||||
|
||||
/* we're done-- free control block */
|
||||
attcSignCbFree((dmConnId_t) pMsg->hdr.param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol signed Write Command.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param signCounter Value of the sign counter.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcSignedWriteCmd(dmConnId_t connId, uint16_t handle, uint32_t signCounter,
|
||||
uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* if connection already encrypted with security mode 1 level 2 or higher */
|
||||
if (DmConnSecLevel(connId) > DM_SEC_LEVEL_NONE)
|
||||
{
|
||||
/* use write command instead */
|
||||
AttcWriteCmd(connId, handle, valueLen, pValue);
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_SIGNED_WRITE_CMD_BUF_LEN + valueLen)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_SIGNED_WRITE_CMD_LEN + valueLen;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_SIGNED_WRITE_CMD);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pValue, valueLen);
|
||||
p += valueLen;
|
||||
UINT32_TO_BSTREAM(p, signCounter);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_SIGNED_WRITE_CMD, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATT client for data signing.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcSignInit(void)
|
||||
{
|
||||
/* set up callback interface */
|
||||
attcCb.pSign = &attcSignFcnIf;
|
||||
}
|
||||
Vendored
+189
@@ -0,0 +1,189 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client optional write PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "attc_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Prepare Write response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcPrepWriteRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* if no more data to send */
|
||||
if (pCcb->outReqParams.w.len == 0)
|
||||
{
|
||||
/* we're done */
|
||||
pCcb->outReq.hdr.status = ATTC_NOT_CONTINUING;
|
||||
}
|
||||
}
|
||||
|
||||
/* adjust attribute value and its length (adjusted by ATT header length already) */
|
||||
pEvt->pValue += (ATT_PREP_WRITE_RSP_LEN - ATT_HDR_LEN);
|
||||
pEvt->valueLen -= (ATT_PREP_WRITE_RSP_LEN - ATT_HDR_LEN);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Write Command.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcWriteCmd(dmConnId_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_WRITE_CMD_BUF_LEN + valueLen)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_WRITE_CMD_LEN + valueLen;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_WRITE_CMD);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pValue, valueLen);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_WRITE_CMD, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Prepare Write Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param offset Write attribute data starting at this offset.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
* \param valueByRef TRUE if pValue data is accessed by reference rather than copied.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcPrepareWriteReq(dmConnId_t connId, uint16_t handle, uint16_t offset, uint16_t valueLen,
|
||||
uint8_t *pValue, bool_t valueByRef, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
uint16_t bufLen;
|
||||
|
||||
if (continuing && valueByRef)
|
||||
{
|
||||
bufLen = ATT_PREP_WRITE_REQ_BUF_LEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
bufLen = ATT_PREP_WRITE_REQ_BUF_LEN + valueLen;
|
||||
}
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(bufLen)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->w.len = valueLen;
|
||||
pPkt->w.offset = offset;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_PREP_WRITE_REQ);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
/* skip over offset field */
|
||||
p += sizeof(uint16_t);
|
||||
|
||||
/* set value pointer and copy data to packet, if not valueByRef */
|
||||
if (continuing && valueByRef)
|
||||
{
|
||||
pPkt->w.pValue = pValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(p, pValue, valueLen);
|
||||
pPkt->w.pValue = p;
|
||||
}
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_PREP_WRITE, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Execute Write Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param writeAll TRUE to write all queued writes, FALSE to cancel all queued writes.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcExecuteWriteReq(dmConnId_t connId, bool_t writeAll)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_EXEC_WRITE_REQ_BUF_LEN)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_EXEC_WRITE_REQ_LEN;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_EXEC_WRITE_REQ);
|
||||
UINT8_TO_BSTREAM(p, writeAll);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, 0, ATTC_MSG_API_EXEC_WRITE, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
+457
@@ -0,0 +1,457 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client characteristic configuration module.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "util/bstream.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t *pCccTbl[DM_CONN_MAX]; /* Pointer to descriptor value tables */
|
||||
attsCccSet_t *pSet; /* Array of CCC descriptor settings */
|
||||
attsCccCback_t cback; /* Client callback function */
|
||||
uint8_t setLen; /* Length of settings array */
|
||||
} AttsCccCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
static AttsCccCb_t attsCccCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute the client callback function.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param idx Index of descriptor in CCC descriptor handle table.
|
||||
* \param handle Attribute handle of the descriptor.
|
||||
* \param value Attribute value of the descriptor.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsCccCback(dmConnId_t connId, uint8_t idx, uint16_t handle, uint16_t value)
|
||||
{
|
||||
attsCccEvt_t evt;
|
||||
|
||||
evt.hdr.event = ATTS_CCC_STATE_IND;
|
||||
evt.hdr.param = connId;
|
||||
evt.idx = idx;
|
||||
evt.handle = handle;
|
||||
evt.value = value;
|
||||
|
||||
(*attsCccCb.cback)(&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate the CCC table for this connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return Pointer into the CCC table.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint16_t *attsCccAllocTbl(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
/* if not already allocated */
|
||||
if (attsCccCb.pCccTbl[connId - 1] == NULL)
|
||||
{
|
||||
WSF_ASSERT(attsCccCb.setLen > 0);
|
||||
|
||||
/* allocate new buffer */
|
||||
attsCccCb.pCccTbl[connId - 1] = WsfBufAlloc(attsCccCb.setLen * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
return attsCccCb.pCccTbl[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the pointer into the CCC table for this connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return Pointer into the CCC table.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint16_t *attsCccGetTbl(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return attsCccCb.pCccTbl[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Free the CCC table for this connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsCccFreeTbl(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
if (attsCccCb.pCccTbl[connId - 1] != NULL)
|
||||
{
|
||||
WsfBufFree(attsCccCb.pCccTbl[connId - 1]);
|
||||
attsCccCb.pCccTbl[connId - 1] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read the value of a client characteristic configuration descriptor. Note the
|
||||
* value is treated as a little endian byte array.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle of the descriptor.
|
||||
* \param pValue The attribute value of the descriptor is copied to this pointer.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attsCccReadValue(dmConnId_t connId, uint16_t handle, uint8_t *pValue)
|
||||
{
|
||||
attsCccSet_t *pSet;
|
||||
uint16_t *pTbl;
|
||||
uint8_t i;
|
||||
|
||||
/* find handle in handle array */
|
||||
for (pSet = attsCccCb.pSet, i = 0; i < attsCccCb.setLen; i++, pSet++)
|
||||
{
|
||||
if (pSet->handle == handle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if handle not found return error */
|
||||
if (i == attsCccCb.setLen)
|
||||
{
|
||||
return ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* get pointer to the table for this connection */
|
||||
if ((pTbl = attsCccGetTbl(connId)) != NULL)
|
||||
{
|
||||
/* read value */
|
||||
UINT16_TO_BSTREAM(pValue, pTbl[i]);
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Write the value of a client characteristic configuration descriptor. Note the
|
||||
* value is treated as a little endian byte array.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle of the descriptor.
|
||||
* \param pValue Pointer to the attribute value of the descriptor.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attsCccWriteValue(dmConnId_t connId, uint16_t handle, uint8_t *pValue)
|
||||
{
|
||||
attsCccSet_t *pSet;
|
||||
uint16_t *pTbl;
|
||||
uint8_t i;
|
||||
uint16_t value;
|
||||
uint16_t prevValue;
|
||||
|
||||
/* find handle in handle array */
|
||||
for (pSet = attsCccCb.pSet, i = 0; i < attsCccCb.setLen; i++, pSet++)
|
||||
{
|
||||
if (pSet->handle == handle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if handle not found return error */
|
||||
if (i == attsCccCb.setLen)
|
||||
{
|
||||
return ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
BYTES_TO_UINT16(value, pValue);
|
||||
|
||||
/* verify value range */
|
||||
if (((value != 0) && (value != ATT_CLIENT_CFG_NOTIFY) && (value != ATT_CLIENT_CFG_INDICATE)) ||
|
||||
((value != 0) && ((value & pSet->valueRange) == 0)))
|
||||
{
|
||||
return ATT_ERR_VALUE_RANGE;
|
||||
}
|
||||
|
||||
/* get pointer to the table for this connection */
|
||||
if ((pTbl = attsCccGetTbl(connId)) != NULL)
|
||||
{
|
||||
/* write value */
|
||||
prevValue = pTbl[i];
|
||||
pTbl[i] = value;
|
||||
|
||||
/* if value changed call callback */
|
||||
if (prevValue != value)
|
||||
{
|
||||
attsCccCback(connId, i, handle, value);
|
||||
}
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief CCC callback function executed by ATTS and CCC read or write.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param method Read or write.
|
||||
* \param handle Attribute handle of the descriptor.
|
||||
* \param pValue The attribute value of the descriptor is copied to this pointer.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attsCccMainCback(dmConnId_t connId, uint8_t method, uint16_t handle, uint8_t *pValue)
|
||||
{
|
||||
ATT_TRACE_INFO2("attsCccMainCback connId=%d handle=%d", connId, handle);
|
||||
|
||||
if (method == ATT_METHOD_READ)
|
||||
{
|
||||
return attsCccReadValue(connId, handle, pValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return attsCccWriteValue(connId, handle, pValue);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register the utility service for managing client characteristic
|
||||
* configuration descriptors. This function is typically called once on
|
||||
* system initialization.
|
||||
*
|
||||
* \param setLen Length of settings array.
|
||||
* \param pSet Array of CCC descriptor settings.
|
||||
* \param cback Client callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCccRegister(uint8_t setLen, attsCccSet_t *pSet, attsCccCback_t cback)
|
||||
{
|
||||
attsCccCb.setLen = setLen;
|
||||
attsCccCb.pSet = pSet;
|
||||
attsCccCb.cback = cback;
|
||||
|
||||
attsCb.cccCback = attsCccMainCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the client characteristic configuration descriptor value table for a
|
||||
* connection. This function is typically called when a connection is established
|
||||
* or when a device is bonded.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCccTbl Pointer to the descriptor value array. The length of the array
|
||||
* must equal the value of setLen passed to AttsCccRegister().
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCccInitTable(dmConnId_t connId, uint16_t *pCccTbl)
|
||||
{
|
||||
uint8_t i;
|
||||
uint16_t *pTbl;
|
||||
|
||||
ATT_TRACE_INFO1("AttsCccInitTable connId=%d", connId);
|
||||
|
||||
if ((pTbl = attsCccAllocTbl(connId)) != NULL)
|
||||
{
|
||||
/* if initializer table is passed in */
|
||||
if (pCccTbl != NULL)
|
||||
{
|
||||
/* initialize table */
|
||||
for (i = 0; i < attsCccCb.setLen; i++, pCccTbl++, pTbl++)
|
||||
{
|
||||
/* copy value */
|
||||
*pTbl = *pCccTbl;
|
||||
|
||||
/* execute callback for each nonzero entry in table */
|
||||
if (*pCccTbl != 0)
|
||||
{
|
||||
attsCccCback(connId, i, ATT_HANDLE_NONE, *pCccTbl);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* initialize table to zero */
|
||||
memset(pTbl, 0, (sizeof(uint16_t) * attsCccCb.setLen));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear and deallocate the client characteristic configuration descriptor value
|
||||
* table for a connection. This function must be called when a connection is closed.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCccClearTable(dmConnId_t connId)
|
||||
{
|
||||
ATT_TRACE_INFO1("AttsCccClearTable connId=%d", connId);
|
||||
|
||||
attsCccFreeTbl(connId);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the value of a client characteristic configuration descriptor by its index.
|
||||
* If not found, return zero.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param idx Index of descriptor in CCC descriptor handle table.
|
||||
*
|
||||
* \return Value of the descriptor.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t AttsCccGet(dmConnId_t connId, uint8_t idx)
|
||||
{
|
||||
uint16_t *pTbl;
|
||||
|
||||
WSF_ASSERT(idx < attsCccCb.setLen);
|
||||
|
||||
if ((pTbl = attsCccGetTbl(connId)) != NULL)
|
||||
{
|
||||
/* return value from table */
|
||||
return pTbl[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the value of a client characteristic configuration descriptor by its index.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param idx Index of descriptor in CCC descriptor handle table.
|
||||
* \param value Value of the descriptor.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCccSet(dmConnId_t connId, uint8_t idx, uint16_t value)
|
||||
{
|
||||
uint16_t *pTbl;
|
||||
|
||||
WSF_ASSERT(idx < attsCccCb.setLen);
|
||||
|
||||
if ((pTbl = attsCccGetTbl(connId)) != NULL)
|
||||
{
|
||||
pTbl[idx] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if a client characteristic configuration descriptor is enabled and if
|
||||
* the characteristic's security level has been met.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param idx Index of descriptor in CCC descriptor handle table.
|
||||
*
|
||||
* \return Value of the descriptor if security level is met, otherwise zero.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t AttsCccEnabled(dmConnId_t connId, uint8_t idx)
|
||||
{
|
||||
WSF_ASSERT(idx < attsCccCb.setLen);
|
||||
|
||||
/* check security level */
|
||||
if (DmConnSecLevel(connId) < attsCccCb.pSet[idx].secLevel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get value */
|
||||
return AttsCccGet(connId, idx);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get number of CCC entries in table.
|
||||
*
|
||||
* \param None
|
||||
*
|
||||
* \return Number of CCC entries in table.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttsGetCccTableLen(void)
|
||||
{
|
||||
return attsCccCb.setLen;
|
||||
}
|
||||
+412
@@ -0,0 +1,412 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client supported features module.
|
||||
*
|
||||
* Copyright (c) 2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
#include "util/bstream.h"
|
||||
#include "svc_core.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
attsCsfCb_t attsCsfCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set status of on-going database hash update operation.
|
||||
*
|
||||
* \param isUpdating \ref TRUE is updating, otherwise \ref FALSE;
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsCsfSetHashUpdateStatus(bool_t isUpdating)
|
||||
{
|
||||
if (attsCsfCb.isHashUpdating == isUpdating)
|
||||
{
|
||||
/* Already in the current state, nothing to do. */
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Update state. */
|
||||
attsCsfCb.isHashUpdating = isUpdating;
|
||||
}
|
||||
|
||||
/* Update complete.
|
||||
* Check if clients were pending on the hash value and fulfill their requests.
|
||||
*/
|
||||
if (isUpdating == FALSE)
|
||||
{
|
||||
ATT_TRACE_INFO0("Database hash calculation complete");
|
||||
|
||||
attsCheckPendDbHashReadRsp();
|
||||
|
||||
/* Note: Clients which were pending on a Database Hash read from a Read by Type Request are not
|
||||
* transitioned to the change-aware state here. The application is expected to initiate the
|
||||
* state transition of all clients when the new hash is set. If this is not done, the
|
||||
* state of pending Clients will be out of sync, and will be corrected on the next database
|
||||
* sync.
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_INFO0("Calculating database hash");
|
||||
|
||||
/* If the application, for whatever reason, previously recalculated the database hash over an
|
||||
* unchanged database and a client pended on a Read By Type Request of the database hash, then
|
||||
* that clients state may be out of step if the application did not initiate a state
|
||||
* transition. That state transition is forced here to keep handle next transition.
|
||||
*/
|
||||
for (uint8_t i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
if (attsCsfCb.attsCsfTable[i].changeAwareState == ATTS_CLIENT_CHANGE_AWARE_DB_READ_PENDING)
|
||||
{
|
||||
attsCsfCb.attsCsfTable[i].changeAwareState = ATTS_CLIENT_CHANGE_PENDING_AWARE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if database hash update is in progress.
|
||||
*
|
||||
* \return \ref TRUE if update in progress, \ref FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t attsCsfGetHashUpdateStatus(void)
|
||||
{
|
||||
return attsCsfCb.isHashUpdating;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check client awareness to database hash before sending notification or indication.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle ATT handle.
|
||||
*
|
||||
* \return \ref TRUE if client is aware, otherwise \ref FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t attsCsfIsClientChangeAware(dmConnId_t connId, uint16_t handle)
|
||||
{
|
||||
if ((attsCsfCb.attsCsfTable[connId - 1].csf & ATTS_CSF_ROBUST_CACHING) &&
|
||||
(attsCsfCb.attsCsfTable[connId - 1].changeAwareState == ATTS_CLIENT_CHANGE_UNAWARE) &&
|
||||
(handle != GATT_SC_HDL))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Update client change-aware state based on protocol event.
|
||||
*
|
||||
* \param connId Connection handle.
|
||||
* \param opcode ATT PDU type.
|
||||
* \param pPacket Data packet from L2CAP.
|
||||
*
|
||||
* \return \ref ATT_SUCCESS if client is change-aware, else \ref ATT_ERR_DATABASE_OUT_OF_SYNC.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t attsCsfActClientState(uint16_t handle, uint8_t opcode, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
attsCsfRec_t *pRec;
|
||||
|
||||
/* PDU which do not operate on att handles are handled agnostically of the client's state. */
|
||||
if (opcode == ATT_PDU_MTU_REQ || opcode == ATT_PDU_VALUE_CNF)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
pRec = &attsCsfCb.attsCsfTable[handle];
|
||||
|
||||
/* If the client is change-unaware */
|
||||
if (pRec->changeAwareState == ATTS_CLIENT_CHANGE_UNAWARE)
|
||||
{
|
||||
/* If not a command */
|
||||
if ((opcode & ATT_PDU_MASK_COMMAND) == 0)
|
||||
{
|
||||
/* Note: there is no need to call back to the application here. The application only
|
||||
* needs to know when a transition to or from the change-aware state occurs.
|
||||
*/
|
||||
|
||||
/* Move client change-aware state to pending */
|
||||
pRec->changeAwareState = ATTS_CLIENT_CHANGE_PENDING_AWARE;
|
||||
|
||||
ATT_TRACE_INFO2("ConnId %d change aware state is %d", handle + 1,
|
||||
ATTS_CLIENT_CHANGE_PENDING_AWARE);
|
||||
}
|
||||
|
||||
/* If this is a command or the Client has indicated Robust Caching, set an error so that
|
||||
* this command or request is not processed.
|
||||
*/
|
||||
if ((opcode & ATT_PDU_MASK_COMMAND) ||
|
||||
(pRec->csf & ATTS_CSF_ROBUST_CACHING))
|
||||
{
|
||||
/* return a database out of sync error */
|
||||
err = ATT_ERR_DATABASE_OUT_OF_SYNC;
|
||||
}
|
||||
}
|
||||
else if (pRec->changeAwareState == ATTS_CLIENT_CHANGE_PENDING_AWARE)
|
||||
{
|
||||
/* If not a command */
|
||||
if ((opcode & ATT_PDU_MASK_COMMAND) == 0)
|
||||
{
|
||||
/* Move client change-aware state to aware */
|
||||
pRec->changeAwareState = ATTS_CLIENT_CHANGE_AWARE;
|
||||
|
||||
ATT_TRACE_INFO2("ConnId %d change aware state is %d", handle + 1, ATTS_CLIENT_CHANGE_AWARE);
|
||||
|
||||
/* Callback to application to store updated awareness, if bonded. */
|
||||
if (attsCsfCb.writeCback != NULL)
|
||||
{
|
||||
attsCsfCb.writeCback(handle + 1, pRec->changeAwareState, &pRec->csf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Return an error so that command is not processed. */
|
||||
err = ATT_ERR_DATABASE_OUT_OF_SYNC;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this is Read by Type request */
|
||||
if (opcode == ATT_PDU_READ_TYPE_REQ)
|
||||
{
|
||||
uint16_t uuid;
|
||||
|
||||
/* Extract UUID: Skip L2C, ATT Header and 4 byte handle range */
|
||||
BYTES_TO_UINT16(uuid, (pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN + 4));
|
||||
|
||||
/* If this is a Read By Type Request of the Database Hash characteristic value */
|
||||
if (uuid == ATT_UUID_DATABASE_HASH)
|
||||
{
|
||||
err = ATT_SUCCESS;
|
||||
|
||||
/* Reading the hash during a hash update causes the new hash to be returned and counts
|
||||
* towards the peer's progression towards a change-aware state.
|
||||
*/
|
||||
if (attsCsfCb.isHashUpdating)
|
||||
{
|
||||
/* This read will not be processed until after the hash update completes, so this read
|
||||
* request shall be counted as a move from change-unaware to chang-aware pending.
|
||||
*/
|
||||
pRec->changeAwareState = ATTS_CLIENT_CHANGE_AWARE_DB_READ_PENDING;
|
||||
|
||||
ATT_TRACE_INFO2("ConnId %d change aware state is %d", handle + 1,
|
||||
ATTS_CLIENT_CHANGE_AWARE_DB_READ_PENDING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (err == ATT_ERR_DATABASE_OUT_OF_SYNC)
|
||||
{
|
||||
ATT_TRACE_INFO2("ConnId %d out of sync, PDU with opcode 0x%02x ignored!", handle + 1, opcode);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Update a client's state of awareness to a change in the database.
|
||||
*
|
||||
* \param connId DM connection ID. if \ref DM_CONN_ID_NONE, sets the state for all connected
|
||||
* clients.
|
||||
* \param state The state of awareness to a change, see ::attClientAwareStates.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note A callback to application is not needed as it is expected the caller (i.e. the
|
||||
* application) will have updated all persistent records prior to calling this function.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCsfSetClientsChangeAwarenessState(dmConnId_t connId, uint8_t state)
|
||||
{
|
||||
if (connId == DM_CONN_ID_NONE)
|
||||
{
|
||||
for (uint8_t i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
if (attsCsfCb.attsCsfTable[i].changeAwareState == ATTS_CLIENT_CHANGE_AWARE_DB_READ_PENDING)
|
||||
{
|
||||
attsCsfCb.attsCsfTable[i].changeAwareState = ATTS_CLIENT_CHANGE_PENDING_AWARE;
|
||||
}
|
||||
else
|
||||
{
|
||||
attsCsfCb.attsCsfTable[i].changeAwareState = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
attsCsfCb.attsCsfTable[connId - 1].changeAwareState = state;
|
||||
|
||||
ATT_TRACE_INFO2("ConnId %d change aware state is %d", connId, state);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the client supported features for a connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param changeAwareState The state of awareness to a change in the database.
|
||||
* \param pCsf Pointer to the client supported features value to cache. \ref NULL or
|
||||
* buffer of length \ref ATT_CSF_LEN.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCsfConnOpen(dmConnId_t connId, uint8_t changeAwareState, uint8_t *pCsf)
|
||||
{
|
||||
if (pCsf != NULL)
|
||||
{
|
||||
attsCsfCb.attsCsfTable[connId - 1].changeAwareState = changeAwareState;
|
||||
memcpy(&attsCsfCb.attsCsfTable[connId - 1].csf, pCsf, ATT_CSF_LEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Note: this set client to the change-aware state. */
|
||||
memset(&attsCsfCb.attsCsfTable[connId - 1], 0, sizeof(attsCsfRec_t));
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register callback.
|
||||
*
|
||||
* \param writeCback Application callback for when features or status change.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCsfRegister(attsCsfWriteCback_t writeCback)
|
||||
{
|
||||
attsCsfCb.writeCback = writeCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATTS client supported features module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCsfInit(void)
|
||||
{
|
||||
attsCsfCb.isHashUpdating = FALSE;
|
||||
attsCsfCb.writeCback = NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief GATT write of client supported feature characteristic value.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param offset offset into csf characteristic.
|
||||
* \param valueLen length of write in bytes.
|
||||
* \param pValue Pointer to client's supported features characteristic value.
|
||||
*
|
||||
* \return \ref ATT_SUCCESS is successful, \ref ATT_ERR_VALUE_NOT_ALLOWED if any supported
|
||||
* features are flipped from 1 to 0.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttsCsfWriteFeatures(dmConnId_t connId, uint16_t offset, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attsCsfRec_t *pCsfRec = &attsCsfCb.attsCsfTable[connId - 1];
|
||||
|
||||
/* future parameter in case the client supported features characteristic becomes a multi-octet
|
||||
* structure.
|
||||
*/
|
||||
(void)offset;
|
||||
|
||||
if (valueLen > ATT_CSF_LEN)
|
||||
{
|
||||
return ATT_ERR_LENGTH;
|
||||
}
|
||||
|
||||
/* A client can not clear any bits it has set. */
|
||||
if ((pCsfRec->csf & *pValue) < pCsfRec->csf)
|
||||
{
|
||||
return ATT_ERR_VALUE_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
pCsfRec->csf = *pValue & ATTS_CSF_OCT0_FEATURES;
|
||||
|
||||
ATT_TRACE_INFO2("connId %d updated csf to 0x%02x", connId, pCsfRec->csf);
|
||||
|
||||
/* Callback to application to store updated features, if bonded. */
|
||||
if (attsCsfCb.writeCback != NULL)
|
||||
{
|
||||
attsCsfCb.writeCback(connId, pCsfRec->changeAwareState, &pCsfRec->csf);
|
||||
}
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get client supported feature record.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCsfOut Output parameter for client supported features buffer.
|
||||
* \param pCsfOutLen Length of output parameter buffer.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCsfGetFeatures(dmConnId_t connId, uint8_t *pCsfOut, uint8_t pCsfOutLen)
|
||||
{
|
||||
if (pCsfOutLen <= ATT_CSF_LEN)
|
||||
{
|
||||
memcpy(pCsfOut, &attsCsfCb.attsCsfTable[connId - 1].csf, pCsfOutLen);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get client state of awareness to a change in the database.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return Client's change-aware state.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttsCsfGetChangeAwareState(dmConnId_t connId)
|
||||
{
|
||||
return attsCsfCb.attsCsfTable[connId - 1].changeAwareState;
|
||||
}
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Dynamic ATT services and attributes.
|
||||
*
|
||||
* Copyright (c) 2017-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_math.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Configurable number of bytes in the Dynamic Service and Attribute Heap */
|
||||
#ifndef ATTS_DYN_HEAP_SIZE
|
||||
#define ATTS_DYN_HEAP_SIZE 1280
|
||||
#endif
|
||||
|
||||
/* Configurable memory byte alignment Dynamic Service and Attribute Heap */
|
||||
#ifndef ATTS_DYN_ALIGNMENT
|
||||
#define ATTS_DYN_ALIGNMENT 4
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Dynamic service and attributes control block */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t numServices;
|
||||
uint8_t *pNextBuffer;
|
||||
} attsDynCb_t;
|
||||
|
||||
/* Dynamic service group control block */
|
||||
typedef struct
|
||||
{
|
||||
attsGroup_t group;
|
||||
uint16_t currentAttr;
|
||||
} attsDynGroupCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Dynamic Service and Attribute Heap */
|
||||
static uint8_t attsDynHeap[ATTS_DYN_HEAP_SIZE];
|
||||
|
||||
/* Dynamic Service and Attribute control block */
|
||||
static attsDynCb_t attsDynCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate memory from the Dynamic Service and Attribute Heap.
|
||||
*
|
||||
* \param size Size of buffer to allocate in bytes
|
||||
*
|
||||
* \return Allocated buffer or NULL if failed to allocate a buffer.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void *attsDynAlloc(uint16_t size)
|
||||
{
|
||||
uint8_t *pMem = attsDynCb.pNextBuffer;
|
||||
|
||||
/* Verify enough space in heap for buffer */
|
||||
if (pMem + size <= attsDynHeap + ATTS_DYN_HEAP_SIZE)
|
||||
{
|
||||
#if ATTS_DYN_ALIGNMENT > 1
|
||||
/* Increase size if size not a multiple of the memory alignment */
|
||||
if (size % ATTS_DYN_ALIGNMENT)
|
||||
{
|
||||
size += ATTS_DYN_ALIGNMENT - (size % ATTS_DYN_ALIGNMENT);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set the next buffer location */
|
||||
attsDynCb.pNextBuffer += size;
|
||||
|
||||
return pMem;
|
||||
}
|
||||
|
||||
/* Out of heap */
|
||||
WSF_ASSERT(FALSE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the Dynamic ATT Service subsystem.
|
||||
*
|
||||
* \param None
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsDynInit()
|
||||
{
|
||||
attsDynCb.numServices = 0;
|
||||
attsDynCb.pNextBuffer = attsDynHeap;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Dynamically create an ATT Service at runtime.
|
||||
*
|
||||
* \param startHandle Starting attribute handle in the service
|
||||
* \param endHandle Last attribute handle in the service
|
||||
*
|
||||
* \return Service Handle.
|
||||
*
|
||||
* \note It is recommended this function only be used when no connections are open and the
|
||||
* device is not in a connectable mode.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void *AttsDynCreateGroup(uint16_t startHandle, uint16_t endHandle)
|
||||
{
|
||||
attsDynGroupCb_t *pGroup = NULL;
|
||||
|
||||
/* Allocate memory for the service control block */
|
||||
pGroup = attsDynAlloc(sizeof(attsDynGroupCb_t));
|
||||
WSF_ASSERT(pGroup);
|
||||
|
||||
if (pGroup != NULL)
|
||||
{
|
||||
|
||||
/* Initialize the service group */
|
||||
pGroup->group.startHandle = startHandle;
|
||||
pGroup->group.endHandle = endHandle;
|
||||
pGroup->group.readCback = NULL;
|
||||
pGroup->group.writeCback = NULL;
|
||||
|
||||
/* Allocate memory for the attributes */
|
||||
pGroup->group.pAttr = attsDynAlloc(sizeof(attsAttr_t) * (endHandle - startHandle + 1));
|
||||
WSF_ASSERT(pGroup->group.pAttr);
|
||||
|
||||
pGroup->currentAttr = 0;
|
||||
attsDynCb.numServices++;
|
||||
}
|
||||
|
||||
return pGroup;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Dynamically delete an ATT Service at runtime.
|
||||
*
|
||||
* \param pSvcHandle Service handle returned by AttsDynCreateGroup
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note It is recommended this function only be used when no connections are open and the
|
||||
* device is not in a connectable mode.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsDynDeleteGroup(void *pSvcHandle)
|
||||
{
|
||||
attsDynGroupCb_t *pGroup = pSvcHandle;
|
||||
|
||||
WSF_ASSERT(attsDynCb.numServices);
|
||||
WSF_ASSERT(pGroup);
|
||||
|
||||
/* Remove the group */
|
||||
AttsRemoveGroup(pGroup->group.startHandle);
|
||||
|
||||
attsDynCb.numServices--;
|
||||
|
||||
/* If there are no more dynamic groups, initialize the subsystem to free the memory in the heap */
|
||||
if (attsDynCb.numServices == 0)
|
||||
{
|
||||
AttsDynInit();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register callback functions for a dynamic ATT Service at runtime.
|
||||
*
|
||||
* \param pSvcHandle Service handle returned by AttsDynCreateGroup
|
||||
* \param readCback Read callback function.
|
||||
* \param writeCback Write callback function.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note It is recommended this function only be used when no connections are open and the
|
||||
* device is not in a connectable mode.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsDynRegister(void *pSvcHandle, attsReadCback_t readCback, attsWriteCback_t writeCback)
|
||||
{
|
||||
attsDynGroupCb_t *pGroup = pSvcHandle;
|
||||
|
||||
WSF_ASSERT(pGroup);
|
||||
|
||||
pGroup->group.readCback = readCback;
|
||||
pGroup->group.writeCback = writeCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Dynamically add an attribute to a dynamic ATT Services at app initialization.
|
||||
*
|
||||
* \param pSvcHandle Service handle returned by AttsDynCreateGroup
|
||||
* \param pUuid Constant UUID
|
||||
* \param pValue Initial value of attribute (copied into attribute memory)
|
||||
* \param len Length of pValue in bytes
|
||||
* \param maxLen Maximum length of the attribute in bytes
|
||||
* \param settings Attribute settings
|
||||
* \param permissions Attribute permissions
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note It is recommended this function only be used when no connections are open and the
|
||||
* device is not in a connectable mode.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsDynAddAttr(void *pSvcHandle, const uint8_t *pUuid, const uint8_t *pValue, uint16_t len,
|
||||
const uint16_t maxLen, uint8_t settings, uint8_t permissions)
|
||||
{
|
||||
attsAttr_t *pAttr;
|
||||
attsDynGroupCb_t *pGroup = pSvcHandle;
|
||||
uint16_t handle = pGroup->group.startHandle + pGroup->currentAttr++;
|
||||
|
||||
/* Verify inputs */
|
||||
WSF_ASSERT(handle <= pGroup->group.endHandle);
|
||||
WSF_ASSERT(pUuid);
|
||||
WSF_ASSERT(len <= maxLen);
|
||||
|
||||
pAttr = pGroup->group.pAttr + (handle - pGroup->group.startHandle);
|
||||
|
||||
|
||||
/* Allocate a buffer for the length of the attribute */
|
||||
pAttr->pLen = attsDynAlloc(sizeof(uint16_t));
|
||||
WSF_ASSERT(pAttr->pLen);
|
||||
|
||||
if (pAttr->pLen == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate a buffer for the value of the attribute */
|
||||
pAttr->pValue = attsDynAlloc(maxLen);
|
||||
WSF_ASSERT(pAttr->pValue);
|
||||
|
||||
if (pAttr->pValue == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the attribute values */
|
||||
pAttr->pUuid = pUuid;
|
||||
pAttr->maxLen = maxLen;
|
||||
pAttr->settings = settings;
|
||||
pAttr->permissions = permissions;
|
||||
|
||||
|
||||
|
||||
if (pValue)
|
||||
{
|
||||
/* Copy the initial value */
|
||||
memcpy(pAttr->pValue, pValue, len);
|
||||
*pAttr->pLen = len;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No initial value, zero value and length */
|
||||
memset(pAttr->pValue, 0, maxLen);
|
||||
*pAttr->pLen = 0;
|
||||
}
|
||||
|
||||
/* Add the service when the last attribute has been added */
|
||||
if (handle == pGroup->group.endHandle)
|
||||
{
|
||||
AttsAddGroup(&pGroup->group);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Dynamically add an attribute with a constant value to a dynamic ATT Services.
|
||||
*
|
||||
* \param pSvcHandle Service handle returned by AttsDynCreateGroup
|
||||
* \param pUuid Constant UUID
|
||||
* \param pValue Pointer to constant attribute memory
|
||||
* \param len Length of pValue in bytes
|
||||
* \param settings Attribute settings
|
||||
* \param permissions Attribute permissions
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note It is recommended this function only be used when no connections are open and the
|
||||
* device is not in a connectable mode.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsDynAddAttrConst(void *pSvcHandle, const uint8_t *pUuid, const uint8_t *pValue,
|
||||
const uint16_t len, uint8_t settings, uint8_t permissions)
|
||||
{
|
||||
attsAttr_t *pAttr;
|
||||
attsDynGroupCb_t *pGroup = pSvcHandle;
|
||||
uint16_t handle = pGroup->group.startHandle + pGroup->currentAttr++;
|
||||
|
||||
/* Verify inputs */
|
||||
WSF_ASSERT(handle <= pGroup->group.endHandle);
|
||||
WSF_ASSERT(pValue);
|
||||
WSF_ASSERT(pUuid);
|
||||
|
||||
pAttr = pGroup->group.pAttr + (handle - pGroup->group.startHandle);
|
||||
|
||||
/* Allocate a buffer for the length of the attribute */
|
||||
pAttr->pLen = attsDynAlloc(sizeof(uint16_t));
|
||||
WSF_ASSERT(pAttr->pLen);
|
||||
|
||||
if (pAttr->pLen == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the attribute values */
|
||||
*pAttr->pLen = len;
|
||||
pAttr->pUuid = pUuid;
|
||||
pAttr->pValue = (uint8_t *) pValue;
|
||||
pAttr->maxLen = len;
|
||||
pAttr->settings = settings;
|
||||
pAttr->permissions = permissions;
|
||||
|
||||
/* Add the service when the last attribute has been added */
|
||||
if (handle == pGroup->group.endHandle)
|
||||
{
|
||||
AttsAddGroup(&pGroup->group);
|
||||
}
|
||||
}
|
||||
+654
@@ -0,0 +1,654 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server indication and notification functions.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
static void attsIndConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt);
|
||||
static void attsIndMsgCback(attsApiMsg_t *pMsg);
|
||||
static void attsIndCtrlCback(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Interface to ATT */
|
||||
static const attFcnIf_t attsIndFcnIf =
|
||||
{
|
||||
attEmptyDataCback,
|
||||
attsIndCtrlCback,
|
||||
(attMsgHandler_t) attsIndMsgCback,
|
||||
attsIndConnCback
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
attsIndCb_t attsIndCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the ATTS connection control block connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to connection control block or NULL if not in use.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static attsIndCcb_t *attsIndCcbByConnId(dmConnId_t connId)
|
||||
{
|
||||
if (DmConnInUse(connId))
|
||||
{
|
||||
return &attsIndCb.ccb[connId - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN1("atts ccb not in use: %d", connId);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if application callback is pending for indication or a given notification, or
|
||||
* the maximum number of simultaneous notifications has been reached.
|
||||
*
|
||||
* \param pCcb ATTS ind control block.
|
||||
* \param pPkt Pointer to packet.
|
||||
*
|
||||
* \return TRUE if app callback's pending or max number of simultaneous notifications reached.
|
||||
* FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t attsPendIndNtfHandle(attsIndCcb_t *pCcb, attsPktParam_t *pPkt)
|
||||
{
|
||||
uint8_t opcode;
|
||||
uint8_t pendNtfs;
|
||||
uint8_t i;
|
||||
|
||||
/* extract opcode */
|
||||
opcode = *(((uint8_t *) pPkt) + L2C_PAYLOAD_START);
|
||||
|
||||
/* if indication */
|
||||
if (opcode == ATT_PDU_VALUE_IND)
|
||||
{
|
||||
/* see if callback pending for indication */
|
||||
return (pCcb->pendIndHandle == ATT_HANDLE_NONE) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
/* initialize number of notification callbacks pending */
|
||||
pendNtfs = 0;
|
||||
|
||||
for (i = 0; i < ATT_NUM_SIMUL_NTF; i++)
|
||||
{
|
||||
/* if callback pending for notification */
|
||||
if (pCcb->pendNtfHandle[i] != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* if callback pending for this handle */
|
||||
if (pCcb->pendNtfHandle[i] == pPkt->handle)
|
||||
{
|
||||
/* callback pending for this notification */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
pendNtfs++;
|
||||
}
|
||||
}
|
||||
|
||||
/* no callback is pending for this notification but see if the maximum number of simultaneous
|
||||
notifications has been reached */
|
||||
return (pendNtfs < ATT_NUM_SIMUL_NTF) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set pending notification callback for a given handle.
|
||||
*
|
||||
* \param pCcb ATTS ind control block.
|
||||
* \param handle Notification handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsSetPendNtfHandle(attsIndCcb_t *pCcb, uint16_t handle)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < ATT_NUM_SIMUL_NTF; i++)
|
||||
{
|
||||
/* if entry free */
|
||||
if (pCcb->pendNtfHandle[i] == ATT_HANDLE_NONE)
|
||||
{
|
||||
/* set pending notification handle */
|
||||
pCcb->pendNtfHandle[i] = handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute application callback function with confirmation event.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param status Callback event status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsExecCallback(dmConnId_t connId, uint16_t handle, uint8_t status)
|
||||
{
|
||||
attExecCallback(connId, ATTS_HANDLE_VALUE_CNF, handle, status, 0);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Call pending indication or/and notification(s) application callback.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCcb ATTS ind control block.
|
||||
* \param status Callback event status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsIndNtfCallback(dmConnId_t connId, attsIndCcb_t *pCcb, uint8_t status)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* if pending indication callback */
|
||||
if (pCcb->pendIndHandle != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* call indication callback with status */
|
||||
attsExecCallback(connId, pCcb->pendIndHandle, status);
|
||||
pCcb->pendIndHandle = ATT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
/* if any pending notification callback */
|
||||
for (i = 0; i < ATT_NUM_SIMUL_NTF; i++)
|
||||
{
|
||||
if (pCcb->pendNtfHandle[i] != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* call notification callback with status */
|
||||
attsExecCallback(connId, pCcb->pendNtfHandle[i], status);
|
||||
pCcb->pendNtfHandle[i] = ATT_HANDLE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up and send an attribute server indication or notification.
|
||||
*
|
||||
* \param pCcb ATTS ind control block.
|
||||
* \param connId DM connection ID.
|
||||
* \param pPkt Pointer to packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsSetupMsg(attsIndCcb_t *pCcb, dmConnId_t connId, attsPktParam_t *pPkt)
|
||||
{
|
||||
uint8_t opcode;
|
||||
uint16_t handle;
|
||||
|
||||
/* extract opcode */
|
||||
opcode = *(((uint8_t *) pPkt) + L2C_PAYLOAD_START);
|
||||
|
||||
/* copy handle (it may be overwritten in pPkt) */
|
||||
handle = pPkt->handle;
|
||||
|
||||
/* send pdu */
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, pPkt->len, (uint8_t *) pPkt);
|
||||
|
||||
/* if indication store handle and start timer */
|
||||
if (opcode == ATT_PDU_VALUE_IND)
|
||||
{
|
||||
pCcb->outIndHandle = pCcb->pendIndHandle = handle;
|
||||
pCcb->outIndTimer.msg.event = ATTS_MSG_IND_TIMEOUT;
|
||||
WsfTimerStartSec(&pCcb->outIndTimer, pAttCfg->transTimeout);
|
||||
}
|
||||
/* else if a notification and flow not disabled call callback now */
|
||||
else if (!(pCcb->pMainCcb->control & ATT_CCB_STATUS_FLOW_DISABLED))
|
||||
{
|
||||
attsExecCallback(connId, handle, ATT_SUCCESS);
|
||||
}
|
||||
/* else set pending notification callback for this handle */
|
||||
else
|
||||
{
|
||||
attsSetPendNtfHandle(pCcb, handle);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection callback for ATTS indications/notifications.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsIndConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt)
|
||||
{
|
||||
attsIndCcb_t *pIndCcb;
|
||||
uint8_t status;
|
||||
|
||||
/* if connection opened */
|
||||
if (pDmEvt->hdr.event == DM_CONN_OPEN_IND)
|
||||
{
|
||||
|
||||
}
|
||||
/* if connection closed */
|
||||
else if (pDmEvt->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* set status */
|
||||
if (pDmEvt->connClose.hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
status = pDmEvt->connClose.reason + ATT_HCI_ERR_BASE;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = pDmEvt->connClose.hdr.status + ATT_HCI_ERR_BASE;
|
||||
}
|
||||
|
||||
/* get server control block directly */
|
||||
pIndCcb = &attsIndCb.ccb[pCcb->connId - 1];
|
||||
|
||||
/* if outstanding indication */
|
||||
if (pIndCcb->outIndHandle != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* stop timer */
|
||||
WsfTimerStop(&pIndCcb->outIndTimer);
|
||||
pIndCcb->outIndHandle = ATT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
/* call pending indication and notification callback */
|
||||
attsIndNtfCallback(pCcb->connId, pIndCcb, status);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF message handler callback for ATTS indications/notifications.
|
||||
*
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsIndMsgCback(attsApiMsg_t *pMsg)
|
||||
{
|
||||
attsIndCcb_t *pCcb;
|
||||
|
||||
/* get CCB and verify connection still in use */
|
||||
if ((pCcb = attsIndCcbByConnId((dmConnId_t) pMsg->hdr.param)) == NULL)
|
||||
{
|
||||
/* if message has a packet buffer free packet buffer */
|
||||
if (pMsg->hdr.event == ATTS_MSG_API_VALUE_IND_NTF)
|
||||
{
|
||||
WsfMsgFree(pMsg->pPkt);
|
||||
}
|
||||
|
||||
/* ignore if connection not in use */
|
||||
return;
|
||||
}
|
||||
|
||||
/* if an API message to send packet */
|
||||
if (pMsg->hdr.event == ATTS_MSG_API_VALUE_IND_NTF)
|
||||
{
|
||||
/* verify no API message already pending */
|
||||
if (attsPendIndNtfHandle(pCcb, pMsg->pPkt))
|
||||
{
|
||||
/* call callback with failure status and free packet buffer */
|
||||
attsExecCallback((dmConnId_t) pMsg->hdr.param, pMsg->pPkt->handle, ATT_ERR_OVERFLOW);
|
||||
WsfMsgFree(pMsg->pPkt);
|
||||
}
|
||||
/* otherwise ready to send; set up request */
|
||||
else
|
||||
{
|
||||
attsSetupMsg(pCcb, (dmConnId_t) pMsg->hdr.param, pMsg->pPkt);
|
||||
}
|
||||
}
|
||||
/* else if indication timeout */
|
||||
else if (pMsg->hdr.event == ATTS_MSG_IND_TIMEOUT)
|
||||
{
|
||||
/* if outstanding indication */
|
||||
if (pCcb->outIndHandle != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* clear out handle */
|
||||
pCcb->outIndHandle = ATT_HANDLE_NONE;
|
||||
|
||||
/* call callback with timeout error */
|
||||
attsExecCallback((dmConnId_t) pMsg->hdr.param, pCcb->pendIndHandle, ATT_ERR_TIMEOUT);
|
||||
pCcb->pendIndHandle = ATT_HANDLE_NONE;
|
||||
pCcb->pMainCcb->control |= ATT_CCB_STATUS_TX_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2CAP control callback.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsIndCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
attsIndCcb_t *pCcb;
|
||||
|
||||
/* note this function is currently only called when flow is enabled */
|
||||
|
||||
/* get CCB */
|
||||
if ((pCcb = attsIndCcbByConnId((dmConnId_t) pMsg->param)) != NULL)
|
||||
{
|
||||
/* call pending indication and notification callback */
|
||||
attsIndNtfCallback((dmConnId_t) pMsg->param, pCcb, ATT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol Handle Value Indication or Notification.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
* \param opcode Opcode for notification or indication.
|
||||
* \param zeroCpy Whether or not to copy attribute value data into new buffer.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsHandleValueIndNtf(dmConnId_t connId, uint16_t handle, uint16_t valueLen,
|
||||
uint8_t *pValue, uint8_t opcode, bool_t zeroCpy)
|
||||
{
|
||||
attsIndCcb_t *pCcb;
|
||||
uint16_t mtu;
|
||||
bool_t transTimedOut;
|
||||
bool_t pktSent = FALSE;
|
||||
|
||||
WsfTaskLock();
|
||||
|
||||
/* get CCB and verify connection still in use */
|
||||
if ((pCcb = attsIndCcbByConnId(connId)) != NULL)
|
||||
{
|
||||
/* get MTU size */
|
||||
mtu = pCcb->pMainCcb->mtu;
|
||||
transTimedOut = !!(pCcb->pMainCcb->control & ATT_CCB_STATUS_TX_TIMEOUT);
|
||||
}
|
||||
/* else connection not in use */
|
||||
else
|
||||
{
|
||||
/* MTU size unknown */
|
||||
mtu = 0;
|
||||
transTimedOut = FALSE;
|
||||
}
|
||||
|
||||
WsfTaskUnlock();
|
||||
|
||||
/* if MTU size known for connection */
|
||||
if (mtu > 0)
|
||||
{
|
||||
/* if no transaction's timed out */
|
||||
if (!transTimedOut)
|
||||
{
|
||||
/* Only send notifications and indications if client is aware of any database changes. */
|
||||
if (attsCsfIsClientChangeAware(connId, handle))
|
||||
{
|
||||
/* if packet length is less than or equal to negotiated MTU */
|
||||
if ((valueLen + ATT_VALUE_NTF_LEN) <= mtu)
|
||||
{
|
||||
attsApiMsg_t *pMsg;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate message buffer */
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(attsApiMsg_t))) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->hdr.event = ATTS_MSG_API_VALUE_IND_NTF;
|
||||
|
||||
if (zeroCpy)
|
||||
{
|
||||
/* use packet buffer provided */
|
||||
pMsg->pPkt = (attsPktParam_t *)(pValue - ATT_VALUE_IND_NTF_BUF_LEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* allocate packet buffer */
|
||||
pMsg->pPkt = attMsgAlloc(ATT_VALUE_IND_NTF_BUF_LEN + valueLen);
|
||||
}
|
||||
|
||||
if (pMsg->pPkt != NULL)
|
||||
{
|
||||
/* set data length and handle (ind and ntf have same header length) */
|
||||
pMsg->pPkt->len = ATT_VALUE_IND_LEN + valueLen;
|
||||
pMsg->pPkt->handle = handle;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *)pMsg->pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, opcode);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
if (!zeroCpy)
|
||||
{
|
||||
memcpy(p, pValue, valueLen);
|
||||
}
|
||||
|
||||
/* send message */
|
||||
WsfMsgSend(attCb.handlerId, pMsg);
|
||||
pktSent = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* free message buffer if packet buffer alloc failed */
|
||||
WsfMsgFree(pMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* packet length exceeds MTU size */
|
||||
else
|
||||
{
|
||||
/* call callback with failure status */
|
||||
attsExecCallback(connId, handle, ATT_ERR_MTU_EXCEEDED);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
/* transaction's timed out */
|
||||
{
|
||||
/* call callback with failure status */
|
||||
attsExecCallback(connId, handle, ATT_ERR_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/* if packet wasn't sent and it's a zero-copy packet */
|
||||
if (!pktSent && zeroCpy)
|
||||
{
|
||||
/* free packet buffer provided */
|
||||
AttMsgFree(pValue, opcode);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received handle value confirm packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcValueCnf(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
attsIndCcb_t *pIndCcb;
|
||||
|
||||
/* get server indication CCB */
|
||||
if ((pIndCcb = attsIndCcbByConnId(pCcb->connId)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* if an outstanding indication */
|
||||
if (pIndCcb->outIndHandle != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* clear outstanding indication */
|
||||
pIndCcb->outIndHandle = ATT_HANDLE_NONE;
|
||||
|
||||
/* stop indication timer */
|
||||
WsfTimerStop(&pIndCcb->outIndTimer);
|
||||
|
||||
/* call callback if flow control permits */
|
||||
if (!(pCcb->control & ATT_CCB_STATUS_FLOW_DISABLED))
|
||||
{
|
||||
attsExecCallback(pCcb->connId, pIndCcb->pendIndHandle, ATT_SUCCESS);
|
||||
pIndCcb->pendIndHandle = ATT_HANDLE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATT server for indications/notifications.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsIndInit(void)
|
||||
{
|
||||
uint8_t i;
|
||||
attsIndCcb_t *pCcb;
|
||||
|
||||
/* Initialize control block CCBs */
|
||||
for (i = 0, pCcb = attsIndCb.ccb; i < DM_CONN_MAX; i++, pCcb++)
|
||||
{
|
||||
/* set pointer to main CCB */
|
||||
pCcb->pMainCcb = &attCb.ccb[i];
|
||||
|
||||
/* initialize timer */
|
||||
pCcb->outIndTimer.handlerId = attCb.handlerId;
|
||||
pCcb->outIndTimer.msg.param = i + 1; /* param stores the conn id */
|
||||
}
|
||||
|
||||
/* set up callback interface */
|
||||
attsCb.pInd = &attsIndFcnIf;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol Handle Value Indication.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsHandleValueInd(dmConnId_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attsHandleValueIndNtf(connId, handle, valueLen, pValue, ATT_PDU_VALUE_IND, FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol Handle Value Notification.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsHandleValueNtf(dmConnId_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attsHandleValueIndNtf(connId, handle, valueLen, pValue, ATT_PDU_VALUE_NTF, FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol Handle Value Indication without copying the attribute
|
||||
* value data.
|
||||
*
|
||||
* Note: attribute value buffer 'pValue' must be allocated with AttMsgAlloc().
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsHandleValueIndZeroCpy(dmConnId_t connId, uint16_t handle, uint16_t valueLen,
|
||||
uint8_t *pValue)
|
||||
{
|
||||
attsHandleValueIndNtf(connId, handle, valueLen, pValue, ATT_PDU_VALUE_IND, TRUE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol Handle Value Notification without copying the attribute
|
||||
* value data.
|
||||
*
|
||||
* Note: attribute value buffer 'pValue' must be allocated with AttMsgAlloc().
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsHandleValueNtfZeroCpy(dmConnId_t connId, uint16_t handle, uint16_t valueLen,
|
||||
uint8_t *pValue)
|
||||
{
|
||||
attsHandleValueIndNtf(connId, handle, valueLen, pValue, ATT_PDU_VALUE_NTF, TRUE);
|
||||
}
|
||||
Vendored
+881
@@ -0,0 +1,881 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/wstr.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
#include "att_uuid.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* ATTS control block */
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
static void attsDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket);
|
||||
static void attsConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt);
|
||||
static void attsMsgCback(wsfMsgHdr_t *pMsg);
|
||||
static void attsL2cCtrlCback(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Interface to ATT */
|
||||
static const attFcnIf_t attsFcnIf =
|
||||
{
|
||||
attsDataCback,
|
||||
attsL2cCtrlCback,
|
||||
(attMsgHandler_t) attsMsgCback,
|
||||
attsConnCback
|
||||
};
|
||||
|
||||
/* Minimum PDU lengths, indexed by method */
|
||||
static const uint8_t attsMinPduLen[] =
|
||||
{
|
||||
0, /* ATT_METHOD_ERR */
|
||||
ATT_MTU_REQ_LEN, /* ATT_METHOD_MTU */
|
||||
ATT_FIND_INFO_REQ_LEN, /* ATT_METHOD_FIND_INFO */
|
||||
ATT_FIND_TYPE_REQ_LEN, /* ATT_METHOD_FIND_TYPE */
|
||||
ATT_READ_TYPE_REQ_LEN, /* ATT_METHOD_READ_TYPE */
|
||||
ATT_READ_REQ_LEN, /* ATT_METHOD_READ */
|
||||
ATT_READ_BLOB_REQ_LEN, /* ATT_METHOD_READ_BLOB */
|
||||
ATT_READ_MULT_REQ_LEN + 4, /* ATT_METHOD_READ_MULTIPLE */
|
||||
ATT_READ_GROUP_TYPE_REQ_LEN, /* ATT_METHOD_READ_GROUP_TYPE */
|
||||
ATT_WRITE_REQ_LEN, /* ATT_METHOD_WRITE */
|
||||
ATT_WRITE_CMD_LEN, /* ATT_METHOD_WRITE_CMD */
|
||||
ATT_PREP_WRITE_REQ_LEN, /* ATT_METHOD_PREPARE_WRITE */
|
||||
ATT_EXEC_WRITE_REQ_LEN, /* ATT_METHOD_EXECUTE_WRITE */
|
||||
0, /* ATT_METHOD_VALUE_NTF */
|
||||
0, /* ATT_METHOD_VALUE_IND */
|
||||
ATT_VALUE_CNF_LEN, /* ATT_METHOD_VALUE_CNF */
|
||||
ATT_SIGNED_WRITE_CMD_LEN /* ATT_METHOD_SIGNED_WRITE_CMD */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* PDU processing function lookup table, indexed by method */
|
||||
attsProcFcn_t attsProcFcnTbl[ATT_METHOD_SIGNED_WRITE_CMD+1] =
|
||||
{
|
||||
NULL, /* ATT_METHOD_ERR */
|
||||
attsProcMtuReq, /* ATT_METHOD_MTU */
|
||||
attsProcFindInfoReq, /* ATT_METHOD_FIND_INFO */
|
||||
attsProcFindTypeReq, /* ATT_METHOD_FIND_TYPE */
|
||||
attsProcReadTypeReq, /* ATT_METHOD_READ_TYPE */
|
||||
attsProcReadReq, /* ATT_METHOD_READ */
|
||||
attsProcReadBlobReq, /* ATT_METHOD_READ_BLOB */
|
||||
attsProcReadMultReq, /* ATT_METHOD_READ_MULT */
|
||||
attsProcReadGroupTypeReq, /* ATT_METHOD_READ_GROUP_TYPE */
|
||||
attsProcWrite, /* ATT_METHOD_WRITE */
|
||||
attsProcWrite, /* ATT_METHOD_WRITE_CMD */
|
||||
attsProcPrepWriteReq, /* ATT_METHOD_PREP_WRITE */
|
||||
attsProcExecWriteReq, /* ATT_METHOD_EXEC_WRITE */
|
||||
NULL, /* ATT_METHOD_VALUE_NTF */
|
||||
NULL, /* ATT_METHOD_VALUE_IND */
|
||||
attsProcValueCnf, /* ATT_METHOD_VALUE_CNF */
|
||||
NULL /* ATT_METHOD_SIGNED_WRITE_CMD */
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
attsCb_t attsCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Data callback for ATTS.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t opcode;
|
||||
uint8_t method;
|
||||
uint8_t err;
|
||||
attsProcFcn_t procFcn;
|
||||
attCcb_t *pCcb;
|
||||
uint16_t attHandle;
|
||||
|
||||
/* get connection cb for this handle */
|
||||
if ((pCcb = attCcbByHandle(handle)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse opcode */
|
||||
opcode = *(pPacket + L2C_PAYLOAD_START);
|
||||
|
||||
/* get method */
|
||||
if ((opcode <= ATT_PDU_WRITE_REQ) ||
|
||||
((opcode >= ATT_PDU_PREP_WRITE_REQ) && (opcode <= ATT_PDU_VALUE_CNF)))
|
||||
{
|
||||
method = ATT_OPCODE_2_METHOD(opcode);
|
||||
}
|
||||
else if (opcode == ATT_PDU_WRITE_CMD)
|
||||
{
|
||||
method = ATT_METHOD_WRITE_CMD;
|
||||
}
|
||||
else if (opcode == ATT_PDU_SIGNED_WRITE_CMD)
|
||||
{
|
||||
method = ATT_METHOD_SIGNED_WRITE_CMD;
|
||||
}
|
||||
else
|
||||
{
|
||||
method = ATT_METHOD_ERR;
|
||||
}
|
||||
|
||||
/* ignore packet if write response is pending. */
|
||||
if (pCcb->control & ATT_CCB_STATUS_RSP_PENDING)
|
||||
{
|
||||
if (method != ATT_METHOD_VALUE_CNF)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* check client's status to see if server is allowed to process this PDU. */
|
||||
err = attsCsfActClientState(handle, opcode, pPacket);
|
||||
if (err)
|
||||
{
|
||||
BYTES_TO_UINT16(attHandle, pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
attHandle = ATT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
#if defined(ATTS_ERROR_TEST) && (ATTS_ERROR_TEST == TRUE)
|
||||
if (attCb.errTest != ATT_SUCCESS)
|
||||
{
|
||||
BYTES_TO_UINT16(attHandle, pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN);
|
||||
attsErrRsp(handle, opcode, attHandle, attCb.errTest);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* if no error process request */
|
||||
if (!err)
|
||||
{
|
||||
/* look up processing function */
|
||||
procFcn = attsProcFcnTbl[method];
|
||||
|
||||
/* if method is supported */
|
||||
if (procFcn != NULL)
|
||||
{
|
||||
/* verify length */
|
||||
if (len >= attsMinPduLen[method])
|
||||
{
|
||||
/* execute processing function */
|
||||
(*procFcn)(pCcb, len, pPacket);
|
||||
err = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* invalid PDU length */
|
||||
err = ATT_ERR_INVALID_PDU;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* PDU not supported */
|
||||
err = ATT_ERR_NOT_SUP;
|
||||
}
|
||||
}
|
||||
|
||||
/* if there's an error and an error response can be sent for this opcode */
|
||||
if (err && (opcode != ATT_PDU_MTU_REQ) && (opcode != ATT_PDU_VALUE_CNF) &&
|
||||
((opcode & ATT_PDU_MASK_COMMAND) == 0))
|
||||
{
|
||||
attsErrRsp(handle, opcode, attHandle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection callback for ATTS.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt)
|
||||
{
|
||||
/* if connection closed */
|
||||
if (pDmEvt->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* clear prepare write queue */
|
||||
attsClearPrepWrites(pCcb);
|
||||
|
||||
/* stop service discovery idle timer, if running */
|
||||
if (DmConnCheckIdle(pCcb->connId) & DM_IDLE_ATTS_DISC)
|
||||
{
|
||||
WsfTimerStop(&pCcb->idleTimer);
|
||||
}
|
||||
}
|
||||
|
||||
/* pass event to indication interface */
|
||||
(*attsCb.pInd->connCback)(pCcb, pDmEvt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF message handler callback for ATTS.
|
||||
*
|
||||
* \param pMsg DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsMsgCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* handle service discovery idle timeout */
|
||||
if (pMsg->event == ATTS_MSG_IDLE_TIMEOUT)
|
||||
{
|
||||
/* set channel as idle */
|
||||
DmConnSetIdle((dmConnId_t) pMsg->param, DM_IDLE_ATTS_DISC, DM_CONN_IDLE);
|
||||
}
|
||||
/* pass event to indication interface */
|
||||
else if (pMsg->event <= ATTS_MSG_IND_TIMEOUT)
|
||||
{
|
||||
(*attsCb.pInd->msgCback)(pMsg);
|
||||
}
|
||||
/* pass event to signed data interface */
|
||||
else if (pMsg->event == ATTS_MSG_SIGN_CMAC_CMPL)
|
||||
{
|
||||
(*attsCb.signMsgCback)(pMsg);
|
||||
}
|
||||
else if (pMsg->event == ATTS_MSG_DBH_CMAC_CMPL)
|
||||
{
|
||||
/* handle database hash update */
|
||||
attsProcessDatabaseHashUpdate((secCmacMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2C control callback for ATTS.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsL2cCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* pass event to indication interface */
|
||||
(*attsCb.pInd->ctrlCback)(pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an error response PDU.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param opcode Opcode of the request that generated this error.
|
||||
* \param attHandle Attribute handle in request, if applicable.
|
||||
* \param reason Error reason.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsErrRsp(uint16_t handle, uint8_t opcode, uint16_t attHandle, uint8_t reason)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate buffer */
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_ERR_RSP_LEN)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_ERR_RSP);
|
||||
UINT8_TO_BSTREAM(p, opcode);
|
||||
UINT16_TO_BSTREAM(p, attHandle);
|
||||
UINT8_TO_BSTREAM(p, reason);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, handle, ATT_ERR_RSP_LEN, pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear the prepared write queue.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsClearPrepWrites(attCcb_t *pCcb)
|
||||
{
|
||||
void *pBuf;
|
||||
|
||||
while ((pBuf = WsfQueueDeq(&pCcb->prepWriteQueue)) != NULL)
|
||||
{
|
||||
WsfBufFree(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set connection idle status to busy for service discovery.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsDiscBusy(attCcb_t *pCcb)
|
||||
{
|
||||
if (pAttCfg->discIdleTimeout > 0)
|
||||
{
|
||||
/* set channel as busy */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_ATTS_DISC, DM_CONN_BUSY);
|
||||
|
||||
/* start service discovery idle timer */
|
||||
pCcb->idleTimer.handlerId = attCb.handlerId;
|
||||
pCcb->idleTimer.msg.event = ATTS_MSG_IDLE_TIMEOUT;
|
||||
pCcb->idleTimer.msg.param = pCcb->connId;
|
||||
WsfTimerStartSec(&pCcb->idleTimer, pAttCfg->discIdleTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process updated database hash.
|
||||
*
|
||||
* \param pMsg Event containing the new database hash.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcessDatabaseHashUpdate(secCmacMsg_t *pMsg)
|
||||
{
|
||||
attEvt_t evt;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t dbhCharHandle;
|
||||
|
||||
/* send to application */
|
||||
evt.hdr.event = ATTS_DB_HASH_CALC_CMPL_IND;
|
||||
evt.hdr.status = ATT_SUCCESS;
|
||||
evt.hdr.param = DM_CONN_ID_NONE;
|
||||
|
||||
evt.valueLen = ATT_DATABASE_HASH_LEN;
|
||||
evt.handle = ATT_HANDLE_NONE;
|
||||
evt.continuing = FALSE;
|
||||
evt.mtu = 0;
|
||||
|
||||
/* free plain text buffer */
|
||||
if (pMsg->pPlainText != NULL)
|
||||
{
|
||||
WsfBufFree(pMsg->pPlainText);
|
||||
pMsg->pPlainText = NULL;
|
||||
}
|
||||
|
||||
/* copy in little endian */
|
||||
evt.pValue = pMsg->pCiphertext;
|
||||
|
||||
/* find GATT database handle */
|
||||
dbhCharHandle = attsFindUuidInRange(ATT_HANDLE_START, ATT_HANDLE_MAX, ATT_16_UUID_LEN,
|
||||
(uint8_t *) attGattDbhChUuid, &pAttr, &pGroup);
|
||||
|
||||
if (dbhCharHandle != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* Set hash in service. */
|
||||
AttsSetAttr(dbhCharHandle, SEC_CMAC_HASH_LEN, evt.pValue);
|
||||
}
|
||||
|
||||
/* set hash update complete */
|
||||
attsCsfSetHashUpdateStatus(FALSE);
|
||||
|
||||
attCb.cback(&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if any clients are pending on a new database hash value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsCheckPendDbHashReadRsp(void)
|
||||
{
|
||||
for (uint8_t i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
attCcb_t *pCcb = &attCb.ccb[i];
|
||||
|
||||
if (pCcb->pPendDbHashRsp)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_TYPE_RSP);
|
||||
|
||||
/* set length parameter in response message */
|
||||
UINT8_TO_BSTREAM(p, ATT_DATABASE_HASH_LEN + sizeof(uint16_t));
|
||||
|
||||
/* copy result to response message */
|
||||
UINT16_TO_BSTREAM(p, pCcb->pPendDbHashRsp->handle);
|
||||
|
||||
if ((pAttr = attsFindByHandle(pCcb->pPendDbHashRsp->handle, &pGroup)) != NULL)
|
||||
{
|
||||
memcpy(p, pAttr->pValue, *pAttr->pLen);
|
||||
p += *pAttr->pLen;
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, p - (pBuf + L2C_PAYLOAD_START), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->connId, ATT_PDU_READ_TYPE_REQ, pCcb->pPendDbHashRsp->startHandle, ATT_ERR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->connId, ATT_PDU_READ_TYPE_REQ, pCcb->pPendDbHashRsp->startHandle, ATT_ERR_RESOURCES);
|
||||
}
|
||||
|
||||
/* Free pending state information. */
|
||||
WsfBufFree(pCcb->pPendDbHashRsp);
|
||||
pCcb->pPendDbHashRsp = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Determine if attribute is hashable and return length of hashable data.
|
||||
*
|
||||
* \param pAttr Attribute to check for inclusion in hash.
|
||||
*
|
||||
* \return 0 if not hashable, else length of hashable data in bytes.
|
||||
*
|
||||
* \note Hashable attributes include Primary Service, Secondary Service, Included Service,
|
||||
* Characteristic Declaration and Characteristic Extended Properties which contribute their
|
||||
* ATT handle, ATT type and ATT value. Hashable attributes also include Characteristic User
|
||||
* Description, Client Characteristic Configuration, Server Characteristic Configuration,
|
||||
* Characteristic Format, and Characteristic Aggreate Format which contribute their ATT
|
||||
* handle and ATT type.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t attsIsHashableAttr(attsAttr_t *pAttr)
|
||||
{
|
||||
/* Initialize length to 2 for ATT Handle length */
|
||||
uint16_t length = 2;
|
||||
uint16_t uuid;
|
||||
static bool_t isAttrCharVal = FALSE;
|
||||
|
||||
/* Characteristic values are skipped */
|
||||
if (isAttrCharVal)
|
||||
{
|
||||
isAttrCharVal = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
BYTES_TO_UINT16(uuid, pAttr->pUuid);
|
||||
switch (uuid)
|
||||
{
|
||||
/* Top cases include Attribute Value length */
|
||||
case ATT_UUID_CHARACTERISTIC:
|
||||
/* Set the next characteristic in database to be skipped */
|
||||
isAttrCharVal = TRUE;
|
||||
/* Fallthrough */
|
||||
case ATT_UUID_PRIMARY_SERVICE:
|
||||
case ATT_UUID_SECONDARY_SERVICE:
|
||||
case ATT_UUID_INCLUDE:
|
||||
case ATT_UUID_CHARACTERISTIC_EXT:
|
||||
length += *pAttr->pLen;
|
||||
/* Fallthrough */
|
||||
|
||||
/* All values fall through to include Attribute Type length */
|
||||
case ATT_UUID_CHAR_USER_DESC:
|
||||
case ATT_UUID_CLIENT_CHAR_CONFIG:
|
||||
case ATT_UUID_SERVER_CHAR_CONFIG:
|
||||
case ATT_UUID_AGGREGATE_FORMAT:
|
||||
if (pAttr->settings & ATTS_SET_UUID_128)
|
||||
{
|
||||
length += 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
length += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
length = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATT server.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsInit(void)
|
||||
{
|
||||
/* Initialize control block */
|
||||
WSF_QUEUE_INIT(&attsCb.groupQueue);
|
||||
attsCb.pInd = &attFcnDefault;
|
||||
attsCb.signMsgCback = (attMsgHandler_t) attEmptyHandler;
|
||||
|
||||
/* set up callback interfaces */
|
||||
attCb.pServer = &attsFcnIf;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register an authorization callback with the attribute server.
|
||||
*
|
||||
* \param cback Client callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsAuthorRegister(attsAuthorCback_t cback)
|
||||
{
|
||||
attsCb.authorCback = cback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Create hash from the database string.
|
||||
*
|
||||
* \param pKey Key for hashing.
|
||||
* \param pMsg Plaintext to hash.
|
||||
* \param msgLen Length of Plaintext data.
|
||||
*
|
||||
* \return \ref TRUE if successful, \ref FALSE if not.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AttsHashDatabaseString(uint8_t *pKey, uint8_t *pMsg, uint16_t msgLen)
|
||||
{
|
||||
return SecCmac(pKey, pMsg, msgLen, attCb.handlerId, 0, ATTS_MSG_DBH_CMAC_CMPL);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate database hash from the GATT database.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCalculateDbHash(void)
|
||||
{
|
||||
uint16_t msgLen = 0;
|
||||
uint8_t *pMsg;
|
||||
attsGroup_t *pGroup = (attsGroup_t *) attsCb.groupQueue.pHead;
|
||||
|
||||
/* Determine length of message. */
|
||||
while (pGroup != NULL)
|
||||
{
|
||||
uint8_t numAttrs = (pGroup->endHandle - pGroup->startHandle) + 1;
|
||||
|
||||
for (attsAttr_t *pAttr = pGroup->pAttr; numAttrs != 0; numAttrs--, pAttr++)
|
||||
{
|
||||
msgLen += attsIsHashableAttr(pAttr);
|
||||
}
|
||||
|
||||
pGroup = pGroup->pNext;
|
||||
}
|
||||
|
||||
/* Allocate buffer for message. */
|
||||
if ((pMsg = WsfBufAlloc(msgLen)) != NULL)
|
||||
{
|
||||
pGroup = (attsGroup_t *)attsCb.groupQueue.pHead;
|
||||
uint8_t hashingKey[16] = { 0, };
|
||||
uint8_t *p = pMsg;
|
||||
|
||||
/* For each service in services */
|
||||
while (pGroup)
|
||||
{
|
||||
uint16_t attHandle = pGroup->startHandle;
|
||||
|
||||
/* For each attribute in the service */
|
||||
for (attsAttr_t *pAttr = pGroup->pAttr; attHandle <= pGroup->endHandle; attHandle++, pAttr++)
|
||||
{
|
||||
uint16_t valLen;
|
||||
uint8_t uuidLen = 2;
|
||||
|
||||
valLen = attsIsHashableAttr(pAttr);
|
||||
if (valLen)
|
||||
{
|
||||
/* Add handle */
|
||||
UINT16_TO_BSTREAM(p, attHandle);
|
||||
|
||||
/* Add attribute type*/
|
||||
if (pAttr->settings & ATTS_SET_UUID_128)
|
||||
{
|
||||
memcpy(p, pAttr->pUuid, 16);
|
||||
p += 16;
|
||||
uuidLen = 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t uuid;
|
||||
BYTES_TO_UINT16(uuid, pAttr->pUuid);
|
||||
UINT16_TO_BSTREAM(p,uuid);
|
||||
}
|
||||
|
||||
/* Add Attribute value if required */
|
||||
if (valLen - (uuidLen + 2))
|
||||
{
|
||||
memcpy(p, pAttr->pValue, *pAttr->pLen);
|
||||
p += *pAttr->pLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pGroup = pGroup->pNext;
|
||||
}
|
||||
|
||||
/* Send to CMAC */
|
||||
if (AttsHashDatabaseString(hashingKey, pMsg, msgLen))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Assert on failure to initiate database hash generation. */
|
||||
WSF_ASSERT(FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add an attribute group to the attribute server.
|
||||
*
|
||||
* \param pGroup Pointer to an attribute group structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsAddGroup(attsGroup_t *pGroup)
|
||||
{
|
||||
attsGroup_t *pElem;
|
||||
attsGroup_t *pPrev = NULL;
|
||||
|
||||
/* task schedule lock */
|
||||
WsfTaskLock();
|
||||
|
||||
pElem = (attsGroup_t *) attsCb.groupQueue.pHead;
|
||||
|
||||
/* iterate over queue sorted by increasing handle value */
|
||||
while (pElem != NULL)
|
||||
{
|
||||
if (pGroup->startHandle < pElem->startHandle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pPrev = pElem;
|
||||
pElem = pElem->pNext;
|
||||
}
|
||||
|
||||
/* insert new group */
|
||||
WsfQueueInsert(&attsCb.groupQueue, pGroup, pPrev);
|
||||
|
||||
/* set database hash update status to true until a new hash is generated */
|
||||
attsCsfSetHashUpdateStatus(TRUE);
|
||||
|
||||
/* task schedule unlock */
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove an attribute group from the attribute server.
|
||||
*
|
||||
* \param startHandle Start handle of attribute group to be removed.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsRemoveGroup(uint16_t startHandle)
|
||||
{
|
||||
attsGroup_t *pElem;
|
||||
attsGroup_t *pPrev = NULL;
|
||||
|
||||
/* task schedule lock */
|
||||
WsfTaskLock();
|
||||
|
||||
pElem = (attsGroup_t *) attsCb.groupQueue.pHead;
|
||||
|
||||
/* find group in queue */
|
||||
while (pElem != NULL)
|
||||
{
|
||||
if (pElem->startHandle == startHandle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pPrev = pElem;
|
||||
pElem = pElem->pNext;
|
||||
}
|
||||
|
||||
/* if group found remove from queue */
|
||||
if (pElem != NULL)
|
||||
{
|
||||
WsfQueueRemove(&attsCb.groupQueue, pElem, pPrev);
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN1("AttsRemoveGroup start handle not found: 0x%04x", startHandle);
|
||||
}
|
||||
|
||||
/* set database hash update status to true until a new hash is generated */
|
||||
attsCsfSetHashUpdateStatus(TRUE);
|
||||
|
||||
/* task schedule unlock */
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set an attribute value in the attribute server.
|
||||
*
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Attribute length.
|
||||
* \param pValue Attribute value.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttsSetAttr(uint16_t handle, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
WsfTaskLock();
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify write length */
|
||||
if (valueLen > pAttr->maxLen)
|
||||
{
|
||||
err = ATT_ERR_LENGTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set attribute value */
|
||||
memcpy(pAttr->pValue, pValue, valueLen);
|
||||
|
||||
/* set the length if variable length attribute */
|
||||
if ((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0)
|
||||
{
|
||||
*(pAttr->pLen) = valueLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* else attribute not found */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
WsfTaskUnlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get an attribute value in the attribute server. Tasks should be locked before
|
||||
* calling this function and remain locked until pLen and pValue are no longer used.
|
||||
*
|
||||
* \param handle Attribute handle.
|
||||
* \param pLen Returned attribute length pointer.
|
||||
* \param pValue Returned attribute value pointer.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful or other error code if failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttsGetAttr(uint16_t handle, uint16_t *pLen, uint8_t **pValue)
|
||||
{
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* set length and value pointers */
|
||||
*pLen = *(pAttr->pLen);
|
||||
*pValue = pAttr->pValue;
|
||||
}
|
||||
/* else attribute not found */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For testing purposes only.
|
||||
*
|
||||
* \param status ATT status
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsErrorTest(uint8_t status)
|
||||
{
|
||||
attCb.errTest = status;
|
||||
}
|
||||
Vendored
+174
@@ -0,0 +1,174 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef ATTS_MAIN_H
|
||||
#define ATTS_MAIN_H
|
||||
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "att_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* ATTS event handler messages */
|
||||
enum
|
||||
{
|
||||
ATTS_MSG_IDLE_TIMEOUT = ATTS_MSG_START,
|
||||
ATTS_MSG_API_VALUE_IND_NTF,
|
||||
ATTS_MSG_IND_TIMEOUT,
|
||||
ATTS_MSG_SIGN_CMAC_CMPL,
|
||||
ATTS_MSG_DBH_CMAC_CMPL
|
||||
};
|
||||
|
||||
/*!
|
||||
* Data buffer format for API request messages:
|
||||
*
|
||||
* | attsPktParam_t | ATT request data |
|
||||
* | bytes 0 to 7 | bytes 8 - |
|
||||
*/
|
||||
|
||||
/* API parameters */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t len;
|
||||
uint16_t handle;
|
||||
} attsPktParam_t;
|
||||
|
||||
/* verify attsPktParam_t will work in data buffer format described above */
|
||||
WSF_CT_ASSERT(sizeof(attsPktParam_t) <= L2C_PAYLOAD_START);
|
||||
|
||||
/* API message structure */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
attsPktParam_t *pPkt;
|
||||
} attsApiMsg_t;
|
||||
|
||||
/* Connection control block for indications/notifications */
|
||||
typedef struct
|
||||
{
|
||||
wsfTimer_t outIndTimer; /* Outstanding indication timer */
|
||||
attCcb_t *pMainCcb; /* Pointer to ATT main CCB */
|
||||
uint16_t outIndHandle; /* Waiting for confirm from peer for this indication handle */
|
||||
uint16_t pendIndHandle; /* Callback to application pending for this indication handle */
|
||||
uint16_t pendNtfHandle[ATT_NUM_SIMUL_NTF]; /* Callback to application pending for this notification handle */
|
||||
} attsIndCcb_t;
|
||||
|
||||
/* Control block for indications/notifications */
|
||||
typedef struct
|
||||
{
|
||||
attsIndCcb_t ccb[DM_CONN_MAX];
|
||||
} attsIndCb_t;
|
||||
|
||||
/* Client characteristic configuration descriptor callback type
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param method Read or write.
|
||||
* \param handle Attribute handle of the descriptor.
|
||||
* \param pValue Pointer to the attribute value of the descriptor.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
typedef uint8_t (*attsCccFcn_t)(dmConnId_t connId, uint8_t method, uint16_t handle, uint8_t *pValue);
|
||||
|
||||
/* Main control block of the ATTS subsystem */
|
||||
typedef struct
|
||||
{
|
||||
wsfQueue_t groupQueue; /* Queue of attribute groups */
|
||||
attFcnIf_t const *pInd; /* Indication callback interface */
|
||||
attMsgHandler_t signMsgCback; /* Signed data callback interface */
|
||||
attsAuthorCback_t authorCback; /* Authorization callback */
|
||||
attsCccFcn_t cccCback; /* CCC callback */
|
||||
} attsCb_t;
|
||||
|
||||
/* PDU processing function type */
|
||||
typedef void (*attsProcFcn_t)(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
|
||||
/* CSF Control block */
|
||||
typedef struct
|
||||
{
|
||||
attsCsfRec_t attsCsfTable[DM_CONN_MAX]; /* connected clients' supported features record table. */
|
||||
attsCsfWriteCback_t writeCback; /* Write callback. */
|
||||
uint8_t isHashUpdating; /* Database hash update status. */
|
||||
} attsCsfCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* PDU processing function lookup table, indexed by method */
|
||||
extern attsProcFcn_t attsProcFcnTbl[ATT_METHOD_SIGNED_WRITE_CMD+1];
|
||||
|
||||
/* Control block for indications/notifications */
|
||||
extern attsIndCb_t attsIndCb;
|
||||
|
||||
/* Control block */
|
||||
extern attsCb_t attsCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
void attsErrRsp(uint16_t handle, uint8_t opcode, uint16_t attHandle, uint8_t reason);
|
||||
void attsClearPrepWrites(attCcb_t *pCcb);
|
||||
bool_t attsUuidCmp(attsAttr_t *pAttr, uint8_t uuidLen, uint8_t *pUuid);
|
||||
bool_t attsUuid16Cmp(uint8_t *pUuid16, uint8_t uuidLen, uint8_t *pUuid);
|
||||
attsAttr_t *attsFindByHandle(uint16_t handle, attsGroup_t **pAttrGroup);
|
||||
uint16_t attsFindInRange(uint16_t startHandle, uint16_t endHandle, attsAttr_t **pAttr);
|
||||
uint16_t attsFindUuidInRange(uint16_t startHandle, uint16_t endHandle, uint8_t uuidLen,
|
||||
uint8_t *pUuid, attsAttr_t **pAttr, attsGroup_t **pAttrGroup);
|
||||
uint8_t attsPermissions(dmConnId_t connId, uint8_t permit, uint16_t handle, uint8_t permissions);
|
||||
void attsDiscBusy(attCcb_t *pCcb);
|
||||
void attsCheckPendDbHashReadRsp(void);
|
||||
void attsProcessDatabaseHashUpdate(secCmacMsg_t *pMsg);
|
||||
uint16_t attsIsHashableAttr(attsAttr_t *pAttr);
|
||||
|
||||
void attsProcMtuReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcFindInfoReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcFindTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcReadTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcReadReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcReadBlobReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcReadMultReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcReadGroupTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcWrite(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcPrepWriteReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcExecWriteReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcValueCnf(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
|
||||
uint8_t attsCsfActClientState(uint16_t handle, uint8_t opcode, uint8_t *pPacket);
|
||||
uint8_t attsCsfIsClientChangeAware(dmConnId_t connId, uint16_t handle);
|
||||
void attsCsfSetHashUpdateStatus(bool_t isUpdating);
|
||||
uint8_t attsCsfGetHashUpdateStatus(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* ATTS_MAIN_H */
|
||||
Vendored
+469
@@ -0,0 +1,469 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server mandatory PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_math.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Compare the given attribute's UUID to the given UUID.
|
||||
*
|
||||
* \param pAttr Pointer to attribute.
|
||||
* \param uuidLen UUID length, either 2 or 16.
|
||||
* \param pUuid Pointer to UUID.
|
||||
*
|
||||
* \return TRUE of UUIDs match, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t attsUuidCmp(attsAttr_t *pAttr, uint8_t uuidLen, uint8_t *pUuid)
|
||||
{
|
||||
/* if both uuids are the same length */
|
||||
if ((((pAttr->settings & ATTS_SET_UUID_128) == 0) && (uuidLen == ATT_16_UUID_LEN)) ||
|
||||
(((pAttr->settings & ATTS_SET_UUID_128) != 0) && (uuidLen == ATT_128_UUID_LEN)))
|
||||
{
|
||||
/* simply compare the data */
|
||||
return (memcmp(pAttr->pUuid, pUuid, uuidLen) == 0);
|
||||
}
|
||||
/* else we need to convert one of the uuids */
|
||||
else if (((pAttr->settings & ATTS_SET_UUID_128) == 0) && (uuidLen == ATT_128_UUID_LEN))
|
||||
{
|
||||
return attUuidCmp16to128(pAttr->pUuid, pUuid);
|
||||
}
|
||||
else
|
||||
{
|
||||
return attUuidCmp16to128(pUuid, pAttr->pUuid);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Compare the given 16 bit UUID to the given UUID.
|
||||
*
|
||||
* \param pUuid16 Pointer to the 16 bit UUID.
|
||||
* \param uuidLen UUID length, either 2 or 16.
|
||||
* \param pUuid Pointer to UUID.
|
||||
*
|
||||
* \return TRUE of UUIDs match, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t attsUuid16Cmp(uint8_t *pUuid16, uint8_t uuidLen, uint8_t *pUuid)
|
||||
{
|
||||
if (uuidLen == ATT_16_UUID_LEN)
|
||||
{
|
||||
return ((pUuid16[0] == pUuid[0]) && (pUuid16[1] == pUuid[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
return attUuidCmp16to128(pUuid16, pUuid);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find an attribute with the given handle.
|
||||
*
|
||||
* \param handle Attribute handle.
|
||||
* \param pAttrGroup Return value pointer to found attribute's group.
|
||||
*
|
||||
* \return Pointer to attribute if found, othewise NULL.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
attsAttr_t *attsFindByHandle(uint16_t handle, attsGroup_t **pAttrGroup)
|
||||
{
|
||||
attsGroup_t *pGroup;
|
||||
|
||||
/* iterate over attribute group list */
|
||||
for (pGroup = attsCb.groupQueue.pHead; pGroup != NULL; pGroup = pGroup->pNext)
|
||||
{
|
||||
/* if start handle within handle range of group */
|
||||
if ((handle >= pGroup->startHandle) && (handle <= pGroup->endHandle))
|
||||
{
|
||||
/* index by handle into attribute array to return attribute */
|
||||
*pAttrGroup = pGroup;
|
||||
return &pGroup->pAttr[handle - pGroup->startHandle];
|
||||
}
|
||||
}
|
||||
|
||||
/* handle not found */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find the first attribute within the given handle range.
|
||||
*
|
||||
* \param startHandle Starting attribute handle.
|
||||
* \param endHandle Ending attribute handle.
|
||||
* \param pAttr Return value pointer to found attribute.
|
||||
*
|
||||
* \return Attribute handle or ATT_HANDLE_NONE if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t attsFindInRange(uint16_t startHandle, uint16_t endHandle, attsAttr_t **pAttr)
|
||||
{
|
||||
attsGroup_t *pGroup;
|
||||
|
||||
/* iterate over attribute group list */
|
||||
for (pGroup = attsCb.groupQueue.pHead; pGroup != NULL; pGroup = pGroup->pNext)
|
||||
{
|
||||
/* if start handle is less than group start handle but handle range is within group */
|
||||
if ((startHandle < pGroup->startHandle) && (endHandle >= pGroup->startHandle))
|
||||
{
|
||||
/* set start handle to first handle in group */
|
||||
startHandle = pGroup->startHandle;
|
||||
}
|
||||
|
||||
/* if start handle within handle range of group */
|
||||
if ((startHandle >= pGroup->startHandle) && (startHandle <= pGroup->endHandle))
|
||||
{
|
||||
/* index by handle into attribute array to return attribute */
|
||||
*pAttr = &pGroup->pAttr[startHandle - pGroup->startHandle];
|
||||
return startHandle;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle within range not found */
|
||||
return ATT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform required permission and security checks when reading or writing an attribute.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
* \param permit Either ATTS_PERMIT_READ or ATTS_PERMIT_WRITE.
|
||||
* \param handle Attribute handle.
|
||||
* \param permissions Attribute permissions.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful or error code on failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t attsPermissions(dmConnId_t connId, uint8_t permit, uint16_t handle, uint8_t permissions)
|
||||
{
|
||||
uint8_t secLevel;
|
||||
|
||||
/* verify read or write permissions */
|
||||
if (!(permissions & permit))
|
||||
{
|
||||
return (permit == ATTS_PERMIT_READ) ? ATT_ERR_READ : ATT_ERR_WRITE;
|
||||
}
|
||||
|
||||
/* convert write permissions to read permissions for easier masking */
|
||||
if (permit == ATTS_PERMIT_WRITE)
|
||||
{
|
||||
permissions >>= 4;
|
||||
}
|
||||
|
||||
/* if no security requirements return quickly */
|
||||
if ((permissions & (ATTS_PERMIT_READ_AUTH | ATTS_PERMIT_READ_AUTHORIZ | ATTS_PERMIT_READ_ENC)) == 0)
|
||||
{
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/* get security level for this connection */
|
||||
secLevel = DmConnSecLevel(connId);
|
||||
|
||||
/* check if encryption required */
|
||||
if ((permissions & ATTS_PERMIT_READ_ENC) && (secLevel == DM_SEC_LEVEL_NONE))
|
||||
{
|
||||
return ATT_ERR_AUTH;
|
||||
}
|
||||
|
||||
/* check if encryption required with authenticated key */
|
||||
if (((permissions & (ATTS_PERMIT_READ_AUTH | ATTS_PERMIT_READ_ENC)) ==
|
||||
(ATTS_PERMIT_READ_AUTH | ATTS_PERMIT_READ_ENC)) && (secLevel < DM_SEC_LEVEL_ENC_AUTH))
|
||||
{
|
||||
return ATT_ERR_AUTH;
|
||||
}
|
||||
|
||||
/* authorization check */
|
||||
if (permissions & ATTS_PERMIT_READ_AUTHORIZ)
|
||||
{
|
||||
if (attsCb.authorCback == NULL)
|
||||
{
|
||||
return ATT_ERR_AUTHOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (*attsCb.authorCback)(connId, permit, handle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process an MTU request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcMtuReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *p;
|
||||
uint16_t mtu;
|
||||
uint16_t localMtu;
|
||||
uint8_t *pRsp;
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
|
||||
/* parse mtu */
|
||||
BYTES_TO_UINT16(mtu, p);
|
||||
|
||||
/* verify */
|
||||
if (mtu < ATT_DEFAULT_MTU)
|
||||
{
|
||||
mtu = ATT_DEFAULT_MTU;
|
||||
}
|
||||
|
||||
/* get desired MTU */
|
||||
localMtu = WSF_MIN(pAttCfg->mtu, (HciGetMaxRxAclLen() - L2C_HDR_LEN));
|
||||
|
||||
/* send response */
|
||||
if ((pRsp = attMsgAlloc(L2C_PAYLOAD_START + ATT_MTU_RSP_LEN)) != NULL)
|
||||
{
|
||||
p = pRsp + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_MTU_RSP);
|
||||
UINT16_TO_BSTREAM(p, localMtu);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, ATT_MTU_RSP_LEN, pRsp);
|
||||
}
|
||||
|
||||
/* set mtu for the connection */
|
||||
attSetMtu(pCcb, mtu, localMtu);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a find information request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcFindInfoReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
uint16_t handle;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handles */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(startHandle, pPacket);
|
||||
BSTREAM_TO_UINT16(endHandle, pPacket);
|
||||
|
||||
/* verify handles */
|
||||
if ((startHandle == 0) || (startHandle > endHandle))
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
if (!err)
|
||||
{
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_FIND_INFO_RSP);
|
||||
|
||||
/* set result format */
|
||||
UINT8_TO_BSTREAM(p, ATT_FIND_HANDLE_16_UUID);
|
||||
|
||||
/* find attributes within handle range */
|
||||
handle = startHandle;
|
||||
while ((handle = attsFindInRange(handle, endHandle, &pAttr)) != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* copy handle and UUID into response buffer */
|
||||
|
||||
/* if 128 bit UUID */
|
||||
if (pAttr->settings & ATTS_SET_UUID_128)
|
||||
{
|
||||
/* if this is the first result */
|
||||
if (p == (pBuf + L2C_PAYLOAD_START + 2))
|
||||
{
|
||||
p--;
|
||||
UINT8_TO_BSTREAM(p, ATT_FIND_HANDLE_128_UUID);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pAttr->pUuid, ATT_128_UUID_LEN);
|
||||
p += ATT_128_UUID_LEN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* else 16 bit UUID */
|
||||
else
|
||||
{
|
||||
/* check if result fits */
|
||||
if ((p + ATT_16_UUID_LEN + sizeof(uint16_t)) <=
|
||||
(pBuf + pCcb->mtu + L2C_PAYLOAD_START))
|
||||
{
|
||||
/* copy result */
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
UINT8_TO_BSTREAM(p, pAttr->pUuid[0]);
|
||||
UINT8_TO_BSTREAM(p, pAttr->pUuid[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* response buffer full, we're done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* special case of handle at max range */
|
||||
if (handle == ATT_HANDLE_MAX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* try next handle */
|
||||
if (++handle > endHandle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if no results found set error, free buffer */
|
||||
if (p == (pBuf + L2C_PAYLOAD_START + 2))
|
||||
{
|
||||
WsfMsgFree(pBuf);
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer allocation failed */
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
/* set channel as busy for service discovery */
|
||||
attsDiscBusy(pCcb);
|
||||
|
||||
/* if no error send response, else send error */
|
||||
if (!err)
|
||||
{
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (p - (pBuf + L2C_PAYLOAD_START)), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_FIND_INFO_REQ, startHandle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a read request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcReadReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t handle;
|
||||
uint16_t readLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handle */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(handle, pPacket);
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify permissions */
|
||||
if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions)) == ATT_SUCCESS)
|
||||
{
|
||||
/* call read callback if desired */
|
||||
if ((pAttr->settings & ATTS_SET_READ_CBACK) &&
|
||||
(pGroup->readCback != NULL))
|
||||
{
|
||||
err = (*pGroup->readCback)(pCcb->connId, handle, ATT_PDU_READ_REQ, 0, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_READ, handle, pAttr->pValue);
|
||||
}
|
||||
|
||||
if (err == ATT_SUCCESS)
|
||||
{
|
||||
/* determine length of data to read */
|
||||
readLen = (*pAttr->pLen < (pCcb->mtu - ATT_READ_RSP_LEN)) ?
|
||||
*pAttr->pLen : (pCcb->mtu - ATT_READ_RSP_LEN);
|
||||
|
||||
/* Allocate response buffer */
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_READ_RSP_LEN + readLen)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_RSP);
|
||||
memcpy(p, pAttr->pValue, readLen);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (ATT_READ_RSP_LEN + readLen), pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* else invalid handle */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_REQ, handle, err);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+833
@@ -0,0 +1,833 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server optional read PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find the first attribute within the given handle range with a matching UUID.
|
||||
*
|
||||
* \param startHandle Starting attribute handle.
|
||||
* \param endHandle Ending attribute handle.
|
||||
* \param uuidLen UUID length, either 2 or 16.
|
||||
* \param pUUID Pointer to UUID.
|
||||
* \param pAttr Return value pointer to found attribute.
|
||||
* \param pAttrGroup Return value pointer to found attribute's group.
|
||||
*
|
||||
* \return Attribute handle or ATT_HANDLE_NONE if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t attsFindUuidInRange(uint16_t startHandle, uint16_t endHandle, uint8_t uuidLen,
|
||||
uint8_t *pUuid, attsAttr_t **pAttr, attsGroup_t **pAttrGroup)
|
||||
{
|
||||
attsGroup_t *pGroup;
|
||||
|
||||
/* iterate over attribute group list */
|
||||
for (pGroup = attsCb.groupQueue.pHead; pGroup != NULL; pGroup = pGroup->pNext)
|
||||
{
|
||||
/* if start handle is less than group start handle but handle range is within group */
|
||||
if ((startHandle < pGroup->startHandle) && (endHandle >= pGroup->startHandle))
|
||||
{
|
||||
/* set start handle to first handle in group */
|
||||
startHandle = pGroup->startHandle;
|
||||
}
|
||||
|
||||
/* if start handle within handle range of group */
|
||||
if ((startHandle >= pGroup->startHandle) && (startHandle <= pGroup->endHandle))
|
||||
{
|
||||
/* compare uuid with each attribute in group */
|
||||
*pAttr = &pGroup->pAttr[startHandle - pGroup->startHandle];
|
||||
while ((startHandle <= pGroup->endHandle) && (startHandle <= endHandle))
|
||||
{
|
||||
/* compare uuid in attribute */
|
||||
if (attsUuidCmp(*pAttr, uuidLen, pUuid))
|
||||
{
|
||||
*pAttrGroup = pGroup;
|
||||
return startHandle;
|
||||
}
|
||||
|
||||
/* special case of max handle value */
|
||||
if (startHandle == ATT_HANDLE_MAX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
startHandle++;
|
||||
(*pAttr)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* no match found */
|
||||
return ATT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find the handle of the last attribute in a service group.
|
||||
*
|
||||
* \param startHandle Starting attribute handle of service.
|
||||
*
|
||||
* \return Service group end handle.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t attsFindServiceGroupEnd(uint16_t startHandle)
|
||||
{
|
||||
attsGroup_t *pGroup;
|
||||
attsAttr_t *pAttr;
|
||||
uint16_t prevHandle;
|
||||
uint8_t primSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PRIMARY_SERVICE)};
|
||||
uint8_t secSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SECONDARY_SERVICE)};
|
||||
|
||||
/* special case for max handle */
|
||||
if (startHandle == ATT_HANDLE_MAX)
|
||||
{
|
||||
return ATT_HANDLE_MAX;
|
||||
}
|
||||
|
||||
prevHandle = startHandle;
|
||||
startHandle++;
|
||||
|
||||
/* iterate over attribute group list */
|
||||
for (pGroup = attsCb.groupQueue.pHead; pGroup != NULL; pGroup = pGroup->pNext)
|
||||
{
|
||||
/* if start handle is less than group start handle */
|
||||
if (startHandle < pGroup->startHandle)
|
||||
{
|
||||
/* set start handle to first handle in group */
|
||||
startHandle = pGroup->startHandle;
|
||||
}
|
||||
|
||||
/* if start handle within handle range of group */
|
||||
if (startHandle <= pGroup->endHandle)
|
||||
{
|
||||
/* compare uuid with each attribute in group */
|
||||
pAttr = &pGroup->pAttr[startHandle - pGroup->startHandle];
|
||||
while (startHandle <= pGroup->endHandle)
|
||||
{
|
||||
/* compare uuid in attribute to service uuids */
|
||||
if (attsUuidCmp(pAttr, ATT_16_UUID_LEN, primSvcUuid) ||
|
||||
attsUuidCmp(pAttr, ATT_16_UUID_LEN, secSvcUuid))
|
||||
{
|
||||
/* found next service; return handle of previous attribute */
|
||||
return prevHandle;
|
||||
}
|
||||
|
||||
/* special case of max handle value */
|
||||
if (startHandle == ATT_HANDLE_MAX)
|
||||
{
|
||||
return ATT_HANDLE_MAX;
|
||||
}
|
||||
|
||||
prevHandle = startHandle;
|
||||
startHandle++;
|
||||
pAttr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* next service not found; return 0xFFFF as the last handle in the database */
|
||||
return ATT_HANDLE_MAX;
|
||||
}
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a read blob request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcReadBlobReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t handle;
|
||||
uint16_t offset;
|
||||
uint16_t readLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handle and offset */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(handle, pPacket);
|
||||
BSTREAM_TO_UINT16(offset, pPacket);
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify permissions */
|
||||
if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
/* err has been set; fail */
|
||||
}
|
||||
/* verify offset */
|
||||
else if (offset > *pAttr->pLen)
|
||||
{
|
||||
err = ATT_ERR_OFFSET;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* call read callback if desired */
|
||||
if ((pAttr->settings & ATTS_SET_READ_CBACK) &&
|
||||
(pGroup->readCback != NULL))
|
||||
{
|
||||
err = (*pGroup->readCback)(pCcb->connId, handle, ATT_PDU_READ_BLOB_REQ, offset, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_READ, handle, pAttr->pValue);
|
||||
}
|
||||
|
||||
if (err == ATT_SUCCESS)
|
||||
{
|
||||
/* determine length of data to read */
|
||||
readLen = ((*pAttr->pLen - offset) < (pCcb->mtu - ATT_READ_BLOB_RSP_LEN)) ?
|
||||
(*pAttr->pLen - offset) : (pCcb->mtu - ATT_READ_BLOB_RSP_LEN);
|
||||
|
||||
/* Allocate response buffer */
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_READ_BLOB_RSP_LEN + readLen)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_BLOB_RSP);
|
||||
memcpy(p, (pAttr->pValue + offset), readLen);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (ATT_READ_BLOB_RSP_LEN + readLen), pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* else invalid handle */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_BLOB_REQ, handle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a find by type value request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcFindTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
uint8_t *pUuid;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
uint16_t handle;
|
||||
uint16_t nextHandle;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handles and uuid; pPacket then points to the value in the request */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(startHandle, pPacket);
|
||||
BSTREAM_TO_UINT16(endHandle, pPacket);
|
||||
pUuid = pPacket;
|
||||
pPacket += ATT_16_UUID_LEN;
|
||||
|
||||
/* set len to the value length */
|
||||
len -= ATT_FIND_TYPE_REQ_LEN;
|
||||
|
||||
/* verify handles */
|
||||
if ((startHandle == 0) || (startHandle > endHandle))
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
if (!err)
|
||||
{
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_FIND_TYPE_RSP);
|
||||
|
||||
/* find attributes with matching uuid within handle range */
|
||||
handle = startHandle;
|
||||
while ((handle = attsFindUuidInRange(handle, endHandle, ATT_16_UUID_LEN,
|
||||
pUuid, &pAttr, &pGroup)) != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* if value and length matches */
|
||||
if ((pAttr->permissions & ATTS_PERMIT_READ) &&
|
||||
((len == 0) ||
|
||||
((len == *pAttr->pLen) && (memcmp(pPacket, pAttr->pValue, len) == 0))))
|
||||
{
|
||||
/* if uuid in request is for primary service */
|
||||
if (pUuid[0] == UINT16_TO_BYTE0(ATT_UUID_PRIMARY_SERVICE) &&
|
||||
pUuid[1] == UINT16_TO_BYTE1(ATT_UUID_PRIMARY_SERVICE))
|
||||
{
|
||||
/* next handle is service group end handle */
|
||||
nextHandle = attsFindServiceGroupEnd(handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* for any other uuid next handle is same as found handle */
|
||||
nextHandle = handle;
|
||||
}
|
||||
|
||||
/* copy result into response buffer; first check if it fits */
|
||||
if ((p + (sizeof(uint16_t) * 2)) <= (pBuf + pCcb->mtu + L2C_PAYLOAD_START))
|
||||
{
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
UINT16_TO_BSTREAM(p, nextHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer full, we're done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* value doesn't match; still need to set next handle */
|
||||
else
|
||||
{
|
||||
nextHandle = handle;
|
||||
}
|
||||
|
||||
/* check if handle has reached end */
|
||||
if ((nextHandle >= endHandle) || (nextHandle == ATT_HANDLE_MAX))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* try next handle */
|
||||
handle = nextHandle + 1;
|
||||
}
|
||||
|
||||
/* if no results found set error, free buffer */
|
||||
if (p == (pBuf + L2C_PAYLOAD_START + 1))
|
||||
{
|
||||
WsfMsgFree(pBuf);
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer allocation failed */
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
/* set channel as busy for service discovery */
|
||||
attsDiscBusy(pCcb);
|
||||
|
||||
/* if no error send response, else send error */
|
||||
if (!err)
|
||||
{
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (p - (pBuf + L2C_PAYLOAD_START)), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_FIND_TYPE_REQ, startHandle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a read by type request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcReadTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
uint16_t handle;
|
||||
uint8_t uuidLen;
|
||||
uint8_t attLen;
|
||||
uint8_t cbackErr = ATT_SUCCESS;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handles; pPacket then points to the uuid */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(startHandle, pPacket);
|
||||
BSTREAM_TO_UINT16(endHandle, pPacket);
|
||||
|
||||
/* get and verify uuid length */
|
||||
uuidLen = len - ATT_READ_TYPE_REQ_LEN;
|
||||
if (!((uuidLen == ATT_16_UUID_LEN) || (uuidLen == ATT_128_UUID_LEN)))
|
||||
{
|
||||
err = ATT_ERR_INVALID_PDU;
|
||||
}
|
||||
/* verify handles */
|
||||
else if ((startHandle == 0) || (startHandle > endHandle))
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
if (!err)
|
||||
{
|
||||
/* find first attribute with matching uuid within handle range */
|
||||
handle = attsFindUuidInRange(startHandle, endHandle, uuidLen, pPacket, &pAttr, &pGroup);
|
||||
startHandle = handle;
|
||||
|
||||
if (handle == ATT_HANDLE_NONE)
|
||||
{
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
/* check permissions */
|
||||
else if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
/* err is set above */
|
||||
}
|
||||
/* check if read callback should be called */
|
||||
else if ((pAttr->settings & ATTS_SET_READ_CBACK) &&
|
||||
(pGroup->readCback != NULL))
|
||||
{
|
||||
err = (*pGroup->readCback)(pCcb->connId, handle, ATT_PDU_READ_TYPE_REQ, 0, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_READ, handle, pAttr->pValue);
|
||||
}
|
||||
|
||||
if (err == ATT_SUCCESS)
|
||||
{
|
||||
/* Check if UUID is the Database Hash Characteristic Value and the value is being
|
||||
* re-calculated
|
||||
*/
|
||||
if ((memcmp(pPacket, attGattDbhChUuid, ATT_16_UUID_LEN) == 0) && attsCsfGetHashUpdateStatus())
|
||||
{
|
||||
/* Store info and return */
|
||||
pCcb->pPendDbHashRsp = WsfBufAlloc(sizeof(attPendDbHashRsp_t));
|
||||
if (pCcb->pPendDbHashRsp)
|
||||
{
|
||||
pCcb->pPendDbHashRsp->startHandle = startHandle;
|
||||
pCcb->pPendDbHashRsp->handle = handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_TYPE_REQ, startHandle, ATT_ERR_RESOURCES);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_TYPE_RSP);
|
||||
|
||||
/* get length of this first attribute */
|
||||
attLen = (*pAttr->pLen < (pCcb->mtu - ATT_READ_TYPE_RSP_LEN - sizeof(uint16_t))) ?
|
||||
*pAttr->pLen : (pCcb->mtu - ATT_READ_TYPE_RSP_LEN - sizeof(uint16_t));
|
||||
|
||||
/* set length parameter in response message */
|
||||
UINT8_TO_BSTREAM(p, attLen + sizeof(uint16_t));
|
||||
|
||||
/* copy result to response message */
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pAttr->pValue, attLen);
|
||||
p += attLen;
|
||||
|
||||
/* look for additional attributes */
|
||||
handle++;
|
||||
while ((handle = attsFindUuidInRange(handle, endHandle, uuidLen,
|
||||
pPacket, &pAttr, &pGroup)) != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* call read callback if desired */
|
||||
if ((pAttr->settings & ATTS_SET_READ_CBACK) &&
|
||||
(pGroup->readCback != NULL))
|
||||
{
|
||||
cbackErr = (*pGroup->readCback)(pCcb->connId, handle, ATT_PDU_READ_TYPE_REQ, 0, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
cbackErr = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_READ, handle, pAttr->pValue);
|
||||
}
|
||||
|
||||
/* verify no error from read callback
|
||||
* verify length is same as first found attribute
|
||||
* verify attribute permissions
|
||||
*/
|
||||
if ((cbackErr == ATT_SUCCESS) &&
|
||||
(*pAttr->pLen == attLen) &&
|
||||
(attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions) == ATT_SUCCESS))
|
||||
{
|
||||
/* copy result into response buffer; first check if it fits */
|
||||
if ((p + attLen + sizeof(uint16_t)) <= (pBuf + pCcb->mtu + L2C_PAYLOAD_START))
|
||||
{
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pAttr->pValue, attLen);
|
||||
p += attLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer full, we're done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* problem with read callback, length, or permissions; send what we've got so far */
|
||||
break;
|
||||
}
|
||||
|
||||
/* special case of handle at max range */
|
||||
if (handle == ATT_HANDLE_MAX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* try next handle */
|
||||
if (++handle > endHandle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer allocation failed */
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if no error send response, else send error */
|
||||
if (!err)
|
||||
{
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (p - (pBuf + L2C_PAYLOAD_START)), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_TYPE_REQ, startHandle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a read multiple request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcReadMultReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
uint8_t *pEnd;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t handle = ATT_HANDLE_NONE;
|
||||
uint16_t readLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* points to end of payload */
|
||||
pEnd = pPacket + L2C_PAYLOAD_START + len;
|
||||
|
||||
/* points to first handle */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_MULT_RSP);
|
||||
|
||||
/* while there are handles remaining and there is space in response buffer */
|
||||
while (pPacket < pEnd)
|
||||
{
|
||||
/* parse handle */
|
||||
BSTREAM_TO_UINT16(handle, pPacket);
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) == NULL)
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* verify permissions */
|
||||
if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* call read callback if desired */
|
||||
if ((pAttr->settings & ATTS_SET_READ_CBACK) &&
|
||||
(pGroup->readCback != NULL))
|
||||
{
|
||||
err = (*pGroup->readCback)(pCcb->connId, handle, ATT_PDU_READ_MULT_REQ, 0, pAttr);
|
||||
if (err != ATT_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_READ, handle, pAttr->pValue);
|
||||
if (err != ATT_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p < (pBuf + pCcb->mtu + L2C_PAYLOAD_START))
|
||||
{
|
||||
/* calculate remaining space in response buffer */
|
||||
readLen = (pBuf + pCcb->mtu + L2C_PAYLOAD_START) - p;
|
||||
|
||||
/* actual length is minimum of remaining space and attribute length */
|
||||
readLen = (*pAttr->pLen < readLen) ? *pAttr->pLen : readLen;
|
||||
|
||||
/* copy attribute to response buffer */
|
||||
memcpy(p, pAttr->pValue, readLen);
|
||||
p += readLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer allocation failed */
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
|
||||
/* if no error send response, else send error */
|
||||
if (!err)
|
||||
{
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (p - (pBuf + L2C_PAYLOAD_START)), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* discard response buffer */
|
||||
if (pBuf != NULL)
|
||||
{
|
||||
WsfMsgFree(pBuf);
|
||||
}
|
||||
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_MULT_REQ, handle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a read group by type request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcReadGroupTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf = NULL;
|
||||
uint8_t *p = NULL;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
uint16_t handle;
|
||||
uint8_t uuidLen;
|
||||
uint8_t attLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
uint8_t primSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PRIMARY_SERVICE)};
|
||||
|
||||
/* parse handles; pPacket then points to the uuid */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(startHandle, pPacket);
|
||||
BSTREAM_TO_UINT16(endHandle, pPacket);
|
||||
|
||||
/* get and verify uuid length */
|
||||
uuidLen = len - ATT_READ_GROUP_TYPE_REQ_LEN;
|
||||
if (!((uuidLen == ATT_16_UUID_LEN) || (uuidLen == ATT_128_UUID_LEN)))
|
||||
{
|
||||
err = ATT_ERR_INVALID_PDU;
|
||||
}
|
||||
/* verify handles */
|
||||
else if ((startHandle == 0) || (startHandle > endHandle))
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
/* verify uuid is primary service group */
|
||||
else if (!attsUuid16Cmp(primSvcUuid, uuidLen, pPacket))
|
||||
{
|
||||
err = ATT_ERR_GROUP_TYPE;
|
||||
}
|
||||
|
||||
if (!err)
|
||||
{
|
||||
/* find first attribute with matching uuid within handle range */
|
||||
handle = attsFindUuidInRange(startHandle, endHandle, uuidLen, pPacket, &pAttr, &pGroup);
|
||||
|
||||
if (handle == ATT_HANDLE_NONE)
|
||||
{
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
/* check permissions */
|
||||
else if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
startHandle = handle; /* this handle is returned in error response */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_GROUP_TYPE_RSP);
|
||||
|
||||
/* get length of this first attribute */
|
||||
attLen = (*pAttr->pLen < (pCcb->mtu - ATT_READ_GROUP_TYPE_RSP_LEN - (2 * sizeof(uint16_t)))) ?
|
||||
*pAttr->pLen : (pCcb->mtu - ATT_READ_GROUP_TYPE_RSP_LEN - (2 * sizeof(uint16_t)));
|
||||
|
||||
/* set length parameter in response message */
|
||||
UINT8_TO_BSTREAM(p, attLen + (2 * sizeof(uint16_t)));
|
||||
|
||||
/* copy handle to response message */
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
/* get end group handle and copy it to response message */
|
||||
handle = attsFindServiceGroupEnd(handle);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
/* copy the attribute value to response message */
|
||||
memcpy(p, pAttr->pValue, attLen);
|
||||
p += attLen;
|
||||
|
||||
/* look for additional attributes */
|
||||
while (TRUE)
|
||||
{
|
||||
/* special case of handle at max range */
|
||||
if (handle == ATT_HANDLE_MAX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* increment to next handle */
|
||||
if (++handle > endHandle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* find next matching handle */
|
||||
if ((handle = attsFindUuidInRange(handle, endHandle, uuidLen,
|
||||
pPacket, &pAttr, &pGroup)) == ATT_HANDLE_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* verify length is same as first found attribute
|
||||
* verify attribute permissions
|
||||
*/
|
||||
if ((*pAttr->pLen == attLen) &&
|
||||
(attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions) == ATT_SUCCESS))
|
||||
{
|
||||
/* copy result into response buffer; first check if it fits */
|
||||
if ((p + attLen + (2 * sizeof(uint16_t))) <=
|
||||
(pBuf + pCcb->mtu + L2C_PAYLOAD_START))
|
||||
{
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
handle = attsFindServiceGroupEnd(handle);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pAttr->pValue, attLen);
|
||||
p += attLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer full, we're done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* problem with either length or permissions; send what we've got so far */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer allocation failed */
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* set channel as busy for service discovery */
|
||||
attsDiscBusy(pCcb);
|
||||
|
||||
/* if no error send response, else send error */
|
||||
if (!err)
|
||||
{
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (p - (pBuf + L2C_PAYLOAD_START)), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_GROUP_TYPE_REQ, startHandle, err);
|
||||
}
|
||||
}
|
||||
Vendored
+402
@@ -0,0 +1,402 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server signed PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "sec_api.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/calc128.h"
|
||||
#include "util/wstr.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
#include "att_sign.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Signed data buffer structure */
|
||||
typedef struct attsSignBuf_tag
|
||||
{
|
||||
struct attsSignBuf_tag *pNext; /* pointer to next in queue */
|
||||
attCcb_t *pCcb; /* ATT CCB associated with the packet */
|
||||
uint16_t handle; /* ATT handle */
|
||||
uint16_t writeLen; /* length of data to write */
|
||||
dmConnId_t connId; /* Connection ID associated with the packet */
|
||||
uint8_t packet[1]; /* packet */
|
||||
} attsSignBuf_t;
|
||||
|
||||
/* ATTS signed PDU connection control block */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t signCounter; /* sign counter for this connection */
|
||||
uint8_t *pCsrk; /* signing key for this connection */
|
||||
attsSignBuf_t *pBuf; /* current data being processed */
|
||||
bool_t authenticated; /* Indicate if the CSRK is authenticated or not */
|
||||
} attsSignCcb_t;
|
||||
|
||||
/* ATTS signed PDU control block */
|
||||
typedef struct
|
||||
{
|
||||
attsSignCcb_t ccb[DM_CONN_MAX];
|
||||
wsfQueue_t msgQueue;
|
||||
} attsSignCb_t;
|
||||
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
static attsSignCb_t attsSignCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the signing connection control block for the connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to control block.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static attsSignCcb_t *attsSignCcbByConnId(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return &attsSignCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start processing of data for signed write.
|
||||
*
|
||||
* \param pCcb Signed data CCB.
|
||||
* \param pBuf Signed data buffer structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsSignedWriteStart(attsSignCcb_t *pCcb, attsSignBuf_t *pBuf)
|
||||
{
|
||||
if (pCcb->pCsrk != NULL)
|
||||
{
|
||||
uint16_t cmacTxtLen = pBuf->writeLen + ATT_WRITE_CMD_LEN + sizeof(uint32_t);
|
||||
uint8_t *pCmacTxt = WsfBufAlloc(cmacTxtLen);
|
||||
|
||||
pCcb->pBuf = pBuf;
|
||||
|
||||
if (pCmacTxt)
|
||||
{
|
||||
uint8_t revPeerCsrk[SEC_CMAC_KEY_LEN] = {0};
|
||||
|
||||
WStrReverseCpy(revPeerCsrk, pCcb->pCsrk, SEC_CMAC_KEY_LEN);
|
||||
WStrReverseCpy(pCmacTxt, pBuf->packet, cmacTxtLen);
|
||||
|
||||
if (SecCmac(revPeerCsrk, pCmacTxt, cmacTxtLen, attCb.handlerId,
|
||||
pBuf->connId, ATTS_MSG_SIGN_CMAC_CMPL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WsfBufFree(pCmacTxt);
|
||||
}
|
||||
}
|
||||
|
||||
/* no CSRK-- free buffer */
|
||||
WsfBufFree(pBuf);
|
||||
ATT_TRACE_WARN0("ATTS CSRK not set");
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a signed write command PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsProcSignedWrite(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
attsSignCcb_t *pSignCcb;
|
||||
attsSignBuf_t *pBuf;
|
||||
uint16_t handle;
|
||||
uint16_t writeLen;
|
||||
|
||||
/* parse handle, calculate write length */
|
||||
p = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(handle, p);
|
||||
writeLen = len - ATT_SIGNED_WRITE_CMD_LEN;
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify signed write is permitted */
|
||||
if ((pAttr->settings & ATTS_SET_ALLOW_SIGNED) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify that csrk is present */
|
||||
if (attsSignCcbByConnId(pCcb->connId)->pCsrk == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify basic permissions */
|
||||
if ((pAttr->permissions & (ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC)) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify authentication */
|
||||
if ((pAttr->permissions & ATTS_PERMIT_WRITE_AUTH) &&
|
||||
(attsSignCcbByConnId(pCcb->connId)->authenticated == 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Note: authorization not verified at this stage as it is reserved for lesc
|
||||
writes; authorization occurs latter when the write cb is called */
|
||||
|
||||
/* verify write length, fixed length */
|
||||
if (((pAttr->settings & ATTS_SET_VARIABLE_LEN) == 0) &&
|
||||
(writeLen != pAttr->maxLen))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify write length, variable length */
|
||||
if (((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0) &&
|
||||
(writeLen > pAttr->maxLen))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate buffer to store packet and parameters */
|
||||
if ((pBuf = WsfBufAlloc(sizeof(attsSignBuf_t) - 1 + len)) != NULL)
|
||||
{
|
||||
/* initialize buffer */
|
||||
pBuf->pCcb = pCcb;
|
||||
pBuf->handle = handle;
|
||||
pBuf->writeLen = writeLen;
|
||||
pBuf->connId = pCcb->connId;
|
||||
memcpy(pBuf->packet, (pPacket + L2C_PAYLOAD_START), len);
|
||||
|
||||
/* check if a signed write is already in progress */
|
||||
pSignCcb = attsSignCcbByConnId(pCcb->connId);
|
||||
|
||||
if (pSignCcb->pBuf != NULL)
|
||||
{
|
||||
/* signed write in progress; queue packet */
|
||||
WsfQueueEnq(&attsSignCb.msgQueue, pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* start signed data processing */
|
||||
attsSignedWriteStart(pSignCcb, pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Message handler callback for ATTS signed PDU processing.
|
||||
*
|
||||
* \param pMsg ATTS message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsSignMsgCback(secCmacMsg_t *pMsg)
|
||||
{
|
||||
attsSignCcb_t *pCcb;
|
||||
attsSignBuf_t *pBuf;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint32_t signCounter;
|
||||
|
||||
if (pMsg->hdr.event == ATTS_MSG_SIGN_CMAC_CMPL)
|
||||
{
|
||||
uint8_t signature[ATT_CMAC_RESULT_LEN] = {0};
|
||||
|
||||
pCcb = attsSignCcbByConnId((dmConnId_t) pMsg->hdr.param);
|
||||
pBuf = pCcb->pBuf;
|
||||
|
||||
WStrReverseCpy(signature, pMsg->pCiphertext, ATT_CMAC_RESULT_LEN);
|
||||
|
||||
/* if signature ok */
|
||||
if (memcmp(signature, pBuf->packet + pBuf->writeLen + ATT_WRITE_CMD_LEN + 4, ATT_CMAC_RESULT_LEN) == 0)
|
||||
{
|
||||
/* check sign counter */
|
||||
BYTES_TO_UINT32(signCounter, (pBuf->packet + pBuf->writeLen + ATT_WRITE_CMD_LEN));
|
||||
|
||||
if (signCounter >= pCcb->signCounter)
|
||||
{
|
||||
/* update sign counter */
|
||||
pCcb->signCounter = signCounter + 1;
|
||||
|
||||
/* perform write: */
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(pBuf->handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* if write callback is desired */
|
||||
if ((pAttr->settings & ATTS_SET_WRITE_CBACK) &&
|
||||
(pGroup->writeCback != NULL))
|
||||
{
|
||||
/* write value via write callback */
|
||||
(*pGroup->writeCback)(pBuf->connId, pBuf->handle, ATT_PDU_SIGNED_WRITE_CMD,
|
||||
0, pBuf->writeLen, (pBuf->packet + ATT_WRITE_CMD_LEN), pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
(*attsCb.cccCback)(pBuf->connId, ATT_METHOD_WRITE, pBuf->handle,
|
||||
(pBuf->packet + ATT_WRITE_CMD_LEN));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* write attribute value */
|
||||
memcpy(pAttr->pValue, (pBuf->packet + ATT_WRITE_CMD_LEN), pBuf->writeLen);
|
||||
|
||||
/* write the length if variable length attribute */
|
||||
if ((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0)
|
||||
{
|
||||
*(pAttr->pLen) = pBuf->writeLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN0("Signed write counter failed");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN0("Signed write sig failed");
|
||||
}
|
||||
|
||||
/* we're done-- free parameter buffer */
|
||||
WsfBufFree(pBuf);
|
||||
|
||||
WsfBufFree(pMsg->pPlainText);
|
||||
|
||||
/* process next signed write in queue, if any */
|
||||
if ((pBuf = WsfQueueDeq(&attsSignCb.msgQueue)) != NULL)
|
||||
{
|
||||
pCcb = attsSignCcbByConnId(pBuf->connId);
|
||||
attsSignedWriteStart(pCcb, pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
pCcb->pBuf = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATT server for data signing.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsSignInit(void)
|
||||
{
|
||||
/* initialize control block */
|
||||
WSF_QUEUE_INIT(&attsSignCb.msgQueue);
|
||||
|
||||
/* set up callback interface */
|
||||
attsCb.signMsgCback = (attMsgHandler_t) attsSignMsgCback;
|
||||
attsProcFcnTbl[ATT_METHOD_SIGNED_WRITE_CMD] = attsProcSignedWrite;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the peer's data signing key on this connection. This function
|
||||
* is typically called from the ATT connection callback when the connection is
|
||||
* established. The caller is responsible for maintaining the memory that
|
||||
* contains the key.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCsrk Pointer to data signing key (CSRK).
|
||||
* \param authenticated True if CSRK is authenticated and false otherwise.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsSetCsrk(dmConnId_t connId, uint8_t *pCsrk, bool_t authenticated)
|
||||
{
|
||||
attsSignCcbByConnId(connId)->pCsrk = pCsrk;
|
||||
attsSignCcbByConnId(connId)->authenticated = authenticated;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the peer's sign counter on this connection. This function
|
||||
* is typically called from the ATT connection callback when the connection is
|
||||
* established. ATT maintains the value of the sign counter internally and
|
||||
* sets the value when a signed packet is successfully received.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param signCounter Sign counter.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsSetSignCounter(dmConnId_t connId, uint32_t signCounter)
|
||||
{
|
||||
attsSignCcbByConnId(connId)->signCounter = signCounter;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the current value peer's sign counter on this connection. This function
|
||||
* is typically called from the ATT connection callback when the connection is
|
||||
* closed so the application can store the sign counter for use on future
|
||||
* connections.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return Sign counter.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint32_t AttsGetSignCounter(dmConnId_t connId)
|
||||
{
|
||||
return attsSignCcbByConnId(connId)->signCounter;
|
||||
}
|
||||
Vendored
+463
@@ -0,0 +1,463 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server write PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Queued prepare write structure */
|
||||
typedef struct attsPrepWrite_tag
|
||||
{
|
||||
struct attsPrepWrite_tag *pNext;
|
||||
uint16_t writeLen;
|
||||
uint16_t handle;
|
||||
uint16_t offset;
|
||||
uint8_t packet[1];
|
||||
} attsPrepWrite_t;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute a queued prepared write operation.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pPrep Pointer to buffer containing prepared write.
|
||||
*
|
||||
* \return ATT_SUCCESS or failure status.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attsExecPrepWrite(attCcb_t *pCcb, attsPrepWrite_t *pPrep)
|
||||
{
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
p = pPrep->packet;
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(pPrep->handle, &pGroup)) == NULL)
|
||||
{
|
||||
/* handle not found; only possible unless handle was removed */
|
||||
err = ATT_ERR_UNLIKELY;
|
||||
}
|
||||
/* verify write permissions (in theory could have changed) */
|
||||
else if (!(pAttr->permissions & ATTS_PERMIT_WRITE))
|
||||
{
|
||||
err = ATT_ERR_WRITE;
|
||||
}
|
||||
/* if write callback is desired */
|
||||
else if ((pAttr->settings & ATTS_SET_WRITE_CBACK) &&
|
||||
(pGroup->writeCback != NULL))
|
||||
{
|
||||
|
||||
/* write callback performs the write */
|
||||
err = (*pGroup->writeCback)(pCcb->connId, pPrep->handle, ATT_PDU_EXEC_WRITE_REQ,
|
||||
pPrep->offset, pPrep->writeLen, p, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_WRITE, pPrep->handle, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* perform write; parameters have already been vetted by previous procedures */
|
||||
|
||||
/* write attribute value */
|
||||
memcpy((pAttr->pValue + pPrep->offset), p, pPrep->writeLen);
|
||||
|
||||
/* write the length if variable length attribute */
|
||||
if ((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0)
|
||||
{
|
||||
*(pAttr->pLen) = pPrep->writeLen + pPrep->offset;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a write request or write command PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcWrite(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint8_t opcode;
|
||||
uint16_t handle;
|
||||
uint16_t writeLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse opcode handle, calculate write length */
|
||||
pPacket += L2C_PAYLOAD_START;
|
||||
BSTREAM_TO_UINT8(opcode, pPacket);
|
||||
BSTREAM_TO_UINT16(handle, pPacket);
|
||||
writeLen = len - ATT_WRITE_REQ_LEN;
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify permissions */
|
||||
if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_WRITE,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
/* err has been set; fail */
|
||||
}
|
||||
/* verify write length, fixed length */
|
||||
else if (((pAttr->settings & ATTS_SET_VARIABLE_LEN) == 0) &&
|
||||
(writeLen != pAttr->maxLen))
|
||||
{
|
||||
err = ATT_ERR_LENGTH;
|
||||
}
|
||||
/* verify write length, variable length */
|
||||
else if (((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0) &&
|
||||
(writeLen > pAttr->maxLen))
|
||||
{
|
||||
err = ATT_ERR_LENGTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if write callback is desired */
|
||||
if ((pAttr->settings & ATTS_SET_WRITE_CBACK) &&
|
||||
(pGroup->writeCback != NULL))
|
||||
{
|
||||
err = (*pGroup->writeCback)(pCcb->connId, handle, opcode, 0, writeLen,
|
||||
pPacket, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_WRITE, handle, pPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* write attribute value */
|
||||
memcpy(pAttr->pValue, pPacket, writeLen);
|
||||
|
||||
/* write the length if variable length attribute */
|
||||
if ((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0)
|
||||
{
|
||||
*(pAttr->pLen) = writeLen;
|
||||
}
|
||||
}
|
||||
|
||||
/* if success and write req allocate response buffer */
|
||||
if (err == ATT_SUCCESS && opcode == ATT_PDU_WRITE_REQ)
|
||||
{
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_WRITE_RSP_LEN)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_WRITE_RSP);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, ATT_WRITE_RSP_LEN, pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* else attribute not found */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
/* send error response for write req only */
|
||||
if (err && (opcode == ATT_PDU_WRITE_REQ))
|
||||
{
|
||||
if (err == ATT_RSP_PENDING)
|
||||
{
|
||||
/* set response pending */
|
||||
pCcb->control |= ATT_CCB_STATUS_RSP_PENDING;
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_WRITE_REQ, handle, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a prepare write request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcPrepWriteReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
attsPrepWrite_t *pPrep;
|
||||
uint16_t handle;
|
||||
uint16_t offset;
|
||||
uint16_t writeLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handle and offset, calculate write length */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(handle, pPacket);
|
||||
BSTREAM_TO_UINT16(offset, pPacket);
|
||||
writeLen = len - ATT_PREP_WRITE_REQ_LEN; /* length of value being written */
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) == NULL)
|
||||
{
|
||||
/* attribute not found */
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
/* verify permissions */
|
||||
else if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_WRITE,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
/* err has been set; fail */
|
||||
}
|
||||
/* verify offset is allowed */
|
||||
else if ((offset != 0) && ((pAttr->settings & ATTS_SET_ALLOW_OFFSET) == 0))
|
||||
{
|
||||
err = ATT_ERR_NOT_LONG;
|
||||
}
|
||||
/* verify write length, fixed length */
|
||||
else if (((pAttr->settings & ATTS_SET_VARIABLE_LEN) == 0) &&
|
||||
(writeLen != pAttr->maxLen))
|
||||
{
|
||||
err = ATT_ERR_LENGTH;
|
||||
}
|
||||
/* verify prepare write queue limit not reached */
|
||||
else if (WsfQueueCount(&pCcb->prepWriteQueue) >= pAttCfg->numPrepWrites)
|
||||
{
|
||||
err = ATT_ERR_QUEUE_FULL;
|
||||
}
|
||||
/* allocate new buffer to hold prepared write */
|
||||
else if ((pPrep = WsfBufAlloc(sizeof(attsPrepWrite_t) - 1 + writeLen)) == NULL)
|
||||
{
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
else if ((pAttr->settings & ATTS_SET_WRITE_CBACK) &&
|
||||
(pGroup->writeCback != NULL))
|
||||
{
|
||||
err = (*pGroup->writeCback)(pCcb->connId, handle, ATT_PDU_PREP_WRITE_REQ, 0, writeLen,
|
||||
pPacket, pAttr);
|
||||
}
|
||||
|
||||
if (err == ATT_SUCCESS)
|
||||
{
|
||||
/* copy data to new buffer and queue it */
|
||||
pPrep->writeLen = writeLen;
|
||||
pPrep->handle = handle;
|
||||
pPrep->offset = offset;
|
||||
memcpy(pPrep->packet, pPacket, writeLen);
|
||||
WsfQueueEnq(&pCcb->prepWriteQueue, pPrep);
|
||||
|
||||
/* allocate response buffer */
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_PREP_WRITE_RSP_LEN + writeLen)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_PREP_WRITE_RSP);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
UINT16_TO_BSTREAM(p, offset);
|
||||
memcpy(p, pPacket, writeLen);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (ATT_PREP_WRITE_RSP_LEN + writeLen), pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_PREP_WRITE_REQ, handle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process an execute write request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcExecWriteReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsPrepWrite_t *pPrep;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
|
||||
/* if cancelling all prepared writes */
|
||||
if (*pPacket == ATT_EXEC_WRITE_CANCEL)
|
||||
{
|
||||
/* free all queued buffers */
|
||||
attsClearPrepWrites(pCcb);
|
||||
}
|
||||
/* else writing all prepared writes */
|
||||
else if (*pPacket == ATT_EXEC_WRITE_ALL)
|
||||
{
|
||||
/* iterate over prepare write queue and verify offset and length */
|
||||
for (pPrep = pCcb->prepWriteQueue.pHead; pPrep != NULL; pPrep = pPrep->pNext)
|
||||
{
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(pPrep->handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify offset */
|
||||
if (pPrep->offset > pAttr->maxLen)
|
||||
{
|
||||
err = ATT_ERR_OFFSET;
|
||||
}
|
||||
/* verify write length with offset */
|
||||
else if ((pPrep->writeLen + pPrep->offset) > pAttr->maxLen)
|
||||
{
|
||||
err = ATT_ERR_LENGTH;
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
/* verification failed; discard all prepared writes */
|
||||
attsClearPrepWrites(pCcb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if length and offset checks ok then write all buffers in queue */
|
||||
if (err == ATT_SUCCESS)
|
||||
{
|
||||
/* for each buffer */
|
||||
while ((pPrep = WsfQueueDeq(&pCcb->prepWriteQueue)) != NULL)
|
||||
{
|
||||
/* write buffer */
|
||||
if ((err = attsExecPrepWrite(pCcb, pPrep)) != ATT_SUCCESS)
|
||||
{
|
||||
/* write failed; discard remaining prepared writes */
|
||||
attsClearPrepWrites(pCcb);
|
||||
}
|
||||
|
||||
/* free buffer */
|
||||
WsfBufFree(pPrep);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* else unknown operation */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_INVALID_PDU;
|
||||
}
|
||||
|
||||
/* send response or error response */
|
||||
if (err)
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_EXEC_WRITE_REQ, 0, err);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_EXEC_WRITE_RSP_LEN)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_EXEC_WRITE_RSP);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, ATT_EXEC_WRITE_RSP_LEN, pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a response to a pending write request. For use with ATT_RSP_PENDING.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param status Status of the write request.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note When a higher layer returns ATT_RSP_PENDING to an ATT write callback indicating the
|
||||
* response status is pending, the higher layer must subsequently call this function
|
||||
* with the status of the write request.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsContinueWriteReq(dmConnId_t connId, uint16_t handle, uint8_t status)
|
||||
{
|
||||
attCcb_t *pCcb;
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
/* get connection cb for this handle */
|
||||
if ((pCcb = attCcbByConnId(connId)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* clear response pending */
|
||||
pCcb->control &= ~ATT_CCB_STATUS_RSP_PENDING;
|
||||
|
||||
if (status)
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_WRITE_REQ, handle, status);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_WRITE_RSP_LEN)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_WRITE_RSP);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, ATT_WRITE_RSP_LEN, pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+112
@@ -0,0 +1,112 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Stack configuration.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "cfg_stack.h"
|
||||
#include "hci_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "l2c_api.h"
|
||||
#include "att_api.h"
|
||||
#include "smp_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
HCI
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
DM
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Configuration structure */
|
||||
const dmCfg_t dmCfg =
|
||||
{
|
||||
0
|
||||
};
|
||||
|
||||
/* Configuration pointer */
|
||||
dmCfg_t *pDmCfg = (dmCfg_t *) &dmCfg;
|
||||
|
||||
/**************************************************************************************************
|
||||
L2C
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Configuration structure */
|
||||
const l2cCfg_t l2cCfg =
|
||||
{
|
||||
30 /* Request timeout in seconds */
|
||||
};
|
||||
|
||||
/* Configuration pointer */
|
||||
l2cCfg_t *pL2cCfg = (l2cCfg_t *) &l2cCfg;
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Configuration structure */
|
||||
const attCfg_t attCfg =
|
||||
{
|
||||
15, /* ATT server service discovery connection idle timeout in seconds */
|
||||
480,//LL_MAX_DATA_LEN_ABS_MAX - 4, /* desired ATT MTU */
|
||||
ATT_MAX_TRANS_TIMEOUT, /* transcation timeout in seconds */
|
||||
4 /* number of queued prepare writes supported by server */
|
||||
};
|
||||
|
||||
/* Configuration pointer */
|
||||
attCfg_t *pAttCfg = (attCfg_t *) &attCfg;
|
||||
|
||||
/**************************************************************************************************
|
||||
SMP
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Configuration structure */
|
||||
const smpCfg_t smpCfg =
|
||||
{
|
||||
500, /* 'Repeated attempts' timeout in msec */
|
||||
SMP_IO_NO_IN_NO_OUT, /* I/O Capability */
|
||||
7, /* Minimum encryption key length */
|
||||
16, /* Maximum encryption key length */
|
||||
1, /* Attempts to trigger 'repeated attempts' timeout */
|
||||
0, /* Device authentication requirements */
|
||||
64000, /* Maximum repeated attempts timeout in msec */
|
||||
64000, /* Time msec before attemptExp decreases */
|
||||
2 /* Repeated attempts multiplier exponent */
|
||||
};
|
||||
|
||||
/* Configuration pointer */
|
||||
smpCfg_t *pSmpCfg = (smpCfg_t *) &smpCfg;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get Stack version number.
|
||||
*
|
||||
* \param pVersion output parameter for version number.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void StackGetVersionNumber(const char **pVersion)
|
||||
{
|
||||
*pVersion = STACK_VERSION;
|
||||
}
|
||||
Vendored
+170
@@ -0,0 +1,170 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Stack configuration.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef CFG_STACK_H
|
||||
#define CFG_STACK_H
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*! \addtogroup STACK_INIT
|
||||
* \{ */
|
||||
|
||||
/**************************************************************************************************
|
||||
STACK VERSION
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! \brief Stack release version label */
|
||||
#define STACK_VERSION ((const char *)"r19.02\n")
|
||||
/*! \brief Stack release version number */
|
||||
#define STACK_VER_NUM 0x1302 /* Default value. Auto-generated by builder. */
|
||||
|
||||
/**************************************************************************************************
|
||||
HCI
|
||||
**************************************************************************************************/
|
||||
|
||||
/** \name HCI Vendor Specific targets
|
||||
*
|
||||
*/
|
||||
/**@{*/
|
||||
#define HCI_VS_GENERIC 0
|
||||
#define HCI_VS_EMM 1
|
||||
|
||||
/*! \brief Vendor specific target configuration */
|
||||
#ifndef HCI_VS_TARGET
|
||||
#define HCI_VS_TARGET HCI_VS_GENERIC
|
||||
#endif
|
||||
/**@}*/
|
||||
|
||||
/** \name HCI Tx Data Tailroom
|
||||
* Extra byte allocation required for LL operations (i.e. MIC) in single-chip implementation
|
||||
*/
|
||||
/**@{*/
|
||||
#ifndef HCI_TX_DATA_TAILROOM
|
||||
/*! \brief Tx data tailroom. */
|
||||
#define HCI_TX_DATA_TAILROOM 0
|
||||
#endif
|
||||
/**@}*/
|
||||
|
||||
/**************************************************************************************************
|
||||
DM
|
||||
**************************************************************************************************/
|
||||
|
||||
/** \name DM Configuration
|
||||
* DM build-time configuration parameters
|
||||
*/
|
||||
/**@{*/
|
||||
/*! \brief Maximum number of connections */
|
||||
#ifndef DM_CONN_MAX
|
||||
#define DM_CONN_MAX 3
|
||||
#endif
|
||||
|
||||
/*! \brief Maximum number of periodic advertising synchronizations */
|
||||
#ifndef DM_SYNC_MAX
|
||||
#define DM_SYNC_MAX 1
|
||||
#endif
|
||||
|
||||
/*! \brief Number of supported advertising sets: must be set to 1 for legacy advertising */
|
||||
#ifndef DM_NUM_ADV_SETS
|
||||
#define DM_NUM_ADV_SETS 1
|
||||
#endif
|
||||
|
||||
/*! \brief Number of scanner and initiator PHYs (LE 1M, LE 2M and LE Coded): must be set to 1 for
|
||||
legacy scanner and initiator */
|
||||
#ifndef DM_NUM_PHYS
|
||||
#define DM_NUM_PHYS 1
|
||||
#endif
|
||||
/**@}*/
|
||||
|
||||
/**************************************************************************************************
|
||||
L2C
|
||||
**************************************************************************************************/
|
||||
|
||||
/** \name L2CAP Configuration
|
||||
* L2CAP build-time configuration parameters
|
||||
*/
|
||||
/**@{*/
|
||||
/*! \brief Maximum number of connection oriented channels */
|
||||
#ifndef L2C_COC_CHAN_MAX
|
||||
#define L2C_COC_CHAN_MAX 8
|
||||
#endif
|
||||
|
||||
/*! \brief Maximum number of connection oriented channel registered clients */
|
||||
#ifndef L2C_COC_REG_MAX
|
||||
#define L2C_COC_REG_MAX 4
|
||||
#endif
|
||||
/**@}*/
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT
|
||||
**************************************************************************************************/
|
||||
|
||||
/** \name ATT Configuration
|
||||
* ATT build-time configuration parameters
|
||||
*/
|
||||
/**@{*/
|
||||
/*! \brief Maximum number of simultaneous ATT write commands */
|
||||
#ifndef ATT_NUM_SIMUL_WRITE_CMD
|
||||
#define ATT_NUM_SIMUL_WRITE_CMD 1
|
||||
#endif
|
||||
|
||||
/*! \brief Maximum number of simultaneous ATT notifications */
|
||||
#ifndef ATT_NUM_SIMUL_NTF
|
||||
#define ATT_NUM_SIMUL_NTF 1
|
||||
#endif
|
||||
/**@}*/
|
||||
|
||||
/**************************************************************************************************
|
||||
SMP
|
||||
**************************************************************************************************/
|
||||
|
||||
/** \name SMP Configuration
|
||||
* SMP build-time configuration parameters
|
||||
*/
|
||||
/**@{*/
|
||||
/*! Max number of devices in the database */
|
||||
#ifndef SMP_DB_MAX_DEVICES
|
||||
#define SMP_DB_MAX_DEVICES 3
|
||||
#endif
|
||||
/**@}*/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get Stack version number.
|
||||
*
|
||||
* \param pVersion output parameter for version number.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void StackGetVersionNumber(const char **pVersion);
|
||||
|
||||
/*! \} */ /* STACK_INIT */
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* CFG_STACK_H */
|
||||
+523
@@ -0,0 +1,523 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager advertising module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_adv.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* control block */
|
||||
dmAdvCb_t dmAdvCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the advertising CB for a given handle.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvCbInit(uint8_t advHandle)
|
||||
{
|
||||
/* initialize control block */
|
||||
dmAdvCb.advType[advHandle] = DM_ADV_NONE;
|
||||
dmAdvCb.intervalMin[advHandle] = DM_GAP_ADV_SLOW_INT_MIN;
|
||||
dmAdvCb.intervalMax[advHandle] = DM_GAP_ADV_SLOW_INT_MAX;
|
||||
dmAdvCb.channelMap[advHandle] = DM_ADV_CHAN_ALL;
|
||||
dmCb.advFiltPolicy[advHandle] = HCI_ADV_FILT_NONE;
|
||||
dmAdvCb.advState[advHandle] = DM_ADV_STATE_IDLE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the legacy adv module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvInit(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* initialize control block */
|
||||
for (i = 0; i < DM_NUM_ADV_SETS; i++)
|
||||
{
|
||||
dmAdvCbInit(i);
|
||||
}
|
||||
|
||||
dmAdvCb.advTimer.handlerId = dmCb.handlerId;
|
||||
dmCb.advAddrType = DM_ADDR_PUBLIC;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Generate an enhanced connection complete event.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param status Status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvGenConnCmpl(uint8_t advHandle, uint8_t status)
|
||||
{
|
||||
hciLeConnCmplEvt_t leConnCmpl;
|
||||
|
||||
/* generate enhanced connection complete event */
|
||||
memset(&leConnCmpl, 0, sizeof(leConnCmpl));
|
||||
|
||||
leConnCmpl.hdr.event = HCI_LE_ENHANCED_CONN_CMPL_CBACK_EVT;
|
||||
leConnCmpl.hdr.status = leConnCmpl.status = status;
|
||||
leConnCmpl.role = DM_ROLE_SLAVE;
|
||||
leConnCmpl.addrType = dmAdvCb.peerAddrType[advHandle];
|
||||
BdaCpy(leConnCmpl.peerAddr, dmAdvCb.peerAddr[advHandle]);
|
||||
|
||||
/* pass connection complete event to DM connection management module */
|
||||
dmDevPassHciEvtToConn((hciEvt_t *) &leConnCmpl);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the advertising parameters using the given advertising type, and peer address.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param advType Advertising type.
|
||||
* \param peerAddrType Peer address type.
|
||||
* \param pPeerAddr Peer address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvConfig(uint8_t advHandle, uint8_t advType, uint8_t peerAddrType, uint8_t *pPeerAddr)
|
||||
{
|
||||
dmAdvApiConfig_t *pMsg;
|
||||
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmAdvApiConfig_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_ADV_MSG_API_CONFIG;
|
||||
pMsg->advType = advType;
|
||||
pMsg->advHandle = advHandle;
|
||||
pMsg->peerAddrType = peerAddrType;
|
||||
BdaCpy(pMsg->peerAddr, pPeerAddr);
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the advertising or scan response data to the given data.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param op Data operation.
|
||||
* \param location Data location.
|
||||
* \param len Length of the data. Maximum length is 236 bytes.
|
||||
* \param pData Pointer to the data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvSetData(uint8_t advHandle, uint8_t op, uint8_t location, uint8_t len, uint8_t *pData)
|
||||
{
|
||||
dmAdvApiSetData_t *pMsg;
|
||||
|
||||
WSF_ASSERT((location == DM_DATA_LOC_SCAN) || (location == DM_DATA_LOC_ADV));
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmAdvApiSetData_t) + len)) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_ADV_MSG_API_SET_DATA;
|
||||
pMsg->advHandle = advHandle;
|
||||
pMsg->op = op;
|
||||
pMsg->location = location;
|
||||
pMsg->len = len;
|
||||
memcpy(pMsg->pData, pData, len);
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start advertising using the given advertising set and duration.
|
||||
*
|
||||
* \param numSets Number of advertising sets to enable.
|
||||
* \param pAdvHandles Advertising handles array.
|
||||
* \param pDuration Advertising duration (in milliseconds) array.
|
||||
* \param pMaxEaEvents Maximum number of extended advertising events array.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvStart(uint8_t numSets, uint8_t *pAdvHandles, uint16_t *pDuration, uint8_t *pMaxEaEvents)
|
||||
{
|
||||
uint8_t i;
|
||||
dmAdvApiStart_t *pMsg;
|
||||
|
||||
WSF_ASSERT(numSets <= DM_NUM_ADV_SETS);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmAdvApiStart_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_ADV_MSG_API_START;
|
||||
pMsg->numSets = numSets;
|
||||
|
||||
for (i = 0; i < numSets; i++)
|
||||
{
|
||||
pMsg->advHandle[i] = pAdvHandles[i];
|
||||
pMsg->duration[i] = pDuration[i];
|
||||
pMsg->maxEaEvents[i] = pMaxEaEvents[i];
|
||||
}
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop advertising for the given advertising set. If the number of sets is set to 0
|
||||
* then all advertising sets are disabled.
|
||||
*
|
||||
* \param numSets Number of advertising sets to disable.
|
||||
* \param pAdvHandles Advertising handles array.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvStop(uint8_t numSets, uint8_t *pAdvHandles)
|
||||
{
|
||||
uint8_t i;
|
||||
dmAdvApiStop_t *pMsg;
|
||||
|
||||
WSF_ASSERT(numSets <= DM_NUM_ADV_SETS);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmAdvApiStop_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_ADV_MSG_API_STOP;
|
||||
pMsg->numSets = numSets;
|
||||
|
||||
for (i = 0; i < numSets; i++)
|
||||
{
|
||||
pMsg->advHandle[i] = pAdvHandles[i];
|
||||
}
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove an advertising set.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvRemoveAdvSet(uint8_t advHandle)
|
||||
{
|
||||
dmAdvApiRemove_t *pMsg;
|
||||
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmAdvApiRemove_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_ADV_MSG_API_REMOVE;
|
||||
pMsg->advHandle = advHandle;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear advertising sets.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvClearAdvSets(void)
|
||||
{
|
||||
wsfMsgHdr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pMsg->event = DM_ADV_MSG_API_CLEAR;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the random device address for a given advertising set.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param pAddr Random device address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvSetRandAddr(uint8_t advHandle, const uint8_t *pAddr)
|
||||
{
|
||||
dmAdvApiSetRandAddr_t *pMsg;
|
||||
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmAdvApiSetRandAddr_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_ADV_MSG_API_SET_RAND_ADDR;
|
||||
pMsg->advHandle = advHandle;
|
||||
BdaCpy(pMsg->addr, pAddr);
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the minimum and maximum advertising intervals.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param intervalMin Minimum advertising interval.
|
||||
* \param intervalMax Maximum advertising interval.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvSetInterval(uint8_t advHandle, uint16_t intervalMin, uint16_t intervalMax)
|
||||
{
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
WsfTaskLock();
|
||||
dmAdvCb.intervalMin[advHandle] = intervalMin;
|
||||
dmAdvCb.intervalMax[advHandle] = intervalMax;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Include or exclude certain channels from the advertising channel map.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param channelMap Advertising channel map.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvSetChannelMap(uint8_t advHandle, uint8_t channelMap)
|
||||
{
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
WsfTaskLock();
|
||||
dmAdvCb.channelMap[advHandle] = channelMap;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the local address type used while advertising. This function can be used to
|
||||
* configure advertising to use a random address.
|
||||
*
|
||||
* \param addrType Address type.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvSetAddrType(uint8_t addrType)
|
||||
{
|
||||
WsfTaskLock();
|
||||
dmCb.advAddrType = addrType;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the value of an advertising data element in the given advertising or
|
||||
* scan response data. If the element already exists in the data then it is replaced
|
||||
* with the new value. If the element does not exist in the data it is appended
|
||||
* to it, space permitting.
|
||||
*
|
||||
* \param adType Advertising data element type.
|
||||
* \param len Length of the value. Maximum length is 29 bytes.
|
||||
* \param pValue Pointer to the value.
|
||||
* \param pAdvDataLen Advertising or scan response data length. The new length is returned
|
||||
* in this parameter.
|
||||
* \param pAdvData Pointer to advertising or scan response data.
|
||||
* \param advDataBufLen Length of the advertising or scan response data buffer maintained by
|
||||
* Application.
|
||||
*
|
||||
* \return TRUE if the element was successfully added to the data, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmAdvSetAdValue(uint8_t adType, uint8_t len, uint8_t *pValue, uint16_t *pAdvDataLen,
|
||||
uint8_t *pAdvData, uint16_t advDataBufLen)
|
||||
{
|
||||
uint8_t *pElem;
|
||||
uint8_t *pNext;
|
||||
uint16_t totalLen;
|
||||
uint16_t newAdvDataLen;
|
||||
bool_t valueSet = FALSE;
|
||||
|
||||
/* find ad type in data */
|
||||
if ((pElem = DmFindAdType(adType, *pAdvDataLen, pAdvData)) != NULL)
|
||||
{
|
||||
/* if new length equals existing length */
|
||||
if ((len + 1) == pElem[DM_AD_LEN_IDX])
|
||||
{
|
||||
/* copy new ad value to data in existing location */
|
||||
memcpy(&pElem[DM_AD_DATA_IDX], pValue, len);
|
||||
valueSet = TRUE;
|
||||
}
|
||||
/* else if new value can replace old value and still fit */
|
||||
else
|
||||
{
|
||||
/* calculate the advertising data length if old element was replaced with new */
|
||||
newAdvDataLen = *pAdvDataLen + len + 1 - pElem[DM_AD_LEN_IDX];
|
||||
|
||||
/* if length is ok */
|
||||
if (newAdvDataLen <= advDataBufLen)
|
||||
{
|
||||
/* delete item (then we will replace it) */
|
||||
|
||||
/* get the start of element that follows the element to delete */
|
||||
totalLen = pElem[DM_AD_LEN_IDX] + 1;
|
||||
pNext = pElem + totalLen;
|
||||
|
||||
/* move data from start of next element to start of current item;
|
||||
* length is equal the data that remains after pNext
|
||||
*/
|
||||
memmove(pElem, pNext, *pAdvDataLen - (uint8_t)(pNext - pAdvData));
|
||||
|
||||
/* update length */
|
||||
*pAdvDataLen = *pAdvDataLen - totalLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if value not set */
|
||||
if (!valueSet)
|
||||
{
|
||||
/* if new value fits */
|
||||
if ((*pAdvDataLen + len + 2) <= advDataBufLen)
|
||||
{
|
||||
/* construct AD item in advertising data */
|
||||
pElem = &pAdvData[*pAdvDataLen];
|
||||
*pElem++ = len + 1;
|
||||
*pElem++ = adType;
|
||||
memcpy(pElem, pValue, len);
|
||||
|
||||
/* update length */
|
||||
*pAdvDataLen = *pAdvDataLen + len + 2;
|
||||
|
||||
valueSet = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return valueSet;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the device name in the given advertising or scan response data. If the
|
||||
* device can only fit in the data if it is shortened, the name is shortened
|
||||
* and the AD type is changed to DM_ADV_TYPE_SHORT_NAME.
|
||||
*
|
||||
* \param len Length of the name. Maximum length is 29 bytes.
|
||||
* \param pValue Pointer to the name in UTF-8 format.
|
||||
* \param pAdvDataLen Advertising or scan response data length. The new length is returned
|
||||
* in this parameter.
|
||||
* \param pAdvData Pointer to advertising or scan response data.
|
||||
* \param advDataBufLen Length of the advertising or scan response data buffer maintained by
|
||||
* Application.
|
||||
*
|
||||
* \return TRUE if the element was successfully added to the data, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmAdvSetName(uint8_t len, uint8_t *pValue, uint16_t *pAdvDataLen, uint8_t *pAdvData,
|
||||
uint16_t advDataBufLen)
|
||||
{
|
||||
uint8_t *pElem;
|
||||
uint8_t *pNext;
|
||||
uint16_t totalLen;
|
||||
uint8_t adType;
|
||||
|
||||
/* find name in data */
|
||||
if ((pElem = DmFindAdType(DM_ADV_TYPE_LOCAL_NAME, *pAdvDataLen, pAdvData)) == NULL)
|
||||
{
|
||||
pElem = DmFindAdType(DM_ADV_TYPE_SHORT_NAME, *pAdvDataLen, pAdvData);
|
||||
}
|
||||
|
||||
/* if found delete it */
|
||||
if (pElem != NULL)
|
||||
{
|
||||
/* get the start of element that follows the element to delete */
|
||||
totalLen = pElem[DM_AD_LEN_IDX] + 1;
|
||||
pNext = pElem + totalLen;
|
||||
|
||||
/* move data from start of next element to start of current item;
|
||||
* length is equal the data that remains after pNext
|
||||
*/
|
||||
memmove(pElem, pNext, *pAdvDataLen - (uint8_t)(pNext - pAdvData));
|
||||
|
||||
/* update length */
|
||||
*pAdvDataLen = *pAdvDataLen - totalLen;
|
||||
}
|
||||
|
||||
/* if name will fit */
|
||||
if (*pAdvDataLen <= (advDataBufLen - 2))
|
||||
{
|
||||
/* if full device name won't fit */
|
||||
if ((*pAdvDataLen + len + 2) > advDataBufLen)
|
||||
{
|
||||
/* adjust length so that it will fit */
|
||||
len = (advDataBufLen - 2) - *pAdvDataLen;
|
||||
|
||||
/* set ad type to shortened local name */
|
||||
adType = DM_ADV_TYPE_SHORT_NAME;
|
||||
}
|
||||
else
|
||||
{
|
||||
adType = DM_ADV_TYPE_LOCAL_NAME;
|
||||
}
|
||||
|
||||
/* construct AD item in advertising data */
|
||||
pElem = &pAdvData[*pAdvDataLen];
|
||||
*pElem++ = len + 1;
|
||||
*pElem++ = adType;
|
||||
memcpy(pElem, pValue, len);
|
||||
|
||||
/* update length */
|
||||
*pAdvDataLen = *pAdvDataLen + len + 2;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM advertising module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_ADV_H
|
||||
#define DM_ADV_H
|
||||
|
||||
#include "wsf_timer.h"
|
||||
#include "sec_api.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* DM adv event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_ADV_MSG_API_CONFIG = DM_MSG_START(DM_ID_ADV),
|
||||
DM_ADV_MSG_API_SET_DATA,
|
||||
DM_ADV_MSG_API_START,
|
||||
DM_ADV_MSG_API_STOP,
|
||||
DM_ADV_MSG_API_REMOVE,
|
||||
DM_ADV_MSG_API_CLEAR,
|
||||
DM_ADV_MSG_API_SET_RAND_ADDR,
|
||||
DM_ADV_MSG_TIMEOUT
|
||||
};
|
||||
|
||||
/* DM adv periodic event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_ADV_PER_MSG_API_CONFIG = DM_MSG_START(DM_ID_ADV_PER),
|
||||
DM_ADV_PER_MSG_API_SET_DATA,
|
||||
DM_ADV_PER_MSG_API_START,
|
||||
DM_ADV_PER_MSG_API_STOP,
|
||||
};
|
||||
|
||||
/* DM advertising states */
|
||||
enum
|
||||
{
|
||||
DM_ADV_STATE_IDLE, /* idle */
|
||||
DM_ADV_STATE_ADVERTISING, /* advertising */
|
||||
DM_ADV_STATE_STARTING_DIRECTED, /* starting high duty cycle directed advertising */
|
||||
DM_ADV_STATE_STARTING, /* starting undirected or low duty cycle directed advertising */
|
||||
DM_ADV_STATE_STOPPING_DIRECTED, /* stopping high duty cycle directed advertising */
|
||||
DM_ADV_STATE_STOPPING, /* stopping undirected or low duty cycle directed advertising */
|
||||
DM_ADV_STATE_REMOVING_SET, /* removing advertising set */
|
||||
DM_ADV_STATE_CLEARING_SETS /* clearing all advertising sets */
|
||||
};
|
||||
|
||||
/* DM periodic advertising states */
|
||||
enum
|
||||
{
|
||||
DM_ADV_PER_STATE_IDLE,
|
||||
DM_ADV_PER_STATE_ADVERTISING,
|
||||
DM_ADV_PER_STATE_STARTING,
|
||||
DM_ADV_PER_STATE_STOPPING
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Data structure for DM_ADV_MSG_API_CONFIG */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
uint8_t advType;
|
||||
uint8_t peerAddrType;
|
||||
bdAddr_t peerAddr;
|
||||
bool_t scanReqNotifEna;
|
||||
} dmAdvApiConfig_t;
|
||||
|
||||
/* Data structure for DM_ADV_MSG_API_SET_DATA */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
uint8_t op;
|
||||
uint8_t location;
|
||||
uint8_t len;
|
||||
uint8_t pData[];
|
||||
} dmAdvApiSetData_t;
|
||||
|
||||
/* Data structure for DM_ADV_MSG_API_START */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t numSets;
|
||||
uint8_t advHandle[DM_NUM_ADV_SETS];
|
||||
uint16_t duration[DM_NUM_ADV_SETS];
|
||||
uint8_t maxEaEvents[DM_NUM_ADV_SETS];
|
||||
} dmAdvApiStart_t;
|
||||
|
||||
/* Data structure for DM_ADV_MSG_API_STOP */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t numSets;
|
||||
uint8_t advHandle[DM_NUM_ADV_SETS];
|
||||
} dmAdvApiStop_t;
|
||||
|
||||
/* Data structure for DM_ADV_MSG_API_REMOVE */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
} dmAdvApiRemove_t;
|
||||
|
||||
/* Data structure for DM_ADV_MSG_API_SET_RAND_ADDR */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
bdAddr_t addr;
|
||||
} dmAdvApiSetRandAddr_t;
|
||||
|
||||
/* Data structure for DM_ADV_PER_MSG_API_CONFIG */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
} dmAdvPerApiConfig_t;
|
||||
|
||||
/* Data structure for DM_ADV_PER_MSG_API_SET_DATA */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
uint8_t op;
|
||||
uint8_t len;
|
||||
uint8_t pData[];
|
||||
} dmAdvPerApiSetData_t;
|
||||
|
||||
/* Data structure for DM_ADV_PER_MSG_API_START */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
} dmAdvPerApiStart_t;
|
||||
|
||||
/* Data structure for DM_ADV_PER_MSG_API_STOP */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
} dmAdvPerApiStop_t;
|
||||
|
||||
/* Union of all adv messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmAdvApiConfig_t apiConfig;
|
||||
dmAdvApiSetData_t apiSetData;
|
||||
dmAdvApiStart_t apiStart;
|
||||
dmAdvApiStop_t apiStop;
|
||||
dmAdvApiRemove_t apiRemove;
|
||||
dmAdvApiSetRandAddr_t apiSetRandAddr;
|
||||
dmAdvPerApiConfig_t apiPerConfig;
|
||||
dmAdvPerApiSetData_t apiPerSetData;
|
||||
dmAdvPerApiStart_t apiPerStart;
|
||||
dmAdvPerApiStop_t apiPerStop;
|
||||
secAes_t aes;
|
||||
} dmAdvMsg_t;
|
||||
|
||||
/* Action function */
|
||||
typedef void (*dmAdvAct_t)(dmAdvMsg_t *pMsg);
|
||||
|
||||
/* Control block for advertising module */
|
||||
typedef struct
|
||||
{
|
||||
wsfTimer_t advTimer;
|
||||
uint16_t intervalMin[DM_NUM_ADV_SETS];
|
||||
uint16_t intervalMax[DM_NUM_ADV_SETS];
|
||||
uint8_t advType[DM_NUM_ADV_SETS];
|
||||
uint8_t channelMap[DM_NUM_ADV_SETS];
|
||||
uint8_t localAddrType;
|
||||
uint8_t advState[DM_NUM_ADV_SETS];
|
||||
uint16_t advDuration[DM_NUM_ADV_SETS];
|
||||
bool_t advEnabled;
|
||||
bdAddr_t peerAddr[DM_NUM_ADV_SETS];
|
||||
uint8_t peerAddrType[DM_NUM_ADV_SETS];
|
||||
} dmAdvCb_t;
|
||||
|
||||
extern dmAdvCb_t dmAdvCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* legacy adv component inteface */
|
||||
void dmAdvMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmAdvHciHandler(hciEvt_t *pEvent);
|
||||
void dmAdvReset(void);
|
||||
|
||||
/* legacy adv action functions */
|
||||
void dmAdvActConfig(dmAdvMsg_t *pMsg);
|
||||
void dmAdvActSetData(dmAdvMsg_t *pMsg);
|
||||
void dmAdvActStart(dmAdvMsg_t *pMsg);
|
||||
void dmAdvActStop(dmAdvMsg_t *pMsg);
|
||||
void dmAdvActRemoveSet(dmAdvMsg_t *pMsg);
|
||||
void dmAdvActClearSets(dmAdvMsg_t *pMsg);
|
||||
void dmAdvActSetRandAddr(dmAdvMsg_t *pMsg);
|
||||
void dmAdvActTimeout(dmAdvMsg_t *pMsg);
|
||||
|
||||
/* extended adv component inteface */
|
||||
void dmExtAdvMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmExtAdvHciHandler(hciEvt_t *pEvent);
|
||||
void dmExtAdvReset(void);
|
||||
|
||||
/* extended adv action functions */
|
||||
void dmExtAdvActConfig(dmAdvMsg_t *pMsg);
|
||||
void dmExtAdvActSetData(dmAdvMsg_t *pMsg);
|
||||
void dmExtAdvActStart(dmAdvMsg_t *pMsg);
|
||||
void dmExtAdvActStop(dmAdvMsg_t *pMsg);
|
||||
void dmExtAdvActRemoveSet(dmAdvMsg_t *pMsg);
|
||||
void dmExtAdvActClearSets(dmAdvMsg_t *pMsg);
|
||||
void dmExtAdvActSetRandAddr(dmAdvMsg_t *pMsg);
|
||||
void dmExtAdvActTimeout(dmAdvMsg_t *pMsg);
|
||||
|
||||
/* periodic adv action functions */
|
||||
void dmPerAdvActConfig(dmAdvMsg_t *pMsg);
|
||||
void dmPerAdvActSetData(dmAdvMsg_t *pMsg);
|
||||
void dmPerAdvActStart(dmAdvMsg_t *pMsg);
|
||||
void dmPerAdvActStop(dmAdvMsg_t *pMsg);
|
||||
|
||||
/* periodic adv component inteface */
|
||||
void dmPerAdvMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmPerAdvHciHandler(hciEvt_t *pEvent);
|
||||
void dmPerAdvReset(void);
|
||||
|
||||
/* legacy adv directed advertising interface */
|
||||
void dmAdvStartDirected(uint8_t advType, uint16_t duration, uint8_t addrType, uint8_t *pAddr);
|
||||
void dmAdvStopDirected(void);
|
||||
void dmAdvConnected(void);
|
||||
void dmAdvConnectFailed(void);
|
||||
|
||||
/* extended adv directed advertising interface */
|
||||
void dmExtAdvStartDirected(dmConnId_t connId, uint8_t advHandle, uint8_t advType,
|
||||
uint16_t duration, uint8_t maxEaEvents, uint8_t addrType, uint8_t *pAddr);
|
||||
void dmExtAdvStopDirected(dmConnId_t connId);
|
||||
void dmExtAdvConnected(dmConnId_t connId);
|
||||
void dmExtAdvConnectFailed(dmConnId_t connId);
|
||||
|
||||
/* adv utility functions */
|
||||
void dmAdvInit(void);
|
||||
void dmAdvCbInit(uint8_t advHandle);
|
||||
void dmAdvGenConnCmpl(uint8_t advHandle, uint8_t status);
|
||||
|
||||
/* extended and periodic adv utility functions */
|
||||
void dmExtAdvInit(void);
|
||||
void dmPerAdvInit(void);
|
||||
uint8_t dmPerAdvState(uint8_t advHandle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_ADV_H */
|
||||
+1941
File diff suppressed because it is too large
Load Diff
Vendored
+562
@@ -0,0 +1,562 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager advertising module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_adv.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block for legacy advertising module */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t advType; /*!< Advertising type. */
|
||||
} dmLegAdvCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* action function table */
|
||||
static const dmAdvAct_t dmAdvAct[] =
|
||||
{
|
||||
dmAdvActConfig,
|
||||
dmAdvActSetData,
|
||||
dmAdvActStart,
|
||||
dmAdvActStop,
|
||||
dmAdvActRemoveSet,
|
||||
dmAdvActClearSets,
|
||||
dmAdvActSetRandAddr,
|
||||
dmAdvActTimeout
|
||||
};
|
||||
|
||||
/* Component function interface */
|
||||
static const dmFcnIf_t dmAdvFcnIf =
|
||||
{
|
||||
dmAdvReset,
|
||||
dmAdvHciHandler,
|
||||
dmAdvMsgHandler
|
||||
};
|
||||
|
||||
/* control block */
|
||||
static dmLegAdvCb_t dmLegAdvCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the advertising parameters using the given advertising type, and peer address.
|
||||
*
|
||||
* \param advType Advertising type.
|
||||
* \param peerAddrType Peer address type.
|
||||
* \param pPeerAddr Peer address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmAdvConfig(uint8_t advType, uint8_t peerAddrType, uint8_t *pPeerAddr)
|
||||
{
|
||||
/* set advertising parameters */
|
||||
HciLeSetAdvParamCmd(dmAdvCb.intervalMin[DM_ADV_HANDLE_DEFAULT], /* advIntervalMin */
|
||||
dmAdvCb.intervalMax[DM_ADV_HANDLE_DEFAULT], /* advIntervalMax */
|
||||
advType, /* advType */
|
||||
DmLlAddrType(dmCb.advAddrType), /* ownAddrType */
|
||||
peerAddrType, /* peerAddrType */
|
||||
pPeerAddr, /* pPeerAddr */
|
||||
dmAdvCb.channelMap[DM_ADV_HANDLE_DEFAULT], /* advChanMap */
|
||||
dmCb.advFiltPolicy[DM_ADV_HANDLE_DEFAULT]); /* advFiltPolicy */
|
||||
|
||||
/* store advertising type */
|
||||
dmLegAdvCb.advType = advType;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start advertising action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActConfig(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
DM_TRACE_INFO1("dmAdvActConfig: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_IDLE)
|
||||
{
|
||||
/* if doing directed advertising ignore the request */
|
||||
if ((dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] == DM_ADV_CONN_DIRECT) ||
|
||||
(dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] == DM_ADV_CONN_DIRECT_LO_DUTY))
|
||||
{
|
||||
DM_TRACE_WARN0("DmAdvConfig during directed advertising!");
|
||||
return;
|
||||
}
|
||||
|
||||
/* set advertising parameters */
|
||||
dmAdvConfig(pMsg->apiConfig.advType, pMsg->apiConfig.peerAddrType, pMsg->apiConfig.peerAddr);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the advertising or scan response data to the given data.
|
||||
*
|
||||
* \param location Data location.
|
||||
* \param len Length of the data. Maximum length is 31 bytes.
|
||||
* \param pData Pointer to the data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActSetData(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
WSF_ASSERT(pMsg->apiSetData.len <= HCI_ADV_DATA_LEN);
|
||||
|
||||
DM_TRACE_INFO1("dmAdvActSetData: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_IDLE)
|
||||
{
|
||||
/* set new data in HCI */
|
||||
if (pMsg->apiSetData.location == DM_DATA_LOC_ADV)
|
||||
{
|
||||
HciLeSetAdvDataCmd(pMsg->apiSetData.len, pMsg->apiSetData.pData);
|
||||
}
|
||||
else
|
||||
{
|
||||
HciLeSetScanRespDataCmd(pMsg->apiSetData.len, pMsg->apiSetData.pData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start advertising action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActStart(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
DM_TRACE_INFO1("dmAdvActStart: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_IDLE)
|
||||
{
|
||||
/* if doing directed advertising ignore the request */
|
||||
if ((dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] == DM_ADV_CONN_DIRECT) ||
|
||||
(dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] == DM_ADV_CONN_DIRECT_LO_DUTY))
|
||||
{
|
||||
DM_TRACE_WARN0("dmAdvActStart during directed advertising!");
|
||||
return;
|
||||
}
|
||||
|
||||
/* start advertising */
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_STARTING;
|
||||
dmAdvCb.advDuration[DM_ADV_HANDLE_DEFAULT] = pMsg->apiStart.duration[DM_ADV_HANDLE_DEFAULT];
|
||||
HciLeSetAdvEnableCmd(TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop advertising action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActStop(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
DM_TRACE_INFO1("dmAdvActStop: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_ADVERTISING)
|
||||
{
|
||||
/* if doing directed advertising ignore the request */
|
||||
if ((dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] == DM_ADV_CONN_DIRECT) ||
|
||||
(dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] == DM_ADV_CONN_DIRECT_LO_DUTY))
|
||||
{
|
||||
DM_TRACE_WARN0("DmAdvStop during directed advertising!");
|
||||
return;
|
||||
}
|
||||
|
||||
/* disable advertising */
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_STOPPING;
|
||||
HciLeSetAdvEnableCmd(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove an advertising set action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActRemoveSet(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear advertising sets action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActClearSets(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the random device address for a given advertising set.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActSetRandAddr(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an advertising timeout.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvActTimeout(dmAdvMsg_t *pMsg)
|
||||
{
|
||||
DM_TRACE_INFO0("dmAdvActTimeout!");
|
||||
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_ADVERTISING)
|
||||
{
|
||||
/* disable advertising */
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_STOPPING;
|
||||
HciLeSetAdvEnableCmd(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the legacy adv module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvReset(void)
|
||||
{
|
||||
wsfMsgHdr_t advStop;
|
||||
|
||||
/* if stopping undirected advertisement or advertising but not high duty cycle directed adv */
|
||||
if ((dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_STOPPING) ||
|
||||
((dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_ADVERTISING) &&
|
||||
(dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] != DM_ADV_CONN_DIRECT)))
|
||||
{
|
||||
/* stop advertising timer */
|
||||
WsfTimerStop(&dmAdvCb.advTimer);
|
||||
|
||||
/* generate advertising stop event */
|
||||
advStop.status = HCI_SUCCESS;
|
||||
advStop.event = DM_ADV_STOP_IND;
|
||||
|
||||
/* call callback */
|
||||
(*dmCb.cback)((dmEvt_t *) &advStop);
|
||||
}
|
||||
|
||||
/* reset legacy adv module */
|
||||
dmAdvInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM adv HCI event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
if (pEvent->hdr.event == HCI_LE_ADV_ENABLE_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
uint8_t cbackEvent = 0;
|
||||
|
||||
DM_TRACE_INFO1("HCI_LE_ADV_ENABLE_CMD_CMPL_CBACK_EVT: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
switch (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT])
|
||||
{
|
||||
case DM_ADV_STATE_STARTING:
|
||||
case DM_ADV_STATE_STARTING_DIRECTED:
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_STARTING)
|
||||
{
|
||||
/* start advertising timer if applicable */
|
||||
if (dmAdvCb.advDuration[DM_ADV_HANDLE_DEFAULT] > 0)
|
||||
{
|
||||
dmAdvCb.advTimer.msg.event = DM_ADV_MSG_TIMEOUT;
|
||||
WsfTimerStartMs(&dmAdvCb.advTimer, dmAdvCb.advDuration[DM_ADV_HANDLE_DEFAULT]);
|
||||
}
|
||||
|
||||
/* Application callbacks only sent in undirected state */
|
||||
if (dmLegAdvCb.advType != DM_ADV_CONN_DIRECT_LO_DUTY)
|
||||
{
|
||||
cbackEvent = DM_ADV_START_IND;
|
||||
}
|
||||
}
|
||||
|
||||
/* pass advertising start event to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_RPA_START, DM_ADV_START_IND, 0, 0);
|
||||
|
||||
/* store advertising type and state */
|
||||
dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] = dmLegAdvCb.advType;
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_ADVERTISING;
|
||||
}
|
||||
else
|
||||
{
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case DM_ADV_STATE_STOPPING:
|
||||
case DM_ADV_STATE_STOPPING_DIRECTED:
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_STOPPING)
|
||||
{
|
||||
/* stop advertising timer */
|
||||
WsfTimerStop(&dmAdvCb.advTimer);
|
||||
|
||||
/* Application and DM callbacks only sent in undirected or low duty cycle directed state */
|
||||
if (dmLegAdvCb.advType == DM_ADV_CONN_DIRECT_LO_DUTY)
|
||||
{
|
||||
cbackEvent = HCI_LE_ENHANCED_CONN_CMPL_CBACK_EVT;
|
||||
}
|
||||
else
|
||||
{
|
||||
cbackEvent = DM_ADV_STOP_IND;
|
||||
}
|
||||
}
|
||||
|
||||
/* pass advertising stop event to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_RPA_STOP, DM_ADV_STOP_IND, 0, 0);
|
||||
|
||||
/* store advertising type and state */
|
||||
dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] = DM_ADV_NONE;
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_ADVERTISING;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* ignore the event */
|
||||
break;
|
||||
}
|
||||
|
||||
/* if DM conn notify needed */
|
||||
if (cbackEvent == HCI_LE_ENHANCED_CONN_CMPL_CBACK_EVT)
|
||||
{
|
||||
dmAdvGenConnCmpl(DM_ADV_HANDLE_DEFAULT, HCI_ERR_ADV_TIMEOUT);
|
||||
}
|
||||
/* else if app callback needed */
|
||||
else if (cbackEvent)
|
||||
{
|
||||
pEvent->hdr.event = cbackEvent;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM adv event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmAdvAct[DM_MSG_MASK(pMsg->event)])((dmAdvMsg_t *)pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start directed advertising.
|
||||
*
|
||||
* \param advType Advertising type.
|
||||
* \param duration Advertising duration (in ms).
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvStartDirected(uint8_t advType, uint16_t duration, uint8_t addrType, uint8_t *pAddr)
|
||||
{
|
||||
DM_TRACE_INFO1("dmAdvStartDirected: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
/* if not advertising */
|
||||
if (dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_IDLE)
|
||||
{
|
||||
/* start advertising */
|
||||
HciLeSetAdvEnableCmd(TRUE);
|
||||
|
||||
/* store advertising info */
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = (advType == DM_ADV_CONN_DIRECT) ? \
|
||||
DM_ADV_STATE_STARTING_DIRECTED : DM_ADV_STATE_STARTING;
|
||||
|
||||
dmAdvCb.advDuration[DM_ADV_HANDLE_DEFAULT] = duration;
|
||||
BdaCpy(dmAdvCb.peerAddr[DM_ADV_HANDLE_DEFAULT], pAddr);
|
||||
dmAdvCb.peerAddrType[DM_ADV_HANDLE_DEFAULT] = addrType;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop directed advertising.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvStopDirected(void)
|
||||
{
|
||||
DM_TRACE_INFO1("dmAdvStopDirected: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
/* if advertising or starting advertising */
|
||||
if ((dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_ADVERTISING) ||
|
||||
(dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_STARTING) ||
|
||||
(dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] == DM_ADV_STATE_STARTING_DIRECTED))
|
||||
{
|
||||
/* disable advertising */
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = (dmLegAdvCb.advType == DM_ADV_CONN_DIRECT) ? \
|
||||
DM_ADV_STATE_STOPPING_DIRECTED : DM_ADV_STATE_STOPPING;
|
||||
HciLeSetAdvEnableCmd(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called when a connection is established from directed advertising.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvConnected(void)
|
||||
{
|
||||
DM_TRACE_INFO1("dmAdvConnected: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
WsfTimerStop(&dmAdvCb.advTimer);
|
||||
|
||||
/* pass advertising stop event to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_RPA_STOP, DM_ADV_STOP_IND, 0, 0);
|
||||
|
||||
dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] = DM_ADV_NONE;
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_IDLE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called when a directed advertising connection fails.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmAdvConnectFailed(void)
|
||||
{
|
||||
DM_TRACE_INFO1("dmAdvConnectFailed: state: %d", dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT]);
|
||||
|
||||
WsfTimerStop(&dmAdvCb.advTimer);
|
||||
|
||||
/* pass advertising stop event to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_RPA_STOP, DM_ADV_STOP_IND, 0, 0);
|
||||
|
||||
dmAdvCb.advType[DM_ADV_HANDLE_DEFAULT] = DM_ADV_NONE;
|
||||
dmAdvCb.advState[DM_ADV_HANDLE_DEFAULT] = DM_ADV_STATE_IDLE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM legacy advertising.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAdvInit(void)
|
||||
{
|
||||
WsfTaskLock();
|
||||
|
||||
/* set function interface table */
|
||||
dmFcnIfTbl[DM_ID_ADV] = (dmFcnIf_t *) &dmAdvFcnIf;
|
||||
|
||||
/* initialize legacy adv module */
|
||||
dmAdvInit();
|
||||
|
||||
/* clear set advertising set random address callback */
|
||||
dmDevCb.advSetRandAddrCback = NULL;
|
||||
|
||||
/* initialize HCI VS module */
|
||||
HciVsInit(0);
|
||||
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Whether DM advertising is in legacy mode.
|
||||
*
|
||||
* \return TRUE if DM advertising is in legacy mode. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmAdvModeLeg(void)
|
||||
{
|
||||
return (dmFcnIfTbl[DM_ID_ADV] == (dmFcnIf_t *) &dmAdvFcnIf) ? TRUE : FALSE;
|
||||
}
|
||||
+1595
File diff suppressed because it is too large
Load Diff
+369
@@ -0,0 +1,369 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM connection management module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_CONN_H
|
||||
#define DM_CONN_H
|
||||
|
||||
#include "dm_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Uninitialized HCI handle */
|
||||
#define DM_CONN_HCI_HANDLE_NONE 0xFFFF
|
||||
|
||||
/* Action set initializer */
|
||||
#define DM_CONN_ACT_SET_INIT(n) ((n) << 4)
|
||||
|
||||
/* Get action set ID from action */
|
||||
#define DM_CONN_ACT_SET_ID(action) ((action) >> 4)
|
||||
|
||||
/* Get action ID from action */
|
||||
#define DM_CONN_ACT_ID(action) ((action) & 0x0F)
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! DM conn event handler messages for state machine */
|
||||
enum
|
||||
{
|
||||
/* messages from API */
|
||||
DM_CONN_MSG_API_OPEN = DM_MSG_START(DM_ID_CONN), /*!< Open Connection */
|
||||
DM_CONN_MSG_API_CLOSE, /*!< Close Connection */
|
||||
DM_CONN_MSG_API_ACCEPT, /*!< Accept Connection */
|
||||
DM_CONN_MSG_API_UPDATE_MASTER, /*!< Master Connection Parameter Update */
|
||||
DM_CONN_MSG_API_UPDATE_SLAVE, /*!< Slave Connecteion Parameter Update */
|
||||
|
||||
/* messages from L2C */
|
||||
DM_CONN_MSG_L2C_UPDATE_IND, /*!< L2CAP Parameter update indication */
|
||||
DM_CONN_MSG_L2C_UPDATE_CNF, /*!< L2CAP Parameter update confirmation */
|
||||
|
||||
/* messages from HCI */
|
||||
DM_CONN_MSG_HCI_LE_CONN_CMPL_FAIL, /*!< HCI LE Connection Complete Failure Event */
|
||||
DM_CONN_MSG_HCI_LE_CONN_CMPL, /*!< HCI LE Connection Compelte Event */
|
||||
DM_CONN_MSG_HCI_DISCONNECT_CMPL, /*!< HCI Disconnection Complete Event */
|
||||
DM_CONN_MSG_HCI_LE_CONN_UPDATE_CMPL, /*!< HCI LE Connection Update Complete Event */
|
||||
DM_CONN_MSG_HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL, /*!< HCI LE Create Connection Cancel Command Complet Event */
|
||||
|
||||
/* other internal messages */
|
||||
DM_CONN_MSG_INT_UPDATE_TIMEOUT /*!< Internal Update Timeout */
|
||||
};
|
||||
|
||||
/* Number of messages */
|
||||
#define DM_CONN_NUM_MSGS (DM_CONN_MSG_INT_UPDATE_TIMEOUT - DM_CONN_MSG_API_OPEN + 1)
|
||||
|
||||
/* DM conn event handler messages, non-state machine */
|
||||
enum
|
||||
{
|
||||
DM_CONN_MSG_API_FEAT = DM_MSG_START(DM_ID_CONN_2),
|
||||
DM_CONN_MSG_API_READ_RSSI,
|
||||
DM_CONN_MSG_API_REM_CONN_PARAM_REQ_REPLY,
|
||||
DM_CONN_MSG_API_REM_CONN_PARAM_REQ_NEG_REPLY,
|
||||
DM_CONN_MSG_API_SET_DATA_LEN,
|
||||
DM_CONN_MSG_API_WRITE_AUTH_TO,
|
||||
|
||||
/* messages from HCI */
|
||||
DM_CONN_MSG_HCI_READ_RSSI_CMPL,
|
||||
DM_CONN_MSG_HCI_FEAT_CMPL
|
||||
};
|
||||
|
||||
/* State machine action function sets */
|
||||
enum
|
||||
{
|
||||
DM_CONN_ACT_SET_MAIN,
|
||||
DM_CONN_ACT_SET_MASTER,
|
||||
DM_CONN_ACT_SET_SLAVE,
|
||||
DM_CONN_NUM_ACT_SETS
|
||||
};
|
||||
|
||||
/*! State machine actions */
|
||||
enum
|
||||
{
|
||||
DM_CONN_SM_ACT_NONE = DM_CONN_ACT_SET_INIT(DM_CONN_ACT_SET_MAIN), /*!< No Action */
|
||||
DM_CONN_SM_ACT_CLOSE, /*!< Process Connection Close */
|
||||
DM_CONN_SM_ACT_CONN_OPENED, /*!< Procoess Connection Opened */
|
||||
DM_CONN_SM_ACT_CONN_FAILED, /*!< Process Connection Failed */
|
||||
DM_CONN_SM_ACT_CONN_CLOSED, /*!< Process Connection Closed */
|
||||
DM_CONN_SM_ACT_HCI_UPDATED, /*!< Process HCI Connection Update */
|
||||
|
||||
DM_CONN_SM_ACT_OPEN = DM_CONN_ACT_SET_INIT(DM_CONN_ACT_SET_MASTER), /*!< Process Master Connection Open */
|
||||
DM_CONN_SM_ACT_CANCEL_OPEN, /*!< Process Master Cancel Connection Open */
|
||||
DM_CONN_SM_ACT_UPDATE_MASTER, /*!< Process Master Connection Parameter Update */
|
||||
DM_CONN_SM_ACT_L2C_UPDATE_IND, /*!< Process Master L2CAP Connection Parameter Update Indication */
|
||||
|
||||
DM_CONN_SM_ACT_ACCEPT = DM_CONN_ACT_SET_INIT(DM_CONN_ACT_SET_SLAVE), /*!< Process Slave Connection Accept */
|
||||
DM_CONN_SM_ACT_CANCEL_ACCEPT, /*!< Process Slave Cancel Connection Accept */
|
||||
DM_CONN_SM_ACT_UPDATE_SLAVE, /*!< Process Slave Connection Update */
|
||||
DM_CONN_SM_ACT_CONN_ACCEPTED, /*!< Process Slave Connection Accepted */
|
||||
DM_CONN_SM_ACT_ACCEPT_FAILED, /*!< Process Slave Connection Accept Failure */
|
||||
DM_CONN_SM_ACT_L2C_UPDATE_CNF /*!< Process Slave L2CAP Connection Parameter Update Confirmation */
|
||||
};
|
||||
|
||||
/*! State machine states */
|
||||
enum
|
||||
{
|
||||
DM_CONN_SM_ST_IDLE, /*!< Idle State */
|
||||
DM_CONN_SM_ST_CONNECTING, /*!< Connecting State */
|
||||
DM_CONN_SM_ST_ACCEPTING, /*!< Accepting State */
|
||||
DM_CONN_SM_ST_CONNECTED, /*!< Connected State */
|
||||
DM_CONN_SM_ST_DISCONNECTING, /*!< Disconnecting State */
|
||||
DM_CONN_SM_NUM_STATES
|
||||
};
|
||||
|
||||
/* Data structure for DM_CONN_MSG_API_OPEN and DM_CONN_MSG_API_ACCEPT */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t initPhys;
|
||||
uint8_t advHandle;
|
||||
uint8_t advType;
|
||||
uint16_t duration;
|
||||
uint8_t maxEaEvents;
|
||||
bdAddr_t peerAddr;
|
||||
uint8_t addrType;
|
||||
uint8_t clientId;
|
||||
} dmConnApiOpen_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_CLOSE */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t reason;
|
||||
uint8_t clientId;
|
||||
} dmConnApiClose_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_UPDATE_MASTER and SLAVE */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
hciConnSpec_t connSpec;
|
||||
} dmConnApiUpdate_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_L2C_UPDATE_IND */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
hciConnSpec_t *pConnSpec;
|
||||
uint8_t identifier;
|
||||
} dmConnL2cUpdateInd_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_L2C_UPDATE_CNF */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint16_t result;
|
||||
} dmConnL2cUpdateCnf_t;
|
||||
|
||||
/*! Union of all DM Conn state machine messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmConnApiOpen_t apiOpen;
|
||||
dmConnApiClose_t apiClose;
|
||||
dmConnApiUpdate_t apiUpdate;
|
||||
dmConnL2cUpdateInd_t l2cUpdateInd;
|
||||
dmConnL2cUpdateCnf_t l2cUpdateCnf;
|
||||
hciLeConnCmplEvt_t hciLeConnCmpl;
|
||||
hciDisconnectCmplEvt_t hciDisconnectCmpl;
|
||||
hciLeConnUpdateCmplEvt_t hciLeConnUpdateCmpl;
|
||||
} dmConnMsg_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_FEAT */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint16_t handle;
|
||||
uint8_t type;
|
||||
uint8_t feature;
|
||||
} dmConnApiFeat_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_READ_RSSI */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
} dmConnApiReadRssi_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_REM_CONN_PARAM_REQ_REPLY */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
hciConnSpec_t connSpec;
|
||||
} dmConnApiRemConnParamReqReply_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_REM_CONN_PARAM_REQ_NEG_REPLY */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t reason;
|
||||
} dmConnApiRemConnParamReqNegReply_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_SET_DATA_LEN */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint16_t txOctets;
|
||||
uint16_t txTime;
|
||||
} dmConnApiSetDataLen_t;
|
||||
|
||||
/*! Data structure for DM_CONN_MSG_API_WRITE_AUTH_TO */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint16_t timeout;
|
||||
} dmConnApiWriteAuthPayloadTo_t;
|
||||
|
||||
/*! Union of all DM Conn 2 messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmConnApiFeat_t apiFeat;
|
||||
dmConnApiReadRssi_t apiReadRssi;
|
||||
dmConnApiRemConnParamReqReply_t apiRemConnParamReqReply;
|
||||
dmConnApiRemConnParamReqNegReply_t apiRemConnParamReqNegReply;
|
||||
dmConnApiSetDataLen_t apiSetDataLen;
|
||||
dmConnApiWriteAuthPayloadTo_t apiWriteAuthPayloadTo;
|
||||
} dmConn2Msg_t;
|
||||
|
||||
/*! Connection control block */
|
||||
typedef struct
|
||||
{
|
||||
bdAddr_t peerAddr;
|
||||
bdAddr_t localAddr;
|
||||
uint16_t handle;
|
||||
uint16_t idleMask;
|
||||
dmConnId_t connId;
|
||||
bool_t updating;
|
||||
bool_t usingLtk;
|
||||
uint8_t peerAddrType;
|
||||
uint8_t localAddrType;
|
||||
uint8_t state;
|
||||
uint8_t inUse;
|
||||
uint8_t secLevel;
|
||||
uint8_t tmpSecLevel;
|
||||
uint8_t role;
|
||||
|
||||
/* enhanced fields */
|
||||
bdAddr_t localRpa;
|
||||
bdAddr_t peerRpa;
|
||||
|
||||
uint32_t features;
|
||||
bool_t featuresPresent;
|
||||
} dmConnCcb_t;
|
||||
|
||||
/*! Action function */
|
||||
typedef void (*dmConnAct_t)(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/*! Control block of the DM conn module */
|
||||
typedef struct
|
||||
{
|
||||
dmConnCcb_t ccb[DM_CONN_MAX];
|
||||
dmCback_t connCback[DM_CLIENT_ID_MAX];
|
||||
hciConnSpec_t connSpec[DM_NUM_PHYS];
|
||||
uint16_t scanInterval[DM_NUM_PHYS];
|
||||
uint16_t scanWindow[DM_NUM_PHYS];
|
||||
} dmConnCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! State machine action sets */
|
||||
extern dmConnAct_t *dmConnActSet[DM_CONN_NUM_ACT_SETS];
|
||||
|
||||
/*! Control block */
|
||||
extern dmConnCb_t dmConnCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* utility functions */
|
||||
dmConnCcb_t *dmConnCcbAlloc(uint8_t *pAddr);
|
||||
void dmConnCcbDealloc(dmConnCcb_t *pCcb);
|
||||
dmConnCcb_t *dmConnCcbByHandle(uint16_t handle);
|
||||
dmConnCcb_t *dmConnCcbByBdAddr(uint8_t *pAddr);
|
||||
dmConnCcb_t *dmConnCcbById(dmConnId_t connId);
|
||||
uint8_t dmConnNum(void);
|
||||
dmConnId_t dmConnOpenAccept(uint8_t clientId, uint8_t initPhys, uint8_t advHandle, uint8_t advType,
|
||||
uint16_t duration, uint8_t maxEaEvents, uint8_t addrType, uint8_t *pAddr,
|
||||
uint8_t role);
|
||||
void dmConnExecCback(dmConnMsg_t *pMsg);
|
||||
|
||||
/* component inteface */
|
||||
void dmConnReset(void);
|
||||
void dmConnMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmConnHciHandler(hciEvt_t *pEvent);
|
||||
|
||||
/* component 2 inteface */
|
||||
void dmConn2MsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmConn2HciHandler(hciEvt_t *pEvent);
|
||||
|
||||
/* state machine */
|
||||
void dmConnSmExecute(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/* main action functions */
|
||||
void dmConnSmActNone(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActClose(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActConnOpened(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActConnFailed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActConnClosed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActHciUpdated(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/* common master action functions */
|
||||
void dmConnSmActCancelOpen(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActUpdateMaster(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActL2cUpdateInd(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/* legacy master action functions */
|
||||
void dmConnSmActOpen(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/* extended master action functions */
|
||||
void dmExtConnSmActOpen(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/* common slave action functions */
|
||||
void dmConnSmActUpdateSlave(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActL2cUpdateCnf(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/* legacy slave action functions */
|
||||
void dmConnSmActAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActCancelAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActConnAccepted(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmConnSmActAcceptFailed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
/* extended slave action functions */
|
||||
void dmExtConnSmActAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmExtConnSmActCancelAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmExtConnSmActConnAccepted(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
void dmExtConnSmActAcceptFailed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_CONN_H */
|
||||
Vendored
+840
@@ -0,0 +1,840 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager Connection Constant Tone Extension (CTE) module.
|
||||
*
|
||||
* Copyright (c) 2018-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! DM connection CTE states */
|
||||
enum
|
||||
{
|
||||
DM_CONN_CTE_STATE_IDLE, /*!< Idle */
|
||||
DM_CONN_CTE_STATE_INITIATING, /*!< Initiating CTE request */
|
||||
DM_CONN_CTE_STATE_RESPONDING, /*!< Responding to CTE request */
|
||||
DM_CONN_CTE_STATE_SAMPLING, /*!< Sampling received CTE */
|
||||
DM_CONN_CTE_STATE_STARTING, /*!< Starting CTE request, CTE response or sampling received CTE */
|
||||
DM_CONN_CTE_STATE_STOPPING, /*!< Stopping CTE request, CTE response or sampling received CTE */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Data structure for DM_CONN_CTE_MSG_API_RX_SAMPLE_START */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
dmConnId_t connId; /*!< Connection id */
|
||||
uint8_t slotDurations; /*!< Switching and sampling slot durations */
|
||||
uint8_t switchPatternLen; /*!< Number of Antenna IDs in switching pattern */
|
||||
uint8_t *pAntennaIDs; /*!< List of Antenna IDs in switching pattern */
|
||||
} dmConnCteApiRxSampleStart_t;
|
||||
|
||||
/*! Data structure for DM_CONN_CTE_MSG_API_RX_SAMPLE_STOP */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
dmConnId_t connId; /*!< Connection id */
|
||||
} dmConnCteApiRxSampleStop_t;
|
||||
|
||||
/*! Data structure for DM_CONN_CTE_MSG_API_TX_CFG */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
dmConnId_t connId; /*!< Connection id */
|
||||
uint8_t cteTypeBits; /*!< Permitted CTE type bits */
|
||||
uint8_t switchPatternLen; /*!< Number of Antenna IDs in switching pattern */
|
||||
uint8_t *pAntennaIDs; /*!< List of Antenna IDs in switching pattern */
|
||||
} dmConnCteApiTxConfig_t;
|
||||
|
||||
/*! Data structure for DM_CONN_CTE_MSG_API_REQ_START */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
dmConnId_t connId; /*!< Connection id */
|
||||
uint16_t cteReqInt; /*!< CTE request interval */
|
||||
uint8_t reqCteLen; /*!< Minimum length of CTE being requested in 8 us units */
|
||||
uint8_t reqCteType; /*!< Requested CTE type */
|
||||
} dmConnCteApiReqStart_t;
|
||||
|
||||
/*! Data structure for DM_CONN_CTE_MSG_API_REQ_STOP */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
dmConnId_t connId; /*!< Connection id */
|
||||
} dmConnCteApiReqStop_t;
|
||||
|
||||
/*! Data structure for DM_CONN_CTE_MSG_API_RSP_START and DM_CONN_CTE_MSG_API_RSP_STOP */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
dmConnId_t connId; /*!< Connection id */
|
||||
} dmConnCteApiRspEnable_t;
|
||||
|
||||
/*! Union of all DM Connection CTE API messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
dmConnCteApiRxSampleStart_t rxSampleStart; /*!< Start sampling received CTE, and configure CTE Rx parameters to be used */
|
||||
dmConnCteApiRxSampleStop_t rxSampleStop; /*!< Stop sampling received CTE */
|
||||
dmConnCteApiTxConfig_t txCfg; /*!< Configure CTE Tx parameters */
|
||||
dmConnCteApiReqStart_t reqStart; /*!< Start initiating CTE request */
|
||||
dmConnCteApiReqStop_t reqStop; /*!< Stop initiating CTE request */
|
||||
dmConnCteApiRspEnable_t rspEnable; /*!< Start or stop responding to CTE request */
|
||||
} dmConnCteMsg_t;
|
||||
|
||||
/*! Action function */
|
||||
typedef void (*dmConnCteAct_t)(dmConnCteMsg_t *pMsg);
|
||||
|
||||
/*! Control block for connection CTE module */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t rxSampleState; /*!< Sampling received CTE state */
|
||||
uint8_t reqState; /*!< Connection CTE Request state */
|
||||
uint8_t rspState; /*!< Connection CTE Response state */
|
||||
} dmConnCteCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
void dmConnCteInit(void);
|
||||
void dmConnCteReset(void);
|
||||
void dmConnCteHciHandler(hciEvt_t *pEvent);
|
||||
void dmConnCteMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/*! Action functions */
|
||||
static void dmConnCteActRxSampleStart(dmConnCteMsg_t *pMsg);
|
||||
static void dmConnCteActRxSampleStop(dmConnCteMsg_t *pMsg);
|
||||
static void dmConnCteActTxCfg(dmConnCteMsg_t *pMsg);
|
||||
static void dmConnCteActReqStart(dmConnCteMsg_t *pMsg);
|
||||
static void dmConnCteActReqStop(dmConnCteMsg_t *pMsg);
|
||||
static void dmConnCteActRspStart(dmConnCteMsg_t *pMsg);
|
||||
static void dmConnCteActRspStop(dmConnCteMsg_t *pMsg);
|
||||
static void dmConnCteActState(dmConnCteMsg_t *pMsg);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Action function table */
|
||||
static const dmConnCteAct_t dmConnCteAct[] =
|
||||
{
|
||||
dmConnCteActRxSampleStart,
|
||||
dmConnCteActRxSampleStop,
|
||||
dmConnCteActTxCfg,
|
||||
dmConnCteActReqStart,
|
||||
dmConnCteActReqStop,
|
||||
dmConnCteActRspStart,
|
||||
dmConnCteActRspStop,
|
||||
dmConnCteActState
|
||||
};
|
||||
|
||||
/*! DM Connection CTE component function interface */
|
||||
static const dmFcnIf_t dmConnCteFcnIf =
|
||||
{
|
||||
dmConnCteReset,
|
||||
dmConnCteHciHandler,
|
||||
dmConnCteMsgHandler
|
||||
};
|
||||
|
||||
/*! Connection CTE control block */
|
||||
static dmConnCteCb_t dmConnCteCb[DM_CONN_MAX];
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize connection CTE control block entry.
|
||||
*
|
||||
* \param pCteCb Pointer to connection CTE control block structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnCteCbInit(dmConnCteCb_t *pCteCb)
|
||||
{
|
||||
pCteCb->rxSampleState = DM_CONN_CTE_STATE_IDLE;
|
||||
pCteCb->reqState = DM_CONN_CTE_STATE_IDLE;
|
||||
pCteCb->rspState = DM_CONN_CTE_STATE_IDLE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize connection CTE control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnCteInit(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* initialize control block */
|
||||
for (i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
dmConnCteCbInit(&dmConnCteCb[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM Connection Constant Tone Extension (CTE) module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteInit(void)
|
||||
{
|
||||
/* set function interface table */
|
||||
dmFcnIfTbl[DM_ID_CONN_CTE] = (dmFcnIf_t *) &dmConnCteFcnIf;
|
||||
|
||||
/* initialize control block */
|
||||
dmConnCteInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the connection CTE module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnCteReset(void)
|
||||
{
|
||||
/* reset control block */
|
||||
dmConnCteInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM PAST HCI event handler.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnCteHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
if (pEvent->hdr.event == HCI_LE_READ_ANTENNA_INFO_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
pEvent->hdr.event = DM_READ_ANTENNA_INFO_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
/* if ccb found */
|
||||
if ((pCcb = dmConnCcbByHandle(pEvent->hdr.param)) != NULL)
|
||||
{
|
||||
dmConnCteCb_t *pCteCb = &dmConnCteCb[pCcb->connId - 1];
|
||||
|
||||
/* set conn id */
|
||||
pEvent->hdr.param = pCcb->connId;
|
||||
|
||||
switch (pEvent->hdr.event)
|
||||
{
|
||||
case HCI_LE_CONN_IQ_REPORT_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_CONN_IQ_REPORT_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
break;
|
||||
|
||||
case HCI_LE_CTE_REQ_FAILED_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_CTE_REQ_FAIL_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
break;
|
||||
|
||||
case HCI_LE_SET_CONN_CTE_RX_PARAMS_CMD_CMPL_CBACK_EVT:
|
||||
/* if enabling sampling */
|
||||
if (pCteCb->rxSampleState == DM_CONN_CTE_STATE_STARTING)
|
||||
{
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pCteCb->rxSampleState = DM_CONN_CTE_STATE_SAMPLING;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCteCb->rxSampleState = DM_CONN_CTE_STATE_IDLE;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_CONN_CTE_RX_SAMPLE_START_IND;
|
||||
}
|
||||
/* else if disabling sampling */
|
||||
else if (pCteCb->rxSampleState == DM_CONN_CTE_STATE_STOPPING)
|
||||
{
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pCteCb->rxSampleState = DM_CONN_CTE_STATE_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCteCb->rxSampleState = DM_CONN_CTE_STATE_SAMPLING;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_CONN_CTE_RX_SAMPLE_STOP_IND;
|
||||
}
|
||||
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
break;
|
||||
|
||||
case HCI_LE_SET_CONN_CTE_TX_PARAMS_CMD_CMPL_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_CONN_CTE_TX_CFG_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
break;
|
||||
|
||||
case HCI_LE_CONN_CTE_REQ_ENABLE_CMD_CMPL_CBACK_EVT:
|
||||
/* if enabling request */
|
||||
if (pCteCb->reqState == DM_CONN_CTE_STATE_STARTING)
|
||||
{
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pCteCb->reqState = DM_CONN_CTE_STATE_INITIATING;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCteCb->reqState = DM_CONN_CTE_STATE_IDLE;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_CONN_CTE_REQ_START_IND;
|
||||
}
|
||||
/* else if disabing request */
|
||||
else if (pCteCb->reqState == DM_CONN_CTE_STATE_STOPPING)
|
||||
{
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pCteCb->reqState = DM_CONN_CTE_STATE_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCteCb->reqState = DM_CONN_CTE_STATE_INITIATING;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_CONN_CTE_REQ_STOP_IND;
|
||||
}
|
||||
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
break;
|
||||
|
||||
case HCI_LE_CONN_CTE_RSP_ENABLE_CMD_CMPL_CBACK_EVT:
|
||||
/* if enabling response */
|
||||
if (pCteCb->rspState == DM_CONN_CTE_STATE_STARTING)
|
||||
{
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pCteCb->rspState = DM_CONN_CTE_STATE_RESPONDING;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCteCb->rspState = DM_CONN_CTE_STATE_IDLE;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_CONN_CTE_RSP_START_IND;
|
||||
}
|
||||
/* else if disabling response */
|
||||
else if (pCteCb->rspState == DM_CONN_CTE_STATE_STOPPING)
|
||||
{
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pCteCb->rspState = DM_CONN_CTE_STATE_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCteCb->rspState = DM_CONN_CTE_STATE_RESPONDING;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_CONN_CTE_RSP_STOP_IND;
|
||||
}
|
||||
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM Connection CTE event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnCteMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmConnCteAct[DM_MSG_MASK(pMsg->event)])((dmConnCteMsg_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start sampling received CTE, and configure CTE receive parameters action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnCteActRxSampleStart(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->rxSampleStart.connId)) != NULL)
|
||||
{
|
||||
dmConnCteCb_t *pCteCb = &dmConnCteCb[pCcb->connId - 1];
|
||||
|
||||
/* if currently idle */
|
||||
if (pCteCb->rxSampleState == DM_CONN_CTE_STATE_IDLE)
|
||||
{
|
||||
HciLeSetConnCteRxParamsCmd(pCcb->handle, TRUE, pMsg->rxSampleStart.slotDurations,
|
||||
pMsg->rxSampleStart.switchPatternLen,
|
||||
pMsg->rxSampleStart.pAntennaIDs);
|
||||
|
||||
pCteCb->rxSampleState = DM_CONN_CTE_STATE_STARTING;
|
||||
}
|
||||
else
|
||||
{
|
||||
DM_TRACE_WARN0("DmConnCteRxSampleStart ignored due to rxSampleState or pending command complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop sampling received CTE action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnCteActRxSampleStop(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->rxSampleStop.connId)) != NULL)
|
||||
{
|
||||
dmConnCteCb_t *pCteCb = &dmConnCteCb[pCcb->connId - 1];
|
||||
|
||||
/* if currently sampling */
|
||||
if (pCteCb->rxSampleState == DM_CONN_CTE_STATE_SAMPLING)
|
||||
{
|
||||
HciLeSetConnCteRxParamsCmd(pCcb->handle, FALSE, 0, 0, NULL);
|
||||
|
||||
pCteCb->rxSampleState = DM_CONN_CTE_STATE_STOPPING;
|
||||
}
|
||||
else
|
||||
{
|
||||
DM_TRACE_WARN0("DmConnCteRxSampleStop ignored due to rxSampleState or pending command complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Configure connection CTE transmit action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnCteActTxCfg(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->txCfg.connId)) != NULL)
|
||||
{
|
||||
HciLeSetConnCteTxParamsCmd(pCcb->handle, pMsg->txCfg.cteTypeBits, pMsg->txCfg.switchPatternLen,
|
||||
pMsg->txCfg.pAntennaIDs);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start initiating CTE request action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnCteActReqStart(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->reqStart.connId)) != NULL)
|
||||
{
|
||||
dmConnCteCb_t *pCteCb = &dmConnCteCb[pCcb->connId - 1];
|
||||
|
||||
/* if currently idle */
|
||||
if (pCteCb->reqState == DM_CONN_CTE_STATE_IDLE)
|
||||
{
|
||||
HciLeConnCteReqEnableCmd(pCcb->handle, TRUE, pMsg->reqStart.cteReqInt,
|
||||
pMsg->reqStart.reqCteLen, pMsg->reqStart.reqCteType);
|
||||
|
||||
pCteCb->reqState = DM_CONN_CTE_STATE_STARTING;
|
||||
}
|
||||
else
|
||||
{
|
||||
DM_TRACE_WARN0("DmConnCteReqStart ignored due to reqState or pending command complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop initiating CTE request action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnCteActReqStop(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->reqStop.connId)) != NULL)
|
||||
{
|
||||
dmConnCteCb_t *pCteCb = &dmConnCteCb[pCcb->connId - 1];
|
||||
|
||||
/* if currently initiating */
|
||||
if (pCteCb->reqState == DM_CONN_CTE_STATE_INITIATING)
|
||||
{
|
||||
HciLeConnCteReqEnableCmd(pCcb->handle, FALSE, 0, 0, 0);
|
||||
|
||||
pCteCb->reqState = DM_CONN_CTE_STATE_STOPPING;
|
||||
}
|
||||
else
|
||||
{
|
||||
DM_TRACE_WARN0("DmConnCteReqStop ignored due to reqState or pending command complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start responding to CTE request action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnCteActRspStart(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->rspEnable.connId)) != NULL)
|
||||
{
|
||||
dmConnCteCb_t *pCteCb = &dmConnCteCb[pCcb->connId - 1];
|
||||
|
||||
/* if currently idle */
|
||||
if (pCteCb->rspState == DM_CONN_CTE_STATE_IDLE)
|
||||
{
|
||||
HciLeConnCteRspEnableCmd(pCcb->handle, TRUE);
|
||||
|
||||
pCteCb->rspState = DM_CONN_CTE_STATE_STARTING;
|
||||
}
|
||||
else
|
||||
{
|
||||
DM_TRACE_WARN0("DmConnCteRspStart ignored due to rspState or pending command complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop responding to CTE request action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnCteActRspStop(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->rspEnable.connId)) != NULL)
|
||||
{
|
||||
dmConnCteCb_t *pCteCb = &dmConnCteCb[pCcb->connId - 1];
|
||||
|
||||
/* if currently responding */
|
||||
if (pCteCb->rspState == DM_CONN_CTE_STATE_RESPONDING)
|
||||
{
|
||||
HciLeConnCteRspEnableCmd(pCcb->handle, FALSE);
|
||||
|
||||
pCteCb->rspState = DM_CONN_CTE_STATE_STOPPING;
|
||||
}
|
||||
else
|
||||
{
|
||||
DM_TRACE_WARN0("DmConnCteRspStop ignored due to rspState or pending command complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection state change action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnCteActState(dmConnCteMsg_t *pMsg)
|
||||
{
|
||||
/* if connection closed */
|
||||
if (pMsg->hdr.status == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* reset all states */
|
||||
dmConnCteCbInit(&dmConnCteCb[pMsg->hdr.param - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Enable sampling received CTE fields on the specified connection, and configure the
|
||||
* antenna switching pattern, and switching and sampling slot durations to be used.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param slotDurations Switching and sampling slot durations to be used while receiving CTE.
|
||||
* \param switchPatternLen Number of Antenna IDs in switching pattern.
|
||||
* \param pAntennaIDs List of Antenna IDs in switching pattern.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteRxSampleStart(dmConnId_t connId, uint8_t slotDurations, uint8_t switchPatternLen,
|
||||
uint8_t *pAntennaIDs)
|
||||
{
|
||||
dmConnCteApiRxSampleStart_t *pMsg;
|
||||
|
||||
WSF_ASSERT((switchPatternLen >= HCI_MIN_NUM_ANTENNA_IDS) && \
|
||||
(switchPatternLen <= HCI_MAX_NUM_ANTENNA_IDS));
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmConnCteApiRxSampleStart_t) + switchPatternLen)) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_CONN_CTE_MSG_API_RX_SAMPLE_START;
|
||||
pMsg->connId = connId;
|
||||
pMsg->slotDurations = slotDurations;
|
||||
pMsg->switchPatternLen = switchPatternLen;
|
||||
|
||||
/* Copy antenna IDs to space after end of config struct */
|
||||
pMsg->pAntennaIDs = (uint8_t *)(pMsg + 1);
|
||||
memcpy(pMsg->pAntennaIDs, pAntennaIDs, switchPatternLen);
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Disable sampling received CTE fields on the specified connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteRxSampleStop(dmConnId_t connId)
|
||||
{
|
||||
dmConnCteApiRxSampleStop_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmConnCteApiRxSampleStop_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_CONN_CTE_MSG_API_RX_SAMPLE_STOP;
|
||||
pMsg->connId = connId;
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Configure the antenna switching pattern, and permitted CTE types used for transmitting
|
||||
* CTEs requested by the peer device on the specified connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param cteTypeBits Permitted CTE type bits used for transmitting CTEs requested by peer.
|
||||
* \param switchPatternLen Number of Antenna IDs in switching pattern.
|
||||
* \param pAntennaIDs List of Antenna IDs in switching pattern.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteTxConfig(dmConnId_t connId, uint8_t cteTypeBits, uint8_t switchPatternLen,
|
||||
uint8_t *pAntennaIDs)
|
||||
{
|
||||
dmConnCteApiTxConfig_t *pMsg;
|
||||
|
||||
WSF_ASSERT((switchPatternLen >= HCI_MIN_NUM_ANTENNA_IDS) && \
|
||||
(switchPatternLen <= HCI_MAX_NUM_ANTENNA_IDS));
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmConnCteApiTxConfig_t) + switchPatternLen)) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_CONN_CTE_MSG_API_TX_CFG;
|
||||
pMsg->connId = connId;
|
||||
pMsg->cteTypeBits = cteTypeBits;
|
||||
pMsg->switchPatternLen = switchPatternLen;
|
||||
|
||||
/* Copy antenna IDs to space after end of config struct */
|
||||
pMsg->pAntennaIDs = (uint8_t *)(pMsg + 1);
|
||||
memcpy(pMsg->pAntennaIDs, pAntennaIDs, switchPatternLen);
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate the CTE Request procedure on the specified connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param cteReqInt CTE request interval.
|
||||
* \param reqCteLen Minimum length of CTE being requested in 8 us units.
|
||||
* \param reqCteType Requested CTE type.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteReqStart(dmConnId_t connId, uint16_t cteReqInt, uint8_t reqCteLen,
|
||||
uint8_t reqCteType)
|
||||
{
|
||||
dmConnCteApiReqStart_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmConnCteApiReqStart_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_CONN_CTE_MSG_API_REQ_START;
|
||||
pMsg->connId = connId;
|
||||
pMsg->cteReqInt = cteReqInt;
|
||||
pMsg->reqCteLen = reqCteLen;
|
||||
pMsg->reqCteType = reqCteType;
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop initiating the CTE Request procedure on the specified connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteReqStop(dmConnId_t connId)
|
||||
{
|
||||
dmConnCteApiReqStop_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmConnCteApiReqStop_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_CONN_CTE_MSG_API_REQ_STOP;
|
||||
pMsg->connId = connId;
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start responding to LL_CTE_REQ PDUs with LL_CTE_RSP PDUs on the specified connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteRspStart(dmConnId_t connId)
|
||||
{
|
||||
dmConnCteApiRspEnable_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmConnCteApiRspEnable_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_CONN_CTE_MSG_API_RSP_START;
|
||||
pMsg->connId = connId;
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop responding to LL_CTE_REQ PDUs with LL_CTE_RSP PDUs on the specified connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnCteRspStop(dmConnId_t connId)
|
||||
{
|
||||
dmConnCteApiRspEnable_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmConnCteApiRspEnable_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_CONN_CTE_MSG_API_RSP_STOP;
|
||||
pMsg->connId = connId;
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read the switching rates, the sampling rates, the number of antennae, and the maximum
|
||||
* length of a transmitted Constant Tone Extension supported by the Controller.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note The antenna info will be returned with DM indication \ref DM_READ_ANTENNA_INFO_IND.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmReadAntennaInfo(void)
|
||||
{
|
||||
HciLeReadAntennaInfoCmd();
|
||||
}
|
||||
Vendored
+145
@@ -0,0 +1,145 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager connection management for master.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_os.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "l2c_api.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Cancel an opening connection.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActCancelOpen(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
/* cancel create connection */
|
||||
HciLeCreateConnCancelCmd();
|
||||
|
||||
/* pass connection initiation stopped to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_CTRL, DM_DEV_PRIV_MSG_CONN_INIT_STOP, 0, 0);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Update a connection as a master.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActUpdateMaster(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
/* send HCI command */
|
||||
HciLeConnUpdateCmd(pCcb->handle, &pMsg->apiUpdate.connSpec);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an L2CAP connection update indication.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActL2cUpdateInd(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
/* always send back response */
|
||||
L2cDmConnUpdateRsp(pMsg->l2cUpdateInd.identifier, pCcb->handle, L2C_CONN_PARAM_ACCEPTED);
|
||||
|
||||
/* send HCI command */
|
||||
HciLeConnUpdateCmd(pCcb->handle, pMsg->l2cUpdateInd.pConnSpec);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For internal use only. L2C calls this function when it receives a connection update
|
||||
* request from a peer device.
|
||||
*
|
||||
* \param identifier Identifier value.
|
||||
* \param handle Connection handle.
|
||||
* \param pConnSpec Connection spec parameters.
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmL2cConnUpdateInd(uint8_t identifier, uint16_t handle, hciConnSpec_t *pConnSpec)
|
||||
{
|
||||
dmConnL2cUpdateInd_t updateInd;
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
if ((pCcb = dmConnCcbByHandle(handle)) != NULL)
|
||||
{
|
||||
updateInd.hdr.event = DM_CONN_MSG_L2C_UPDATE_IND;
|
||||
updateInd.pConnSpec = pConnSpec;
|
||||
updateInd.identifier = identifier;
|
||||
|
||||
dmConnSmExecute(pCcb, (dmConnMsg_t *) &updateInd);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Open a connection to a peer device with the given address.
|
||||
*
|
||||
* \param clientId The client identifier.
|
||||
* \param initPhys Initiator PHYs.
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return Connection identifier.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmConnId_t DmConnOpen(uint8_t clientId, uint8_t initPhys, uint8_t addrType, uint8_t *pAddr)
|
||||
{
|
||||
return dmConnOpenAccept(clientId, initPhys, 0, 0, 0, 0, addrType, pAddr, DM_ROLE_MASTER);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the local address type used for connections created with DmConnOpen().
|
||||
*
|
||||
* \param addrType Address type.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnSetAddrType(uint8_t addrType)
|
||||
{
|
||||
WsfTaskLock();
|
||||
dmCb.connAddrType = addrType;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
Vendored
+118
@@ -0,0 +1,118 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager connection management module for extended master.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Action set for this module */
|
||||
static const dmConnAct_t dmConnActSetMaster[] =
|
||||
{
|
||||
dmExtConnSmActOpen,
|
||||
dmConnSmActCancelOpen,
|
||||
dmConnSmActUpdateMaster,
|
||||
dmConnSmActL2cUpdateInd
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Open a connection to a peer device with the given address.
|
||||
*
|
||||
* \param initPhys Initiating PHYs.
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return Connection identifier.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmExtConnOpen(uint8_t initPhys, uint8_t addrType, uint8_t *pAddr)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t idx;
|
||||
uint8_t phyIdx;
|
||||
hciExtInitParam_t initParam;
|
||||
hciConnSpec_t connSpec[DM_NUM_PHYS];
|
||||
hciExtInitScanParam_t scanParam[DM_NUM_PHYS];
|
||||
|
||||
/* set initiating parameters */
|
||||
initParam.filterPolicy = dmCb.initFiltPolicy;
|
||||
initParam.ownAddrType = DmLlAddrType(dmCb.connAddrType);
|
||||
initParam.peerAddrType = addrType;
|
||||
initParam.pPeerAddr = pAddr;
|
||||
initParam.initPhys = initPhys;
|
||||
|
||||
/* see advertising packets to be received on which PHY */
|
||||
for (i = 0, idx = 0; (i < 8) && (idx < DM_NUM_PHYS); i++)
|
||||
{
|
||||
if (initPhys & (1 << i))
|
||||
{
|
||||
phyIdx = DmInitPhyToIdx(1 << i);
|
||||
|
||||
/* set extended create conection parameters for this PHY */
|
||||
scanParam[idx].scanInterval = dmConnCb.scanInterval[phyIdx];
|
||||
scanParam[idx].scanWindow = dmConnCb.scanWindow[phyIdx];
|
||||
connSpec[idx] = dmConnCb.connSpec[phyIdx];
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create connection */
|
||||
HciLeExtCreateConnCmd(&initParam, scanParam, connSpec);
|
||||
|
||||
/* pass connection initiation started to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_CTRL, DM_DEV_PRIV_MSG_CONN_INIT_START, 0, 0);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Open a connection.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtConnSmActOpen(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmExtConnOpen(pMsg->apiOpen.initPhys, pMsg->apiOpen.addrType, pMsg->apiOpen.peerAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM connection manager for operation as extended master.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmExtConnMasterInit(void)
|
||||
{
|
||||
dmConnActSet[DM_CONN_ACT_SET_MASTER] = (dmConnAct_t *) dmConnActSetMaster;
|
||||
}
|
||||
Vendored
+92
@@ -0,0 +1,92 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager connection management module for legacy master.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Action set for this module */
|
||||
static const dmConnAct_t dmConnActSetMaster[] =
|
||||
{
|
||||
dmConnSmActOpen,
|
||||
dmConnSmActCancelOpen,
|
||||
dmConnSmActUpdateMaster,
|
||||
dmConnSmActL2cUpdateInd
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Open a connection to a peer device with the given address.
|
||||
*
|
||||
* \param initPhys Initiating PHYs.
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return Connection identifier.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnOpen(uint8_t initPhys, uint8_t addrType, uint8_t *pAddr)
|
||||
{
|
||||
uint8_t phyIdx = DmScanPhyToIdx(HCI_SCAN_PHY_LE_1M_BIT);
|
||||
|
||||
/* Create connection */
|
||||
HciLeCreateConnCmd(dmConnCb.scanInterval[phyIdx], dmConnCb.scanWindow[phyIdx], dmCb.initFiltPolicy,
|
||||
addrType, pAddr, DmLlAddrType(dmCb.connAddrType), &(dmConnCb.connSpec[phyIdx]));
|
||||
|
||||
/* pass connection initiation started to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_CTRL, DM_DEV_PRIV_MSG_CONN_INIT_START, 0, 0);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Open a connection.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActOpen(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmConnOpen(pMsg->apiOpen.initPhys, pMsg->apiOpen.addrType, pMsg->apiOpen.peerAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM connection manager for operation as legacy master.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnMasterInit(void)
|
||||
{
|
||||
dmConnActSet[DM_CONN_ACT_SET_MASTER] = (dmConnAct_t *) dmConnActSetMaster;
|
||||
}
|
||||
Vendored
+182
@@ -0,0 +1,182 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager slave connection management for slave.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_adv.h"
|
||||
#include "l2c_api.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Call application callback with the connection update complete event.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param status Status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmConnUpdateCback(dmConnCcb_t *pCcb, uint8_t status)
|
||||
{
|
||||
hciLeConnUpdateCmplEvt_t evt;
|
||||
|
||||
/* call callback */
|
||||
evt.hdr.event = DM_CONN_UPDATE_IND;
|
||||
evt.hdr.param = pCcb->connId;
|
||||
evt.status = evt.hdr.status = status;
|
||||
evt.handle = pCcb->handle;
|
||||
(*dmConnCb.connCback[DM_CLIENT_ID_APP])((dmEvt_t *) &evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Update a connection as a slave.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActUpdateSlave(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
if ((pCcb->features & HCI_LE_SUP_FEAT_CONN_PARAM_REQ_PROC) &&
|
||||
(HciGetLeSupFeat() & HCI_LE_SUP_FEAT_CONN_PARAM_REQ_PROC))
|
||||
{
|
||||
HciLeConnUpdateCmd(pCcb->handle, &pMsg->apiUpdate.connSpec);
|
||||
}
|
||||
/* else if L2CAP connection update not already in progress */
|
||||
else if (!pCcb->updating)
|
||||
{
|
||||
pCcb->updating = TRUE;
|
||||
|
||||
/* send request via L2CAP */
|
||||
L2cDmConnUpdateReq(pCcb->handle, &pMsg->apiUpdate.connSpec);
|
||||
}
|
||||
/* else L2CAP connection update pending */
|
||||
else
|
||||
{
|
||||
/* call callback */
|
||||
dmConnUpdateCback(pCcb, (uint8_t) HCI_ERR_CMD_DISALLOWED);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an L2CAP connection update confirm.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActL2cUpdateCnf(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
/* if connection update in progress */
|
||||
if (pCcb->updating)
|
||||
{
|
||||
pCcb->updating = FALSE;
|
||||
|
||||
/* if reason indicates failure */
|
||||
if (pMsg->l2cUpdateCnf.result != L2C_CONN_PARAM_ACCEPTED)
|
||||
{
|
||||
/* call callback */
|
||||
dmConnUpdateCback(pCcb, (uint8_t) pMsg->l2cUpdateCnf.result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For internal use only. L2C calls this function to send the result of an L2CAP
|
||||
* connection update response to DM.
|
||||
*
|
||||
* \param handle Connection handle.
|
||||
* \param result Connection update result code.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmL2cConnUpdateCnf(uint16_t handle, uint16_t result)
|
||||
{
|
||||
dmConnL2cUpdateCnf_t updateCnf;
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
if ((pCcb = dmConnCcbByHandle(handle)) != NULL)
|
||||
{
|
||||
updateCnf.hdr.event = DM_CONN_MSG_L2C_UPDATE_CNF;
|
||||
updateCnf.result = result;
|
||||
|
||||
dmConnSmExecute(pCcb, (dmConnMsg_t *) &updateCnf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For internal use only. L2C calls this function to send the result of an L2CAP
|
||||
* Command Reject indication up to the application.
|
||||
*
|
||||
* \param handle Connection handle.
|
||||
* \param result Connection update result code.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmL2cCmdRejInd(uint16_t handle, uint16_t result)
|
||||
{
|
||||
dmL2cCmdRejEvt_t evt;
|
||||
|
||||
/* call callback */
|
||||
evt.hdr.event = DM_L2C_CMD_REJ_IND;
|
||||
evt.hdr.status = HCI_SUCCESS;
|
||||
evt.reason = result;
|
||||
evt.handle = handle;
|
||||
(*dmConnCb.connCback[DM_CLIENT_ID_APP])((dmEvt_t *)&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Accept a connection from the given peer device by initiating directed advertising.
|
||||
*
|
||||
* \param clientId The client identifier.
|
||||
* \param advHandle Advertising handle.
|
||||
* \param advType Advertising type.
|
||||
* \param duration Advertising duration (in ms).
|
||||
* \param maxEaEvents Maximum number of extended advertising events.
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return Connection identifier.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmConnId_t DmConnAccept(uint8_t clientId, uint8_t advHandle, uint8_t advType, uint16_t duration,
|
||||
uint8_t maxEaEvents, uint8_t addrType, uint8_t *pAddr)
|
||||
{
|
||||
return dmConnOpenAccept(clientId, 0, advHandle, advType, duration, maxEaEvents, addrType, pAddr,
|
||||
DM_ROLE_SLAVE);
|
||||
}
|
||||
Vendored
+126
@@ -0,0 +1,126 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager connection management for extended slave.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_adv.h"
|
||||
#include "l2c_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Action set for this module */
|
||||
static const dmConnAct_t dmConnActSetSlave[] =
|
||||
{
|
||||
dmExtConnSmActAccept,
|
||||
dmExtConnSmActCancelAccept,
|
||||
dmConnSmActUpdateSlave,
|
||||
dmExtConnSmActConnAccepted,
|
||||
dmExtConnSmActAcceptFailed,
|
||||
dmConnSmActL2cUpdateCnf
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Accept a connection.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtConnSmActAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmExtAdvStartDirected(pCcb->connId, pMsg->apiOpen.advHandle, pMsg->apiOpen.advType,
|
||||
pMsg->apiOpen.duration, pMsg->apiOpen.maxEaEvents, pMsg->apiOpen.addrType,
|
||||
pMsg->apiOpen.peerAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Cancel a connection accept.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtConnSmActCancelAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmExtAdvStopDirected(pCcb->connId);
|
||||
|
||||
dmConnSmActConnFailed(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection accepted.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtConnSmActConnAccepted(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmExtAdvConnected(pCcb->connId);
|
||||
|
||||
dmConnSmActConnOpened(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection accept failed.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtConnSmActAcceptFailed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmExtAdvConnectFailed(pCcb->connId);
|
||||
|
||||
dmConnSmActConnFailed(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM connection manager for operation as extended slave.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmExtConnSlaveInit(void)
|
||||
{
|
||||
dmConnActSet[DM_CONN_ACT_SET_SLAVE] = (dmConnAct_t *) dmConnActSetSlave;
|
||||
}
|
||||
Vendored
+125
@@ -0,0 +1,125 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager connection management for legacy slave.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_adv.h"
|
||||
#include "l2c_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Action set for this module */
|
||||
static const dmConnAct_t dmConnActSetSlave[] =
|
||||
{
|
||||
dmConnSmActAccept,
|
||||
dmConnSmActCancelAccept,
|
||||
dmConnSmActUpdateSlave,
|
||||
dmConnSmActConnAccepted,
|
||||
dmConnSmActAcceptFailed,
|
||||
dmConnSmActL2cUpdateCnf
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Accept a connection.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmAdvStartDirected(pMsg->apiOpen.advType, pMsg->apiOpen.duration, pMsg->apiOpen.addrType,
|
||||
pMsg->apiOpen.peerAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Cancel a connection accept.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActCancelAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmAdvStopDirected();
|
||||
|
||||
dmConnSmActConnFailed(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection accepted.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActConnAccepted(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmAdvConnected();
|
||||
|
||||
dmConnSmActConnOpened(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection accept failed.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmActAcceptFailed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmAdvConnectFailed();
|
||||
|
||||
dmConnSmActConnFailed(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM connection manager for operation as legacy slave.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmConnSlaveInit(void)
|
||||
{
|
||||
dmConnActSet[DM_CONN_ACT_SET_SLAVE] = (dmConnAct_t *) dmConnActSetSlave;
|
||||
}
|
||||
Vendored
+187
@@ -0,0 +1,187 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager connection management state machine.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Column position of next state */
|
||||
#define DM_CONN_NEXT_STATE 0
|
||||
|
||||
/*! Column position of action */
|
||||
#define DM_CONN_ACTION 1
|
||||
|
||||
/*! Number of columns in the state machine state tables */
|
||||
#define DM_CONN_NUM_COLS 2
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! DM Conn state machine state tables */
|
||||
static const uint8_t dmConnStateTbl[DM_CONN_SM_NUM_STATES][DM_CONN_NUM_MSGS][DM_CONN_NUM_COLS] =
|
||||
{
|
||||
/* Idle state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_OPEN */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_OPEN},
|
||||
/* API_CLOSE */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* API_ACCEPT */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_ACCEPT},
|
||||
/* API_UPDATE_MASTER */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_SLAVE */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_IND */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_CNF */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CONN_CMPL_FAIL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CONN_CMPL */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_CONN_ACCEPTED},
|
||||
/* HCI_DISCONNECT_CMPL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CONN_UPDATE_CMPL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE},
|
||||
/* INT_UPDATE_TIMEOUT */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_NONE}
|
||||
},
|
||||
/* Connecting state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_OPEN */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_CLOSE */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_CANCEL_OPEN},
|
||||
/* API_ACCEPT */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_MASTER */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_SLAVE */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_IND */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_CNF */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CONN_CMPL_FAIL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_CONN_FAILED},
|
||||
/* HCI_LE_CONN_CMPL */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_CONN_OPENED},
|
||||
/* HCI_DISCONNECT_CMPL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_CONN_FAILED},
|
||||
/* HCI_LE_CONN_UPDATE_CMPL */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* INT_UPDATE_TIMEOUT */ {DM_CONN_SM_ST_CONNECTING, DM_CONN_SM_ACT_NONE}
|
||||
},
|
||||
/* Accepting state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_OPEN */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_CLOSE */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_CANCEL_ACCEPT},
|
||||
/* API_ACCEPT */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_MASTER */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_SLAVE */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_IND */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_CNF */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CONN_CMPL_FAIL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_ACCEPT_FAILED},
|
||||
/* HCI_LE_CONN_CMPL */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_CONN_ACCEPTED},
|
||||
/* HCI_DISCONNECT_CMPL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_ACCEPT_FAILED},
|
||||
/* HCI_LE_CONN_UPDATE_CMPL */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE},
|
||||
/* INT_UPDATE_TIMEOUT */ {DM_CONN_SM_ST_ACCEPTING, DM_CONN_SM_ACT_NONE}
|
||||
},
|
||||
/* Connected state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_OPEN */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_NONE},
|
||||
/* API_CLOSE */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_CLOSE},
|
||||
/* API_ACCEPT */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_MASTER */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_UPDATE_MASTER},
|
||||
/* API_UPDATE_SLAVE */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_UPDATE_SLAVE},
|
||||
/* L2C_UPDATE_IND */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_L2C_UPDATE_IND},
|
||||
/* L2C_UPDATE_CNF */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_L2C_UPDATE_CNF},
|
||||
/* HCI_LE_CONN_CMPL_FAIL */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CONN_CMPL */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_DISCONNECT_CMPL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_CONN_CLOSED},
|
||||
/* HCI_LE_CONN_UPDATE_CMPL */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_HCI_UPDATED},
|
||||
/* HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_NONE},
|
||||
/* INT_UPDATE_TIMEOUT */ {DM_CONN_SM_ST_CONNECTED, DM_CONN_SM_ACT_HCI_UPDATED}
|
||||
},
|
||||
/* Disconnecting state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_OPEN */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_CLOSE */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_ACCEPT */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_MASTER */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* API_UPDATE_SLAVE */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_IND */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* L2C_UPDATE_CNF */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CONN_CMPL_FAIL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_CONN_CLOSED},
|
||||
/* HCI_LE_CONN_CMPL */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_CLOSE},
|
||||
/* HCI_DISCONNECT_CMPL */ {DM_CONN_SM_ST_IDLE, DM_CONN_SM_ACT_CONN_CLOSED},
|
||||
/* HCI_LE_CONN_UPDATE_CMPL */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE},
|
||||
/* INT_UPDATE_TIMEOUT */ {DM_CONN_SM_ST_DISCONNECTING, DM_CONN_SM_ACT_NONE}
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! State machine action set array */
|
||||
dmConnAct_t *dmConnActSet[DM_CONN_NUM_ACT_SETS];
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute the DM connection state machine.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmConnSmExecute(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg)
|
||||
{
|
||||
dmConnAct_t *actSet;
|
||||
uint8_t action;
|
||||
uint8_t event;
|
||||
|
||||
DM_TRACE_INFO2("dmConnSmExecute event=%d state=%d", pMsg->hdr.event, pCcb->state);
|
||||
|
||||
/* get the event */
|
||||
event = DM_MSG_MASK(pMsg->hdr.event);
|
||||
|
||||
/* get action */
|
||||
action = dmConnStateTbl[pCcb->state][event][DM_CONN_ACTION];
|
||||
|
||||
/* set next state */
|
||||
pCcb->state = dmConnStateTbl[pCcb->state][event][DM_CONN_NEXT_STATE];
|
||||
|
||||
/* look up action set */
|
||||
actSet = dmConnActSet[DM_CONN_ACT_SET_ID(action)];
|
||||
|
||||
/* if action set present */
|
||||
if (actSet != NULL)
|
||||
{
|
||||
/* execute action function in action set */
|
||||
(*actSet[DM_CONN_ACT_ID(action)])(pCcb, pMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no action */
|
||||
dmConnSmActNone(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
+423
@@ -0,0 +1,423 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM local device management module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* action function table */
|
||||
static const dmDevAct_t dmDevAct[] =
|
||||
{
|
||||
dmDevActReset
|
||||
};
|
||||
|
||||
/* Component function interface */
|
||||
const dmFcnIf_t dmDevFcnIf =
|
||||
{
|
||||
dmEmptyReset,
|
||||
dmDevHciHandler,
|
||||
dmDevMsgHandler
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
dmDevCb_t dmDevCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevActReset(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* if DM not resetting */
|
||||
if (!dmCb.resetting)
|
||||
{
|
||||
/* set resetting state */
|
||||
dmCb.resetting = TRUE;
|
||||
|
||||
/* for each DM component */
|
||||
for (i = 0; i < DM_NUM_IDS; i++)
|
||||
{
|
||||
/* call component's reset function */
|
||||
(*(dmFcnIfTbl[i]->reset))();
|
||||
}
|
||||
|
||||
/* start HCI reset sequence */
|
||||
HciResetSequence();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a reset complete event from HCI.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmDevHciEvtReset(hciEvt_t *pEvent)
|
||||
{
|
||||
/* reset resetting state */
|
||||
dmCb.resetting = FALSE;
|
||||
|
||||
pEvent->hdr.event = DM_RESET_CMPL_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a vendor specific event from HCI.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmDevHciEvtVendorSpec(hciEvt_t *pEvent)
|
||||
{
|
||||
pEvent->hdr.event = DM_VENDOR_SPEC_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an hardware error event from HCI.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmDevHciEvtHwError(hciEvt_t *pEvent)
|
||||
{
|
||||
pEvent->hdr.event = DM_HW_ERROR_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM dev HCI event handler.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
switch (pEvent->hdr.event)
|
||||
{
|
||||
case HCI_RESET_SEQ_CMPL_CBACK_EVT:
|
||||
dmDevHciEvtReset(pEvent);
|
||||
break;
|
||||
|
||||
case HCI_VENDOR_SPEC_CBACK_EVT:
|
||||
dmDevHciEvtVendorSpec(pEvent);
|
||||
break;
|
||||
|
||||
case HCI_HW_ERROR_CBACK_EVT:
|
||||
dmDevHciEvtHwError(pEvent);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* ignore event */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM dev event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmDevAct[DM_MSG_MASK(pMsg->event)])(pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Pass an event to the device privacy module.
|
||||
*
|
||||
* \param event Device privacy event.
|
||||
* \param param DM or Privacy event.
|
||||
* \param advHandle Advertising handle.
|
||||
* \param connectable TRUE if connectable extended advertising. FALSE, otherwise.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPassEvtToDevPriv(uint8_t event, uint8_t param, uint8_t advHandle, bool_t connectable)
|
||||
{
|
||||
dmDevPrivMsg_t evt;
|
||||
|
||||
DM_TRACE_INFO3("dmDevPassEvtToDevPriv: event: %d, param: %d, advHandle: %d", event, param, advHandle);
|
||||
|
||||
/* build event */
|
||||
evt.hdr.event = event;
|
||||
evt.hdr.param = param;
|
||||
evt.privCtrl.advHandle = advHandle;
|
||||
evt.privCtrl.connectable = connectable;
|
||||
|
||||
/* pass event to device privacy */
|
||||
(*(dmFcnIfTbl[DM_ID_DEV_PRIV]->msgHandler))((wsfMsgHdr_t *) &evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Pass a connection state change event to the Connection CTE module.
|
||||
*
|
||||
* \param state Connection state.
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPassEvtToConnCte(uint8_t state, dmConnId_t connId)
|
||||
{
|
||||
wsfMsgHdr_t evt;
|
||||
|
||||
/* build event */
|
||||
evt.event = DM_CONN_CTE_MSG_STATE;
|
||||
evt.status = state;
|
||||
evt.param = connId;
|
||||
|
||||
/* pass event to Connection CTE */
|
||||
(*(dmFcnIfTbl[DM_ID_CONN_CTE]->msgHandler))(&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the device.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevReset(void)
|
||||
{
|
||||
wsfMsgHdr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pMsg->event = DM_DEV_MSG_API_RESET;
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the random address to be used by the local device.
|
||||
*
|
||||
* \param pAddr Random address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevSetRandAddr(uint8_t *pAddr)
|
||||
{
|
||||
BdaCpy(dmCb.localAddr, pAddr);
|
||||
HciLeSetRandAddrCmd(pAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add a peer device to the white list. Note that this function cannot be called
|
||||
* while advertising, scanning, or connecting with white list filtering active.
|
||||
*
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevWhiteListAdd(uint8_t addrType, uint8_t *pAddr)
|
||||
{
|
||||
HciLeAddDevWhiteListCmd(addrType, pAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove a peer device from the white list. Note that this function cannot be called
|
||||
* while advertising, scanning, or connecting with white list filtering active.
|
||||
*
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevWhiteListRemove(uint8_t addrType, uint8_t *pAddr)
|
||||
{
|
||||
HciLeRemoveDevWhiteListCmd(addrType, pAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear the white list. Note that this function cannot be called while
|
||||
* advertising, scanning, or connecting with white list filtering active.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevWhiteListClear(void)
|
||||
{
|
||||
HciLeClearWhiteListCmd();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the Advertising, Scanning or Initiator filter policy.
|
||||
*
|
||||
* \param advHandle Advertising handle (only applicable to advertising).
|
||||
* \param mode Policy mode.
|
||||
* \param policy Filter policy.
|
||||
*
|
||||
* \return TRUE if the filter policy was successfully set, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t dmDevSetFilterPolicy(uint8_t advHandle, uint8_t mode, uint8_t policy)
|
||||
{
|
||||
bool_t policySet = FALSE;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case DM_FILT_POLICY_MODE_ADV:
|
||||
/* if Advertising filter policy is valid */
|
||||
if (policy <= HCI_ADV_FILT_ALL)
|
||||
{
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
/* update the filter policy */
|
||||
dmCb.advFiltPolicy[advHandle] = policy;
|
||||
policySet = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case DM_FILT_POLICY_MODE_SCAN:
|
||||
/* if Scanning filter policy is valid */
|
||||
if (policy <= HCI_FILT_WHITE_LIST_RES_INIT)
|
||||
{
|
||||
/* update the filter policy */
|
||||
dmCb.scanFiltPolicy = policy;
|
||||
policySet = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case DM_FILT_POLICY_MODE_INIT:
|
||||
/* if Initiator filter policy is valid */
|
||||
if (policy <= HCI_FILT_WHITE_LIST)
|
||||
{
|
||||
/* update the filter policy */
|
||||
dmCb.initFiltPolicy = policy;
|
||||
policySet = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case DM_FILT_POLICY_MODE_SYNC:
|
||||
/* if Synchronization filter policy is valid */
|
||||
if (policy <= HCI_FILT_PER_ADV_LIST)
|
||||
{
|
||||
/* clear the filter policy bit */
|
||||
dmCb.syncOptions &= ~HCI_OPTIONS_FILT_POLICY_BIT;
|
||||
|
||||
/* set the filter policy bit */
|
||||
dmCb.syncOptions |= policy;
|
||||
policySet = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* invalid filter policy mode */
|
||||
break;
|
||||
}
|
||||
|
||||
return policySet;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the Advertising, Scanning or Initiator filter policy.
|
||||
*
|
||||
* \param mode Policy mode.
|
||||
* \param policy Filter policy.
|
||||
*
|
||||
* \return TRUE if the filter policy was successfully set, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmDevSetFilterPolicy(uint8_t mode, uint8_t policy)
|
||||
{
|
||||
return dmDevSetFilterPolicy(DM_ADV_HANDLE_DEFAULT, mode, policy);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the Advertising filter policy for the given advertising, Scanning or Initiator
|
||||
* filter policy.
|
||||
*
|
||||
* \param advHandle Advertising handle (only applicable to advertising).
|
||||
* \param mode Policy mode.
|
||||
* \param policy Filter policy.
|
||||
*
|
||||
* \return TRUE if the filter policy was successfully set, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmDevSetExtFilterPolicy(uint8_t advHandle, uint8_t mode, uint8_t policy)
|
||||
{
|
||||
return dmDevSetFilterPolicy(advHandle, mode, policy);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Vendor-specific controller initialization function.
|
||||
*
|
||||
* \param param Vendor-specific parameter.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevVsInit(uint8_t param)
|
||||
{
|
||||
HciVsInit(param);
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM local device management module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_DEV_H
|
||||
#define DM_DEV_H
|
||||
|
||||
#include "dm_main.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* DM device event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_DEV_MSG_API_RESET = DM_MSG_START(DM_ID_DEV)
|
||||
};
|
||||
|
||||
/* DM device privacy event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_DEV_PRIV_MSG_API_START = DM_MSG_START(DM_ID_DEV_PRIV),
|
||||
DM_DEV_PRIV_MSG_API_STOP,
|
||||
DM_DEV_PRIV_MSG_TIMEOUT,
|
||||
DM_DEV_PRIV_MSG_AES_CMPL,
|
||||
DM_DEV_PRIV_MSG_RPA_START,
|
||||
DM_DEV_PRIV_MSG_RPA_STOP,
|
||||
DM_DEV_PRIV_MSG_CTRL,
|
||||
};
|
||||
|
||||
/* DM device privacy control messages */
|
||||
enum
|
||||
{
|
||||
DM_DEV_PRIV_MSG_CONN_INIT_START, /* connection initiation started */
|
||||
DM_DEV_PRIV_MSG_CONN_INIT_STOP, /* connection initiation stopped */
|
||||
DM_DEV_PRIV_MSG_ADV_SET_ADD, /* advertising set created */
|
||||
DM_DEV_PRIV_MSG_ADV_SET_REMOVE, /* advertising set removed */
|
||||
DM_DEV_PRIV_MSG_ADV_SETS_CLEAR /* advertising sets cleared */
|
||||
};
|
||||
|
||||
/*! DM connection CTE event handler messages */
|
||||
enum
|
||||
{
|
||||
/* messages from API */
|
||||
DM_CONN_CTE_MSG_API_RX_SAMPLE_START = DM_MSG_START(DM_ID_CONN_CTE), /*!< Start sampling received CTE, and configure CTE Rx parameters to be used */
|
||||
DM_CONN_CTE_MSG_API_RX_SAMPLE_STOP, /*!< Stop sampling received CTE */
|
||||
DM_CONN_CTE_MSG_API_TX_CFG, /*!< Configure CTE Tx parameters */
|
||||
DM_CONN_CTE_MSG_API_REQ_START, /*!< Start initiating CTE request */
|
||||
DM_CONN_CTE_MSG_API_REQ_STOP, /*!< Stop initiating CTE request */
|
||||
DM_CONN_CTE_MSG_API_RSP_START, /*!< Start responding to CTE request */
|
||||
DM_CONN_CTE_MSG_API_RSP_STOP, /*!< Stop responding to CTE request */
|
||||
DM_CONN_CTE_MSG_STATE /*!< DM connection state change event */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Data structure for DM_DEV_PRIV_MSG_API_START */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint16_t changeInterval;
|
||||
} dmDevPrivApiStart_t;
|
||||
|
||||
/* Data structure for DM_DEV_PRIV_MSG_CTRL */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advHandle;
|
||||
bool_t connectable;
|
||||
} dmDevPrivCtrl_t;
|
||||
|
||||
/* Union of all dev priv messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmDevPrivApiStart_t apiPrivStart;
|
||||
dmDevPrivCtrl_t privCtrl;
|
||||
secAes_t aes;
|
||||
} dmDevPrivMsg_t;
|
||||
|
||||
/* Action function */
|
||||
typedef void (*dmDevAct_t)(wsfMsgHdr_t *pMsg);
|
||||
typedef void (*dmDevPrivAct_t)(dmDevPrivMsg_t *pMsg);
|
||||
|
||||
/* DM device set advertising set random address callback type */
|
||||
typedef void (*dmDevAdvSetRandAddrCback_t)(uint8_t advHandle, const uint8_t *pAddr);
|
||||
|
||||
/* Main control block of the DM Dev subsystem */
|
||||
typedef struct
|
||||
{
|
||||
/* Set advertising set random address callback */
|
||||
dmDevAdvSetRandAddrCback_t advSetRandAddrCback;
|
||||
} dmDevCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Component function interface */
|
||||
extern const dmFcnIf_t dmDevFcnIf;
|
||||
|
||||
/* Control block */
|
||||
extern dmDevCb_t dmDevCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* dev component inteface */
|
||||
void dmDevActReset(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/* dev action functions */
|
||||
void dmDevMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmDevHciHandler(hciEvt_t *pEvent);
|
||||
|
||||
/* dev priv component inteface */
|
||||
void dmDevPrivHciHandler(hciEvt_t *pEvent);
|
||||
void dmDevPrivMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmDevPrivReset(void);
|
||||
|
||||
/* dev priv action functions */
|
||||
void dmDevPrivActStart(dmDevPrivMsg_t *pMsg);
|
||||
void dmDevPrivActStop(dmDevPrivMsg_t *pMsg);
|
||||
void dmDevPrivActTimeout(dmDevPrivMsg_t *pMsg);
|
||||
void dmDevPrivActAesCmpl(dmDevPrivMsg_t *pMsg);
|
||||
void dmDevPrivActRpaStart(dmDevPrivMsg_t *pMsg);
|
||||
void dmDevPrivActRpaStop(dmDevPrivMsg_t *pMsg);
|
||||
void dmDevPrivActCtrl(dmDevPrivMsg_t *pMsg);
|
||||
|
||||
/* utility function */
|
||||
void dmDevPassEvtToDevPriv(uint8_t event, uint8_t param, uint8_t advHandle, bool_t connectable);
|
||||
void dmDevPassEvtToConnCte(uint8_t state, dmConnId_t connId);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_DEV_H */
|
||||
Vendored
+653
@@ -0,0 +1,653 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager device privacy module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "hci_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Structure for extended advertising */
|
||||
typedef struct
|
||||
{
|
||||
bool_t configured;
|
||||
bool_t connectable;
|
||||
bool_t advertising;
|
||||
} dmDevPrivExtAdv_t;
|
||||
|
||||
/* Control block for device privacy module */
|
||||
typedef struct
|
||||
{
|
||||
wsfTimer_t addrTimer;
|
||||
bool_t addrTimerStarted;
|
||||
uint8_t prand[DM_PRIV_PRAND_LEN];
|
||||
uint16_t changeInterval;
|
||||
bool_t useResolvable;
|
||||
bool_t addrInitialized;
|
||||
bdAddr_t pendingAddr;
|
||||
bool_t advertising;
|
||||
bool_t scanning;
|
||||
bool_t connecting;
|
||||
bool_t connected;
|
||||
dmDevPrivExtAdv_t extAdv[DM_NUM_ADV_SETS];
|
||||
} dmDevPrivCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* action function table */
|
||||
static const dmDevPrivAct_t dmDevPrivAct[] =
|
||||
{
|
||||
dmDevPrivActStart,
|
||||
dmDevPrivActStop,
|
||||
dmDevPrivActTimeout,
|
||||
dmDevPrivActAesCmpl,
|
||||
dmDevPrivActRpaStart,
|
||||
dmDevPrivActRpaStop,
|
||||
dmDevPrivActCtrl
|
||||
};
|
||||
|
||||
/* Component function interface */
|
||||
static const dmFcnIf_t dmDevPrivFcnIf =
|
||||
{
|
||||
dmDevPrivReset,
|
||||
dmDevPrivHciHandler,
|
||||
dmDevPrivMsgHandler
|
||||
};
|
||||
|
||||
/* control block */
|
||||
static dmDevPrivCb_t dmDevPrivCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Whether the legacy advertising or an advertising set using (non)connectable advertising
|
||||
* is enabled.
|
||||
*
|
||||
* \param nonconnectable Non-connectable advertising.
|
||||
*
|
||||
* \return TRUE if advertising is enabled. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t dmDevPrivAdvertising(bool_t nonconnectable)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* if legacy advertising enabled */
|
||||
if (dmDevPrivCb.advertising)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* if (non)connectable extended advertising is enabled */
|
||||
for (i = 0; i < DM_NUM_ADV_SETS; i++)
|
||||
{
|
||||
if (dmDevPrivCb.extAdv[i].configured &&
|
||||
dmDevPrivCb.extAdv[i].advertising &&
|
||||
(dmDevPrivCb.extAdv[i].connectable || nonconnectable))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the address generation timer.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmDevPrivTimerStart(void)
|
||||
{
|
||||
/* if LL Privacy not enabled and device advertising, scanning or connected */
|
||||
if (!dmCb.llPrivEnabled && (dmDevPrivAdvertising(TRUE) || dmDevPrivCb.scanning || dmDevPrivCb.connected))
|
||||
{
|
||||
/* start address generation timer */
|
||||
dmDevPrivCb.addrTimerStarted = TRUE;
|
||||
dmDevPrivCb.addrTimer.msg.event = DM_DEV_PRIV_MSG_TIMEOUT;
|
||||
WsfTimerStartSec(&dmDevPrivCb.addrTimer, dmDevPrivCb.changeInterval);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start resolvable address calculation.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmDevPrivAddrCalc(void)
|
||||
{
|
||||
uint8_t buf[DM_PRIV_PLAINTEXT_LEN];
|
||||
|
||||
/* get random number */
|
||||
SecRand(buf, DM_PRIV_PRAND_LEN);
|
||||
|
||||
/* set address type in random number */
|
||||
buf[2] = (buf[2] & 0x3F) | DM_RAND_ADDR_RESOLV;
|
||||
|
||||
/* pad buffer */
|
||||
memset(buf + DM_PRIV_PRAND_LEN, 0, (DM_PRIV_PLAINTEXT_LEN - DM_PRIV_PRAND_LEN));
|
||||
|
||||
/* run calculation */
|
||||
SecAes(DmSecGetLocalIrk(), buf, dmCb.handlerId, 0, DM_DEV_PRIV_MSG_AES_CMPL);
|
||||
|
||||
/* store random number */
|
||||
memcpy(dmDevPrivCb.prand, buf, DM_PRIV_PRAND_LEN);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the RPA to be used by the local device.
|
||||
*
|
||||
* \param pAddr New RPA.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmDevPrivSetRpa(uint8_t *pAddr)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* set new RPA as local address */
|
||||
BdaCpy(dmCb.localAddr, pAddr);
|
||||
|
||||
/* set RPA in device */
|
||||
HciLeSetRandAddrCmd(dmCb.localAddr);
|
||||
|
||||
if (dmDevCb.advSetRandAddrCback)
|
||||
{
|
||||
for (i = 0; i < DM_NUM_ADV_SETS; i++)
|
||||
{
|
||||
if (dmDevPrivCb.extAdv[i].configured)
|
||||
{
|
||||
/* set RPA for advertising set */
|
||||
(*dmDevCb.advSetRandAddrCback)(i, dmCb.localAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the pending RPA to be used by the local device.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmDevPrivSetPendingRpa(void)
|
||||
{
|
||||
/* if not advertising, scanning or initiating connection, and new RPA has been generated */
|
||||
if (!(dmDevPrivAdvertising(FALSE) || dmDevPrivCb.scanning || dmDevPrivCb.connecting) &&
|
||||
!BdaIsZeros(dmDevPrivCb.pendingAddr))
|
||||
{
|
||||
/* use pending RPA as local address */
|
||||
dmDevPrivSetRpa(dmDevPrivCb.pendingAddr);
|
||||
|
||||
/* clear out pending RPA */
|
||||
memset(dmDevPrivCb.pendingAddr, 0, BDA_ADDR_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start device privacy action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivActStart(dmDevPrivMsg_t *pMsg)
|
||||
{
|
||||
/* initialize and store parameters */
|
||||
dmDevPrivCb.useResolvable = TRUE;
|
||||
dmDevPrivCb.changeInterval = pMsg->apiPrivStart.changeInterval;
|
||||
dmDevPrivCb.addrTimer.handlerId = dmCb.handlerId;
|
||||
|
||||
/* set the local address type to random */
|
||||
DmAdvSetAddrType(DM_ADDR_RANDOM);
|
||||
DmScanSetAddrType(DM_ADDR_RANDOM);
|
||||
DmConnSetAddrType(DM_ADDR_RANDOM);
|
||||
|
||||
/* start the address generation timer if applicable */
|
||||
if (dmDevPrivCb.changeInterval > 0)
|
||||
{
|
||||
/* start address generation timer */
|
||||
dmDevPrivTimerStart();
|
||||
|
||||
/* if LL Privacy is supported */
|
||||
if (HciLlPrivacySupported())
|
||||
{
|
||||
/* Set LL resolvable private address timeout */
|
||||
DmPrivSetResolvablePrivateAddrTimeout(dmDevPrivCb.changeInterval);
|
||||
}
|
||||
}
|
||||
|
||||
/* if private address has never been generated */
|
||||
if (dmDevPrivCb.addrInitialized == FALSE)
|
||||
{
|
||||
/* start address calculation */
|
||||
dmDevPrivAddrCalc();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop device privacy action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivActStop(dmDevPrivMsg_t *pMsg)
|
||||
{
|
||||
/* stop address generation timer */
|
||||
dmDevPrivCb.addrTimerStarted = FALSE;
|
||||
WsfTimerStop(&dmDevPrivCb.addrTimer);
|
||||
|
||||
dmDevPrivCb.useResolvable = FALSE;
|
||||
memset(dmDevPrivCb.pendingAddr, 0, BDA_ADDR_LEN);
|
||||
|
||||
/* set the local address type to public */
|
||||
DmAdvSetAddrType(DM_ADDR_PUBLIC);
|
||||
DmScanSetAddrType(DM_ADDR_PUBLIC);
|
||||
DmConnSetAddrType(DM_ADDR_PUBLIC);
|
||||
|
||||
/* if LL Privacy is supported */
|
||||
if (HciLlPrivacySupported())
|
||||
{
|
||||
/* remove all devices from resolving list and disable LL Privacy */
|
||||
DmPrivClearResList();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a private address generation timeout.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivActTimeout(dmDevPrivMsg_t *pMsg)
|
||||
{
|
||||
/* if Host Privacy still enabled */
|
||||
if (dmDevPrivCb.useResolvable)
|
||||
{
|
||||
/* address generation timer has timed out */
|
||||
dmDevPrivCb.addrTimerStarted = FALSE;
|
||||
|
||||
/* restart address generation timer */
|
||||
dmDevPrivTimerStart();
|
||||
|
||||
/* start address calculation */
|
||||
dmDevPrivAddrCalc();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle AES calculation complete.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivActAesCmpl(dmDevPrivMsg_t *pMsg)
|
||||
{
|
||||
bdAddr_t localAddr;
|
||||
|
||||
if (dmDevPrivCb.useResolvable)
|
||||
{
|
||||
/* build and store address; hash is in ls bytes, random part in ms bytes */
|
||||
memcpy(localAddr, pMsg->aes.pCiphertext, DM_PRIV_HASH_LEN);
|
||||
memcpy(&localAddr[DM_PRIV_HASH_LEN], dmDevPrivCb.prand, DM_PRIV_PRAND_LEN);
|
||||
|
||||
/* set generated address as random resolvable */
|
||||
DM_RAND_ADDR_SET(localAddr, DM_RAND_ADDR_RESOLV);
|
||||
|
||||
/* if not advertising, scanning or initiating connection */
|
||||
if (!(dmDevPrivAdvertising(FALSE) || dmDevPrivCb.scanning || dmDevPrivCb.connecting))
|
||||
{
|
||||
/* use new RPA as local address */
|
||||
dmDevPrivSetRpa(localAddr);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* save new RPA for later */
|
||||
BdaCpy(dmDevPrivCb.pendingAddr, localAddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a device privacy RPA start event.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivActRpaStart(dmDevPrivMsg_t *pMsg)
|
||||
{
|
||||
switch (pMsg->hdr.param)
|
||||
{
|
||||
case DM_ADV_START_IND:
|
||||
/* advertising started */
|
||||
dmDevPrivCb.advertising = TRUE;
|
||||
break;
|
||||
|
||||
case DM_ADV_SET_START_IND:
|
||||
/* extended advertising started */
|
||||
WSF_ASSERT(pMsg->privCtrl.advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
dmDevPrivCb.extAdv[pMsg->privCtrl.advHandle].advertising = TRUE;
|
||||
break;
|
||||
|
||||
case DM_SCAN_START_IND:
|
||||
case DM_EXT_SCAN_START_IND:
|
||||
/* scanning started */
|
||||
dmDevPrivCb.scanning = TRUE;
|
||||
break;
|
||||
|
||||
case DM_CONN_OPEN_IND:
|
||||
/* connection opened */
|
||||
dmDevPrivCb.connected = TRUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* LL Privacy disabled */
|
||||
break;
|
||||
}
|
||||
|
||||
/* if Host Privacy enabled and address generation timer not already running */
|
||||
if (dmDevPrivCb.useResolvable && !dmDevPrivCb.addrTimerStarted)
|
||||
{
|
||||
/* start address generation timer */
|
||||
dmDevPrivTimerStart();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a device privacy RPA stop event.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivActRpaStop(dmDevPrivMsg_t *pMsg)
|
||||
{
|
||||
switch (pMsg->hdr.param)
|
||||
{
|
||||
case DM_ADV_STOP_IND:
|
||||
/* advertising stopped */
|
||||
dmDevPrivCb.advertising = FALSE;
|
||||
|
||||
/* use pending RPA */
|
||||
dmDevPrivSetPendingRpa();
|
||||
break;
|
||||
|
||||
case DM_ADV_SET_STOP_IND:
|
||||
WSF_ASSERT(pMsg->privCtrl.advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
/* extended advertising stopped */
|
||||
dmDevPrivCb.extAdv[pMsg->privCtrl.advHandle].advertising = FALSE;
|
||||
|
||||
/* use pending RPA */
|
||||
dmDevPrivSetPendingRpa();
|
||||
break;
|
||||
|
||||
case DM_SCAN_STOP_IND:
|
||||
case DM_EXT_SCAN_STOP_IND:
|
||||
/* scanning stopped */
|
||||
dmDevPrivCb.scanning = FALSE;
|
||||
|
||||
/* use pending RPA */
|
||||
dmDevPrivSetPendingRpa();
|
||||
break;
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
/* connection closed */
|
||||
dmDevPrivCb.connected = FALSE;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* LL Privacy enabled see if address generation timer running */
|
||||
if (dmDevPrivCb.addrTimerStarted)
|
||||
{
|
||||
/* stop address generation timer (LL will generate RPAs) */
|
||||
dmDevPrivCb.addrTimerStarted = FALSE;
|
||||
WsfTimerStop(&dmDevPrivCb.addrTimer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* let address generation timer timeout (if still running) and update address */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a device privacy control event.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivActCtrl(dmDevPrivMsg_t *pMsg)
|
||||
{
|
||||
switch (pMsg->hdr.param)
|
||||
{
|
||||
case DM_DEV_PRIV_MSG_CONN_INIT_START:
|
||||
/* connection initiation started */
|
||||
dmDevPrivCb.connecting = TRUE;
|
||||
break;
|
||||
|
||||
case DM_DEV_PRIV_MSG_CONN_INIT_STOP:
|
||||
/* connection initiation stopped */
|
||||
dmDevPrivCb.connecting = FALSE;
|
||||
|
||||
/* use pending RPA */
|
||||
dmDevPrivSetPendingRpa();
|
||||
break;
|
||||
|
||||
case DM_DEV_PRIV_MSG_ADV_SET_ADD:
|
||||
/* advertising set created */
|
||||
WSF_ASSERT(pMsg->privCtrl.advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
dmDevPrivCb.extAdv[pMsg->privCtrl.advHandle].configured = TRUE;
|
||||
dmDevPrivCb.extAdv[pMsg->privCtrl.advHandle].connectable = pMsg->privCtrl.connectable;
|
||||
|
||||
/* if Host Privacy enabled and private address has been generated */
|
||||
if (dmDevPrivCb.useResolvable && dmDevPrivCb.addrInitialized)
|
||||
{
|
||||
WSF_ASSERT(dmDevCb.advSetRandAddrCback != NULL);
|
||||
|
||||
/* set RPA for advertising set */
|
||||
(*dmDevCb.advSetRandAddrCback)(pMsg->privCtrl.advHandle, dmCb.localAddr);
|
||||
}
|
||||
break;
|
||||
|
||||
case DM_DEV_PRIV_MSG_ADV_SET_REMOVE:
|
||||
/* advertising set removed */
|
||||
WSF_ASSERT(pMsg->privCtrl.advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
/* clear advertising set */
|
||||
/* note: advertising handle is checked before reaching this function */
|
||||
/* coverity[overrun-buffer-arg] */
|
||||
memset(&dmDevPrivCb.extAdv[pMsg->privCtrl.advHandle], 0, sizeof(dmDevPrivExtAdv_t));
|
||||
break;
|
||||
|
||||
case DM_DEV_PRIV_MSG_ADV_SETS_CLEAR:
|
||||
/* clear advertising sets */
|
||||
memset(dmDevPrivCb.extAdv, 0, sizeof(dmDevPrivCb.extAdv));
|
||||
break;
|
||||
|
||||
default:
|
||||
/* unknown state message */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM device priv HCI callback event handler.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
/* if Host Privacy enabled */
|
||||
if (dmDevPrivCb.useResolvable)
|
||||
{
|
||||
/* handle incoming event */
|
||||
if (pEvent->hdr.event == HCI_LE_SET_RAND_ADDR_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
dmAdvNewAddrIndEvt_t msg;
|
||||
|
||||
msg.hdr.event = DM_ADV_NEW_ADDR_IND;
|
||||
msg.hdr.status = pEvent->hdr.status;
|
||||
BdaCpy(msg.addr, dmCb.localAddr);
|
||||
msg.firstTime = !dmDevPrivCb.addrInitialized;
|
||||
|
||||
/* call callback */
|
||||
(*dmCb.cback)((dmEvt_t *) &msg);
|
||||
|
||||
dmDevPrivCb.addrInitialized = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM device privacy event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmDevPrivAct[DM_MSG_MASK(pMsg->event)])((dmDevPrivMsg_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the device privacy module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPrivReset(void)
|
||||
{
|
||||
if (dmDevPrivCb.useResolvable)
|
||||
{
|
||||
/* stop address generation timer */
|
||||
WsfTimerStop(&dmDevPrivCb.addrTimer);
|
||||
}
|
||||
|
||||
/* initialize control block */
|
||||
memset(&dmDevPrivCb, 0, sizeof(dmDevPrivCb));
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize device privacy module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevPrivInit(void)
|
||||
{
|
||||
dmFcnIfTbl[DM_ID_DEV_PRIV] = (dmFcnIf_t *) &dmDevPrivFcnIf;
|
||||
|
||||
/* initialize set advertising set random address callback */
|
||||
dmDevCb.advSetRandAddrCback = NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start using a private resolvable address.
|
||||
*
|
||||
* \param changeInterval Interval between automatic address changes, in seconds.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevPrivStart(uint16_t changeInterval)
|
||||
{
|
||||
dmDevPrivApiStart_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmDevPrivApiStart_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_DEV_PRIV_MSG_API_START;
|
||||
pMsg->changeInterval = changeInterval;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop using a private resolvable address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmDevPrivStop(void)
|
||||
{
|
||||
wsfMsgHdr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pMsg->event = DM_DEV_PRIV_MSG_API_STOP;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
+580
@@ -0,0 +1,580 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager main module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "hci_api.h"
|
||||
#include "l2c_defs.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_scan.h"
|
||||
#include "dm_adv.h"
|
||||
#include "dm_dev.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* HCI callback event routing table */
|
||||
static const uint8_t dmHciToIdTbl[] =
|
||||
{
|
||||
DM_ID_DEV, /* HCI_RESET_SEQ_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN, /* HCI_LE_CONN_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN, /* HCI_LE_ENHANCED_CONN_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN, /* HCI_DISCONNECT_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN, /* HCI_LE_CONN_UPDATE_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN, /* HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_SCAN, /* HCI_LE_ADV_REPORT_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_READ_RSSI_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_LE_READ_CHAN_MAP_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_READ_TX_PWR_LVL_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_READ_REMOTE_VER_INFO_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_LE_READ_REMOTE_FEAT_CMPL_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_LE_LTK_REQ_REPL_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_LE_LTK_REQ_NEG_REPL_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_ENC_KEY_REFRESH_CMPL_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_ENC_CHANGE_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_LE_LTK_REQ_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_VENDOR_SPEC_CMD_STATUS_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_VENDOR_SPEC_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_VENDOR_SPEC_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_HW_ERROR_CBACK_EVT */
|
||||
DM_ID_PRIV, /* HCI_LE_ADD_DEV_TO_RES_LIST_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PRIV, /* HCI_LE_REM_DEV_FROM_RES_LIST_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PRIV, /* HCI_LE_CLEAR_RES_LIST_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PRIV, /* HCI_LE_READ_PEER_RES_ADDR_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PRIV, /* HCI_LE_READ_LOCAL_RES_ADDR_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PRIV, /* HCI_LE_SET_ADDR_RES_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_LE_ENCRYPT_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_LE_RAND_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_LE_REM_CONN_PARAM_REP_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_LE_REM_CONN_PARAM_NEG_REP_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_LE_READ_DEF_DATA_LEN_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_LE_WRITE_DEF_DATA_LEN_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_LE_SET_DATA_LEN_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_LE_READ_MAX_DATA_LEN_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_LE_REM_CONN_PARAM_REQ_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_LE_DATA_LEN_CHANGE_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_LE_READ_LOCAL_P256_PUB_KEY_CMPL_CBACK_EVT */
|
||||
DM_ID_SEC, /* HCI_LE_GENERATE_DHKEY_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_WRITE_AUTH_PAYLOAD_TO_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_2, /* HCI_AUTH_PAYLOAD_TO_EXPIRED_EVT */
|
||||
DM_ID_PHY, /* HCI_LE_READ_PHY_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PHY, /* HCI_LE_SET_DEF_PHY_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PHY, /* HCI_LE_PHY_UPDATE_CMPL_CBACK_EVT */
|
||||
DM_ID_SCAN, /* HCI_LE_EXT_ADV_REPORT_CBACK_EVT */
|
||||
DM_ID_SCAN, /* HCI_LE_SCAN_TIMEOUT_CBACK_EVT */
|
||||
DM_ID_ADV, /* HCI_LE_ADV_SET_TERM_CBACK_EVT */
|
||||
DM_ID_ADV, /* HCI_LE_SCAN_REQ_RCVD_CBACK_EVT */
|
||||
DM_ID_SYNC, /* HCI_LE_PER_ADV_SYNC_EST_CBACK_EVT */
|
||||
DM_ID_SYNC, /* HCI_LE_PER_ADV_REPORT_CBACK_EVT */
|
||||
DM_ID_SYNC, /* HCI_LE_PER_ADV_SYNC_LOST_CBACK_EVT */
|
||||
DM_ID_DEV, /* HCI_LE_CH_SEL_ALGO_CBACK_EVT */
|
||||
DM_ID_SCAN, /* HCI_LE_SCAN_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_ADV, /* HCI_LE_ADV_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_SCAN, /* HCI_LE_EXT_SCAN_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_ADV, /* HCI_LE_EXT_ADV_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_ADV_PER, /* HCI_LE_PER_ADV_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_DEV_PRIV, /* HCI_LE_SET_RAND_ADDR_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_SYNC, /* HCI_LE_PER_SYNC_TRSF_RCVD_CBACK_EVT */
|
||||
DM_ID_PAST, /* HCI_LE_PER_ADV_SYNC_TRSF_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_PAST, /* HCI_LE_PER_ADV_SET_INFO_TRSF_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_CTE, /* HCI_LE_CONN_IQ_REPORT_CBACK_EVT */
|
||||
DM_ID_CONN_CTE, /* HCI_LE_CTE_REQ_FAILED_CBACK_EVT */
|
||||
DM_ID_CONN_CTE, /* HCI_LE_SET_CONN_CTE_RX_PARAMS_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_CTE, /* HCI_LE_SET_CONN_CTE_TX_PARAMS_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_CTE, /* HCI_LE_CONN_CTE_REQ_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_CTE, /* HCI_LE_CONN_CTE_RSP_ENABLE_CMD_CMPL_CBACK_EVT */
|
||||
DM_ID_CONN_CTE /* HCI_LE_READ_ANTENNA_INFO_CMD_CMPL_CBACK_EVT */
|
||||
};
|
||||
|
||||
/* DM callback event length table */
|
||||
static const uint16_t dmEvtCbackLen[] =
|
||||
{
|
||||
sizeof(wsfMsgHdr_t), /* DM_RESET_CMPL_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_ADV_START_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_ADV_STOP_IND */
|
||||
sizeof(dmAdvNewAddrIndEvt_t), /* DM_ADV_NEW_ADDR_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_SCAN_START_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_SCAN_STOP_IND */
|
||||
sizeof(hciLeAdvReportEvt_t), /* DM_SCAN_REPORT_IND */
|
||||
sizeof(hciLeConnCmplEvt_t), /* DM_CONN_OPEN_IND */
|
||||
sizeof(hciDisconnectCmplEvt_t), /* DM_CONN_CLOSE_IND */
|
||||
sizeof(hciLeConnUpdateCmplEvt_t), /* DM_CONN_UPDATE_IND */
|
||||
sizeof(dmSecPairCmplIndEvt_t), /* DM_SEC_PAIR_CMPL_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_SEC_PAIR_FAIL_IND */
|
||||
sizeof(dmSecEncryptIndEvt_t), /* DM_SEC_ENCRYPT_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_SEC_ENCRYPT_FAIL_IND */
|
||||
sizeof(dmSecAuthReqIndEvt_t), /* DM_SEC_AUTH_REQ_IND */
|
||||
sizeof(dmSecKeyIndEvt_t), /* DM_SEC_KEY_IND */
|
||||
sizeof(hciLeLtkReqEvt_t), /* DM_SEC_LTK_REQ_IND */
|
||||
sizeof(dmSecPairIndEvt_t), /* DM_SEC_PAIR_IND */
|
||||
sizeof(dmSecSlaveIndEvt_t), /* DM_SEC_SLAVE_REQ_IND */
|
||||
sizeof(dmSecOobCalcIndEvt_t), /* DM_SEC_CALC_OOB_IND */
|
||||
sizeof(secEccMsg_t), /* DM_SEC_ECC_KEY_IND */
|
||||
sizeof(dmSecCnfIndEvt_t), /* DM_SEC_COMPARE_IND */
|
||||
sizeof(dmSecKeypressIndEvt_t), /* DM_SEC_KEYPRESS_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_PRIV_RESOLVED_ADDR_IND */
|
||||
sizeof(dmPrivGenAddrIndEvt_t), /* DM_PRIV_GENERATE_RPA_IND */
|
||||
sizeof(hciReadRssiCmdCmplEvt_t), /* DM_CONN_READ_RSSI_IND */
|
||||
sizeof(hciLeAddDevToResListCmdCmplEvt_t), /* DM_PRIV_ADD_DEV_TO_RES_LIST_IND */
|
||||
sizeof(hciLeRemDevFromResListCmdCmplEvt_t), /* DM_PRIV_REM_DEV_FROM_RES_LIST_IND */
|
||||
sizeof(hciLeClearResListCmdCmplEvt_t), /* DM_PRIV_CLEAR_RES_LIST_IND */
|
||||
sizeof(hciLeReadPeerResAddrCmdCmplEvt_t), /* DM_PRIV_READ_PEER_RES_ADDR_IND */
|
||||
sizeof(hciLeReadLocalResAddrCmdCmplEvt_t), /* DM_PRIV_READ_LOCAL_RES_ADDR_IND */
|
||||
sizeof(hciLeSetAddrResEnableCmdCmplEvt_t), /* DM_PRIV_SET_ADDR_RES_ENABLE_IND */
|
||||
sizeof(hciLeRemConnParamReqEvt_t), /* DM_REM_CONN_PARAM_REQ_IND */
|
||||
sizeof(hciLeDataLenChangeEvt_t), /* DM_CONN_DATA_LEN_CHANGE_IND */
|
||||
sizeof(hciWriteAuthPayloadToCmdCmplEvt_t), /* DM_CONN_WRITE_AUTH_TO_IND */
|
||||
sizeof(hciAuthPayloadToExpiredEvt_t), /* DM_CONN_AUTH_TO_EXPIRED_IND */
|
||||
sizeof(hciLeReadPhyCmdCmplEvt_t), /* DM_PHY_READ_IND */
|
||||
sizeof(hciLeSetDefPhyCmdCmplEvt_t), /* DM_PHY_SET_DEF_IND */
|
||||
sizeof(hciLePhyUpdateEvt_t), /* DM_PHY_UPDATE_IND */
|
||||
sizeof(dmAdvSetStartEvt_t), /* DM_ADV_SET_START_IND */
|
||||
sizeof(hciLeAdvSetTermEvt_t), /* DM_ADV_SET_STOP_IND */
|
||||
sizeof(hciLeScanReqRcvdEvt_t), /* DM_SCAN_REQ_RCVD_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_EXT_SCAN_START_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_EXT_SCAN_STOP_IND */
|
||||
sizeof(hciLeExtAdvReportEvt_t), /* DM_EXT_SCAN_REPORT_IND */
|
||||
sizeof(dmPerAdvSetStartEvt_t), /* DM_PER_ADV_SET_START_IND */
|
||||
sizeof(dmPerAdvSetStopEvt_t), /* DM_PER_ADV_SET_STOP_IND */
|
||||
sizeof(hciLePerAdvSyncEstEvt_t), /* DM_PER_ADV_SYNC_EST_IND */
|
||||
sizeof(hciLePerAdvSyncEstEvt_t), /* DM_PER_ADV_SYNC_EST_FAIL_IND */
|
||||
sizeof(hciLePerAdvSyncLostEvt_t), /* DM_PER_ADV_SYNC_LOST_IND */
|
||||
sizeof(HciLePerAdvSyncTrsfRcvdEvt_t), /* DM_PER_ADV_SYNC_TRSF_EST_IND */
|
||||
sizeof(HciLePerAdvSyncTrsfRcvdEvt_t), /* DM_PER_ADV_SYNC_TRSF_EST_FAIL_IND */
|
||||
sizeof(hciLePerAdvSyncTrsfCmdCmplEvt_t), /* DM_PER_ADV_SYNC_TRSF_IND */
|
||||
sizeof(hciLePerAdvSetInfoTrsfCmdCmplEvt_t), /* DM_PER_ADV_SET_INFO_TRSF_IND */
|
||||
sizeof(hciLePerAdvReportEvt_t), /* DM_PER_ADV_REPORT_IND */
|
||||
sizeof(hciLeReadRemoteFeatCmplEvt_t), /* DM_REMOTE_FEATURES_IND */
|
||||
sizeof(hciReadRemoteVerInfoCmplEvt_t), /* DM_READ_REMOTE_VER_INFO_IND */
|
||||
sizeof(hciLeConnIQReportEvt_t), /* DM_CONN_IQ_REPORT_IND */
|
||||
sizeof(hciLeCteReqFailedEvt_t), /* DM_CTE_REQ_FAIL_IND */
|
||||
sizeof(hciLeSetConnCteRxParamsCmdCmplEvt_t), /* DM_CONN_CTE_RX_SAMPLE_START_IND */
|
||||
sizeof(hciLeSetConnCteRxParamsCmdCmplEvt_t), /* DM_CONN_CTE_RX_SAMPLE_START_IND */
|
||||
sizeof(hciLeSetConnCteTxParamsCmdCmplEvt_t), /* DM_CONN_CTE_TX_CFG_IND */
|
||||
sizeof(hciLeConnCteReqEnableCmdCmplEvt_t), /* DM_CONN_CTE_REQ_START_IND */
|
||||
sizeof(hciLeConnCteReqEnableCmdCmplEvt_t), /* DM_CONN_CTE_REQ_STOP_IND */
|
||||
sizeof(hciLeConnCteRspEnableCmdCmplEvt_t), /* DM_CONN_CTE_RSP_START_IND */
|
||||
sizeof(hciLeConnCteRspEnableCmdCmplEvt_t), /* DM_CONN_CTE_RSP_STOP_IND */
|
||||
sizeof(hciLeReadAntennaInfoCmdCmplEvt_t), /* DM_READ_ANTENNA_INFO_IND */
|
||||
sizeof(dmL2cCmdRejEvt_t), /* DM_L2C_CMD_REJ_IND */
|
||||
sizeof(wsfMsgHdr_t), /* DM_ERROR_IND */
|
||||
sizeof(hciHwErrorEvt_t), /* DM_HW_ERROR_IND */
|
||||
sizeof(hciVendorSpecEvt_t) /* DM_VENDOR_SPEC_IND */
|
||||
};
|
||||
|
||||
/* Default component function inteface */
|
||||
static const dmFcnIf_t dmFcnDefault =
|
||||
{
|
||||
dmEmptyReset,
|
||||
(dmHciHandler_t) dmEmptyHandler,
|
||||
(dmMsgHandler_t) dmEmptyHandler
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Component function interface table indexed DM component ID */
|
||||
dmFcnIf_t *dmFcnIfTbl[DM_NUM_IDS] =
|
||||
{
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_ADV */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_DEV_PRIV */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_SCAN */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_CONN */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_CONN_2 */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_SEC */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_PRIV */
|
||||
(dmFcnIf_t *) &dmDevFcnIf, /* DM_ID_DEV */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_LESC */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_PHY */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_ADV_PER */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_SYNC */
|
||||
(dmFcnIf_t *) &dmFcnDefault, /* DM_ID_PAST */
|
||||
(dmFcnIf_t *) &dmFcnDefault /* DM_ID_CONN_CTE */
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
dmCb_t dmCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI event callback function.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmHciEvtCback(hciEvt_t *pEvent)
|
||||
{
|
||||
WSF_ASSERT(pEvent->hdr.event <= HCI_LE_READ_ANTENNA_INFO_CMD_CMPL_CBACK_EVT);
|
||||
|
||||
/* if DM not resetting or resetting but incoming event is HCI reset sequence complete event */
|
||||
if (!dmCb.resetting || (pEvent->hdr.event == HCI_RESET_SEQ_CMPL_CBACK_EVT))
|
||||
{
|
||||
/* route event to DM component handling function */
|
||||
(*(dmFcnIfTbl[dmHciToIdTbl[pEvent->hdr.event]]->hciHandler))(pEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM empty reset handler.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmEmptyReset(void)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM empty event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmEmptyHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Pass an HCI event to the DM connection management module.
|
||||
*
|
||||
* \param pEvent HCI event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmDevPassHciEvtToConn(hciEvt_t *pEvent)
|
||||
{
|
||||
/* pass event to DM connection management module */
|
||||
(*(dmFcnIfTbl[DM_ID_CONN]->hciHandler))(pEvent);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback with DM for scan and advertising events.
|
||||
*
|
||||
* \param cback Client callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmRegister(dmCback_t cback)
|
||||
{
|
||||
dmCb.cback = cback;
|
||||
|
||||
/* if LESC is enabled */
|
||||
if (dmFcnIfTbl[DM_ID_LESC] != &dmFcnDefault)
|
||||
{
|
||||
/* if largest LESC key length is larger than maximum RX PDU length */
|
||||
if (SMP_PUB_KEY_MSG_LEN > (HciGetMaxRxAclLen() - L2C_HDR_LEN))
|
||||
{
|
||||
dmEvt_t evt;
|
||||
|
||||
evt.hdr.param = 0;
|
||||
evt.hdr.event = DM_ERROR_IND;
|
||||
evt.hdr.status = DM_ERR_SMP_RX_PDU_LEN_EXCEEDED;
|
||||
|
||||
(*dmCb.cback)(&evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find an advertising data element in the given advertising or scan response data.
|
||||
*
|
||||
* \param adType Advertising data element type to find.
|
||||
* \param dataLen Data length.
|
||||
* \param pData Pointer to advertising or scan response data.
|
||||
*
|
||||
* \return Pointer to advertising data element byte array or NULL if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *DmFindAdType(uint8_t adType, uint16_t dataLen, uint8_t *pData)
|
||||
{
|
||||
/* while not at end of data and
|
||||
* data element length is not zero and
|
||||
* data element length is not erroneously more than the data length
|
||||
*/
|
||||
while ((dataLen != 0) && (pData[DM_AD_LEN_IDX] != 0) && (pData[DM_AD_LEN_IDX] < dataLen))
|
||||
{
|
||||
/* if found */
|
||||
if (pData[DM_AD_TYPE_IDX] == adType)
|
||||
{
|
||||
return pData;
|
||||
}
|
||||
|
||||
/* else go to next element */
|
||||
dataLen = dataLen - pData[DM_AD_LEN_IDX] - 1;
|
||||
pData = pData + pData[DM_AD_LEN_IDX] + 1;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for DM.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
/* store handler ID */
|
||||
dmCb.handlerId = handlerId;
|
||||
|
||||
dmCb.llPrivEnabled = FALSE;
|
||||
dmCb.resetting = FALSE;
|
||||
|
||||
/* register with the HCI event interface */
|
||||
HciEvtRegister(dmHciEvtCback);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for DM.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* Handle message */
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
WSF_ASSERT(DM_ID_FROM_MSG(pMsg->event) < DM_NUM_IDS);
|
||||
|
||||
/* if DM not resetting */
|
||||
if (!dmCb.resetting)
|
||||
{
|
||||
/* route message to DM component handling function */
|
||||
(*(dmFcnIfTbl[DM_ID_FROM_MSG(pMsg->event)]->msgHandler))(pMsg);
|
||||
}
|
||||
}
|
||||
/* Handle events */
|
||||
else if (event)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Whether LL Privacy is enabled.
|
||||
*
|
||||
* \return TRUE if LL Privacy is enabled. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmLlPrivEnabled(void)
|
||||
{
|
||||
return dmCb.llPrivEnabled;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Map an address type to a type used by LL.
|
||||
*
|
||||
* \param addrType Address type used by Host.
|
||||
*
|
||||
* \return Address type used by LL.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t DmLlAddrType(uint8_t addrType)
|
||||
{
|
||||
uint8_t llAddrType = addrType;
|
||||
|
||||
/* if LL Privacy is enabled */
|
||||
if (dmCb.llPrivEnabled)
|
||||
{
|
||||
if (addrType == DM_ADDR_PUBLIC)
|
||||
{
|
||||
llAddrType = DM_ADDR_PUBLIC_IDENTITY;
|
||||
}
|
||||
else if (addrType == DM_ADDR_RANDOM)
|
||||
{
|
||||
llAddrType = DM_ADDR_RANDOM_IDENTITY;
|
||||
}
|
||||
}
|
||||
|
||||
return llAddrType;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Map an address type to a type used by Host.
|
||||
*
|
||||
* \param addrType Address type used by LL.
|
||||
*
|
||||
* \return Address type used by Host.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t DmHostAddrType(uint8_t addrType)
|
||||
{
|
||||
uint8_t hostAddrType = addrType;
|
||||
|
||||
/* if LL Privacy is enabled */
|
||||
if (dmCb.llPrivEnabled)
|
||||
{
|
||||
if (addrType == DM_ADDR_PUBLIC_IDENTITY)
|
||||
{
|
||||
hostAddrType = DM_ADDR_PUBLIC;
|
||||
}
|
||||
else if (addrType == DM_ADDR_RANDOM_IDENTITY)
|
||||
{
|
||||
hostAddrType = DM_ADDR_RANDOM;
|
||||
}
|
||||
}
|
||||
|
||||
return hostAddrType;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return size of a DM callback event.
|
||||
*
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return Size of DM callback event.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t DmSizeOfEvt(dmEvt_t *pDmEvt)
|
||||
{
|
||||
uint16_t len;
|
||||
|
||||
/* if a valid DM event ID */
|
||||
if ((pDmEvt->hdr.event >= DM_CBACK_START) && (pDmEvt->hdr.event <= DM_CBACK_END))
|
||||
{
|
||||
len = dmEvtCbackLen[pDmEvt->hdr.event - DM_CBACK_START];
|
||||
}
|
||||
else
|
||||
{
|
||||
len = sizeof(wsfMsgHdr_t);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the PHY index for the given scanner PHY.
|
||||
*
|
||||
* \param numPhys Number of scanner PHYs.
|
||||
* \param scanPhy Scanner PHY.
|
||||
*
|
||||
* \return PHY index.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t dmScanPhyToIdx(uint8_t numPhys, uint8_t scanPhy)
|
||||
{
|
||||
/* if number of supported PHYs is 1 */
|
||||
if (numPhys == 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if number of supported PHYs is 2 */
|
||||
if (numPhys == 2)
|
||||
{
|
||||
return (scanPhy == HCI_SCAN_PHY_LE_1M_BIT) ? 0 : 1;
|
||||
}
|
||||
|
||||
/* all three PHYs are supported */
|
||||
return (scanPhy == HCI_SCAN_PHY_LE_1M_BIT) ? 0 : (scanPhy == HCI_SCAN_PHY_LE_2M_BIT) ? 1 : 2;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the PHY index for the given scanner PHY.
|
||||
*
|
||||
* \param scanPhy Scanner PHY.
|
||||
*
|
||||
* \return PHY index.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t DmScanPhyToIdx(uint8_t scanPhy)
|
||||
{
|
||||
return dmScanPhyToIdx(DM_NUM_PHYS, scanPhy);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the PHY index for the given initiator PHY.
|
||||
*
|
||||
* \param numPhys Number of initiator PHYs.
|
||||
* \param initPhy Initiator PHY.
|
||||
*
|
||||
* \return PHY index.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t dmInitPhyToIdx(uint8_t numPhys, uint8_t initPhy)
|
||||
{
|
||||
/* if number of supported PHYs is 1 */
|
||||
if (numPhys == 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if number of supported PHYs is 2 */
|
||||
if (numPhys == 2)
|
||||
{
|
||||
return (initPhy == HCI_INIT_PHY_LE_1M_BIT) ? 0 : 1;
|
||||
}
|
||||
|
||||
/* all three PHYs are supported */
|
||||
return (initPhy == HCI_INIT_PHY_LE_1M_BIT) ? 0 : (initPhy == HCI_INIT_PHY_LE_2M_BIT) ? 1 : 2;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the PHY index for the given initiator PHY.
|
||||
*
|
||||
* \param initPhy Initiator PHY.
|
||||
*
|
||||
* \return PHY index.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t DmInitPhyToIdx(uint8_t initPhy)
|
||||
{
|
||||
return dmInitPhyToIdx(DM_NUM_PHYS, initPhy);
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM main module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_MAIN_H
|
||||
#define DM_MAIN_H
|
||||
|
||||
#include "util/bda.h"
|
||||
#include "wsf_os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* DM component IDs */
|
||||
#define DM_ID_ADV 0
|
||||
#define DM_ID_DEV_PRIV 1
|
||||
#define DM_ID_SCAN 2
|
||||
#define DM_ID_CONN 3
|
||||
#define DM_ID_CONN_2 4
|
||||
#define DM_ID_SEC 5
|
||||
#define DM_ID_PRIV 6
|
||||
#define DM_ID_DEV 7
|
||||
#define DM_ID_LESC 8
|
||||
#define DM_ID_PHY 9
|
||||
#define DM_ID_ADV_PER 10
|
||||
#define DM_ID_SYNC 11
|
||||
#define DM_ID_PAST 12
|
||||
#define DM_ID_CONN_CTE 13
|
||||
#define DM_NUM_IDS 14
|
||||
|
||||
/* Start of component message enumeration */
|
||||
#define DM_MSG_START(id) ((id) << 4)
|
||||
|
||||
/* Get the component ID from a message ID */
|
||||
#define DM_ID_FROM_MSG(msg) ((msg) >> 4)
|
||||
|
||||
/* Mask off the ID from the message ID */
|
||||
#define DM_MSG_MASK(msg) ((msg) & 0x0F)
|
||||
|
||||
/* Length of hash part of private resolvable address */
|
||||
#define DM_PRIV_HASH_LEN 3
|
||||
|
||||
/* Length of random part of private resolvable address */
|
||||
#define DM_PRIV_PRAND_LEN 3
|
||||
|
||||
/* Length of plaintext used for private resolvable address calculation */
|
||||
#define DM_PRIV_PLAINTEXT_LEN 16
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* DM reset function type */
|
||||
typedef void (*dmReset_t)(void);
|
||||
|
||||
/* DM message handling function types */
|
||||
typedef void (*dmMsgHandler_t)(wsfMsgHdr_t *pMsg);
|
||||
typedef void (*dmHciHandler_t)(hciEvt_t *pEvent);
|
||||
|
||||
/* DM component reset and handler function interface */
|
||||
typedef struct
|
||||
{
|
||||
dmReset_t reset;
|
||||
dmHciHandler_t hciHandler;
|
||||
dmMsgHandler_t msgHandler;
|
||||
} dmFcnIf_t;
|
||||
|
||||
/* Main control block of the DM subsystem */
|
||||
typedef struct
|
||||
{
|
||||
bdAddr_t localAddr;
|
||||
dmCback_t cback;
|
||||
wsfHandlerId_t handlerId;
|
||||
uint8_t connAddrType;
|
||||
uint8_t advAddrType;
|
||||
uint8_t scanAddrType;
|
||||
bool_t resetting;
|
||||
|
||||
/* Filter policies for Advertising, Scanning, Initiator and Synchronization */
|
||||
uint8_t advFiltPolicy[DM_NUM_ADV_SETS];
|
||||
uint8_t scanFiltPolicy;
|
||||
uint8_t initFiltPolicy;
|
||||
|
||||
/* Options (filter policies and periodic advertising report enablement) for Synchronization */
|
||||
uint8_t syncOptions;
|
||||
|
||||
/* LL Privacy */
|
||||
bool_t llPrivEnabled;
|
||||
} dmCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Component function interface table indexed DM component ID */
|
||||
extern dmFcnIf_t *dmFcnIfTbl[DM_NUM_IDS];
|
||||
|
||||
/* Control block */
|
||||
extern dmCb_t dmCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
void dmEmptyReset(void);
|
||||
void dmEmptyHandler(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/* utility functions */
|
||||
uint8_t DmScanPhyToIdx(uint8_t scanPhy);
|
||||
uint8_t DmInitPhyToIdx(uint8_t initPhy);
|
||||
void dmDevPassHciEvtToConn(hciEvt_t *pEvent);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_MAIN_H */
|
||||
+380
@@ -0,0 +1,380 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager periodic advertising sync transfer (PAST) module.
|
||||
*
|
||||
* Copyright (c) 2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_adv.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_scan.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! DM past event handler messages */
|
||||
enum
|
||||
{
|
||||
/* messages from API */
|
||||
DM_PAST_MSG_API_RCV_ENABLE = DM_MSG_START(DM_ID_PAST), /*!< Enable receiving report */
|
||||
DM_PAST_MSG_API_SYNC_TRSF, /*!< Transfer sync */
|
||||
DM_PAST_MSG_API_SET_INFO_TRSF, /*!< Transfer set info */
|
||||
DM_PAST_MSG_API_CFG, /*!< Configure PAST parameters */
|
||||
DM_PAST_MSG_API_DEFAULT_CFG /*!< Configure PAST default parameters */
|
||||
};
|
||||
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* action function table */
|
||||
static const dmPastAct_t dmPastAct[] =
|
||||
{
|
||||
dmPastActRptRcvEnable,
|
||||
dmPastActSyncTsfr,
|
||||
dmPastActSetInfoTrsf,
|
||||
dmPastActConfig,
|
||||
dmPastActDefaultConfig
|
||||
};
|
||||
|
||||
/*! DM PAST component function interface */
|
||||
static const dmFcnIf_t dmPastFcnIf =
|
||||
{
|
||||
dmEmptyReset,
|
||||
dmPastHciHandler,
|
||||
dmPastMsgHandler
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM Periodic Advertising Sync Transfer (PAST) module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPastInit(void)
|
||||
{
|
||||
/* set function interface table */
|
||||
dmFcnIfTbl[DM_ID_PAST] = (dmFcnIf_t *) &dmPastFcnIf;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM PAST HCI event handler.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPastHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
dmConnCcb_t *pCcb = dmConnCcbByHandle(pEvent->hdr.param);
|
||||
|
||||
/* if ccb found */
|
||||
if (pCcb != NULL)
|
||||
{
|
||||
/* set conn id */
|
||||
pEvent->hdr.param = pCcb->connId;
|
||||
|
||||
if (pEvent->hdr.event == HCI_LE_PER_ADV_SYNC_TRSF_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
pEvent->hdr.event = DM_PER_ADV_SYNC_TRSF_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
else if (pEvent->hdr.event == HCI_LE_PER_ADV_SET_INFO_TRSF_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
pEvent->hdr.event = DM_PER_ADV_SET_INFO_TRSF_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM PAST event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPastMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmPastAct[DM_MSG_MASK(pMsg->event)])((dmPastMsg_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Enable receiving report action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPastActRptRcvEnable(dmPastMsg_t *pMsg)
|
||||
{
|
||||
dmSyncCb_t *pScb;
|
||||
|
||||
/* look up scb from sync id */
|
||||
if ((pScb = dmSyncCbById((dmSyncId_t) pMsg->hdr.param)) != NULL)
|
||||
{
|
||||
HciLeSetPerAdvRcvEnableCmd(pScb->handle, (pMsg->hdr.status ? TRUE : FALSE));
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Transfer sync action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPastActSyncTsfr(dmPastMsg_t *pMsg)
|
||||
{
|
||||
dmSyncCb_t *pScb;
|
||||
|
||||
/* look up scb from sync id */
|
||||
if ((pScb = dmSyncCbById((dmSyncId_t) pMsg->hdr.param)) != NULL)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->apiPastTrsf.connId)) != NULL)
|
||||
{
|
||||
HciLePerAdvSyncTrsfCmd(pCcb->handle, pMsg->apiPastTrsf.serviceData, pScb->handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Transfer set info action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPastActSetInfoTrsf(dmPastMsg_t *pMsg)
|
||||
{
|
||||
uint8_t advHandle = (uint8_t) pMsg->hdr.param;
|
||||
|
||||
/* if periodic advertising is currently in progress for the advertising set */
|
||||
if (dmPerAdvState(advHandle) == DM_ADV_PER_STATE_ADVERTISING)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById(pMsg->apiPastTrsf.connId)) != NULL)
|
||||
{
|
||||
HciLePerAdvSetInfoTrsfCmd(pCcb->handle, pMsg->apiPastTrsf.serviceData, advHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Configure PAST action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPastActConfig(dmPastMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb from conn id */
|
||||
if ((pCcb = dmConnCcbById((dmConnId_t) pMsg->hdr.param)) != NULL)
|
||||
{
|
||||
HciLeSetPerAdvSyncTrsfParamsCmd(pCcb->handle, pMsg->apiPastCfg.mode, pMsg->apiPastCfg.skip,
|
||||
pMsg->apiPastCfg.syncTimeout, pMsg->apiPastCfg.cteType);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Configure default PAST action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPastActDefaultConfig(dmPastMsg_t *pMsg)
|
||||
{
|
||||
HciLeSetDefaultPerAdvSyncTrsfParamsCmd(pMsg->apiPastCfg.mode, pMsg->apiPastCfg.skip,
|
||||
pMsg->apiPastCfg.syncTimeout, pMsg->apiPastCfg.cteType);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Enable or disable reports for the periodic advertising identified by the sync id.
|
||||
*
|
||||
* \param syncId Sync identifier.
|
||||
* \param enable TRUE to enable reporting, FALSE to disable reporting.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPastRptRcvEnable(dmSyncId_t syncId, bool_t enable)
|
||||
{
|
||||
wsfMsgHdr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pMsg->param = syncId;
|
||||
pMsg->event = DM_PAST_MSG_API_RCV_ENABLE;
|
||||
pMsg->status = enable;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send synchronization information about the periodic advertising identified by the
|
||||
* sync id to a connected device.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param serviceData Value provided by the Host.
|
||||
* \param syncId Sync identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPastSyncTrsf(dmConnId_t connId, uint16_t serviceData, dmSyncId_t syncId)
|
||||
{
|
||||
dmPastApiTrsf_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPastApiTrsf_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.param = syncId;
|
||||
pMsg->hdr.event = DM_PAST_MSG_API_SYNC_TRSF;
|
||||
pMsg->serviceData = serviceData;
|
||||
pMsg->connId = connId;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send synchronization information about the periodic advertising in an advertising
|
||||
* set to a connected device.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param serviceData Value provided by the Host.
|
||||
* \param advHandle Advertising handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPastSetInfoTrsf(dmConnId_t connId, uint16_t serviceData, uint8_t advHandle)
|
||||
{
|
||||
dmPastApiTrsf_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPastApiTrsf_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.param = advHandle;
|
||||
pMsg->hdr.event = DM_PAST_MSG_API_SET_INFO_TRSF;
|
||||
pMsg->serviceData = serviceData;
|
||||
pMsg->connId = connId;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Specify how the Controller should process periodic advertising synchronization
|
||||
* information received from the device identified by the connection handle.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param mode Action to be taken when periodic advertising info is received.
|
||||
* \param skip Number of consecutive periodic advertising packets that the receiver
|
||||
* may skip after successfully receiving a periodic advertising packet.
|
||||
* \param syncTimeout Maximum permitted time between successful receives. If this time is
|
||||
* exceeded, synchronization is lost.
|
||||
* \param cteType Whether to only synchronize to periodic advertising with certain
|
||||
* types of Constant Tone Extension.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPastConfig(dmConnId_t connId, uint8_t mode, uint16_t skip, uint16_t syncTimeout,
|
||||
uint8_t cteType)
|
||||
{
|
||||
dmPastApiCfg_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPastApiCfg_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->hdr.event = DM_PAST_MSG_API_CFG;
|
||||
pMsg->mode = mode;
|
||||
pMsg->skip = skip;
|
||||
pMsg->syncTimeout = syncTimeout;
|
||||
pMsg->cteType = cteType;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Specify the initial value for the mode, skip, timeout, and Constant Tone Extension type
|
||||
* to be used for all subsequent connections over the LE transport.
|
||||
*
|
||||
* \param mode Action to be taken when periodic advertising info is received.
|
||||
* \param skip Number of consecutive periodic advertising packets that the receiver
|
||||
* may skip after successfully receiving a periodic advertising packet.
|
||||
* \param syncTimeout Maximum permitted time between successful receives. If this time is
|
||||
* exceeded, synchronization is lost.
|
||||
* \param cteType Whether to only synchronize to periodic advertising with certain
|
||||
* types of Constant Tone Extension.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPastDefaultConfig(uint8_t mode, uint16_t skip, uint16_t syncTimeout, uint8_t cteType)
|
||||
{
|
||||
dmPastApiCfg_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPastApiCfg_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PAST_MSG_API_DEFAULT_CFG;
|
||||
pMsg->mode = mode;
|
||||
pMsg->skip = skip;
|
||||
pMsg->syncTimeout = syncTimeout;
|
||||
pMsg->cteType = cteType;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM PHY module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_phy.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Component function interface */
|
||||
static const dmFcnIf_t dmPhyFcnIf =
|
||||
{
|
||||
dmEmptyReset,
|
||||
dmPhyHciHandler,
|
||||
(dmMsgHandler_t)dmEmptyHandler
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
static void dmPhyActDefPhySet(hciEvt_t *pEvent);
|
||||
static void dmPhyActPhyRead(dmConnCcb_t *pCcb, hciEvt_t *pEvent);
|
||||
static void dmPhyActPhyUpdate(dmConnCcb_t *pCcb, hciEvt_t *pEvent);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM PHY HCI event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPhyHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
if (pEvent->hdr.event == HCI_LE_SET_DEF_PHY_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
dmPhyActDefPhySet(pEvent);
|
||||
}
|
||||
/* look up ccb from conn handle */
|
||||
else if ((pCcb = dmConnCcbByHandle(pEvent->hdr.param)) != NULL)
|
||||
{
|
||||
/* handle incoming event */
|
||||
switch (pEvent->hdr.event)
|
||||
{
|
||||
case HCI_LE_READ_PHY_CMD_CMPL_CBACK_EVT:
|
||||
dmPhyActPhyRead(pCcb, pEvent);
|
||||
break;
|
||||
|
||||
case HCI_LE_PHY_UPDATE_CMPL_CBACK_EVT:
|
||||
dmPhyActPhyUpdate(pCcb, pEvent);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* should never get here */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a read PHY event from HCI.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmPhyActPhyRead(dmConnCcb_t *pCcb, hciEvt_t *pEvent)
|
||||
{
|
||||
hciLeReadPhyCmdCmplEvt_t evt;
|
||||
|
||||
/* call callback */
|
||||
evt.hdr.event = DM_PHY_READ_IND;
|
||||
evt.hdr.param = pCcb->connId;
|
||||
evt.status = evt.hdr.status = (uint8_t)pEvent->leReadPhyCmdCmpl.status;
|
||||
evt.handle = pCcb->handle;
|
||||
evt.txPhy = pEvent->leReadPhyCmdCmpl.txPhy;
|
||||
evt.rxPhy = pEvent->leReadPhyCmdCmpl.rxPhy;
|
||||
|
||||
(*dmConnCb.connCback[DM_CLIENT_ID_APP])((dmEvt_t *)&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a set default PHY event from HCI.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmPhyActDefPhySet(hciEvt_t *pEvent)
|
||||
{
|
||||
hciLeSetDefPhyCmdCmplEvt_t evt;
|
||||
|
||||
/* call callback */
|
||||
evt.hdr.event = DM_PHY_SET_DEF_IND;
|
||||
evt.hdr.param = 0;
|
||||
evt.status = evt.hdr.status = (uint8_t)pEvent->leSetDefPhyCmdCmpl.status;
|
||||
(*dmConnCb.connCback[DM_CLIENT_ID_APP])((dmEvt_t *)&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a PHY update event from HCI.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmPhyActPhyUpdate(dmConnCcb_t *pCcb, hciEvt_t *pEvent)
|
||||
{
|
||||
hciLePhyUpdateEvt_t evt;
|
||||
|
||||
/* call callback */
|
||||
evt.hdr.event = DM_PHY_UPDATE_IND;
|
||||
evt.hdr.param = pCcb->connId;
|
||||
evt.status = evt.hdr.status = (uint8_t)pEvent->lePhyUpdate.status;
|
||||
evt.handle = pCcb->handle;
|
||||
evt.txPhy = pEvent->lePhyUpdate.txPhy;
|
||||
evt.rxPhy = pEvent->lePhyUpdate.rxPhy;
|
||||
|
||||
(*dmConnCb.connCback[DM_CLIENT_ID_APP])((dmEvt_t *)&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read the current transmitter PHY and receiver PHY for a given connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmReadPhy(dmConnId_t connId)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
WsfTaskLock();
|
||||
/* look up ccb from conn id */
|
||||
pCcb = dmConnCcbById(connId);
|
||||
WsfTaskUnlock();
|
||||
|
||||
/* if ccb found */
|
||||
if (pCcb != NULL)
|
||||
{
|
||||
HciLeReadPhyCmd(pCcb->handle);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the preferred values for the transmitter PHY and receiver PHY for all subsequent
|
||||
* connections.
|
||||
*
|
||||
* \param allPhys All PHYs preferences.
|
||||
* \param txPhys Preferred transmitter PHYs.
|
||||
* \param rxPhys Preferred receiver PHYs.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSetDefaultPhy(uint8_t allPhys, uint8_t txPhys, uint8_t rxPhys)
|
||||
{
|
||||
HciLeSetDefaultPhyCmd(allPhys, txPhys, rxPhys);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the PHY preferences for a given connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param allPhys All PHYs preferences.
|
||||
* \param txPhys Preferred transmitter PHYs.
|
||||
* \param rxPhys Preferred receiver PHYs.
|
||||
* \param phyOptions PHY options.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSetPhy(dmConnId_t connId, uint8_t allPhys, uint8_t txPhys, uint8_t rxPhys, uint16_t phyOptions)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
WsfTaskLock();
|
||||
/* look up ccb from conn id */
|
||||
pCcb = dmConnCcbById(connId);
|
||||
WsfTaskUnlock();
|
||||
|
||||
/* if ccb found */
|
||||
if (pCcb != NULL)
|
||||
{
|
||||
HciLeSetPhyCmd(pCcb->handle, allPhys, txPhys, rxPhys, phyOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM PHY.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPhyInit(void)
|
||||
{
|
||||
dmFcnIfTbl[DM_ID_PHY] = (dmFcnIf_t *) &dmPhyFcnIf;
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM PHY module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_PHY_H
|
||||
#define DM_PHY_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Data types
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Function declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* component interface */
|
||||
void dmPhyHciHandler(hciEvt_t *pEvent);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_PHY_H */
|
||||
+698
@@ -0,0 +1,698 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager privacy module.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "sec_api.h"
|
||||
#include "util/calc128.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_priv.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* progress (dmPrivCb.inProgress) bitmask bits */
|
||||
#define DM_PRIV_INPROGRESS_RES_ADDR (1 << 0) /* resolve address in progress */
|
||||
#define DM_PRIV_INPROGRESS_GEN_ADDR (1 << 1) /* generate address in progress */
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* action function table */
|
||||
static const dmPrivAct_t dmPrivAct[] =
|
||||
{
|
||||
dmPrivActResolveAddr,
|
||||
dmPrivActResAddrAesCmpl,
|
||||
dmPrivActAddDevToResList,
|
||||
dmPrivActRemDevFromResList,
|
||||
dmPrivActClearResList,
|
||||
dmPrivActSetAddrResEnable,
|
||||
dmPrivActSetPrivacyMode,
|
||||
dmPrivActGenAddr,
|
||||
dmPrivActGenAddrAesCmpl
|
||||
};
|
||||
|
||||
/* Component function interface */
|
||||
static const dmFcnIf_t dmPrivFcnIf =
|
||||
{
|
||||
dmPrivReset,
|
||||
dmPrivHciHandler,
|
||||
dmPrivMsgHandler
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
dmPrivCb_t dmPrivCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
static void dmPrivSetAddrResEnable(bool_t enable);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start address resolution procedure
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActResolveAddr(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
uint8_t buf[DM_PRIV_PLAINTEXT_LEN];
|
||||
|
||||
/* verify no resolution procedure currently in progress */
|
||||
if ((dmPrivCb.inProgress & DM_PRIV_INPROGRESS_RES_ADDR) == 0)
|
||||
{
|
||||
/* store hash */
|
||||
memcpy(dmPrivCb.hash, pMsg->apiResolveAddr.addr, DM_PRIV_HASH_LEN);
|
||||
|
||||
/* copy random part of address with padding for address resolution calculation */
|
||||
memcpy(buf, &pMsg->apiResolveAddr.addr[3], DM_PRIV_PRAND_LEN);
|
||||
memset(buf + DM_PRIV_PRAND_LEN, 0, (DM_PRIV_PLAINTEXT_LEN - DM_PRIV_PRAND_LEN));
|
||||
|
||||
/* set in progress */
|
||||
dmPrivCb.inProgress |= DM_PRIV_INPROGRESS_RES_ADDR;
|
||||
|
||||
/* run calculation */
|
||||
SecAes(pMsg->apiResolveAddr.irk, buf, dmCb.handlerId,
|
||||
pMsg->hdr.param, DM_PRIV_MSG_RESOLVE_AES_CMPL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* call callback with error (note hdr.param is already set) */
|
||||
pMsg->hdr.status = HCI_ERR_MEMORY_EXCEEDED;
|
||||
pMsg->hdr.event = DM_PRIV_RESOLVED_ADDR_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Finish address resolution procedure upon completion of AES calculation.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActResAddrAesCmpl(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
/* compare calculated value with hash */
|
||||
if (memcmp(dmPrivCb.hash, pMsg->aes.pCiphertext, DM_PRIV_HASH_LEN) == 0)
|
||||
{
|
||||
pMsg->hdr.status = HCI_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
pMsg->hdr.status = HCI_ERR_AUTH_FAILURE;
|
||||
}
|
||||
|
||||
/* clear in progress */
|
||||
dmPrivCb.inProgress &= ~DM_PRIV_INPROGRESS_RES_ADDR;
|
||||
|
||||
/* call client callback (note hdr.param is already set) */
|
||||
pMsg->hdr.event = DM_PRIV_RESOLVED_ADDR_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add device to resolving list command.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActAddDevToResList(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
dmPrivApiAddDevToResList_t *pDev = &pMsg->apiAddDevToResList;
|
||||
|
||||
/* save whether asked to enable address resolution */
|
||||
dmPrivCb.enableLlPriv = pDev->enableLlPriv;
|
||||
|
||||
/* save client-defined parameter for callback event */
|
||||
dmPrivCb.addDevToResListParam = pMsg->hdr.param;
|
||||
|
||||
/* add device to resolving list */
|
||||
HciLeAddDeviceToResolvingListCmd(pDev->addrType, pDev->peerAddr, pDev->peerIrk, pDev->localIrk);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove device from resolving list command.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActRemDevFromResList(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
dmPrivApiRemDevFromResList_t *pDev = &pMsg->apiRemDevFromResList;
|
||||
|
||||
/* save client-defined parameter for callback event */
|
||||
dmPrivCb.remDevFromResListParam = pMsg->hdr.param;
|
||||
|
||||
/* remove device from resolving list */
|
||||
HciLeRemoveDeviceFromResolvingList(pDev->addrType, pDev->peerAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear resolving list command.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActClearResList(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
/* clear resolving list */
|
||||
HciLeClearResolvingList();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set address resolution enable command.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActSetAddrResEnable(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
dmPrivApiSetAddrResEnable_t *pAddrRes = &pMsg->apiSetAddrResEnable;
|
||||
|
||||
/* enable or disable address resolution in LL */
|
||||
dmPrivSetAddrResEnable(pAddrRes->enable);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set privacy mode command.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActSetPrivacyMode(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
dmPrivApiSetPrivacyMode_t *pPrivacyMode = &pMsg->apiSetPrivacyMode;
|
||||
|
||||
/* set privacy mode */
|
||||
HciLeSetPrivacyModeCmd(pPrivacyMode->addrType, pPrivacyMode->peerAddr, pPrivacyMode->mode);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start address generation procedure.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActGenAddr(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
if ((dmPrivCb.inProgress & DM_PRIV_INPROGRESS_GEN_ADDR) == 0)
|
||||
{
|
||||
/* get random number */
|
||||
SecRand(dmPrivCb.genAddrBuf, DM_PRIV_PRAND_LEN);
|
||||
|
||||
/* set address type in random number */
|
||||
dmPrivCb.genAddrBuf[2] = (dmPrivCb.genAddrBuf[2] & 0x3F) | DM_RAND_ADDR_RESOLV;
|
||||
|
||||
/* pad buffer */
|
||||
memset(dmPrivCb.genAddrBuf + DM_PRIV_PRAND_LEN, 0, (DM_PRIV_PLAINTEXT_LEN - DM_PRIV_PRAND_LEN));
|
||||
|
||||
/* set in progress */
|
||||
dmPrivCb.inProgress |= DM_PRIV_INPROGRESS_GEN_ADDR;
|
||||
|
||||
/* run calculation */
|
||||
SecAes(pMsg->apiGenerateAddr.irk, dmPrivCb.genAddrBuf, dmCb.handlerId,
|
||||
pMsg->hdr.param, DM_PRIV_MSG_GEN_ADDR_AES_CMPL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* call callback with error (note hdr.param is already set) */
|
||||
pMsg->hdr.status = HCI_ERR_MEMORY_EXCEEDED;
|
||||
pMsg->hdr.event = DM_PRIV_GENERATE_ADDR_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Finish generate RPA procedure upon completion of AES calculation.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivActGenAddrAesCmpl(dmPrivMsg_t *pMsg)
|
||||
{
|
||||
dmPrivGenAddrIndEvt_t *pAddrEvt = (dmPrivGenAddrIndEvt_t*) pMsg;
|
||||
|
||||
/* copy the hash and address to buffer */
|
||||
memcpy(pAddrEvt->addr, pMsg->aes.pCiphertext, DM_PRIV_HASH_LEN);
|
||||
memcpy(pAddrEvt->addr + DM_PRIV_HASH_LEN, dmPrivCb.genAddrBuf, DM_PRIV_PRAND_LEN);
|
||||
|
||||
/* clear in progress */
|
||||
dmPrivCb.inProgress &= ~DM_PRIV_INPROGRESS_GEN_ADDR;
|
||||
|
||||
/* call client callback */
|
||||
pAddrEvt->hdr.event = DM_PRIV_GENERATE_ADDR_IND;
|
||||
pMsg->hdr.status = HCI_SUCCESS;
|
||||
(*dmCb.cback)((dmEvt_t *) pAddrEvt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM priv HCI callback event handler.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
/* handle incoming event */
|
||||
switch (pEvent->hdr.event)
|
||||
{
|
||||
case HCI_LE_ADD_DEV_TO_RES_LIST_CMD_CMPL_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_PRIV_ADD_DEV_TO_RES_LIST_IND;
|
||||
pEvent->hdr.param = dmPrivCb.addDevToResListParam;
|
||||
|
||||
/* if LE add device to resolving list command succeeded and been asked to enable address
|
||||
* resolution in LL and it's not enabled yet
|
||||
*/
|
||||
if ((pEvent->hdr.status == HCI_SUCCESS) && dmPrivCb.enableLlPriv && !dmCb.llPrivEnabled)
|
||||
{
|
||||
/* enable address resolution in LL */
|
||||
dmPrivSetAddrResEnable(TRUE);
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_LE_REM_DEV_FROM_RES_LIST_CMD_CMPL_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_PRIV_REM_DEV_FROM_RES_LIST_IND;
|
||||
pEvent->hdr.param = dmPrivCb.remDevFromResListParam;
|
||||
break;
|
||||
|
||||
case HCI_LE_CLEAR_RES_LIST_CMD_CMPL_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_PRIV_CLEAR_RES_LIST_IND;
|
||||
|
||||
/* if LE clear resolving list command succeeded and address resolution's enabled in LL */
|
||||
if ((pEvent->hdr.status == HCI_SUCCESS) && dmCb.llPrivEnabled)
|
||||
{
|
||||
/* disable address resolution in LL */
|
||||
dmPrivSetAddrResEnable(FALSE);
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_LE_READ_PEER_RES_ADDR_CMD_CMPL_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_PRIV_READ_PEER_RES_ADDR_IND;
|
||||
break;
|
||||
|
||||
case HCI_LE_READ_LOCAL_RES_ADDR_CMD_CMPL_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_PRIV_READ_LOCAL_RES_ADDR_IND;
|
||||
break;
|
||||
|
||||
case HCI_LE_SET_ADDR_RES_ENABLE_CMD_CMPL_CBACK_EVT:
|
||||
pEvent->hdr.event = DM_PRIV_SET_ADDR_RES_ENABLE_IND;
|
||||
|
||||
/* if LE set address resoultion enable command succeeded */
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
/* update LL Privacy Enabled flag */
|
||||
dmCb.llPrivEnabled = dmPrivCb.addrResEnable;
|
||||
|
||||
/* pass LL Privacy enable/disable event to dev priv */
|
||||
dmDevPassEvtToDevPriv(dmCb.llPrivEnabled ? DM_DEV_PRIV_MSG_RPA_STOP : DM_DEV_PRIV_MSG_RPA_START,
|
||||
dmCb.llPrivEnabled ? TRUE : FALSE, 0, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* should never get here */
|
||||
return;
|
||||
}
|
||||
|
||||
/* call callback (note hdr.status is already set) */
|
||||
(*dmCb.cback)((dmEvt_t *)pEvent);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set address resolution enable command.
|
||||
*
|
||||
* \param enable Set to TRUE to enable address resolution or FALSE to disable it.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmPrivSetAddrResEnable(bool_t enable)
|
||||
{
|
||||
/* save input parameter */
|
||||
dmPrivCb.addrResEnable = enable;
|
||||
|
||||
/* enable or disable address resolution in LL */
|
||||
HciLeSetAddrResolutionEnable(enable);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM priv event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmPrivAct[DM_MSG_MASK(pMsg->event)])((dmPrivMsg_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the privacy module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmPrivReset(void)
|
||||
{
|
||||
/* initialize control block */
|
||||
dmPrivCb.inProgress = 0;
|
||||
dmCb.llPrivEnabled = FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM privacy module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivInit(void)
|
||||
{
|
||||
dmFcnIfTbl[DM_ID_PRIV] = (dmFcnIf_t *) &dmPrivFcnIf;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Resolve a private resolvable address. When complete the client's callback function
|
||||
* is called with a DM_PRIV_RESOLVED_ADDR_IND event. The client must wait to receive
|
||||
* this event before executing this function again.
|
||||
*
|
||||
* \param pAddr Peer device address.
|
||||
* \param pIrk The peer's identity resolving key.
|
||||
* \param param client-defined parameter returned with callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivResolveAddr(uint8_t *pAddr, uint8_t *pIrk, uint16_t param)
|
||||
{
|
||||
dmPrivApiResolveAddr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPrivApiResolveAddr_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PRIV_MSG_API_RESOLVE_ADDR;
|
||||
pMsg->hdr.param = param;
|
||||
Calc128Cpy(pMsg->irk, pIrk);
|
||||
BdaCpy(pMsg->addr, pAddr);
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add device to resolving list. When complete the client's callback function
|
||||
* is called with a DM_PRIV_ADD_DEV_TO_RES_LIST_IND event. The client must wait
|
||||
* to receive this event before executing this function again.
|
||||
*
|
||||
* \param addrType Peer identity address type.
|
||||
* \param pIdentityAddr Peer identity address.
|
||||
* \param pPeerIrk The peer's identity resolving key.
|
||||
* \param pLocalIrk The local identity resolving key.
|
||||
* \param enableLlPriv Set to TRUE to enable address resolution in LL.
|
||||
* \param param client-defined parameter returned with callback event.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \Note This command cannot be used when address resolution is enabled in the Controller and:
|
||||
* - Advertising (other than periodic advertising) is enabled,
|
||||
* - Scanning is enabled, or
|
||||
* - (Extended) Create connection or Create Sync command is outstanding.
|
||||
*
|
||||
* \Note If the local or peer IRK associated with the peer Identity Address is all zeros then
|
||||
* the Controller will use or accept the local or peer Identity Address respectively.
|
||||
*
|
||||
* \Note Parameter 'enableLlPriv' should be set to TRUE when the last device is being added
|
||||
* to resolving list to enable address resolution in the Controller.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivAddDevToResList(uint8_t addrType, const uint8_t *pIdentityAddr, uint8_t *pPeerIrk,
|
||||
uint8_t *pLocalIrk, bool_t enableLlPriv, uint16_t param)
|
||||
{
|
||||
dmPrivApiAddDevToResList_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPrivApiAddDevToResList_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PRIV_MSG_API_ADD_DEV_TO_RES_LIST;
|
||||
pMsg->hdr.param = param;
|
||||
pMsg->addrType = addrType;
|
||||
BdaCpy(pMsg->peerAddr, pIdentityAddr);
|
||||
Calc128Cpy(pMsg->peerIrk, pPeerIrk);
|
||||
Calc128Cpy(pMsg->localIrk, pLocalIrk);
|
||||
pMsg->enableLlPriv = enableLlPriv;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove device from resolving list. When complete the client's callback function
|
||||
* is called with a DM_PRIV_REM_DEV_FROM_RES_LIST_IND event. The client must wait to
|
||||
* receive this event before executing this function again.
|
||||
*
|
||||
* \param addrType Peer identity address type.
|
||||
* \param pIdentityAddr Peer identity address.
|
||||
* \param param client-defined parameter returned with callback event.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \Note This command cannot be used when address resolution is enabled in the Controller and:
|
||||
* - Advertising (other than periodic advertising) is enabled,
|
||||
* - Scanning is enabled, or
|
||||
* - (Extended) Create connection or Create Sync command is outstanding.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivRemDevFromResList(uint8_t addrType, const uint8_t *pIdentityAddr, uint16_t param)
|
||||
{
|
||||
dmPrivApiRemDevFromResList_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPrivApiRemDevFromResList_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PRIV_MSG_API_REM_DEV_FROM_RES_LIST;
|
||||
pMsg->hdr.param = param;
|
||||
pMsg->addrType = addrType;
|
||||
BdaCpy(pMsg->peerAddr, pIdentityAddr);
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear resolving list. When complete the client's callback function is called with a
|
||||
* DM_PRIV_CLEAR_RES_LIST_IND event. The client must wait to receive this event before
|
||||
* executing this function again.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \Note This command cannot be used when address resolution is enabled in the Controller and:
|
||||
* - Advertising (other than periodic advertising) is enabled,
|
||||
* - Scanning is enabled, or
|
||||
* - (Extended) Create connection or Create Sync command is outstanding.
|
||||
*
|
||||
* \Note Address resolution in the Controller will be disabled when resolving list's cleared
|
||||
* successfully.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivClearResList(void)
|
||||
{
|
||||
dmPrivMsg_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPrivMsg_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PRIV_MSG_API_CLEAR_RES_LIST;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI read peer resolvable address command. When complete the client's callback
|
||||
* function is called with a DM_PRIV_READ_PEER_RES_ADDR_IND event. The client must
|
||||
* wait to receive this event before executing this function again.
|
||||
*
|
||||
* \param addrType Peer identity address type.
|
||||
* \param pIdentityAddr Peer identity address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivReadPeerResolvableAddr(uint8_t addrType, const uint8_t *pIdentityAddr)
|
||||
{
|
||||
HciLeReadPeerResolvableAddr(addrType, pIdentityAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read local resolvable address command. When complete the client's callback
|
||||
* function is called with a DM_PRIV_READ_LOCAL_RES_ADDR_IND event. The client must
|
||||
* wait to receive this event before executing this function again.
|
||||
*
|
||||
* \param addrType Peer identity address type.
|
||||
* \param pIdentityAddr Peer identity address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivReadLocalResolvableAddr(uint8_t addrType, const uint8_t *pIdentityAddr)
|
||||
{
|
||||
HciLeReadLocalResolvableAddr(addrType, pIdentityAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Enable or disable address resolution in LL. When complete the client's callback
|
||||
* function is called with a DM_PRIV_SET_ADDR_RES_ENABLE_IND event. The client must
|
||||
* wait to receive this event before executing this function again.
|
||||
*
|
||||
* \param enable Set to TRUE to enable address resolution or FALSE to disable it.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \Note This command can be used at any time except when:
|
||||
* - Advertising (other than periodic advertising) is enabled,
|
||||
* - Scanning is enabled, or
|
||||
* - (Extended) Create connection or Create Sync command is outstanding.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivSetAddrResEnable(bool_t enable)
|
||||
{
|
||||
dmPrivApiSetAddrResEnable_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPrivMsg_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PRIV_MSG_API_SET_ADDR_RES_ENABLE;
|
||||
pMsg->hdr.param = 0;
|
||||
pMsg->enable = enable;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set resolvable private address timeout command.
|
||||
*
|
||||
* \param rpaTimeout Timeout measured in seconds.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivSetResolvablePrivateAddrTimeout(uint16_t rpaTimeout)
|
||||
{
|
||||
HciLeSetResolvablePrivateAddrTimeout(rpaTimeout);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set privacy mode for a given entry in the resolving list.
|
||||
*
|
||||
* \param addrType Peer identity address type.
|
||||
* \param pIdentityAddr Peer identity address.
|
||||
* \param mode Privacy mode (by default, network privacy mode is used).
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \Note This command can be used at any time except when:
|
||||
* - Advertising (other than periodic advertising) is enabled,
|
||||
* - Scanning is enabled, or
|
||||
* - (Extended) Create connection or Create Sync command is outstanding.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivSetPrivacyMode(uint8_t addrType, const uint8_t *pIdentityAddr, uint8_t mode)
|
||||
{
|
||||
dmPrivApiSetPrivacyMode_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPrivApiSetPrivacyMode_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PRIV_MSG_API_SET_PRIVACY_MODE;
|
||||
pMsg->addrType = addrType;
|
||||
BdaCpy(pMsg->peerAddr, pIdentityAddr);
|
||||
pMsg->mode = mode;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Generate a Resolvable Private Address (RPA).
|
||||
*
|
||||
* \param pIrk The identity resolving key.
|
||||
* \param param Client-defined parameter returned with callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmPrivGenerateAddr(uint8_t *pIrk, uint16_t param)
|
||||
{
|
||||
dmPrivApiGenAddr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmPrivApiGenAddr_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_PRIV_MSG_API_GEN_ADDR;
|
||||
pMsg->hdr.param = param;
|
||||
Calc128Cpy(pMsg->irk, pIrk);
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM privacy module.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_PRIV_H
|
||||
#define DM_PRIV_H
|
||||
|
||||
#include "util/bda.h"
|
||||
#include "wsf_os.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "sec_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "smp_defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* DM priv event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_PRIV_MSG_API_RESOLVE_ADDR = DM_MSG_START(DM_ID_PRIV),
|
||||
DM_PRIV_MSG_RESOLVE_AES_CMPL,
|
||||
DM_PRIV_MSG_API_ADD_DEV_TO_RES_LIST,
|
||||
DM_PRIV_MSG_API_REM_DEV_FROM_RES_LIST,
|
||||
DM_PRIV_MSG_API_CLEAR_RES_LIST,
|
||||
DM_PRIV_MSG_API_SET_ADDR_RES_ENABLE,
|
||||
DM_PRIV_MSG_API_SET_PRIVACY_MODE,
|
||||
DM_PRIV_MSG_API_GEN_ADDR,
|
||||
DM_PRIV_MSG_GEN_ADDR_AES_CMPL
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Data structure for DM_PRIV_MSG_API_RESOLVE_ADDR */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t irk[SMP_KEY_LEN];
|
||||
bdAddr_t addr;
|
||||
} dmPrivApiResolveAddr_t;
|
||||
|
||||
/* Data structure for DM_PRIV_MSG_API_GEN_ADDR */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t irk[SMP_KEY_LEN];
|
||||
} dmPrivApiGenAddr_t;
|
||||
|
||||
/* Data structure for DM_PRIV_MSG_API_ADD_DEV_TO_RES_LIST */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t addrType;
|
||||
bdAddr_t peerAddr;
|
||||
uint8_t peerIrk[SMP_KEY_LEN];
|
||||
uint8_t localIrk[SMP_KEY_LEN];
|
||||
bool_t enableLlPriv;
|
||||
} dmPrivApiAddDevToResList_t;
|
||||
|
||||
/* Data structure for DM_PRIV_MSG_API_REM_DEV_FROM_RES_LIST */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t addrType;
|
||||
bdAddr_t peerAddr;
|
||||
} dmPrivApiRemDevFromResList_t;
|
||||
|
||||
/* Data structure for DM_PRIV_MSG_API_SET_ADDR_RES_ENABLE */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
bool_t enable;
|
||||
} dmPrivApiSetAddrResEnable_t;
|
||||
|
||||
/* Data structure for DM_PRIV_MSG_API_SET_PRIVACY_MODE */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t addrType;
|
||||
bdAddr_t peerAddr;
|
||||
uint8_t mode;
|
||||
} dmPrivApiSetPrivacyMode_t;
|
||||
|
||||
/* Union of all priv messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmPrivApiResolveAddr_t apiResolveAddr;
|
||||
dmPrivApiAddDevToResList_t apiAddDevToResList;
|
||||
dmPrivApiRemDevFromResList_t apiRemDevFromResList;
|
||||
dmPrivApiSetAddrResEnable_t apiSetAddrResEnable;
|
||||
dmPrivApiSetPrivacyMode_t apiSetPrivacyMode;
|
||||
dmPrivApiGenAddr_t apiGenerateAddr;
|
||||
dmPrivGenAddrIndEvt_t genAddrInd;
|
||||
secAes_t aes;
|
||||
} dmPrivMsg_t;
|
||||
|
||||
/* Action function */
|
||||
typedef void (*dmPrivAct_t)(dmPrivMsg_t *pMsg);
|
||||
|
||||
/* Control block for privacy module */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t hash[DM_PRIV_HASH_LEN]; /* Hash part of resolvable address */
|
||||
bool_t inProgress; /* Address resolution in progress */
|
||||
uint16_t addDevToResListParam; /* 'Add device to resolving list' callback param */
|
||||
uint16_t remDevFromResListParam; /* 'Remove device from resolving list' callback param */
|
||||
bool_t enableLlPriv; /* 'Add device to resolving list' input param */
|
||||
bool_t addrResEnable; /* 'Set address resolution enable' input param */
|
||||
uint8_t genAddrBuf[HCI_ENCRYPT_DATA_LEN]; /* Random value buffer for generating an RPA */
|
||||
} dmPrivCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* component inteface */
|
||||
void dmPrivMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmPrivHciHandler(hciEvt_t *pEvent);
|
||||
void dmPrivReset(void);
|
||||
|
||||
/* action functions */
|
||||
void dmPrivActResolveAddr(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActResAddrAesCmpl(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActAddDevToResList(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActRemDevFromResList(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActSetAddrResEnable(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActClearResList(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActSetPrivacyMode(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActGenAddr(dmPrivMsg_t *pMsg);
|
||||
void dmPrivActGenAddrAesCmpl(dmPrivMsg_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_PRIV_H */
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager scan module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_scan.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
dmScanCb_t dmScanCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the scan module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmScanInit(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* initialize control block */
|
||||
for (i = 0; i < DM_NUM_PHYS; i++)
|
||||
{
|
||||
dmScanCb.scanInterval[i] = DM_GAP_SCAN_FAST_INT_MIN;
|
||||
dmScanCb.scanWindow[i] = DM_GAP_SCAN_FAST_WINDOW;
|
||||
}
|
||||
|
||||
dmCb.scanFiltPolicy = HCI_FILT_NONE;
|
||||
dmScanCb.scanTimer.handlerId = dmCb.handlerId;
|
||||
dmScanCb.scanState = DM_SCAN_STATE_IDLE;
|
||||
dmCb.scanAddrType = DM_ADDR_PUBLIC;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start scanning on the given PHYs.
|
||||
*
|
||||
* \param scanPhys Scanner PHYs.
|
||||
* \param mode Discoverability mode.
|
||||
* \param pScanType Scan type array.
|
||||
* \param filterDup Filter duplicates. Set to TRUE to filter duplicate responses received
|
||||
* from the same device. Set to FALSE to receive all responses.
|
||||
* \param duration The scan duration, in milliseconds. If set to zero or both duration and
|
||||
* period set to non-zero, scanning will continue until DmScanStop() is called.
|
||||
* \param period The scan period, in 1.28 sec units (only applicable to AE). If set to zero,
|
||||
* periodic scanning is disabled.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmScanStart(uint8_t scanPhys, uint8_t mode, const uint8_t *pScanType, bool_t filterDup,
|
||||
uint16_t duration, uint16_t period)
|
||||
{
|
||||
uint8_t i; /* scanPhy bit position */
|
||||
uint8_t idx; /* param array index */
|
||||
dmScanApiStart_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmScanApiStart_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_SCAN_MSG_API_START;
|
||||
pMsg->scanPhys = scanPhys;
|
||||
|
||||
for (i = 0, idx = 0; (i < 8) && (idx < DM_NUM_PHYS); i++)
|
||||
{
|
||||
if (scanPhys & (1 << i))
|
||||
{
|
||||
/* scan type for this PHY */
|
||||
pMsg->scanType[idx] = pScanType[idx];
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
pMsg->mode = mode;
|
||||
pMsg->duration = duration;
|
||||
pMsg->period = period;
|
||||
pMsg->filterDup = filterDup;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop scanning.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmScanStop(void)
|
||||
{
|
||||
wsfMsgHdr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pMsg->event = DM_SCAN_MSG_API_STOP;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the scan interval and window.
|
||||
*
|
||||
* \param phyIdx The scanning PHY.
|
||||
* \param scanInterval The scan interval.
|
||||
* \param scanWindow The scan window.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmScanSetInterval(uint8_t scanPhy, uint16_t scanInterval, uint16_t scanWindow)
|
||||
{
|
||||
uint8_t phyIdx;
|
||||
|
||||
WSF_ASSERT((scanPhy == HCI_INIT_PHY_LE_1M_BIT) || (scanPhy == HCI_INIT_PHY_LE_CODED_BIT));
|
||||
|
||||
WsfTaskLock();
|
||||
phyIdx = DmScanPhyToIdx(scanPhy);
|
||||
dmScanCb.scanInterval[phyIdx] = scanInterval;
|
||||
dmScanCb.scanWindow[phyIdx] = scanWindow;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the scan interval and window for the specified PHYs.
|
||||
*
|
||||
* \param scanPhys Scanning PHYs.
|
||||
* \param pScanInterval Scan interval array.
|
||||
* \param pScanWindow Scan window array.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmScanSetInterval(uint8_t scanPhys, uint16_t *pScanInterval, uint16_t *pScanWindow)
|
||||
{
|
||||
uint8_t i; /* scanPhy bit position */
|
||||
uint8_t idx; /* param array index */
|
||||
|
||||
for (i = 0, idx = 0; (i < 8) && (idx < DM_NUM_PHYS); i++)
|
||||
{
|
||||
if (scanPhys & (1 << i))
|
||||
{
|
||||
dmScanSetInterval((1 << i), pScanInterval[idx], pScanWindow[idx]);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the local address type used while scanning. This function can be used to
|
||||
* configure scanning to use a random address.
|
||||
*
|
||||
* \param addrType Address type.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmScanSetAddrType(uint8_t addrType)
|
||||
{
|
||||
WsfTaskLock();
|
||||
dmCb.scanAddrType = addrType;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM scan module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_SCAN_H
|
||||
#define DM_SCAN_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* DM scan event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_SCAN_MSG_API_START = DM_MSG_START(DM_ID_SCAN),
|
||||
DM_SCAN_MSG_API_STOP,
|
||||
DM_SCAN_MSG_TIMEOUT
|
||||
};
|
||||
|
||||
/* DM scan states */
|
||||
enum
|
||||
{
|
||||
DM_SCAN_STATE_IDLE,
|
||||
DM_SCAN_STATE_STARTING,
|
||||
DM_SCAN_STATE_SCANNING,
|
||||
DM_SCAN_STATE_STOPPING
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Data structure for DM_SCAN_MSG_API_START */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t scanPhys;
|
||||
uint8_t scanType[DM_NUM_PHYS];
|
||||
uint8_t mode;
|
||||
uint16_t duration;
|
||||
uint16_t period;
|
||||
bool_t filterDup;
|
||||
} dmScanApiStart_t;
|
||||
|
||||
/* Data structure for DM_SYNC_MSG_API_START */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advSid;
|
||||
uint8_t advAddrType;
|
||||
bdAddr_t advAddr;
|
||||
uint16_t skip;
|
||||
uint16_t syncTimeout;
|
||||
uint8_t unused;
|
||||
} dmSyncApiStart_t;
|
||||
|
||||
/* Data structure for DM_SYNC_MSG_API_ADD_DEV_TO_PER_ADV_LIST */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advAddrType;
|
||||
bdAddr_t advAddr;
|
||||
uint8_t advSid;
|
||||
} dmSyncApiAddDevToPerAdvList_t;
|
||||
|
||||
/* Data structure for DM_SYNC_MSG_API_REM_DEV_FROM_PER_ADV_LIST */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t advAddrType;
|
||||
bdAddr_t advAddr;
|
||||
uint8_t advSid;
|
||||
} dmSyncApiRemDevFromPerAdvList_t;
|
||||
|
||||
/* Union of all scan messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmScanApiStart_t apiStart;
|
||||
} dmScanMsg_t;
|
||||
|
||||
/* Union of all DM Sync state machine messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmSyncApiStart_t apiSyncStart;
|
||||
hciLePerAdvSyncEstEvt_t perAdvSyncEst;
|
||||
hciLePerAdvSyncLostEvt_t perAdvSyncLost;
|
||||
HciLePerAdvSyncTrsfRcvdEvt_t perAdvSyncTrsfEst;
|
||||
} dmSyncMsg_t;
|
||||
|
||||
/* Action function */
|
||||
typedef void (*dmScanAct_t)(dmScanMsg_t *pMsg);
|
||||
|
||||
/* Control block for scan module */
|
||||
typedef struct
|
||||
{
|
||||
wsfTimer_t scanTimer;
|
||||
uint16_t scanInterval[DM_NUM_PHYS];
|
||||
uint16_t scanWindow[DM_NUM_PHYS];
|
||||
bool_t scanState;
|
||||
uint16_t scanDuration;
|
||||
bool_t filterNextScanRsp;
|
||||
uint8_t discFilter;
|
||||
} dmScanCb_t;
|
||||
|
||||
/* Control block for periodic advertising sync module */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t advSid; /*!< advertising SID */
|
||||
bdAddr_t advAddr; /*!< advertiser address */
|
||||
uint8_t advAddrType; /*!< advertiser address type */
|
||||
uint16_t handle; /*!< sync handle */
|
||||
dmSyncId_t syncId; /*!< sync id */
|
||||
uint8_t state; /*!< sync state */
|
||||
uint8_t inUse; /*!< TRUE if entry in use */
|
||||
} dmSyncCb_t;
|
||||
|
||||
/* Data structure for DM_PAST_MSG_API_SYNC_TRSF and DM_PAST_MSG_API_INFO_TRSF */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
uint16_t serviceData; /*!< Value provided by the Host */
|
||||
dmConnId_t connId; /*!< Connection id */
|
||||
} dmPastApiTrsf_t;
|
||||
|
||||
/* Data structure for DM_PAST_MSG_API_CONFIG and DM_PAST_MSG_API_DEFAULT_CONFIG */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /*!< Header */
|
||||
uint8_t mode; /*!< Mode */
|
||||
uint16_t skip; /*!< Skip */
|
||||
uint16_t syncTimeout; /*!< Sync timeout */
|
||||
uint8_t cteType; /*!< CTE type */
|
||||
} dmPastApiCfg_t;
|
||||
|
||||
/* Union of all DM PAST API messages */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmPastApiTrsf_t apiPastTrsf;
|
||||
dmPastApiCfg_t apiPastCfg;
|
||||
} dmPastMsg_t;
|
||||
|
||||
/*! Action function */
|
||||
typedef void (*dmPastAct_t)(dmPastMsg_t *pMsg);
|
||||
|
||||
extern dmScanCb_t dmScanCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* common scanning component inteface */
|
||||
void dmScanInit(void);
|
||||
|
||||
/* legacy scanning component inteface */
|
||||
void dmScanReset(void);
|
||||
void dmScanMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmScanHciHandler(hciEvt_t *pEvent);
|
||||
|
||||
/* legacy scanning action functions */
|
||||
void dmScanActStart(dmScanMsg_t *pMsg);
|
||||
void dmScanActStop(dmScanMsg_t *pMsg);
|
||||
void dmScanActTimeout(dmScanMsg_t *pMsg);
|
||||
|
||||
/* extended scanning component inteface */
|
||||
void dmExtScanReset(void);
|
||||
void dmExtScanMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmExtScanHciHandler(hciEvt_t *pEvent);
|
||||
|
||||
/* extended scanning action functions */
|
||||
void dmExtScanActStart(dmScanMsg_t *pMsg);
|
||||
void dmExtScanActStop(dmScanMsg_t *pMsg);
|
||||
void dmExtScanActTimeout(dmScanMsg_t *pMsg);
|
||||
|
||||
/* sync and sync transfer action functions */
|
||||
void dmSyncSmActNone(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActStart(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActStop(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActCancelStart(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActSyncEst(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActSyncEstFailed(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActSyncLost(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActSyncTrsfEst(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
void dmSyncSmActSyncTrsfEstFailed(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg);
|
||||
|
||||
/* sync component inteface */
|
||||
void dmSyncInit(void);
|
||||
void dmSyncReset(void);
|
||||
void dmSyncMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmSyncHciHandler(hciEvt_t *pEvent);
|
||||
|
||||
/* past action functions */
|
||||
void dmPastActRptRcvEnable(dmPastMsg_t *pMsg);
|
||||
void dmPastActSyncTsfr(dmPastMsg_t *pMsg);
|
||||
void dmPastActSetInfoTrsf(dmPastMsg_t *pMsg);
|
||||
void dmPastActConfig(dmPastMsg_t *pMsg);
|
||||
void dmPastActDefaultConfig(dmPastMsg_t *pMsg);
|
||||
|
||||
/* past component inteface */
|
||||
void dmPastMsgHandler(wsfMsgHdr_t *pMsg);
|
||||
void dmPastHciHandler(hciEvt_t *pEvent);
|
||||
|
||||
/* sync utility functions */
|
||||
dmSyncCb_t *dmSyncCbById(dmSyncId_t syncId);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_SCAN_H */
|
||||
Vendored
+362
@@ -0,0 +1,362 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager extended scan module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_scan.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* extended scan action function table */
|
||||
static const dmScanAct_t dmScanAct[] =
|
||||
{
|
||||
dmExtScanActStart,
|
||||
dmExtScanActStop,
|
||||
dmExtScanActTimeout
|
||||
};
|
||||
|
||||
/* extended scan component function interface */
|
||||
static const dmFcnIf_t dmScanFcnIf =
|
||||
{
|
||||
dmExtScanReset,
|
||||
dmExtScanHciHandler,
|
||||
dmExtScanMsgHandler
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* event being reported used for a legacy advertising PDU or a scan response */
|
||||
#define DM_ADV_RPT_SCAN_RSP(evtType) ((((evtType) == HCI_ADV_RPT_LEG_CONN_UNDIRECT_SCAN_RSP) || \
|
||||
((evtType) == HCI_ADV_RPT_LEG_SCAN_UNDIRECT_SCAN_RSP)) || \
|
||||
((((evtType) & HCI_ADV_RPT_LEG_ADV_BIT) == 0) && \
|
||||
((evtType) & HCI_ADV_RPT_SCAN_RSP_BIT)))
|
||||
|
||||
/* data status field of extended advertising report event type */
|
||||
#define DM_ADV_RPT_DATA_STATUS(evtType) (((evtType) & HCI_ADV_RPT_DATA_STATUS_BITS) >> 5)
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start extended scanning action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtScanActStart(dmScanMsg_t *pMsg)
|
||||
{
|
||||
uint8_t i;
|
||||
uint8_t idx;
|
||||
uint8_t phyIdx;
|
||||
hciExtScanParam_t scanParam[DM_NUM_PHYS];
|
||||
|
||||
if (dmScanCb.scanState == DM_SCAN_STATE_IDLE)
|
||||
{
|
||||
/* see advertising packets to be received on which PHY */
|
||||
for (i = 0, idx = 0; (i < 8) && (idx < DM_NUM_PHYS); i++)
|
||||
{
|
||||
if (pMsg->apiStart.scanPhys & (1 << i))
|
||||
{
|
||||
phyIdx = DmScanPhyToIdx(1 << i);
|
||||
|
||||
/* set extended scan parameters for this PHY */
|
||||
scanParam[idx].scanType = pMsg->apiStart.scanType[idx];
|
||||
scanParam[idx].scanInterval = dmScanCb.scanInterval[phyIdx];
|
||||
scanParam[idx].scanWindow = dmScanCb.scanWindow[phyIdx];
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
/* set extended scan parameters to be used on primary advertising channel */
|
||||
HciLeSetExtScanParamCmd(DmLlAddrType(dmCb.scanAddrType), dmCb.scanFiltPolicy,
|
||||
pMsg->apiStart.scanPhys, scanParam);
|
||||
|
||||
/* initialize scan result filtering */
|
||||
if (pMsg->apiStart.mode == DM_DISC_MODE_LIMITED)
|
||||
{
|
||||
dmScanCb.discFilter = DM_FLAG_LE_LIMITED_DISC;
|
||||
}
|
||||
else if (pMsg->apiStart.mode == DM_DISC_MODE_GENERAL)
|
||||
{
|
||||
dmScanCb.discFilter = DM_FLAG_LE_LIMITED_DISC | DM_FLAG_LE_GENERAL_DISC;
|
||||
}
|
||||
else
|
||||
{
|
||||
dmScanCb.discFilter = 0;
|
||||
}
|
||||
|
||||
dmScanCb.filterNextScanRsp = FALSE;
|
||||
|
||||
/* enable scan */
|
||||
dmScanCb.scanState = DM_SCAN_STATE_STARTING;
|
||||
HciLeExtScanEnableCmd(TRUE, pMsg->apiStart.filterDup, (pMsg->apiStart.duration / 10), pMsg->apiStart.period);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop extended scanning action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtScanActStop(dmScanMsg_t *pMsg)
|
||||
{
|
||||
if (dmScanCb.scanState == DM_SCAN_STATE_SCANNING)
|
||||
{
|
||||
/* disable scan */
|
||||
dmScanCb.scanState = DM_SCAN_STATE_STOPPING;
|
||||
HciLeExtScanEnableCmd(FALSE, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an scan timeout.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtScanActTimeout(dmScanMsg_t *pMsg)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an extended advertising report event from HCI.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmExtScanActHciReport(hciEvt_t *pEvent)
|
||||
{
|
||||
uint8_t *p;
|
||||
static bool_t filtered = FALSE;
|
||||
static bool_t firstFrag = TRUE;
|
||||
|
||||
/* ignore if not scanning */
|
||||
if (dmScanCb.scanState == DM_SCAN_STATE_SCANNING)
|
||||
{
|
||||
/* if filtering results for limited or general discovery, and first fragment */
|
||||
if ((dmScanCb.discFilter != 0) && firstFrag)
|
||||
{
|
||||
/* if this is a scan response */
|
||||
if (DM_ADV_RPT_SCAN_RSP(pEvent->leExtAdvReport.eventType))
|
||||
{
|
||||
/* check if filtering next scan response */
|
||||
if (dmScanCb.filterNextScanRsp)
|
||||
{
|
||||
filtered = TRUE;
|
||||
dmScanCb.filterNextScanRsp = FALSE;
|
||||
}
|
||||
}
|
||||
/* else it's an advertising response */
|
||||
else
|
||||
{
|
||||
/* find flags in advertising data */
|
||||
p = DmFindAdType(DM_ADV_TYPE_FLAGS, pEvent->leExtAdvReport.len, pEvent->leExtAdvReport.pData);
|
||||
if (p == NULL)
|
||||
{
|
||||
/* flags not found */
|
||||
filtered = TRUE;
|
||||
dmScanCb.filterNextScanRsp = TRUE;
|
||||
}
|
||||
/* else flags found; check them */
|
||||
else if ((p[DM_AD_DATA_IDX] & dmScanCb.discFilter) == 0)
|
||||
{
|
||||
/* flags do not match discovery mode */
|
||||
filtered = TRUE;
|
||||
dmScanCb.filterNextScanRsp = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
firstFrag = FALSE;
|
||||
}
|
||||
|
||||
if (!filtered)
|
||||
{
|
||||
pEvent->hdr.event = DM_EXT_SCAN_REPORT_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
|
||||
/* if filtering results for limited or general discovery, and no more fragmented data to come */
|
||||
if ((dmScanCb.discFilter != 0) &&
|
||||
(DM_ADV_RPT_DATA_STATUS(pEvent->leExtAdvReport.eventType) != HCI_ADV_RPT_DATA_INCMPL_MORE))
|
||||
{
|
||||
/* reset our flags */
|
||||
filtered = FALSE;
|
||||
firstFrag = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM extended scan reset function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtScanReset(void)
|
||||
{
|
||||
hciLeScanTimeoutEvt_t scanTimeout;
|
||||
|
||||
/* if stopping scan or scanning */
|
||||
if ((dmScanCb.scanState == DM_SCAN_STATE_STOPPING) ||
|
||||
(dmScanCb.scanState == DM_SCAN_STATE_SCANNING))
|
||||
{
|
||||
/* generate HCI scan timeout event */
|
||||
scanTimeout.hdr.event = HCI_LE_SCAN_TIMEOUT_CBACK_EVT;
|
||||
scanTimeout.hdr.status = HCI_SUCCESS;
|
||||
|
||||
/* handle the event */
|
||||
dmExtScanHciHandler((hciEvt_t *) &scanTimeout);
|
||||
}
|
||||
|
||||
/* reset scan module */
|
||||
dmScanInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM extended scan HCI event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtScanHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
DM_TRACE_INFO2("dmExtScanHciHandler: event: %d state: %d", pEvent->hdr.event, dmScanCb.scanState);
|
||||
|
||||
if (pEvent->hdr.event == HCI_LE_EXT_ADV_REPORT_CBACK_EVT)
|
||||
{
|
||||
dmExtScanActHciReport(pEvent);
|
||||
}
|
||||
else if (pEvent->hdr.event == HCI_LE_SCAN_TIMEOUT_CBACK_EVT)
|
||||
{
|
||||
dmScanCb.scanState = DM_SCAN_STATE_IDLE;
|
||||
|
||||
/* pass scanning stop event to dev priv */
|
||||
dmDevPassEvtToDevPriv(DM_DEV_PRIV_MSG_RPA_STOP, DM_EXT_SCAN_STOP_IND, 0, 0);
|
||||
|
||||
/* call callback */
|
||||
pEvent->hdr.event = DM_EXT_SCAN_STOP_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
else if (pEvent->hdr.event == HCI_LE_EXT_SCAN_ENABLE_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
switch (dmScanCb.scanState)
|
||||
{
|
||||
case DM_SCAN_STATE_STARTING:
|
||||
pEvent->hdr.event = DM_EXT_SCAN_START_IND;
|
||||
dmScanCb.scanState = pEvent->hdr.status == HCI_SUCCESS? DM_SCAN_STATE_SCANNING : DM_SCAN_STATE_IDLE;
|
||||
break;
|
||||
|
||||
case DM_SCAN_STATE_STOPPING:
|
||||
pEvent->hdr.event = DM_EXT_SCAN_STOP_IND;
|
||||
dmScanCb.scanState = pEvent->hdr.status == HCI_SUCCESS? DM_SCAN_STATE_IDLE : DM_SCAN_STATE_SCANNING;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Should never happen */
|
||||
WSF_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* pass scanning start/stop event to dev priv */
|
||||
dmDevPassEvtToDevPriv((pEvent->hdr.event == DM_EXT_SCAN_START_IND) ? \
|
||||
DM_DEV_PRIV_MSG_RPA_START : DM_DEV_PRIV_MSG_RPA_STOP, pEvent->hdr.event,
|
||||
0, 0);
|
||||
|
||||
/* call callback */
|
||||
(*dmCb.cback)((dmEvt_t *)pEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM scan event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmExtScanMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmScanAct[DM_MSG_MASK(pMsg->event)])((dmScanMsg_t *)pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM extended scanning.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmExtScanInit(void)
|
||||
{
|
||||
WsfTaskLock();
|
||||
|
||||
/* set function interface table */
|
||||
dmFcnIfTbl[DM_ID_SCAN] = (dmFcnIf_t *) &dmScanFcnIf;
|
||||
|
||||
/* initialize scan/sync modules */
|
||||
dmScanInit();
|
||||
dmSyncInit();
|
||||
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Whether DM scanning is in extended mode.
|
||||
*
|
||||
* \return TRUE if DM scanning is in extended mode. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmScanModeExt(void)
|
||||
{
|
||||
return (dmFcnIfTbl[DM_ID_SCAN] == (dmFcnIf_t *) &dmScanFcnIf) ? TRUE : FALSE;
|
||||
}
|
||||
Vendored
+336
@@ -0,0 +1,336 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager scan module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_scan.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_dev.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* action function table */
|
||||
static const dmScanAct_t dmScanAct[] =
|
||||
{
|
||||
dmScanActStart,
|
||||
dmScanActStop,
|
||||
dmScanActTimeout
|
||||
};
|
||||
|
||||
/* Component function interface */
|
||||
static const dmFcnIf_t dmScanFcnIf =
|
||||
{
|
||||
dmScanReset,
|
||||
dmScanHciHandler,
|
||||
dmScanMsgHandler
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start scanning action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmScanActStart(dmScanMsg_t *pMsg)
|
||||
{
|
||||
uint8_t phyIdx = DmScanPhyToIdx(HCI_SCAN_PHY_LE_1M_BIT);
|
||||
|
||||
if (dmScanCb.scanState == DM_SCAN_STATE_IDLE)
|
||||
{
|
||||
/* set scan parameters */
|
||||
HciLeSetScanParamCmd(pMsg->apiStart.scanType[phyIdx], dmScanCb.scanInterval[phyIdx],
|
||||
dmScanCb.scanWindow[phyIdx], DmLlAddrType(dmCb.scanAddrType),
|
||||
dmCb.scanFiltPolicy);
|
||||
|
||||
/* initialize scan result filtering */
|
||||
if (pMsg->apiStart.mode == DM_DISC_MODE_LIMITED)
|
||||
{
|
||||
dmScanCb.discFilter = DM_FLAG_LE_LIMITED_DISC;
|
||||
}
|
||||
else if (pMsg->apiStart.mode == DM_DISC_MODE_GENERAL)
|
||||
{
|
||||
dmScanCb.discFilter = DM_FLAG_LE_LIMITED_DISC | DM_FLAG_LE_GENERAL_DISC;
|
||||
}
|
||||
else
|
||||
{
|
||||
dmScanCb.discFilter = 0;
|
||||
}
|
||||
dmScanCb.filterNextScanRsp = FALSE;
|
||||
|
||||
/* enable scan */
|
||||
dmScanCb.scanDuration = pMsg->apiStart.duration;
|
||||
dmScanCb.scanState = DM_SCAN_STATE_STARTING;
|
||||
HciLeSetScanEnableCmd(TRUE, pMsg->apiStart.filterDup);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop scanning action function.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmScanActStop(dmScanMsg_t *pMsg)
|
||||
{
|
||||
if (dmScanCb.scanState == DM_SCAN_STATE_SCANNING)
|
||||
{
|
||||
/* disable scan */
|
||||
dmScanCb.scanState = DM_SCAN_STATE_STOPPING;
|
||||
HciLeSetScanEnableCmd(FALSE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an scan timeout.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmScanActTimeout(dmScanMsg_t *pMsg)
|
||||
{
|
||||
dmScanActStop(pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle an advertising report event from HCI.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmScanActHciReport(hciEvt_t *pEvent)
|
||||
{
|
||||
uint8_t *p;
|
||||
bool_t filtered = FALSE;
|
||||
|
||||
/* ignore if not scanning */
|
||||
if (dmScanCb.scanState == DM_SCAN_STATE_SCANNING)
|
||||
{
|
||||
/* if filtering results for limited or general discovery */
|
||||
if (dmScanCb.discFilter != 0)
|
||||
{
|
||||
/* if this is a scan response */
|
||||
if (pEvent->leAdvReport.eventType == DM_RPT_SCAN_RESPONSE)
|
||||
{
|
||||
/* check if filtering next scan response */
|
||||
if (dmScanCb.filterNextScanRsp)
|
||||
{
|
||||
filtered = TRUE;
|
||||
dmScanCb.filterNextScanRsp = FALSE;
|
||||
}
|
||||
}
|
||||
/* else it's an advertising response */
|
||||
else
|
||||
{
|
||||
/* find flags in advertising data */
|
||||
p = DmFindAdType(DM_ADV_TYPE_FLAGS, pEvent->leAdvReport.len, pEvent->leAdvReport.pData);
|
||||
if (p == NULL)
|
||||
{
|
||||
/* flags not found */
|
||||
filtered = TRUE;
|
||||
dmScanCb.filterNextScanRsp = TRUE;
|
||||
}
|
||||
/* else flags found; check them */
|
||||
else if ((p[DM_AD_DATA_IDX] & dmScanCb.discFilter) == 0)
|
||||
{
|
||||
/* flags do not match discovery mode */
|
||||
filtered = TRUE;
|
||||
dmScanCb.filterNextScanRsp = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!filtered)
|
||||
{
|
||||
pEvent->hdr.event = DM_SCAN_REPORT_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the scan module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmScanReset(void)
|
||||
{
|
||||
wsfMsgHdr_t scanStop;
|
||||
|
||||
/* if stopping scan or scanning */
|
||||
if ((dmScanCb.scanState == DM_SCAN_STATE_STOPPING) ||
|
||||
(dmScanCb.scanState == DM_SCAN_STATE_SCANNING))
|
||||
{
|
||||
/* stop scan timer */
|
||||
WsfTimerStop(&dmScanCb.scanTimer);
|
||||
|
||||
/* generate scan stop event */
|
||||
scanStop.event = DM_SCAN_STOP_IND;
|
||||
scanStop.status = HCI_SUCCESS;
|
||||
|
||||
/* call callback */
|
||||
(*dmCb.cback)((dmEvt_t *) &scanStop);
|
||||
|
||||
dmScanCb.scanState = DM_SCAN_STATE_IDLE;
|
||||
}
|
||||
|
||||
/* reset scan module */
|
||||
dmScanInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM scan HCI event handler.
|
||||
*
|
||||
* \param pEvent WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmScanHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
if (pEvent->hdr.event == HCI_LE_ADV_REPORT_CBACK_EVT)
|
||||
{
|
||||
dmScanActHciReport(pEvent);
|
||||
}
|
||||
else if (pEvent->hdr.event == HCI_LE_SCAN_ENABLE_CMD_CMPL_CBACK_EVT)
|
||||
{
|
||||
switch (dmScanCb.scanState)
|
||||
{
|
||||
case DM_SCAN_STATE_STARTING:
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
/* start scan timer if applicable */
|
||||
if (dmScanCb.scanDuration > 0)
|
||||
{
|
||||
dmScanCb.scanTimer.msg.event = DM_SCAN_MSG_TIMEOUT;
|
||||
WsfTimerStartMs(&dmScanCb.scanTimer, dmScanCb.scanDuration);
|
||||
}
|
||||
|
||||
dmScanCb.scanState = DM_SCAN_STATE_SCANNING;
|
||||
}
|
||||
else
|
||||
{
|
||||
dmScanCb.scanState = DM_SCAN_STATE_IDLE;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_SCAN_START_IND;
|
||||
break;
|
||||
|
||||
case DM_SCAN_STATE_STOPPING:
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
/* stop scan timer */
|
||||
WsfTimerStop(&dmScanCb.scanTimer);
|
||||
dmScanCb.scanState = DM_SCAN_STATE_IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
dmScanCb.scanState = DM_SCAN_STATE_SCANNING;
|
||||
}
|
||||
|
||||
pEvent->hdr.event = DM_SCAN_STOP_IND;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
/* Should never happen */
|
||||
WSF_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
/* pass scanning start/stop event to dev priv */
|
||||
dmDevPassEvtToDevPriv((pEvent->hdr.event == DM_SCAN_START_IND) ? \
|
||||
DM_DEV_PRIV_MSG_RPA_START : DM_DEV_PRIV_MSG_RPA_STOP, pEvent->hdr.event,
|
||||
0, 0);
|
||||
|
||||
/* call callback */
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM scan event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmScanMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* execute action function */
|
||||
(*dmScanAct[DM_MSG_MASK(pMsg->event)])((dmScanMsg_t *)pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM legacy scanning.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmScanInit(void)
|
||||
{
|
||||
WsfTaskLock();
|
||||
|
||||
/* set function interface table */
|
||||
dmFcnIfTbl[DM_ID_SCAN] = (dmFcnIf_t *) &dmScanFcnIf;
|
||||
|
||||
/* initialize scan module */
|
||||
dmScanInit();
|
||||
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Whether DM scanning is in legacy mode.
|
||||
*
|
||||
* \return TRUE if DM scanning is in legacy mode. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t DmScanModeLeg(void)
|
||||
{
|
||||
return (dmFcnIfTbl[DM_ID_SCAN] == (dmFcnIf_t *) &dmScanFcnIf) ? TRUE : FALSE;
|
||||
}
|
||||
+395
@@ -0,0 +1,395 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM security module.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "sec_api.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/calc128.h"
|
||||
#include "smp_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_sec.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
dmSecCb_t dmSecCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Component function interface */
|
||||
static const dmFcnIf_t dmSecFcnIf =
|
||||
{
|
||||
dmSecReset,
|
||||
dmSecHciHandler,
|
||||
(dmMsgHandler_t) dmSecMsgHandler
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Utility function for API encrypt request or ltk response.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param status HCI_SUCCESS if LTK available.
|
||||
* \param *pLTK Pointer to LTK parameter structure.
|
||||
* \param event DM handler event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSecApiLtkMsg(dmConnId_t connId, uint8_t status, dmSecLtk_t *pLtk, uint8_t event)
|
||||
{
|
||||
dmSecApiEncryptReq_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmSecApiEncryptReq_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = event;
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->hdr.status = status;
|
||||
memcpy(&pMsg->ltk, pLtk, sizeof(dmSecLtk_t));
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM dev HCI event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSecHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
uint8_t *pKey;
|
||||
dmSecEncryptIndEvt_t encryptInd;
|
||||
uint8_t secLevel;
|
||||
|
||||
if ((pCcb = dmConnCcbByHandle(pEvent->hdr.param)) != NULL)
|
||||
{
|
||||
if (pEvent->hdr.event == HCI_LE_LTK_REQ_CBACK_EVT)
|
||||
{
|
||||
/* if ediv and rand are zero then check if STK is available from SMP */
|
||||
if ((pEvent->leLtkReq.encDiversifier == 0) &&
|
||||
(memcmp(pEvent->leLtkReq.randNum, calc128Zeros, HCI_RAND_LEN) == 0))
|
||||
{
|
||||
if ((pKey = SmpDmGetStk(pCcb->connId, &secLevel)) != NULL)
|
||||
{
|
||||
/* store security level */
|
||||
pCcb->tmpSecLevel = secLevel;
|
||||
|
||||
/* not using LTK */
|
||||
pCcb->usingLtk = FALSE;
|
||||
|
||||
/* provide key to HCI */
|
||||
HciLeLtkReqReplCmd(pEvent->hdr.param, pKey);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* call callback to get key from app */
|
||||
|
||||
/* set connection busy */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_DM_ENC, DM_CONN_BUSY);
|
||||
|
||||
/* using LTK */
|
||||
pCcb->usingLtk = TRUE;
|
||||
|
||||
/* use the header from the encryptInd struct for efficiency */
|
||||
pEvent->hdr.param = pCcb->connId;
|
||||
pEvent->hdr.event = DM_SEC_LTK_REQ_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
}
|
||||
else if (pEvent->hdr.event == HCI_ENC_KEY_REFRESH_CMPL_CBACK_EVT ||
|
||||
pEvent->hdr.event == HCI_ENC_CHANGE_CBACK_EVT)
|
||||
{
|
||||
/* set connection idle */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_DM_ENC, DM_CONN_IDLE);
|
||||
|
||||
encryptInd.hdr.param = pCcb->connId;
|
||||
encryptInd.hdr.status = pEvent->hdr.status;
|
||||
if (encryptInd.hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
encryptInd.hdr.event = DM_SEC_ENCRYPT_IND;
|
||||
|
||||
/* update security level of connection */
|
||||
pCcb->secLevel = pCcb->tmpSecLevel;
|
||||
|
||||
/* set LTK flag */
|
||||
encryptInd.usingLtk = pCcb->usingLtk;
|
||||
}
|
||||
else
|
||||
{
|
||||
encryptInd.hdr.event = DM_SEC_ENCRYPT_FAIL_IND;
|
||||
}
|
||||
|
||||
/* call callback before passing to SMP */
|
||||
DmSmpCbackExec((dmEvt_t *) &encryptInd);
|
||||
|
||||
/* pass to SMP */
|
||||
encryptInd.hdr.param = pCcb->connId;
|
||||
encryptInd.hdr.status = pEvent->hdr.status;
|
||||
SmpDmEncryptInd((wsfMsgHdr_t *) &encryptInd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM dev event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSecMsgHandler(dmSecMsg_t *pMsg)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
/* look up ccb */
|
||||
if ((pCcb = dmConnCcbById((dmConnId_t) pMsg->hdr.param)) != NULL)
|
||||
{
|
||||
/* process API encrypt req */
|
||||
switch (pMsg->hdr.event)
|
||||
{
|
||||
case DM_SEC_MSG_API_ENCRYPT_REQ:
|
||||
/* set connection busy */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_DM_ENC, DM_CONN_BUSY);
|
||||
|
||||
/* store security level */
|
||||
pCcb->tmpSecLevel = pMsg->encryptReq.secLevel;
|
||||
|
||||
/* using LTK */
|
||||
pCcb->usingLtk = TRUE;
|
||||
|
||||
/* start encryption */
|
||||
HciLeStartEncryptionCmd(pCcb->handle, pMsg->encryptReq.ltk.rand,
|
||||
pMsg->encryptReq.ltk.ediv, pMsg->encryptReq.ltk.key);
|
||||
|
||||
break;
|
||||
|
||||
case DM_SEC_MSG_API_LTK_RSP:
|
||||
/* if key found */
|
||||
if (pMsg->ltkRsp.keyFound)
|
||||
{
|
||||
/* store security level */
|
||||
pCcb->tmpSecLevel = pMsg->ltkRsp.secLevel;
|
||||
|
||||
/* provide key to HCI */
|
||||
HciLeLtkReqReplCmd(pCcb->handle, pMsg->ltkRsp.key);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* key not found; set connection idle */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_DM_ENC, DM_CONN_IDLE);
|
||||
|
||||
HciLeLtkReqNegReplCmd(pCcb->handle);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For internal use only. Execute DM callback from SMP procedures.
|
||||
*
|
||||
* \param pDmEvt Pointer to callback event data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSmpCbackExec(dmEvt_t *pDmEvt)
|
||||
{
|
||||
/* certain messages need to get to ATT */
|
||||
if (pDmEvt->hdr.event == DM_SEC_PAIR_CMPL_IND ||
|
||||
pDmEvt->hdr.event == DM_SEC_ENCRYPT_IND)
|
||||
{
|
||||
if (dmConnCb.connCback[DM_CLIENT_ID_ATT] != NULL)
|
||||
{
|
||||
(*dmConnCb.connCback[DM_CLIENT_ID_ATT])(pDmEvt);
|
||||
}
|
||||
}
|
||||
|
||||
/* execute DM client callback */
|
||||
(*dmCb.cback)(pDmEvt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called to cancel the pairing process.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param reason Failure reason.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecCancelReq(dmConnId_t connId, uint8_t reason)
|
||||
{
|
||||
wsfMsgHdr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pMsg->event = SMP_MSG_API_CANCEL_REQ;
|
||||
pMsg->param = connId;
|
||||
pMsg->status = reason;
|
||||
|
||||
/* note we're sending this to SMP */
|
||||
SmpDmMsgSend((smpDmMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called in response to a DM_SEC_AUTH_REQ_IND event to provide
|
||||
* PIN or OOB data during pairing.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param authDataLen Length of PIN or OOB data.
|
||||
* \param pAuthData pointer to PIN or OOB data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecAuthRsp(dmConnId_t connId, uint8_t authDataLen, uint8_t *pAuthData)
|
||||
{
|
||||
smpDmAuthRsp_t *pMsg;
|
||||
|
||||
WSF_ASSERT(authDataLen <= SMP_OOB_LEN);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(smpDmAuthRsp_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = SMP_MSG_API_AUTH_RSP;
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->authDataLen = authDataLen;
|
||||
|
||||
if (pAuthData != NULL)
|
||||
{
|
||||
memcpy(pMsg->authData, pAuthData, authDataLen);
|
||||
}
|
||||
|
||||
/* note we're sending this to SMP */
|
||||
SmpDmMsgSend((smpDmMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM security.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecInit(void)
|
||||
{
|
||||
dmFcnIfTbl[DM_ID_SEC] = (dmFcnIf_t *) &dmSecFcnIf;
|
||||
|
||||
dmSecCb.pCsrk = dmSecCb.pIrk = (uint8_t *) calc128Zeros;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function sets the local CSRK used by the device.
|
||||
*
|
||||
* \param pCsrk Pointer to CSRK.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecSetLocalCsrk(uint8_t *pCsrk)
|
||||
{
|
||||
WsfTaskLock();
|
||||
dmSecCb.pCsrk = pCsrk;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function sets the local IRK used by the device.
|
||||
*
|
||||
* \param pCsrk Pointer to IRK.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecSetLocalIrk(uint8_t *pIrk)
|
||||
{
|
||||
WsfTaskLock();
|
||||
dmSecCb.pIrk = pIrk;
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function gets the local CSRK used by the device.
|
||||
*
|
||||
* \return Pointer to CSRK.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *DmSecGetLocalCsrk(void)
|
||||
{
|
||||
return dmSecCb.pCsrk;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function gets the local IRK used by the device.
|
||||
*
|
||||
* \return Pointer to IRK.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *DmSecGetLocalIrk(void)
|
||||
{
|
||||
return dmSecCb.pIrk;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the sec module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSecReset(void)
|
||||
{
|
||||
/* initialize smp database */
|
||||
SmpDbInit();
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM security module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DM_SEC_H
|
||||
#define DM_SEC_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
#include "smp_api.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* DM sec event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_SEC_MSG_API_ENCRYPT_REQ = DM_MSG_START(DM_ID_SEC),
|
||||
DM_SEC_MSG_API_LTK_RSP
|
||||
};
|
||||
|
||||
/* DM lesc sec event handler messages */
|
||||
enum
|
||||
{
|
||||
DM_SEC_MSG_CALC_OOB_CNF = DM_MSG_START(DM_ID_LESC),
|
||||
DM_SEC_MSG_ECC_KEY_CNF
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Data type for DM_SEC_MSG_API_ENCRYPT_REQ */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmSecLtk_t ltk;
|
||||
uint8_t secLevel;
|
||||
} dmSecApiEncryptReq_t;
|
||||
|
||||
/* Data type for DM_SEC_MSG_API_LTK_RSP */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
uint8_t key[SMP_KEY_LEN];
|
||||
bool_t keyFound;
|
||||
uint8_t secLevel;
|
||||
} dmSecApiLtkRsp_t;
|
||||
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmSecApiEncryptReq_t encryptReq;
|
||||
dmSecApiLtkRsp_t ltkRsp;
|
||||
} dmSecMsg_t;
|
||||
|
||||
/* Security control block type */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t *pIrk;
|
||||
uint8_t *pCsrk;
|
||||
} dmSecCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
extern dmSecCb_t dmSecCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* component interface */
|
||||
void dmSecHciHandler(hciEvt_t *pEvent);
|
||||
void dmSecMsgHandler(dmSecMsg_t *pMsg);
|
||||
void dmSecReset(void);
|
||||
|
||||
/* component interface */
|
||||
void dmSecLescMsgHandler(dmSecMsg_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DM_SEC_H */
|
||||
Vendored
+328
@@ -0,0 +1,328 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM security module for LE Secure Connections.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "sec_api.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/calc128.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_sc_main.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_sec.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Component function interface */
|
||||
static const dmFcnIf_t dmSecLescFcnIf =
|
||||
{
|
||||
dmEmptyReset,
|
||||
(dmHciHandler_t) dmEmptyHandler,
|
||||
(dmMsgHandler_t) dmSecLescMsgHandler
|
||||
};
|
||||
|
||||
/* OOB random value */
|
||||
static uint8_t *dmSecOobRand;
|
||||
|
||||
/* Local device's ECC Key */
|
||||
static secEccKey_t localEccKey;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM dev event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSecLescMsgHandler(dmSecMsg_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.event == DM_SEC_MSG_ECC_KEY_CNF)
|
||||
{
|
||||
pMsg->hdr.event = DM_SEC_ECC_KEY_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
else if (pMsg->hdr.event == DM_SEC_MSG_CALC_OOB_CNF)
|
||||
{
|
||||
dmSecOobCalcIndEvt_t oobEvt;
|
||||
secCmacMsg_t *pCmacMsg = (secCmacMsg_t *) pMsg;
|
||||
|
||||
WsfBufFree(pCmacMsg->pPlainText);
|
||||
|
||||
/* Notify the application of the local confirm and random values */
|
||||
oobEvt.hdr.event = DM_SEC_CALC_OOB_IND;
|
||||
oobEvt.hdr.status = HCI_SUCCESS;
|
||||
|
||||
Calc128Cpy(oobEvt.confirm, ((secAes_t *) pMsg)->pCiphertext);
|
||||
Calc128Cpy(oobEvt.random, dmSecOobRand);
|
||||
|
||||
WsfBufFree(dmSecOobRand);
|
||||
|
||||
(*dmCb.cback)((dmEvt_t *) &oobEvt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function sends keypress cmd messages to the peer.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecKeypressReq(dmConnId_t connId, uint8_t keypressType)
|
||||
{
|
||||
smpDmKeypress_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(smpDmKeypress_t))) != NULL)
|
||||
{
|
||||
/* Execution an an SMP state machine event to send the keypress to the peer device */
|
||||
pMsg->keypress = keypressType;
|
||||
pMsg->hdr.param = connId;
|
||||
|
||||
pMsg->hdr.event = SMP_MSG_API_USER_KEYPRESS;
|
||||
|
||||
SmpDmMsgSend((smpDmMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function records the peer random value and peer confirm value exchanged via
|
||||
* out-of-band (OOB) methods.
|
||||
*
|
||||
* \param connId ID of the connection
|
||||
* \param pCfg OOB Configuration
|
||||
*
|
||||
* \return Pointer to IRK.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecSetOob(dmConnId_t connId, dmSecLescOobCfg_t *pCfg)
|
||||
{
|
||||
/* Update the Security Manager control structure with random and confirm values from the peer */
|
||||
SmpScSetOobCfg(connId, pCfg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function calcualtes the local random and confirm values used in LESC OOB pairing.
|
||||
* The operation's result is posted as a DM_SEC_CALC_OOB_IND event to the application's DM
|
||||
* callback handler. The local rand and confirm values are exchanged with the peer via
|
||||
* out-of-band (OOB) methods and passed into the DmSecSetOob after DM_CONN_OPEN_IND.
|
||||
*
|
||||
* \param pRand Random value used in calculation.
|
||||
* \param pPubKeyX X component of the local public key.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecCalcOobReq(uint8_t *pRand, uint8_t *pPubKeyX)
|
||||
{
|
||||
uint8_t *pCmacText;
|
||||
dmSecOobCalcIndEvt_t pEvt;
|
||||
|
||||
SMP_TRACE_256("DmSecCalcOobReq Key", pPubKeyX);
|
||||
SMP_TRACE_128("DmSecCalcOobReq Rand", pRand);
|
||||
|
||||
if ((dmSecOobRand = WsfBufAlloc(SMP_RAND_LEN)) != NULL)
|
||||
{
|
||||
/* Store the random value */
|
||||
Calc128Cpy(dmSecOobRand, pRand);
|
||||
|
||||
/* Cnf = f4(PKx, PKx, rand, 0x00) where f4(U, V, x, Z) = AES-CMACx (U || V || Z) */
|
||||
if ((pCmacText = (uint8_t*) WsfBufAlloc(SMP_F4_TEXT_LEN)) != NULL)
|
||||
{
|
||||
uint8_t *pCatBuf = pCmacText;
|
||||
|
||||
/* Concatinate PKx, PKx, 0x00 */
|
||||
pCatBuf = SmpScCat(pCatBuf, pPubKeyX, SMP_PUB_KEY_LEN);
|
||||
pCatBuf = SmpScCat(pCatBuf, pPubKeyX, SMP_PUB_KEY_LEN);
|
||||
*pCatBuf = 0;
|
||||
|
||||
/* Execute CMAC with rand as the key */
|
||||
if (SecCmac(dmSecOobRand, pCmacText, SMP_F4_TEXT_LEN, dmCb.handlerId, 0, DM_SEC_MSG_CALC_OOB_CNF))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WsfBufFree(pCmacText);
|
||||
}
|
||||
|
||||
WsfBufFree(dmSecOobRand);
|
||||
}
|
||||
|
||||
/* Notify the application of a failure */
|
||||
memset(&pEvt, 0, sizeof(dmSecOobCalcIndEvt_t));
|
||||
|
||||
pEvt.hdr.event = DM_SEC_CALC_OOB_IND;
|
||||
pEvt.hdr.status = HCI_ERR_MEMORY_EXCEEDED;
|
||||
|
||||
(*dmCb.cback)((dmEvt_t *) &pEvt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function generates an ECC key for use with LESC security. When the calculation
|
||||
* completes, the result is posted as a DM_SEC_ECC_KEY_IND event to the application's
|
||||
* DM callback handler.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecGenerateEccKeyReq()
|
||||
{
|
||||
SecEccGenKey(dmCb.handlerId, 0, DM_SEC_MSG_ECC_KEY_CNF);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function sets the ECC key for use with LESC security to standard debug keys values.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecSetDebugEccKey()
|
||||
{
|
||||
const uint8_t privateKey[] = {0x3f, 0x49, 0xf6, 0xd4, 0xa3, 0xc5, 0x5f, 0x38,
|
||||
0x74, 0xc9, 0xb3, 0xe3, 0xd2, 0x10, 0x3f, 0x50,
|
||||
0x4a, 0xff, 0x60, 0x7b, 0xeb, 0x40, 0xb7, 0x99,
|
||||
0x58, 0x99, 0xb8, 0xa6, 0xcd, 0x3c, 0x1a, 0xbd};
|
||||
|
||||
const uint8_t publicKeyX[] = {0x20, 0xb0, 0x03, 0xd2, 0xf2, 0x97, 0xbe, 0x2c,
|
||||
0x5e, 0x2c, 0x83, 0xa7, 0xe9, 0xf9, 0xa5, 0xb9,
|
||||
0xef, 0xf4, 0x91, 0x11, 0xac, 0xf4, 0xfd, 0xdb,
|
||||
0xcc, 0x03, 0x01, 0x48, 0x0e, 0x35, 0x9d, 0xe6};
|
||||
|
||||
const uint8_t publicKeyY[] = {0xdc, 0x80, 0x9c, 0x49, 0x65, 0x2a, 0xeb, 0x6d,
|
||||
0x63, 0x32, 0x9a, 0xbf, 0x5a, 0x52, 0x15, 0x5c,
|
||||
0x76, 0x63, 0x45, 0xc2, 0x8f, 0xed, 0x30, 0x24,
|
||||
0x74, 0x1c, 0x8e, 0xd0, 0x15, 0x89, 0xd2, 0x8b};
|
||||
|
||||
SMP_TRACE_INFO0("Debug LESC Keys Enabled");
|
||||
|
||||
memcpy(localEccKey.privKey, privateKey, SEC_ECC_KEY_LEN);
|
||||
memcpy(localEccKey.pubKey_x, publicKeyX, SEC_ECC_KEY_LEN);
|
||||
memcpy(localEccKey.pubKey_y, publicKeyY, SEC_ECC_KEY_LEN);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function sets the local ECC key for use with LESC security. The key can be
|
||||
* generated using DmSecGenerateEccKeyReq or the key could originate from an application
|
||||
* specific source (e.g. prestored in non-volatile memory, generated with specialized
|
||||
* hardware, etc).
|
||||
*
|
||||
* \param pKey Pointer to local ECC key
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecSetEccKey(secEccKey_t *pKey)
|
||||
{
|
||||
memcpy(&localEccKey, pKey, sizeof(secEccKey_t));
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function gets the local ECC key for use with LESC security.
|
||||
*
|
||||
* \return Pointer to local ECC key.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
secEccKey_t *DmSecGetEccKey(void)
|
||||
{
|
||||
return &localEccKey;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by the application in response to a DM_SEC_COMPARE_IND event.
|
||||
* The valid parameter indicates if the compare value of the DM_SEC_COMPARE_IND was valid.
|
||||
*
|
||||
* \param connId ID of the connection
|
||||
* \param valid TRUE if compare value was valid
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecCompareRsp(dmConnId_t connId, bool_t valid)
|
||||
{
|
||||
smpDmMsg_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(smpDmMsg_t))) != NULL)
|
||||
{
|
||||
/* Execution an an SMP state machine event to send the compare response */
|
||||
pMsg->hdr.param = connId;
|
||||
|
||||
if (valid)
|
||||
{
|
||||
pMsg->hdr.event = SMP_MSG_API_USER_CONFIRM;
|
||||
}
|
||||
else
|
||||
{
|
||||
SmpScGetCancelMsgWithReattempt(connId, (wsfMsgHdr_t *) pMsg, SMP_ERR_NUMERIC_COMPARISON);
|
||||
}
|
||||
|
||||
SmpDmMsgSend((smpDmMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function returns the 6-digit compare value for the specified 128-bit confirm value.
|
||||
*
|
||||
* \param pConfirm Pointer to 128-bit comfirm value.
|
||||
*
|
||||
* \return Six-digit compare value.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint32_t DmSecGetCompareValue(uint8_t *pConfirm)
|
||||
{
|
||||
uint32_t compare = ((uint32_t) pConfirm[15] +
|
||||
((uint32_t) pConfirm[14] << 8) +
|
||||
((uint32_t) pConfirm[13] << 16) +
|
||||
((uint32_t) pConfirm[12] << 24));
|
||||
|
||||
/* return the least significant six digits */
|
||||
return compare % 1000000;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize DM LE Secure Connections security.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecLescInit(void)
|
||||
{
|
||||
dmFcnIfTbl[DM_ID_LESC] = (dmFcnIf_t *) &dmSecLescFcnIf;
|
||||
}
|
||||
Vendored
+121
@@ -0,0 +1,121 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM security module for master.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/calc128.h"
|
||||
#include "smp_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_sec.h"
|
||||
#include "dm_conn.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For internal use only. This function is called by SMP to request encryption.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param secLevel Security level of pairing when key was exchanged.
|
||||
* \param pKey Pointer to key.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSmpEncryptReq(dmConnId_t connId, uint8_t secLevel, uint8_t *pKey)
|
||||
{
|
||||
dmConnCcb_t *pCcb;
|
||||
|
||||
if ((pCcb = dmConnCcbById(connId)) != NULL)
|
||||
{
|
||||
/* store security level */
|
||||
pCcb->tmpSecLevel = secLevel;
|
||||
|
||||
/* not using LTK */
|
||||
pCcb->usingLtk = FALSE;
|
||||
|
||||
/* start encryption; note EDIV and RAND are zero */
|
||||
HciLeStartEncryptionCmd(pCcb->handle, (uint8_t *) calc128Zeros, 0, pKey);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by a master device to initiate pairing.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param oob Out-of-band pairing data present or not present.
|
||||
* \param auth Authentication and bonding flags.
|
||||
* \param iKeyDist Initiator key distribution flags.
|
||||
* \param rKeyDist Responder key distribution flags.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecPairReq(dmConnId_t connId, uint8_t oob, uint8_t auth, uint8_t iKeyDist, uint8_t rKeyDist)
|
||||
{
|
||||
smpDmPair_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(smpDmPair_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = SMP_MSG_API_PAIR_REQ;
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->oob = oob;
|
||||
pMsg->auth = auth;
|
||||
|
||||
/* clear any erroneous key dist bits set by app */
|
||||
pMsg->iKeyDist = iKeyDist & SMP_KEY_DIST_MASK;
|
||||
pMsg->rKeyDist = rKeyDist & SMP_KEY_DIST_MASK;
|
||||
|
||||
/* note we're sending this to SMP */
|
||||
SmpDmMsgSend((smpDmMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by a master device to initiate link encryption.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param secLevel Security level of pairing when key was exchanged.
|
||||
* \param pLtk Pointer to LTK parameter structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecEncryptReq(dmConnId_t connId, uint8_t secLevel, dmSecLtk_t *pLtk)
|
||||
{
|
||||
dmSecApiEncryptReq_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmSecApiEncryptReq_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_SEC_MSG_API_ENCRYPT_REQ;
|
||||
pMsg->hdr.param = connId;
|
||||
memcpy(&pMsg->ltk, pLtk, sizeof(dmSecLtk_t));
|
||||
pMsg->secLevel = secLevel;
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+122
@@ -0,0 +1,122 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief DM security module for slave.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/calc128.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_sec.h"
|
||||
#include "dm_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by a slave device to proceed with pairing after a
|
||||
* DM_SEC_PAIR_IND event is received.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param oob Out-of-band pairing data present or not present.
|
||||
* \param auth Authentication and bonding flags.
|
||||
* \param iKeyDist Initiator key distribution flags.
|
||||
* \param rKeyDist Responder key distribution flags.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecPairRsp(dmConnId_t connId, uint8_t oob, uint8_t auth, uint8_t iKeyDist, uint8_t rKeyDist)
|
||||
{
|
||||
smpDmPair_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(smpDmPair_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = SMP_MSG_API_PAIR_RSP;
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->oob = oob;
|
||||
pMsg->auth = auth;
|
||||
|
||||
/* clear any erroneous key dist bits set by app */
|
||||
pMsg->iKeyDist = iKeyDist & SMP_KEY_DIST_MASK;
|
||||
pMsg->rKeyDist = rKeyDist & SMP_KEY_DIST_MASK;
|
||||
|
||||
/* note we're sending this to SMP */
|
||||
SmpDmMsgSend((smpDmMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by a slave device to request that the master initiates
|
||||
* pairing or link encryption.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param auth Authentication flags.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecSlaveReq(dmConnId_t connId, uint8_t auth)
|
||||
{
|
||||
smpDmSecurityReq_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(smpDmSecurityReq_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = SMP_MSG_API_SECURITY_REQ;
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->auth = auth;
|
||||
|
||||
/* note we're sending this to SMP */
|
||||
SmpDmMsgSend((smpDmMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by a slave in response to a DM_SEC_LTK_REQ_IND event
|
||||
* to provide the key used for encryption.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param keyFound TRUE if key found.
|
||||
* \param secLevel Security level of pairing when key was exchanged.
|
||||
* \param *pKey Pointer to the key, if found.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSecLtkRsp(dmConnId_t connId, bool_t keyFound, uint8_t secLevel, uint8_t *pKey)
|
||||
{
|
||||
dmSecApiLtkRsp_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmSecApiLtkRsp_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.event = DM_SEC_MSG_API_LTK_RSP;
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->keyFound = keyFound;
|
||||
pMsg->secLevel = secLevel;
|
||||
if (keyFound)
|
||||
{
|
||||
Calc128Cpy(pMsg->key, pKey);
|
||||
}
|
||||
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
Vendored
+835
@@ -0,0 +1,835 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Device manager periodic advertising synchronization management and state machine
|
||||
* module.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_main.h"
|
||||
#include "dm_scan.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! DM sync event handler messages for state machine */
|
||||
enum
|
||||
{
|
||||
/* messages from API */
|
||||
DM_SYNC_MSG_API_START = DM_MSG_START(DM_ID_SYNC), /*!< Start Sync */
|
||||
DM_SYNC_MSG_API_STOP, /*!< Stop Sync */
|
||||
|
||||
/* messages from HCI */
|
||||
DM_SYNC_MSG_HCI_LE_SYNC_EST_FAIL, /*!< HCI LE Sync Establishment Failed */
|
||||
DM_SYNC_MSG_HCI_LE_SYNC_EST, /*!< HCI LE Sync Established */
|
||||
DM_SYNC_MSG_HCI_LE_SYNC_LOST, /*!< HCI LE Sync Lost */
|
||||
DM_SYNC_MSG_HCI_LE_SYNC_TRSF_EST_FAIL, /*!< HCI LE Sync Transfer Establishment Failed */
|
||||
DM_SYNC_MSG_HCI_LE_SYNC_TRSF_EST /*!< HCI LE Sync Transfer Established */
|
||||
};
|
||||
|
||||
/*! State machine states */
|
||||
enum
|
||||
{
|
||||
DM_SYNC_SM_ST_IDLE, /*!< Idle State */
|
||||
DM_SYNC_SM_ST_SYNCING, /*!< Synchronizing State */
|
||||
DM_SYNC_SM_ST_SYNCED, /*!< Synced State */
|
||||
DM_SYNC_SM_ST_DESYNCING, /*!< Desynchronizing State */
|
||||
|
||||
DM_SYNC_SM_NUM_STATES
|
||||
};
|
||||
|
||||
/*! State machine actions */
|
||||
enum
|
||||
{
|
||||
DM_SYNC_SM_ACT_NONE, /*!< No Action */
|
||||
DM_SYNC_SM_ACT_START, /*!< Process Start Sync */
|
||||
DM_SYNC_SM_ACT_STOP, /*!< Process Stop Sync */
|
||||
DM_SYNC_SM_ACT_CANCEL_START, /*!< Process Cancel Start Sync */
|
||||
DM_SYNC_SM_ACT_SYNC_EST, /*!< Process Sync Established */
|
||||
DM_SYNC_SM_ACT_SYNC_EST_FAILED, /*!< Process Sync Establishment Failed */
|
||||
DM_SYNC_SM_ACT_SYNC_LOST, /*!< Process Sync Lost */
|
||||
DM_SYNC_SM_ACT_SYNC_TRSF_EST, /*!< Process Sync Transfer Established */
|
||||
DM_SYNC_SM_ACT_SYNC_TRSF_EST_FAILED /*!< Process Sync Transfer Establishment Failed */
|
||||
};
|
||||
|
||||
/*! Column position of next state */
|
||||
#define DM_SYNC_NEXT_STATE 0
|
||||
|
||||
/*! Column position of action */
|
||||
#define DM_SYNC_ACTION 1
|
||||
|
||||
/*! Number of columns in the state machine state tables */
|
||||
#define DM_SYNC_NUM_COLS 2
|
||||
|
||||
/*! Number of messages */
|
||||
#define DM_SYNC_NUM_MSGS (DM_SYNC_MSG_HCI_LE_SYNC_TRSF_EST - DM_SYNC_MSG_API_START + 1)
|
||||
|
||||
/*! Translate HCI event to state machine message */
|
||||
#define DM_SYNC_HCI_EVT_2_MSG(evt) (DM_SYNC_MSG_HCI_LE_SYNC_LOST - HCI_LE_PER_ADV_SYNC_LOST_CBACK_EVT + (evt))
|
||||
|
||||
/*! Uninitialized HCI sync handle */
|
||||
#define DM_SYNC_HCI_HANDLE_NONE 0xFFFF
|
||||
|
||||
/*! Action function */
|
||||
typedef void (*dmSyncAct_t)(dmSyncCb_t *pCcb, dmSyncMsg_t *pMsg);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! DM Sync state machine state tables */
|
||||
static const uint8_t dmSyncStateTbl[DM_SYNC_SM_NUM_STATES][DM_SYNC_NUM_MSGS][DM_SYNC_NUM_COLS] =
|
||||
{
|
||||
/* Idle state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_START */ {DM_SYNC_SM_ST_SYNCING, DM_SYNC_SM_ACT_START},
|
||||
/* API_STOP */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_EST_FAIL */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_EST */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_LOST */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_TRSF_EST_FAIL */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_TRSF_EST_FAILED},
|
||||
/* HCI_LE_SYNC_TRSF_EST */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_SYNC_TRSF_EST}
|
||||
},
|
||||
/* Syncing state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_START */ {DM_SYNC_SM_ST_SYNCING, DM_SYNC_SM_ACT_NONE},
|
||||
/* API_STOP */ {DM_SYNC_SM_ST_DESYNCING, DM_SYNC_SM_ACT_CANCEL_START},
|
||||
/* HCI_LE_SYNC_EST_FAIL */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_EST_FAILED},
|
||||
/* HCI_LE_SYNC_EST */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_SYNC_EST},
|
||||
/* HCI_LE_SYNC_LOST */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_EST_FAILED},
|
||||
/* HCI_LE_SYNC_TRSF_EST_FAIL */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_TRSF_EST_FAILED},
|
||||
/* HCI_LE_SYNC_TRSF_EST */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_SYNC_TRSF_EST},
|
||||
},
|
||||
/* Synced state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_START */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_NONE},
|
||||
/* API_STOP */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_STOP},
|
||||
/* HCI_LE_SYNC_EST_FAIL */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_EST */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_LOST */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_LOST},
|
||||
/* HCI_LE_SYNC_TRSF_EST_FAIL */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_TRSF_EST */ {DM_SYNC_SM_ST_SYNCED, DM_SYNC_SM_ACT_NONE},
|
||||
},
|
||||
/* Desyncing state */
|
||||
{
|
||||
/* Event Next state Action */
|
||||
/* API_START */ {DM_SYNC_SM_ST_DESYNCING, DM_SYNC_SM_ACT_NONE},
|
||||
/* API_STOP */ {DM_SYNC_SM_ST_DESYNCING, DM_SYNC_SM_ACT_NONE},
|
||||
/* HCI_LE_SYNC_EST_FAIL */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_EST_FAILED},
|
||||
/* HCI_LE_SYNC_EST */ {DM_SYNC_SM_ST_DESYNCING, DM_SYNC_SM_ACT_STOP},
|
||||
/* HCI_LE_SYNC_LOST */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_LOST},
|
||||
/* HCI_LE_SYNC_TRSF_EST_FAIL */ {DM_SYNC_SM_ST_IDLE, DM_SYNC_SM_ACT_SYNC_TRSF_EST_FAILED},
|
||||
/* HCI_LE_SYNC_TRSF_EST */ {DM_SYNC_SM_ST_DESYNCING, DM_SYNC_SM_ACT_STOP},
|
||||
}
|
||||
};
|
||||
|
||||
/*! DM Sync action function table */
|
||||
static const dmSyncAct_t dmSyncAct[] =
|
||||
{
|
||||
dmSyncSmActNone,
|
||||
dmSyncSmActStart,
|
||||
dmSyncSmActStop,
|
||||
dmSyncSmActCancelStart,
|
||||
dmSyncSmActSyncEst,
|
||||
dmSyncSmActSyncEstFailed,
|
||||
dmSyncSmActSyncLost,
|
||||
dmSyncSmActSyncTrsfEst,
|
||||
dmSyncSmActSyncTrsfEstFailed
|
||||
};
|
||||
|
||||
/*! DM Sync component function interface */
|
||||
static const dmFcnIf_t dmSyncFcnIf =
|
||||
{
|
||||
dmSyncReset,
|
||||
dmSyncHciHandler,
|
||||
dmSyncMsgHandler
|
||||
};
|
||||
|
||||
/*! DM Periodic advertising sync control block */
|
||||
static dmSyncCb_t dmSyncCb[DM_SYNC_MAX];
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the SCB with particular state conditions.
|
||||
*
|
||||
* \return Pointer to SCB or NULL if failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static dmSyncCb_t *dmSyncCmplState(void)
|
||||
{
|
||||
dmSyncCb_t *pScb = dmSyncCb;
|
||||
uint8_t i;
|
||||
|
||||
/* if there's a scb in accepting state */
|
||||
for (i = DM_SYNC_MAX; i > 0; i--, pScb++)
|
||||
{
|
||||
/* look for sync in desyncing state, cancelled sync */
|
||||
if (pScb->inUse &&
|
||||
(pScb->state == DM_SYNC_SM_ST_DESYNCING) &&
|
||||
(pScb->handle == DM_SYNC_HCI_HANDLE_NONE))
|
||||
{
|
||||
DM_TRACE_INFO1("dmSyncCmplState %d", pScb->syncId);
|
||||
return pScb;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate a DM sync control block.
|
||||
*
|
||||
* \param sid Advertising Sid.
|
||||
* \param pAddr Advertiser BD address.
|
||||
*
|
||||
* \return Pointer to sync CB or NULL if failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static dmSyncCb_t *dmSyncCbAlloc(uint8_t sid, const uint8_t *pAddr)
|
||||
{
|
||||
dmSyncCb_t *pScb = dmSyncCb;
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < DM_SYNC_MAX; i++, pScb++)
|
||||
{
|
||||
if (pScb->inUse == FALSE)
|
||||
{
|
||||
memset(pScb, 0, sizeof(dmSyncCb_t));
|
||||
|
||||
pScb->advSid = sid;
|
||||
BdaCpy(pScb->advAddr, pAddr);
|
||||
pScb->handle = DM_SYNC_HCI_HANDLE_NONE;
|
||||
pScb->syncId = i + 1;
|
||||
pScb->inUse = TRUE;
|
||||
|
||||
DM_TRACE_ALLOC1("dmSyncCbAlloc %d", pScb->syncId);
|
||||
|
||||
return pScb;
|
||||
}
|
||||
}
|
||||
|
||||
DM_TRACE_ERR0("dmSyncCbAlloc failed");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Deallocate a DM sync control block.
|
||||
*
|
||||
* \param pCb Sync control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void dmSyncCbDealloc(dmSyncCb_t *pCb)
|
||||
{
|
||||
DM_TRACE_FREE1("dmSyncCbDealloc %d", pCb->syncId);
|
||||
|
||||
pCb->inUse = FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find a sync control block with matching handle.
|
||||
*
|
||||
* \param handle Handle to find.
|
||||
*
|
||||
* \return Pointer to sync CB or NULL if failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static dmSyncCb_t *dmSyncCbByHandle(uint16_t handle)
|
||||
{
|
||||
dmSyncCb_t *pScb = dmSyncCb;
|
||||
uint8_t i;
|
||||
|
||||
for (i = DM_SYNC_MAX; i > 0; i--, pScb++)
|
||||
{
|
||||
if (pScb->inUse && (pScb->handle == handle))
|
||||
{
|
||||
return pScb;
|
||||
}
|
||||
}
|
||||
|
||||
DM_TRACE_WARN1("dmSyncCbByHandle not found 0x%04x", handle);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find a sync control block with advertising SID and advertiser BD address.
|
||||
*
|
||||
* \param sid Sid to find.
|
||||
* \param pAddr BD address to find.
|
||||
*
|
||||
* \return Pointer to CB or NULL if failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmSyncCb_t *dmSyncCbBySidBdAddr(uint8_t sid, const uint8_t *pAddr)
|
||||
{
|
||||
dmSyncCb_t *pScb = dmSyncCb;
|
||||
uint8_t i;
|
||||
|
||||
for (i = DM_SYNC_MAX; i > 0; i--, pScb++)
|
||||
{
|
||||
if (pScb->inUse && (pScb->advSid == sid) && BdaCmp(pScb->advAddr, pAddr))
|
||||
{
|
||||
return pScb;
|
||||
}
|
||||
}
|
||||
|
||||
DM_TRACE_INFO0("dmSyncCbBySidBdAddr not found");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the sync control block for the given sync ID.
|
||||
*
|
||||
* \param syncId Sync ID.
|
||||
*
|
||||
* \return Pointer to CB or NULL if failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmSyncCb_t *dmSyncCbById(dmSyncId_t syncId)
|
||||
{
|
||||
WSF_ASSERT((syncId > 0) && (syncId <= DM_SYNC_MAX));
|
||||
|
||||
syncId--;
|
||||
if (dmSyncCb[syncId].inUse)
|
||||
{
|
||||
return &dmSyncCb[syncId];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Empty action.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActNone(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Synchronize with periodic advertising from the given advertiser.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActStart(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
HciLePerAdvCreateSyncCmd(dmCb.syncOptions, pMsg->apiSyncStart.advSid,
|
||||
pMsg->apiSyncStart.advAddrType, pMsg->apiSyncStart.advAddr,
|
||||
pMsg->apiSyncStart.skip, pMsg->apiSyncStart.syncTimeout,
|
||||
pMsg->apiSyncStart.unused);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop reception of the periodic advertising identified by the given sync handle.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActStop(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
HciLePerAdvTerminateSyncCmd(pScb->handle);
|
||||
|
||||
dmSyncSmActSyncLost(pScb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Cancel creation of a sync while it's pending.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActCancelStart(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
HciLePerAdvCreateSyncCancelCmd();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a sync established event from HCI.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActSyncEst(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
/* store adv sid, adv address and sync handle */
|
||||
pScb->advSid = pMsg->perAdvSyncEst.advSid;
|
||||
BdaCpy(pScb->advAddr, pMsg->perAdvSyncEst.advAddr);
|
||||
pScb->advAddrType = DmHostAddrType(pMsg->perAdvSyncEst.advAddrType);
|
||||
pScb->handle = pMsg->perAdvSyncEst.syncHandle;
|
||||
|
||||
pMsg->hdr.event = DM_PER_ADV_SYNC_EST_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a sync established failure event from HCI.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActSyncEstFailed(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
/* deallocate scb */
|
||||
dmSyncCbDealloc(pScb);
|
||||
|
||||
pMsg->hdr.event = DM_PER_ADV_SYNC_EST_FAIL_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a sync lost event from HCI.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActSyncLost(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
/* deallocate scb */
|
||||
dmSyncCbDealloc(pScb);
|
||||
|
||||
pMsg->hdr.event = DM_PER_ADV_SYNC_LOST_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a sync transfer established event from HCI.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActSyncTrsfEst(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
/* store adv sid, adv address and sync handle */
|
||||
pScb->advSid = pMsg->perAdvSyncTrsfEst.advSid;
|
||||
BdaCpy(pScb->advAddr, pMsg->perAdvSyncTrsfEst.advAddr);
|
||||
pScb->advAddrType = DmHostAddrType(pMsg->perAdvSyncTrsfEst.advAddrType);
|
||||
pScb->handle = pMsg->perAdvSyncTrsfEst.syncHandle;
|
||||
|
||||
pMsg->hdr.event = DM_PER_ADV_SYNC_TRSF_EST_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a sync transfer established failure event from HCI.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmActSyncTrsfEstFailed(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
/* deallocate scb */
|
||||
dmSyncCbDealloc(pScb);
|
||||
|
||||
pMsg->hdr.event = DM_PER_ADV_SYNC_TRSF_EST_FAIL_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute the DM sync state machine.
|
||||
*
|
||||
* \param pScb Sync control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncSmExecute(dmSyncCb_t *pScb, dmSyncMsg_t *pMsg)
|
||||
{
|
||||
uint8_t action;
|
||||
uint8_t event;
|
||||
|
||||
DM_TRACE_INFO2("dmSyncSmExecute event=%d state=%d", pMsg->hdr.event, pScb->state);
|
||||
|
||||
/* get the event */
|
||||
event = DM_MSG_MASK(pMsg->hdr.event);
|
||||
|
||||
/* get action */
|
||||
action = dmSyncStateTbl[pScb->state][event][DM_SYNC_ACTION];
|
||||
|
||||
/* set next state */
|
||||
pScb->state = dmSyncStateTbl[pScb->state][event][DM_SYNC_NEXT_STATE];
|
||||
|
||||
/* execute action function */
|
||||
(*dmSyncAct[action])(pScb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the sync module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncInit(void)
|
||||
{
|
||||
dmFcnIfTbl[DM_ID_SYNC] = (dmFcnIf_t *) &dmSyncFcnIf;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset the sync module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncReset(void)
|
||||
{
|
||||
dmSyncCb_t *pScb = dmSyncCb;
|
||||
hciLePerAdvSyncLostEvt_t syncLost;
|
||||
uint8_t i;
|
||||
|
||||
/* generate HCI sync lost event */
|
||||
syncLost.hdr.event = HCI_LE_PER_ADV_SYNC_LOST_CBACK_EVT;
|
||||
syncLost.hdr.status = HCI_SUCCESS;
|
||||
|
||||
for (i = DM_SYNC_MAX; i > 0; i--, pScb++)
|
||||
{
|
||||
if (pScb->inUse)
|
||||
{
|
||||
/* set sync handle */
|
||||
syncLost.hdr.param = syncLost.syncHandle = pScb->handle;
|
||||
|
||||
/* handle the event */
|
||||
dmSyncHciHandler((hciEvt_t *) &syncLost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM sync HCI event handler.
|
||||
*
|
||||
* \param pEvent Pointer to HCI callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncHciHandler(hciEvt_t *pEvent)
|
||||
{
|
||||
dmSyncCb_t *pScb;
|
||||
|
||||
/* handle special case for periodic advertising report event */
|
||||
if (pEvent->hdr.event == HCI_LE_PER_ADV_REPORT_CBACK_EVT)
|
||||
{
|
||||
pEvent->hdr.event = DM_PER_ADV_REPORT_IND;
|
||||
(*dmCb.cback)((dmEvt_t *) pEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
/* handle special cases for sync established event */
|
||||
if (pEvent->hdr.event == HCI_LE_PER_ADV_SYNC_EST_CBACK_EVT)
|
||||
{
|
||||
/* first check if scb exists for this sid and bd addr */
|
||||
pScb = dmSyncCbBySidBdAddr(pEvent->lePerAdvSyncEst.advSid, pEvent->lePerAdvSyncEst.advAddr);
|
||||
|
||||
/* if scb not found */
|
||||
if (pScb == NULL)
|
||||
{
|
||||
/* check for special case state */
|
||||
pScb = dmSyncCmplState();
|
||||
}
|
||||
|
||||
/* translate HCI event to state machine event */
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pEvent->hdr.event = DM_SYNC_MSG_HCI_LE_SYNC_EST;
|
||||
}
|
||||
else
|
||||
{
|
||||
pEvent->hdr.event = DM_SYNC_MSG_HCI_LE_SYNC_EST_FAIL;
|
||||
}
|
||||
}
|
||||
/* handle special cases for past received event */
|
||||
else if (pEvent->hdr.event == HCI_LE_PER_SYNC_TRSF_RCVD_CBACK_EVT)
|
||||
{
|
||||
/* first check if scb exists for this sid and bd addr */
|
||||
pScb = dmSyncCbBySidBdAddr(pEvent->lePerAdvSyncTrsfRcvd.advSid, pEvent->lePerAdvSyncTrsfRcvd.advAddr);
|
||||
|
||||
/* if scb not found */
|
||||
if (pScb == NULL)
|
||||
{
|
||||
/* allocate scb */
|
||||
pScb = dmSyncCbAlloc(pEvent->lePerAdvSyncTrsfRcvd.advSid, pEvent->lePerAdvSyncTrsfRcvd.advAddr);
|
||||
}
|
||||
|
||||
/* translate HCI event to state machine event */
|
||||
if (pEvent->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
pEvent->hdr.event = DM_SYNC_MSG_HCI_LE_SYNC_TRSF_EST;
|
||||
}
|
||||
else
|
||||
{
|
||||
pEvent->hdr.event = DM_SYNC_MSG_HCI_LE_SYNC_TRSF_EST_FAIL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pScb = dmSyncCbByHandle(pEvent->hdr.param);
|
||||
|
||||
/* translate HCI event to state machine message */
|
||||
pEvent->hdr.event = DM_SYNC_HCI_EVT_2_MSG(pEvent->hdr.event);
|
||||
}
|
||||
|
||||
/* if scb found */
|
||||
if (pScb != NULL)
|
||||
{
|
||||
/* set sync id */
|
||||
pEvent->hdr.param = pScb->syncId;
|
||||
|
||||
/* execute state machine */
|
||||
dmSyncSmExecute(pScb, (dmSyncMsg_t *) pEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM sync event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void dmSyncMsgHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
dmSyncCb_t *pScb;
|
||||
|
||||
/* look up scb from sync id */
|
||||
if ((pScb = dmSyncCbById((dmSyncId_t) pMsg->param)) != NULL)
|
||||
{
|
||||
/* execute state machine */
|
||||
dmSyncSmExecute(pScb, (dmSyncMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Synchronize with periodic advertising from the given advertiser, and start receiving
|
||||
* periodic advertising packets.
|
||||
*
|
||||
* Note: The synchronization filter policy is used to determine whether the periodic
|
||||
* advertiser list is used. If the periodic advertiser list is not used, the
|
||||
* advertising SID, advertiser address type, and advertiser address parameters
|
||||
* specify the periodic advertising device to listen to; otherwise these parameters
|
||||
* are ignored.
|
||||
*
|
||||
* \param advSid Advertising SID.
|
||||
* \param advAddrType Advertiser address type.
|
||||
* \param pAdvAddr Advertiser address.
|
||||
* \param skip Number of periodic advertising packets that can be skipped after
|
||||
* successful receive.
|
||||
* \param syncTimeout Synchronization timeout.
|
||||
*
|
||||
* \return Sync identifier.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmSyncId_t DmSyncStart(uint8_t advSid, uint8_t advAddrType, const uint8_t *pAdvAddr, uint16_t skip,
|
||||
uint16_t syncTimeout)
|
||||
{
|
||||
dmSyncCb_t *pScb = NULL;
|
||||
dmSyncApiStart_t *pMsg;
|
||||
|
||||
/* make sure scb not already allocated */
|
||||
WsfTaskLock();
|
||||
if ((pScb = dmSyncCbBySidBdAddr(advSid, pAdvAddr)) == NULL)
|
||||
{
|
||||
/* allocate scb */
|
||||
pScb = dmSyncCbAlloc(advSid, pAdvAddr);
|
||||
}
|
||||
WsfTaskUnlock();
|
||||
|
||||
if (pScb != NULL)
|
||||
{
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(dmSyncApiStart_t))) != NULL)
|
||||
{
|
||||
pMsg->hdr.param = pScb->syncId;
|
||||
pMsg->hdr.event = DM_SYNC_MSG_API_START;
|
||||
pMsg->advSid = advSid;
|
||||
pMsg->advAddrType = advAddrType;
|
||||
BdaCpy(pMsg->advAddr, pAdvAddr);
|
||||
pMsg->skip = skip;
|
||||
pMsg->syncTimeout = syncTimeout;
|
||||
pMsg->unused = 0;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
|
||||
/* return sync id */
|
||||
return pScb->syncId;
|
||||
}
|
||||
else
|
||||
{
|
||||
WsfTaskLock();
|
||||
dmSyncCbDealloc(pScb);
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
}
|
||||
|
||||
/* open failed */
|
||||
return DM_SYNC_ID_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop reception of the periodic advertising identified by the given sync identifier.
|
||||
*
|
||||
* \param syncId Sync identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSyncStop(dmSyncId_t syncId)
|
||||
{
|
||||
wsfMsgHdr_t *pMsg;
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pMsg->param = syncId;
|
||||
pMsg->event = DM_SYNC_MSG_API_STOP;
|
||||
WsfMsgSend(dmCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add device to periodic advertiser list.
|
||||
*
|
||||
* \param advAddrType Advertiser address type.
|
||||
* \param pAdvAddr Advertiser address.
|
||||
* \param advSid Advertising SID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmAddDeviceToPerAdvList(uint8_t advAddrType, uint8_t *pAdvAddr, uint8_t advSid)
|
||||
{
|
||||
HciLeAddDeviceToPerAdvListCmd(advAddrType, pAdvAddr, advSid);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM remove device from periodic advertiser list.
|
||||
*
|
||||
* \param advAddrType Advertiser address type.
|
||||
* \param pAdvAddr Advertiser address.
|
||||
* \param advSid Advertising SID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmRemoveDeviceFromPerAdvList(uint8_t advAddrType, uint8_t *pAdvAddr, uint8_t advSid)
|
||||
{
|
||||
HciLeRemoveDeviceFromPerAdvListCmd(advAddrType, pAdvAddr, advSid);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM clear periodic advertiser list.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmClearPerAdvList(void)
|
||||
{
|
||||
HciLeClearPerAdvListCmd();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM enable or disable initial periodic advertising reports once synchronized.
|
||||
*
|
||||
* \param enable TRUE to enable initial reporting, FALSE to disable initial reporting.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DmSyncInitialRptEnable(bool_t enable)
|
||||
{
|
||||
WsfTaskLock();
|
||||
if (enable)
|
||||
{
|
||||
/* Enable initial periodic advertisement reporting */
|
||||
dmCb.syncOptions &= ~HCI_OPTIONS_INIT_RPT_ENABLE_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Disable initial periodic advertisement reporting */
|
||||
dmCb.syncOptions |= HCI_OPTIONS_INIT_RPT_ENABLE_BIT;
|
||||
}
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief HCI main module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This is the main module of the HCI subsystem. It contains the API functions for initialization
|
||||
* and registration.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "hci_api.h"
|
||||
#include "hci_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
hciCb_t hciCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback for HCI events.
|
||||
*
|
||||
* \param evtCback Callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciEvtRegister(hciEvtCback_t evtCback)
|
||||
{
|
||||
hciCb.evtCback = evtCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback for certain HCI security events.
|
||||
*
|
||||
* \param secCback Callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciSecRegister(hciSecCback_t secCback)
|
||||
{
|
||||
hciCb.secCback = secCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register callbacks for the HCI data path.
|
||||
*
|
||||
* \param aclCback ACL data callback function.
|
||||
* \param flowCback Flow control callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciAclRegister(hciAclCback_t aclCback, hciFlowCback_t flowCback)
|
||||
{
|
||||
hciCb.aclCback = aclCback;
|
||||
hciCb.flowCback = flowCback;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for HCI.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
/* store handler ID */
|
||||
hciCb.handlerId = handlerId;
|
||||
|
||||
/* init rx queue */
|
||||
WSF_QUEUE_INIT(&hciCb.rxQueue);
|
||||
|
||||
/* perform other hci initialization */
|
||||
HciCoreInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for HCI.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HciHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
HciCoreHandler(event, pMsg);
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief HCI main module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef HCI_MAIN_H
|
||||
#define HCI_MAIN_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
#include "wsf_queue.h"
|
||||
#include "hci_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Message types for HCI event handler */
|
||||
#define HCI_MSG_CMD_TIMEOUT 1 /* Command timeout timer expired */
|
||||
|
||||
/* Event types for HCI event handler */
|
||||
#define HCI_EVT_RX 0x01 /* Data received on rx queue */
|
||||
|
||||
/* Number of times to send HCI_RAND_CMD on reset to prefill random buffer */
|
||||
#define HCI_RESET_RAND_CNT 4
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Type used in number of completed packets event type */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t handle;
|
||||
uint16_t num;
|
||||
} hciNumPkts_t;
|
||||
|
||||
/* Type for HCI number of completed packets event */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t numHandles;
|
||||
hciNumPkts_t numPkts[1];
|
||||
} hciNumCmplPktsEvt_t;
|
||||
|
||||
/* Main control block of the HCI subsystem */
|
||||
typedef struct
|
||||
{
|
||||
wsfQueue_t rxQueue;
|
||||
hciEvtCback_t evtCback;
|
||||
hciSecCback_t secCback;
|
||||
hciAclCback_t aclCback;
|
||||
hciFlowCback_t flowCback;
|
||||
wsfHandlerId_t handlerId;
|
||||
bool_t resetting;
|
||||
} hciCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
extern hciCb_t hciCb;
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* HCI_MAIN_H */
|
||||
+1876
File diff suppressed because it is too large
Load Diff
+349
@@ -0,0 +1,349 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief L2CAP main module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "l2c_api.h"
|
||||
#include "l2c_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
l2cCb_t l2cCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Default callback function for unregistered CID.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cDefaultDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
L2C_TRACE_WARN0("rcvd data on uregistered cid");
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Default callback function for unregistered CID.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param cid The L2CAP connection ID.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cDefaultDataCidCback(uint16_t handle, uint16_t cid, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
L2C_TRACE_WARN1("unknown cid=0x%04x", cid);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Default L2CAP control callback function.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cDefaultCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received L2CAP signaling packets.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void l2cRxSignalingPkt(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t role;
|
||||
dmConnId_t connId;
|
||||
|
||||
if ((connId = DmConnIdByHandle(handle)) == DM_CONN_ID_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
role = DmConnRole(connId);
|
||||
|
||||
if ((role == DM_ROLE_MASTER) && (l2cCb.masterRxSignalingPkt != NULL))
|
||||
{
|
||||
(*l2cCb.masterRxSignalingPkt)(handle, len, pPacket);
|
||||
}
|
||||
else if ((role == DM_ROLE_SLAVE) && (l2cCb.slaveRxSignalingPkt != NULL))
|
||||
{
|
||||
(*l2cCb.slaveRxSignalingPkt)(handle, len, pPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
L2C_TRACE_ERR1("Invalid role configuration: role=%d", role);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI ACL data callback function.
|
||||
*
|
||||
* \param pPacket A buffer containing an ACL packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cHciAclCback(uint8_t *pPacket)
|
||||
{
|
||||
uint16_t handle;
|
||||
uint16_t hciLen;
|
||||
uint16_t cid;
|
||||
uint16_t l2cLen;
|
||||
uint8_t *p = pPacket;
|
||||
|
||||
/* parse HCI handle and length */
|
||||
BSTREAM_TO_UINT16(handle, p);
|
||||
handle &= HCI_HANDLE_MASK;
|
||||
BSTREAM_TO_UINT16(hciLen, p);
|
||||
|
||||
/* parse L2CAP length */
|
||||
if (hciLen >= L2C_HDR_LEN)
|
||||
{
|
||||
BSTREAM_TO_UINT16(l2cLen, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
l2cLen = 0;
|
||||
}
|
||||
|
||||
/* verify L2CAP length vs HCI length */
|
||||
if (hciLen == (l2cLen + L2C_HDR_LEN))
|
||||
{
|
||||
/* parse CID */
|
||||
BSTREAM_TO_UINT16(cid, p);
|
||||
|
||||
switch (cid)
|
||||
{
|
||||
case L2C_CID_LE_SIGNALING:
|
||||
(*l2cCb.l2cSignalingCback)(handle, l2cLen, pPacket);
|
||||
break;
|
||||
|
||||
case L2C_CID_ATT:
|
||||
(*l2cCb.attDataCback)(handle, l2cLen, pPacket);
|
||||
break;
|
||||
|
||||
case L2C_CID_SMP:
|
||||
(*l2cCb.smpDataCback)(handle, l2cLen, pPacket);
|
||||
break;
|
||||
|
||||
default:
|
||||
(*l2cCb.l2cDataCidCback)(handle, cid, l2cLen, pPacket);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* else length mismatch */
|
||||
else
|
||||
{
|
||||
L2C_TRACE_WARN2("length mismatch: l2c=%u hci=%u", l2cLen, hciLen);
|
||||
}
|
||||
|
||||
/* deallocate buffer */
|
||||
WsfMsgFree(pPacket);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief HCI flow control callback function.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param flowDisabled TRUE if data flow is disabled.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cHciFlowCback(uint16_t handle, bool_t flowDisabled)
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
/* get conn ID for handle */
|
||||
if ((hdr.param = DmConnIdByHandle(handle)) != DM_CONN_ID_NONE)
|
||||
{
|
||||
/* execute higher layer flow control callbacks */
|
||||
hdr.event = flowDisabled;
|
||||
(*l2cCb.attCtrlCback)(&hdr);
|
||||
hdr.event = flowDisabled;
|
||||
(*l2cCb.smpCtrlCback)(&hdr);
|
||||
|
||||
/* execute connection oriented channel flow control callback */
|
||||
hdr.event = flowDisabled;
|
||||
(*l2cCb.l2cCocCtrlCback)(&hdr);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a command reject message with reason "not understood".
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param identifier Identifier value in received message being rejected.
|
||||
* \param reason Why request was rejected.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void l2cSendCmdReject(uint16_t handle, uint8_t identifier, uint16_t reason)
|
||||
{
|
||||
uint8_t *pPacket;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate msg buffer */
|
||||
if ((pPacket = l2cMsgAlloc(L2C_SIG_PKT_BASE_LEN + L2C_SIG_CMD_REJ_LEN)) != NULL)
|
||||
{
|
||||
/* build message */
|
||||
p = pPacket + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, L2C_SIG_CMD_REJ); /* command code */
|
||||
UINT8_TO_BSTREAM(p, identifier); /* identifier */
|
||||
UINT16_TO_BSTREAM(p, L2C_SIG_CMD_REJ_LEN); /* parameter length */
|
||||
UINT16_TO_BSTREAM(p, reason); /* reason */
|
||||
|
||||
/* send packet */
|
||||
L2cDataReq(L2C_CID_LE_SIGNALING, handle, (L2C_SIG_HDR_LEN + L2C_SIG_CMD_REJ_LEN), pPacket);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate an L2CAP data message buffer to be used for the L2CAP protocol messages.
|
||||
*
|
||||
* \param len Message length in bytes.
|
||||
*
|
||||
* \return Pointer to data message buffer or NULL if allocation failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void *l2cMsgAlloc(uint16_t len)
|
||||
{
|
||||
return WsfMsgDataAlloc(len, HCI_TX_DATA_TAILROOM);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize L2C subsystem.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cInit(void)
|
||||
{
|
||||
/* Initialize control block */
|
||||
l2cCb.attDataCback = l2cDefaultDataCback;
|
||||
l2cCb.smpDataCback = l2cDefaultDataCback;
|
||||
l2cCb.l2cSignalingCback = l2cRxSignalingPkt;
|
||||
l2cCb.attCtrlCback = l2cDefaultCtrlCback;
|
||||
l2cCb.smpCtrlCback = l2cDefaultCtrlCback;
|
||||
l2cCb.l2cCocCtrlCback = l2cDefaultCtrlCback;
|
||||
l2cCb.l2cDataCidCback = l2cDefaultDataCidCback;
|
||||
l2cCb.identifier = 1;
|
||||
|
||||
/* Register with HCI */
|
||||
HciAclRegister(l2cHciAclCback, l2cHciFlowCback);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief called by the L2C client, such as ATT or SMP, to register for the given CID.
|
||||
*
|
||||
* \param cid channel identifier.
|
||||
* \param dataCback Callback function for L2CAP data received for this CID.
|
||||
* \param ctrlCback Callback function for control events for this CID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cRegister(uint16_t cid, l2cDataCback_t dataCback, l2cCtrlCback_t ctrlCback)
|
||||
{
|
||||
WSF_ASSERT((cid == L2C_CID_ATT) || (cid == L2C_CID_SMP));
|
||||
|
||||
/* store the callbacks */
|
||||
if (cid == L2C_CID_ATT)
|
||||
{
|
||||
/* registering for attribute protocol */
|
||||
l2cCb.attDataCback = dataCback;
|
||||
l2cCb.attCtrlCback = ctrlCback;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* registering for security manager protocol */
|
||||
l2cCb.smpDataCback = dataCback;
|
||||
l2cCb.smpCtrlCback = ctrlCback;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an L2CAP data packet on the given CID.
|
||||
*
|
||||
* \param cid The channel identifier.
|
||||
* \param handle The connection handle. The client receives this handle from DM.
|
||||
* \param len The length of the payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cDataReq(uint16_t cid, uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *p = pPacket;
|
||||
|
||||
/* Set HCI header */
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
UINT16_TO_BSTREAM(p, (len + L2C_HDR_LEN));
|
||||
|
||||
/* Set L2CAP header */
|
||||
UINT16_TO_BSTREAM(p, len);
|
||||
UINT16_TO_BSTREAM(p, cid);
|
||||
|
||||
/* Send to HCI */
|
||||
HciSendAclData(pPacket);
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief L2CAP main module.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef L2C_MAIN_H
|
||||
#define L2C_MAIN_H
|
||||
|
||||
#include "l2c_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Return the next L2CAP signaling req identifier. Cannot be zero. */
|
||||
#define L2C_NEXT_ID(id) (((id) == 255) ? (1) : ((id) + 1))
|
||||
|
||||
/* L2C event handler message types */
|
||||
#define L2C_MSG_REQ_TIMEOUT 1 /* L2CAP slave signaling request timeout */
|
||||
#define L2C_MSG_TYPE_MAX 1
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Data callback with CID */
|
||||
typedef void (*l2cDataCidCback_t)(uint16_t handle, uint16_t cid, uint16_t len, uint8_t *pPacket);
|
||||
|
||||
/* Main control block of the L2C subsystem */
|
||||
typedef struct
|
||||
{
|
||||
l2cDataCback_t attDataCback; /* Data callback for ATT */
|
||||
l2cDataCback_t smpDataCback; /* Data callback for SMP */
|
||||
l2cDataCback_t l2cSignalingCback; /* Data callback for L2CAP signaling */
|
||||
l2cCtrlCback_t attCtrlCback; /* Control callback for ATT */
|
||||
l2cCtrlCback_t smpCtrlCback; /* Control callback for SMP */
|
||||
l2cCtrlCback_t l2cCocCtrlCback; /* Control callback for L2CAP connection oriented channels */
|
||||
l2cDataCback_t masterRxSignalingPkt; /* Master signaling packet processing function */
|
||||
l2cDataCback_t slaveRxSignalingPkt; /* Slave signaling packet processing function */
|
||||
l2cDataCidCback_t l2cDataCidCback; /* Data callback for L2CAP on other CIDs */
|
||||
uint8_t identifier; /* Signaling request identifier */
|
||||
} l2cCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
void l2cSendCmdReject(uint16_t handle, uint8_t identifier, uint16_t reason);
|
||||
void l2cRxSignalingPkt(uint16_t handle, uint16_t len, uint8_t *pPacket);
|
||||
void *l2cMsgAlloc(uint16_t len);
|
||||
|
||||
/* Control block */
|
||||
extern l2cCb_t l2cCb;
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* L2C_MAIN_H */
|
||||
Vendored
+147
@@ -0,0 +1,147 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief L2CAP module for master operations.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "l2c_api.h"
|
||||
#include "l2c_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Master processing of received L2CAP signaling packets.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param l2cLen The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cMasterRxSignalingPkt(uint16_t handle, uint16_t l2cLen, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t code;
|
||||
uint8_t id;
|
||||
uint16_t len;
|
||||
hciConnSpec_t connSpec;
|
||||
|
||||
/* parse code, len, and identifier */
|
||||
pPacket += L2C_PAYLOAD_START;
|
||||
BSTREAM_TO_UINT8(code, pPacket);
|
||||
BSTREAM_TO_UINT8(id, pPacket);
|
||||
BSTREAM_TO_UINT16(len, pPacket);
|
||||
|
||||
/* verify signaling length vs. l2c length
|
||||
* verify this is a conn param update rsp
|
||||
* verify parameter length
|
||||
*/
|
||||
if ((l2cLen != (len + L2C_SIG_HDR_LEN)) ||
|
||||
(code != L2C_SIG_CONN_UPDATE_REQ) ||
|
||||
(len != L2C_SIG_CONN_UPDATE_REQ_LEN))
|
||||
{
|
||||
L2C_TRACE_WARN3("invalid msg code:%d len:%d l2cLen:%d", code, len, l2cLen);
|
||||
|
||||
/* reject all unknown or invalid commands except command reject. */
|
||||
if (code != L2C_SIG_CMD_REJ)
|
||||
{
|
||||
l2cSendCmdReject(handle, id, L2C_REJ_NOT_UNDERSTOOD);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse parameters */
|
||||
BSTREAM_TO_UINT16(connSpec.connIntervalMin, pPacket);
|
||||
BSTREAM_TO_UINT16(connSpec.connIntervalMax, pPacket);
|
||||
BSTREAM_TO_UINT16(connSpec.connLatency, pPacket);
|
||||
BSTREAM_TO_UINT16(connSpec.supTimeout, pPacket);
|
||||
connSpec.minCeLen = 0;
|
||||
connSpec.maxCeLen = 0;
|
||||
|
||||
/* check parameter range */
|
||||
if ((connSpec.connIntervalMin < HCI_CONN_INTERVAL_MIN) ||
|
||||
(connSpec.connIntervalMin > HCI_CONN_INTERVAL_MAX) ||
|
||||
(connSpec.connIntervalMin > connSpec.connIntervalMax) ||
|
||||
(connSpec.connIntervalMax < HCI_CONN_INTERVAL_MIN) ||
|
||||
(connSpec.connIntervalMax > HCI_CONN_INTERVAL_MAX) ||
|
||||
(connSpec.connLatency > HCI_CONN_LATENCY_MAX) ||
|
||||
(connSpec.supTimeout < HCI_SUP_TIMEOUT_MIN) ||
|
||||
(connSpec.supTimeout > HCI_SUP_TIMEOUT_MAX))
|
||||
{
|
||||
L2cDmConnUpdateRsp(id, handle, L2C_CONN_PARAM_REJECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
DmL2cConnUpdateInd(id, handle, &connSpec);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize L2C for operation as a Bluetooth LE master.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cMasterInit(void)
|
||||
{
|
||||
l2cCb.masterRxSignalingPkt = l2cMasterRxSignalingPkt;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by DM to send an L2CAP connection update response.
|
||||
*
|
||||
* \param identifier Identifier value previously passed from L2C to DM.
|
||||
* \param handle The connection handle.
|
||||
* \param result Connection update response result.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cDmConnUpdateRsp(uint8_t identifier, uint16_t handle, uint16_t result)
|
||||
{
|
||||
uint8_t *pPacket;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate msg buffer */
|
||||
if ((pPacket = l2cMsgAlloc(L2C_SIG_PKT_BASE_LEN + L2C_SIG_CONN_UPDATE_RSP_LEN)) != NULL)
|
||||
{
|
||||
/* build message */
|
||||
p = pPacket + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, L2C_SIG_CONN_UPDATE_RSP); /* command code */
|
||||
UINT8_TO_BSTREAM(p, identifier); /* identifier */
|
||||
UINT16_TO_BSTREAM(p, L2C_SIG_CONN_UPDATE_RSP_LEN); /* parameter length */
|
||||
UINT16_TO_BSTREAM(p, result); /* result */
|
||||
|
||||
/* send packet */
|
||||
L2cDataReq(L2C_CID_LE_SIGNALING, handle, (L2C_SIG_HDR_LEN + L2C_SIG_CONN_UPDATE_RSP_LEN), pPacket);
|
||||
}
|
||||
}
|
||||
Vendored
+311
@@ -0,0 +1,311 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief L2CAP module for slave operations.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_os.h"
|
||||
#include "util/bstream.h"
|
||||
#include "l2c_api.h"
|
||||
#include "l2c_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Signaling request timeout in seconds */
|
||||
#define L2C_SIG_REQ_TIMEOUT 30
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Slave control block */
|
||||
typedef struct
|
||||
{
|
||||
wsfTimer_t reqTimer; /* Signaling request timeout timer */
|
||||
wsfHandlerId_t handlerId; /* ID for this event handler */
|
||||
uint8_t lastCode[DM_CONN_MAX]; /* last code sent on each handle */
|
||||
uint8_t signId[DM_CONN_MAX]; /* expected signaling identifier */
|
||||
} l2cSlaveCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
static l2cSlaveCb_t l2cSlaveCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle slave signaling request timeout.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cSlaveReqTimeout(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
L2C_TRACE_WARN0("conn update req timeout");
|
||||
|
||||
/* Notify DM that connection update has failed (handle is stored in param) */
|
||||
DmL2cConnUpdateCnf(pMsg->param, L2C_CONN_PARAM_REJECTED);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Slave processing of received L2CAP signaling packets.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param l2cLen The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void l2cSlaveRxSignalingPkt(uint16_t handle, uint16_t l2cLen, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t code;
|
||||
uint8_t id;
|
||||
uint16_t len;
|
||||
uint16_t result;
|
||||
|
||||
/* parse code, len, and identifier */
|
||||
pPacket += L2C_PAYLOAD_START;
|
||||
BSTREAM_TO_UINT8(code, pPacket);
|
||||
BSTREAM_TO_UINT8(id, pPacket);
|
||||
BSTREAM_TO_UINT16(len, pPacket);
|
||||
|
||||
/* verify signal identifier is valid */
|
||||
if (id == L2C_SIGNAL_ID_INVALID)
|
||||
{
|
||||
/* not expected, ignore */
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify signal identifier is expected
|
||||
* verify signaling length vs. l2c length
|
||||
* verify this is a conn param update rsp or command reject
|
||||
* verify parameter length
|
||||
*/
|
||||
if ((id == l2cSlaveCb.signId[handle]) &&
|
||||
(l2cLen == (len + L2C_SIG_HDR_LEN)) &&
|
||||
(((code == L2C_SIG_CONN_UPDATE_RSP) && (len == L2C_SIG_CONN_UPDATE_RSP_LEN)) ||
|
||||
(code == L2C_SIG_CMD_REJ)))
|
||||
{
|
||||
/* get last sent code */
|
||||
uint8_t lastCode = l2cSlaveCb.lastCode[handle];
|
||||
|
||||
/* clear pending signal id */
|
||||
l2cSlaveCb.signId[handle] = L2C_SIGNAL_ID_INVALID;
|
||||
|
||||
/* parse result parameter */
|
||||
BSTREAM_TO_UINT16(result, pPacket);
|
||||
|
||||
/* stop req timer */
|
||||
WsfTimerStop(&l2cSlaveCb.reqTimer);
|
||||
|
||||
if (lastCode == L2C_SIG_CONN_UPDATE_REQ)
|
||||
{
|
||||
if (code == L2C_SIG_CMD_REJ)
|
||||
{
|
||||
/* got command reject */
|
||||
result = L2C_CONN_PARAM_REJECTED;
|
||||
}
|
||||
|
||||
/* send to DM */
|
||||
DmL2cConnUpdateCnf(handle, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* send to DM */
|
||||
DmL2cCmdRejInd(handle, result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
L2C_TRACE_WARN3("invalid msg code:%d len:%d l2cLen:%d", code, len, l2cLen);
|
||||
|
||||
/* reject all unknown, invalid or unidentified commands except command reject. */
|
||||
if (code != L2C_SIG_CMD_REJ)
|
||||
{
|
||||
l2cSendCmdReject(handle, id, L2C_REJ_NOT_UNDERSTOOD);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize L2C for operation as a Bluetooth LE slave.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cSlaveInit(void)
|
||||
{
|
||||
l2cCb.slaveRxSignalingPkt = l2cSlaveRxSignalingPkt;
|
||||
|
||||
for (uint8_t i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
l2cSlaveCb.signId[i] = L2C_SIGNAL_ID_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Build and send a signaling packet.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param code Type of command.
|
||||
* \param len Length of \ref pParam.
|
||||
* \param pParam parameters of command to send.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cDmSigReq(uint16_t handle, uint8_t code, uint16_t len, uint8_t *pParam)
|
||||
{
|
||||
uint8_t *pPacket;
|
||||
uint8_t *p;
|
||||
|
||||
WSF_ASSERT(handle < DM_CONN_MAX);
|
||||
|
||||
/* record code */
|
||||
l2cSlaveCb.lastCode[handle] = code;
|
||||
|
||||
/* Start signaling request timer and store handle */
|
||||
WsfTimerStartSec(&l2cSlaveCb.reqTimer, L2C_SIG_REQ_TIMEOUT);
|
||||
l2cSlaveCb.reqTimer.msg.param = handle;
|
||||
|
||||
/* allocate msg buffer */
|
||||
if ((pPacket = l2cMsgAlloc(L2C_SIG_PKT_BASE_LEN + len)) != NULL)
|
||||
{
|
||||
/* build message */
|
||||
p = pPacket + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, code); /* command code */
|
||||
l2cSlaveCb.signId[handle] = l2cCb.identifier;
|
||||
UINT8_TO_BSTREAM(p, l2cCb.identifier); /* identifier */
|
||||
l2cCb.identifier = L2C_NEXT_ID(l2cCb.identifier);
|
||||
UINT16_TO_BSTREAM(p, len); /* parameter length */
|
||||
memcpy(p, pParam, len); /* parameters */
|
||||
|
||||
/* send packet */
|
||||
L2cDataReq(L2C_CID_LE_SIGNALING, handle, (L2C_SIG_HDR_LEN + len), pPacket);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by DM to send an L2CAP connection update request.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param pConnSpec Pointer to the connection specification structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cDmConnUpdateReq(uint16_t handle, hciConnSpec_t *pConnSpec)
|
||||
{
|
||||
uint8_t *pPacket;
|
||||
uint8_t *p;
|
||||
|
||||
/* record code */
|
||||
l2cSlaveCb.lastCode[handle] = L2C_SIG_CONN_UPDATE_REQ;
|
||||
|
||||
/* Start signaling request timer and store handle */
|
||||
WsfTimerStartSec(&l2cSlaveCb.reqTimer, L2C_SIG_REQ_TIMEOUT);
|
||||
l2cSlaveCb.reqTimer.msg.param = handle;
|
||||
|
||||
/* allocate msg buffer */
|
||||
if ((pPacket = l2cMsgAlloc(L2C_SIG_PKT_BASE_LEN + L2C_SIG_CONN_UPDATE_REQ_LEN)) != NULL)
|
||||
{
|
||||
/* build message */
|
||||
p = pPacket + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, L2C_SIG_CONN_UPDATE_REQ); /* command code */
|
||||
UINT8_TO_BSTREAM(p, l2cCb.identifier); /* identifier */
|
||||
l2cSlaveCb.signId[handle] = l2cCb.identifier;
|
||||
l2cCb.identifier = L2C_NEXT_ID(l2cCb.identifier);
|
||||
UINT16_TO_BSTREAM(p, L2C_SIG_CONN_UPDATE_REQ_LEN); /* parameter length */
|
||||
UINT16_TO_BSTREAM(p, pConnSpec->connIntervalMin); /* interval min */
|
||||
UINT16_TO_BSTREAM(p, pConnSpec->connIntervalMax); /* interval max */
|
||||
UINT16_TO_BSTREAM(p, pConnSpec->connLatency); /* slave latency */
|
||||
UINT16_TO_BSTREAM(p, pConnSpec->supTimeout); /* timeout multiplier */
|
||||
|
||||
/* send packet */
|
||||
L2cDataReq(L2C_CID_LE_SIGNALING, handle, (L2C_SIG_HDR_LEN + L2C_SIG_CONN_UPDATE_REQ_LEN), pPacket);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Event handler initialization function for L2C when operating as a slave.
|
||||
*
|
||||
* \param handlerId ID for this event handler.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cSlaveHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
l2cSlaveCb.reqTimer.msg.event = L2C_MSG_REQ_TIMEOUT;
|
||||
l2cSlaveCb.reqTimer.handlerId = handlerId;
|
||||
l2cSlaveCb.handlerId = handlerId;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief The WSF event handler for L2C when operating as a slave.
|
||||
*
|
||||
* \param event Event mask.
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void L2cSlaveHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* Handle message */
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
WSF_ASSERT(pMsg->event > 0 && pMsg->event <= L2C_MSG_TYPE_MAX);
|
||||
|
||||
/* handle slave signaling request timeout */
|
||||
if (pMsg->event == L2C_MSG_REQ_TIMEOUT)
|
||||
{
|
||||
l2cSlaveReqTimeout(pMsg);
|
||||
}
|
||||
}
|
||||
/* Handle events */
|
||||
else if (event)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+874
@@ -0,0 +1,874 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP common utility functions and action functions.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/calc128.h"
|
||||
#include "dm_api.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
|
||||
#ifndef SMP_EXTRA_TRACE
|
||||
#define SMP_EXTRA_TRACE FALSE
|
||||
#endif
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start SMP response timer.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpStartRspTimer(smpCcb_t *pCcb)
|
||||
{
|
||||
/* start smp response timer */
|
||||
pCcb->rspTimer.msg.event = SMP_MSG_INT_RSP_TIMEOUT;
|
||||
pCcb->rspTimer.msg.status = SMP_ERR_TIMEOUT;
|
||||
WsfTimerStartSec(&pCcb->rspTimer, SMP_TIMEOUT);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief No action.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActNone(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Cleanup CCB.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpCleanup(smpCcb_t *pCcb)
|
||||
{
|
||||
/* free scratch buffer */
|
||||
if (pCcb->pScr != NULL)
|
||||
{
|
||||
WsfBufFree(pCcb->pScr);
|
||||
pCcb->pScr = NULL;
|
||||
}
|
||||
|
||||
/* stop response timer */
|
||||
WsfTimerStop(&pCcb->rspTimer);
|
||||
|
||||
/* stop wait interval timer */
|
||||
WsfTimerStop(&pCcb->waitTimer);
|
||||
|
||||
pCcb->secReq = FALSE;
|
||||
pCcb->nextCmdCode = (pCcb->initiator) ? SMP_CMD_SECURITY_REQ : SMP_CMD_PAIR_REQ;
|
||||
pCcb->lastSentKey = 0;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Cleanup CCB.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActCleanup(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
smpCleanup(pCcb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a pairing failed packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param reason Failure reason.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpSendPairingFailed(smpCcb_t *pCcb, uint8_t reason)
|
||||
{
|
||||
uint8_t *pPacket;
|
||||
uint8_t *p;
|
||||
|
||||
if ((pPacket = smpMsgAlloc(L2C_PAYLOAD_START + SMP_PAIR_FAIL_LEN)) != NULL)
|
||||
{
|
||||
p = pPacket + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_FAIL);
|
||||
UINT8_TO_BSTREAM(p, reason);
|
||||
|
||||
smpSendPkt(pCcb, pPacket);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Pairing failed.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActPairingFailed(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* clean up */
|
||||
smpCleanup(pCcb);
|
||||
|
||||
/* set connection idle */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_SMP_PAIR, DM_CONN_IDLE);
|
||||
|
||||
/* notify DM of pairing failure */
|
||||
pMsg->hdr.event = DM_SEC_PAIR_FAIL_IND;
|
||||
DmSmpCbackExec((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Pairing cancelled by user.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActPairingCancel(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* send pairing failed packet */
|
||||
smpSendPairingFailed(pCcb, pMsg->hdr.status);
|
||||
|
||||
smpActPairingFailed(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Store the authentication data.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActStorePin(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* copy authentication data to scratchpad */
|
||||
memcpy(pCcb->pScr->buf.b1, pMsg->dm.authRsp.authData, pMsg->dm.authRsp.authDataLen);
|
||||
|
||||
/* zero out unused pin data */
|
||||
if (pMsg->dm.authRsp.authDataLen == SMP_PIN_LEN)
|
||||
{
|
||||
memset(&pCcb->pScr->buf.b1[SMP_PIN_LEN], 0, SMP_OOB_LEN - SMP_PIN_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a pairing request and response data.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pOob Return parameter, TRUE if out-of-band data requested.
|
||||
* \param pDisplay Return parameter, TRUE if pin is to be displayed.
|
||||
*
|
||||
* \return TRUE on success, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t smpProcPairing(smpCcb_t *pCcb, uint8_t *pOob, uint8_t *pDisplay)
|
||||
{
|
||||
bool_t justWorks = TRUE;
|
||||
uint8_t localAuth;
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
*pDisplay = FALSE;
|
||||
*pOob = FALSE;
|
||||
|
||||
/* if OOB available use that */
|
||||
if (pCcb->pairReq[SMP_OOB_POS] == SMP_OOB_DATA_PRESENT &&
|
||||
pCcb->pairRsp[SMP_OOB_POS] == SMP_OOB_DATA_PRESENT)
|
||||
{
|
||||
*pOob = SMP_OOB_DATA_PRESENT;
|
||||
justWorks = FALSE;
|
||||
}
|
||||
/* if either device set mitm flag */
|
||||
else if ((pCcb->pairReq[SMP_AUTHREQ_POS] & SMP_AUTH_MITM_FLAG) ||
|
||||
(pCcb->pairRsp[SMP_AUTHREQ_POS] & SMP_AUTH_MITM_FLAG))
|
||||
{
|
||||
/* check for compatible I/O settings */
|
||||
if ((pCcb->pairReq[SMP_IO_POS] != SMP_IO_NO_IN_NO_OUT) && /* initiator has i/o and */
|
||||
(pCcb->pairRsp[SMP_IO_POS] != SMP_IO_NO_IN_NO_OUT) && /* responder has i/o and */
|
||||
!(((pCcb->pairReq[SMP_IO_POS] == SMP_IO_DISP_ONLY) || /* both don't have display only */
|
||||
(pCcb->pairReq[SMP_IO_POS] == SMP_IO_DISP_YES_NO)) &&
|
||||
((pCcb->pairRsp[SMP_IO_POS] == SMP_IO_DISP_ONLY) ||
|
||||
(pCcb->pairRsp[SMP_IO_POS] == SMP_IO_DISP_YES_NO))))
|
||||
{
|
||||
/* use pin */
|
||||
justWorks = FALSE;
|
||||
|
||||
/* check if pin should be displayed (as initiator) */
|
||||
*pDisplay =
|
||||
((pCcb->pairReq[SMP_IO_POS] == SMP_IO_DISP_ONLY) || /* initiator is display only or */
|
||||
(pCcb->pairReq[SMP_IO_POS] == SMP_IO_DISP_YES_NO) || /* initiator is display y/n or */
|
||||
((pCcb->pairReq[SMP_IO_POS] == SMP_IO_KEY_DISP) && /* initiator is key/display and */
|
||||
(pCcb->pairRsp[SMP_IO_POS] == SMP_IO_KEY_ONLY || /* responder is key only or key/display */
|
||||
pCcb->pairRsp[SMP_IO_POS] == SMP_IO_KEY_DISP)));
|
||||
|
||||
/* invert display setting if we are not initiator and both are not key only */
|
||||
if (!(pCcb->pairRsp[SMP_IO_POS] == SMP_IO_KEY_ONLY &&
|
||||
pCcb->pairReq[SMP_IO_POS] == SMP_IO_KEY_ONLY))
|
||||
{
|
||||
*pDisplay ^= !pCcb->initiator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!justWorks)
|
||||
{
|
||||
/* set auth flags with mitm bit set */
|
||||
pCcb->auth = (pCcb->pairReq[SMP_AUTHREQ_POS] & pCcb->pairRsp[SMP_AUTHREQ_POS]) | SMP_AUTH_MITM_FLAG;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set auth flags with mitm bit cleared */
|
||||
pCcb->auth = pCcb->pairReq[SMP_AUTHREQ_POS] & pCcb->pairRsp[SMP_AUTHREQ_POS] & ~SMP_AUTH_MITM_FLAG;
|
||||
}
|
||||
|
||||
/* if we ended up with 'just works' but the device configuration requires authentication */
|
||||
localAuth = (pCcb->initiator) ? pCcb->pairReq[SMP_AUTHREQ_POS] : pCcb->pairRsp[SMP_AUTHREQ_POS];
|
||||
if (justWorks && (pSmpCfg->auth & localAuth & SMP_AUTH_MITM_FLAG))
|
||||
{
|
||||
/* cancel pairing */
|
||||
hdr.param = pCcb->connId;
|
||||
hdr.status = SMP_ERR_AUTH_REQ;
|
||||
hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* if max encryption key is less than our minimum */
|
||||
if (pCcb->pairReq[SMP_MAXKEY_POS] < pSmpCfg->minKeyLen ||
|
||||
pCcb->pairRsp[SMP_MAXKEY_POS] < pSmpCfg->minKeyLen)
|
||||
{
|
||||
/* cancel pairing */
|
||||
hdr.param = pCcb->connId;
|
||||
hdr.status = SMP_ERR_ENC_KEY_SIZE;
|
||||
hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Request authentication data or send ourselves an authentication response.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param oob Out-of-band data requested.
|
||||
* \param display TRUE if pin is to be displayed.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpAuthReq(smpCcb_t *pCcb, uint8_t oob, uint8_t display)
|
||||
{
|
||||
/* use a union to save a bit of memory on the stack */
|
||||
union
|
||||
{
|
||||
smpDmAuthRsp_t authRsp;
|
||||
dmSecAuthReqIndEvt_t authReq;
|
||||
} buf;
|
||||
|
||||
/* if authenticated pairing */
|
||||
if (pCcb->auth & SMP_AUTH_MITM_FLAG)
|
||||
{
|
||||
/* request pin or oob from user */
|
||||
buf.authReq.hdr.param = pCcb->connId;
|
||||
buf.authReq.hdr.event = DM_SEC_AUTH_REQ_IND;
|
||||
buf.authReq.oob = oob;
|
||||
buf.authReq.display = display;
|
||||
DmSmpCbackExec((dmEvt_t *) &buf.authReq);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* else use just works; send ourselves a auth rsp with all zero pin */
|
||||
buf.authRsp.hdr.param = pCcb->connId;
|
||||
buf.authRsp.hdr.event = SMP_MSG_API_AUTH_RSP;
|
||||
buf.authRsp.authData[0] = 0;
|
||||
buf.authRsp.authData[1] = 0;
|
||||
buf.authRsp.authData[2] = 0;
|
||||
buf.authRsp.authDataLen = SMP_PIN_LEN;
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &buf.authRsp);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform first part of pairing confirm calculation.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActPairCnfCalc1(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* store authentication data */
|
||||
smpActStorePin(pCcb, pMsg);
|
||||
|
||||
/* get random number to scratchpad */
|
||||
SecRand(pCcb->pScr->buf.b4, SMP_RAND_LEN);
|
||||
|
||||
/* execute calculation */
|
||||
smpCalcC1Part1(pCcb, pCcb->pScr->buf.b1, pCcb->pScr->buf.b4);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform second part of pairing confirm calculation.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActPairCnfCalc2(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
smpCalcC1Part2(pCcb, pCcb->pScr->buf.b1, pMsg->aes.pCiphertext);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a pairing confirm packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActSendPairCnf(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* set next expected packet */
|
||||
pCcb->nextCmdCode = (pCcb->initiator) ? SMP_CMD_PAIR_CNF : SMP_CMD_PAIR_RAND;
|
||||
|
||||
/* start smp response timer */
|
||||
smpStartRspTimer(pCcb);
|
||||
|
||||
/* allocate packet buffer */
|
||||
if ((pPkt = smpMsgAlloc(SMP_PAIR_CNF_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* build packet */
|
||||
p = pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_CNF);
|
||||
memcpy(p, pMsg->aes.pCiphertext, SMP_CONFIRM_LEN);
|
||||
|
||||
/* send packet */
|
||||
smpSendPkt(pCcb, pPkt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform first part of the pairing confirm verification calculation.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActPairCnfVerCalc1(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *p;
|
||||
|
||||
/* go to start of received pairing random packet */
|
||||
p = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;
|
||||
|
||||
/* store random value */
|
||||
memcpy(pCcb->pScr->buf.b2, p, SMP_RAND_LEN);
|
||||
|
||||
/* execute calculation */
|
||||
smpCalcC1Part1(pCcb, pCcb->pScr->buf.b1, p);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform first part of the pairing confirm verification calculation.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActPairCnfVerCalc2(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
smpCalcC1Part2(pCcb, pCcb->pScr->buf.b1, pMsg->aes.pCiphertext);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a key.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param keyDist Key distribution mask.
|
||||
*
|
||||
* \return TRUE if done sending keys, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t smpSendKey(smpCcb_t *pCcb, uint8_t keyDist)
|
||||
{
|
||||
uint8_t *pPkt;
|
||||
uint8_t *p;
|
||||
wsfMsgHdr_t *pHdr;
|
||||
|
||||
if (smpCb.lescSupported && pCcb->pScCcb->lescEnabled && pCcb->lastSentKey == 0)
|
||||
{
|
||||
dmSecKeyIndEvt_t keyInd;
|
||||
|
||||
/* pass LTK to app via DM */
|
||||
if (DmConnRole(pCcb->connId) == DM_ROLE_MASTER)
|
||||
{
|
||||
keyInd.type = DM_KEY_PEER_LTK;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyInd.type = DM_KEY_LOCAL_LTK;
|
||||
}
|
||||
|
||||
keyInd.hdr.event = DM_SEC_KEY_IND;
|
||||
keyInd.hdr.param = pCcb->connId;
|
||||
keyInd.secLevel = smpGetScSecLevel(pCcb);
|
||||
keyInd.keyData.ltk.ediv = 0;
|
||||
memset(keyInd.keyData.ltk.rand, 0, SMP_RAND8_LEN);
|
||||
Calc128Cpy(keyInd.keyData.ltk.key, pCcb->pScCcb->pLtk->ltk_t);
|
||||
DmSmpCbackExec((dmEvt_t *)&keyInd);
|
||||
|
||||
pCcb->lastSentKey = SMP_CMD_MASTER_ID;
|
||||
}
|
||||
|
||||
/* check if we're done sending keys */
|
||||
if ((keyDist == 0) ||
|
||||
(keyDist == SMP_KEY_DIST_ENC && pCcb->lastSentKey == SMP_CMD_MASTER_ID) ||
|
||||
(keyDist <= (SMP_KEY_DIST_ENC | SMP_KEY_DIST_ID) && pCcb->lastSentKey == SMP_CMD_ID_ADDR_INFO) ||
|
||||
(pCcb->lastSentKey == SMP_CMD_SIGN_INFO))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* if flow disabled return */
|
||||
if (pCcb->flowDisabled)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* allocate packet buffer for largest packet size */
|
||||
if ((pPkt = smpMsgAlloc(SMP_ENC_INFO_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pPkt + L2C_PAYLOAD_START;
|
||||
|
||||
/* determine next key to send */
|
||||
if (pCcb->lastSentKey == 0 && (keyDist & SMP_KEY_DIST_ENC))
|
||||
{
|
||||
/* generate LTK, EDIV, and RAND */
|
||||
smpGenerateLtk(pCcb);
|
||||
|
||||
/* send first part of LTK */
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_ENC_INFO);
|
||||
Calc128Cpy(p, pCcb->pScr->keyInd.keyData.ltk.key);
|
||||
}
|
||||
else if (pCcb->lastSentKey == SMP_CMD_ENC_INFO)
|
||||
{
|
||||
/* send second part of LTK */
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_MASTER_ID);
|
||||
UINT16_TO_BSTREAM(p, pCcb->pScr->keyInd.keyData.ltk.ediv);
|
||||
memcpy(p, pCcb->pScr->keyInd.keyData.ltk.rand, SMP_RAND8_LEN);
|
||||
}
|
||||
else if ((keyDist & SMP_KEY_DIST_ID) &&
|
||||
(pCcb->lastSentKey == 0 || pCcb->lastSentKey == SMP_CMD_MASTER_ID))
|
||||
{
|
||||
/* send first part of IRK */
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_ID_INFO);
|
||||
Calc128Cpy(p, DmSecGetLocalIrk());
|
||||
}
|
||||
else if (pCcb->lastSentKey == SMP_CMD_ID_INFO)
|
||||
{
|
||||
/* send second part of IRK */
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_ID_ADDR_INFO);
|
||||
UINT8_TO_BSTREAM(p, DM_ADDR_PUBLIC);
|
||||
BDA_TO_BSTREAM(p, HciGetBdAddr());
|
||||
|
||||
}
|
||||
else if ((keyDist & SMP_KEY_DIST_SIGN) &&
|
||||
(pCcb->lastSentKey == 0 || pCcb->lastSentKey == SMP_CMD_ID_ADDR_INFO ||
|
||||
pCcb->lastSentKey == SMP_CMD_MASTER_ID))
|
||||
{
|
||||
/* send SRK */
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_SIGN_INFO);
|
||||
Calc128Cpy(p, DmSecGetLocalCsrk());
|
||||
}
|
||||
else
|
||||
{
|
||||
/* should never get here */
|
||||
WsfMsgFree(pPkt);
|
||||
SMP_TRACE_WARN2("smpSendKey unexpected state keyDist:%d lastSentKey:%d", keyDist, pCcb->lastSentKey);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* set last sent key to command code */
|
||||
pCcb->lastSentKey = pPkt[L2C_PAYLOAD_START];
|
||||
|
||||
/* send command packet */
|
||||
smpSendPkt(pCcb, pPkt);
|
||||
|
||||
/* if flow not disabled set up to send next key */
|
||||
if (!pCcb->flowDisabled)
|
||||
{
|
||||
if ((pHdr = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
|
||||
{
|
||||
pHdr->event = SMP_MSG_INT_SEND_NEXT_KEY;
|
||||
pHdr->param = pCcb->connId;
|
||||
WsfMsgSend(smpCb.handlerId, pHdr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received key packet and pass it to DM if complete.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pKeyInd Key data structure allocated by caller.
|
||||
* \param pBuf Buffer containing packet.
|
||||
* \param keyDist Key distribution mask.
|
||||
*
|
||||
* \return TRUE if done receiving keys, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t smpProcRcvKey(smpCcb_t *pCcb, dmSecKeyIndEvt_t *pKeyInd, uint8_t *pBuf, uint8_t keyDist)
|
||||
{
|
||||
bool_t keyIndReady = FALSE;
|
||||
bool_t done = FALSE;
|
||||
uint8_t cmdCode;
|
||||
|
||||
/* go to start of packet */
|
||||
pBuf += L2C_PAYLOAD_START;
|
||||
cmdCode = *pBuf++;
|
||||
|
||||
if (cmdCode == SMP_CMD_ENC_INFO)
|
||||
{
|
||||
/* parse encryption information packet */
|
||||
Calc128Cpy(pKeyInd->keyData.ltk.key, pBuf);
|
||||
}
|
||||
else if (cmdCode == SMP_CMD_MASTER_ID)
|
||||
{
|
||||
/* parse master identification packet */
|
||||
BSTREAM_TO_UINT16(pKeyInd->keyData.ltk.ediv, pBuf);
|
||||
memcpy(pKeyInd->keyData.ltk.rand, pBuf, SMP_RAND8_LEN);
|
||||
pKeyInd->secLevel = (pCcb->auth & SMP_AUTH_MITM_FLAG) ? DM_SEC_LEVEL_ENC_AUTH : DM_SEC_LEVEL_ENC;
|
||||
pKeyInd->type = DM_KEY_PEER_LTK;
|
||||
keyIndReady = TRUE;
|
||||
}
|
||||
else if (cmdCode == SMP_CMD_ID_INFO)
|
||||
{
|
||||
/* parse identity information packet */
|
||||
Calc128Cpy(pKeyInd->keyData.irk.key, pBuf);
|
||||
}
|
||||
else if (cmdCode == SMP_CMD_ID_ADDR_INFO)
|
||||
{
|
||||
/* parse identity address information packet */
|
||||
BSTREAM_TO_UINT8(pKeyInd->keyData.irk.addrType, pBuf);
|
||||
BSTREAM_TO_BDA(pKeyInd->keyData.irk.bdAddr, pBuf);
|
||||
pKeyInd->type = DM_KEY_IRK;
|
||||
keyIndReady = TRUE;
|
||||
}
|
||||
else if (cmdCode == SMP_CMD_SIGN_INFO)
|
||||
{
|
||||
/* parse signing information packet */
|
||||
Calc128Cpy(pKeyInd->keyData.csrk.key, pBuf);
|
||||
pKeyInd->type = DM_KEY_CSRK;
|
||||
keyIndReady = TRUE;
|
||||
}
|
||||
|
||||
/* set up to receive next key */
|
||||
|
||||
/* if just got first part of LTK or IRK */
|
||||
if (pCcb->nextCmdCode == SMP_CMD_ENC_INFO || pCcb->nextCmdCode == SMP_CMD_ID_INFO)
|
||||
{
|
||||
/* wait for second part of LTK or IRK info */
|
||||
pCcb->nextCmdCode++;
|
||||
}
|
||||
/* else if got LTK and need IRK */
|
||||
else if ((keyDist & SMP_KEY_DIST_ID) && (pCcb->nextCmdCode == SMP_CMD_MASTER_ID))
|
||||
{
|
||||
/* wait for first part of IRK */
|
||||
pCcb->nextCmdCode = SMP_CMD_ID_INFO;
|
||||
}
|
||||
/* else if got LTK or IRK and need SRK */
|
||||
else if ((keyDist & SMP_KEY_DIST_SIGN) &&
|
||||
(pCcb->nextCmdCode == SMP_CMD_MASTER_ID || pCcb->nextCmdCode == SMP_CMD_ID_ADDR_INFO))
|
||||
{
|
||||
/* wait for SRK */
|
||||
pCcb->nextCmdCode = SMP_CMD_SIGN_INFO;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* done receiving keys */
|
||||
done = TRUE;
|
||||
}
|
||||
|
||||
/* call callback if key ready */
|
||||
if (keyIndReady)
|
||||
{
|
||||
pKeyInd->hdr.event = DM_SEC_KEY_IND;
|
||||
DmSmpCbackExec((dmEvt_t *) pKeyInd);
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Maximum unsuccessful pairing attempts reached.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActMaxAttempts(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint32_t timeout;
|
||||
|
||||
/* send paring failed packet; note this stops the timer so call this first */
|
||||
smpActPairingCancel(pCcb, pMsg);
|
||||
|
||||
/* Check SMP device DB to determine time to wait before pairing can happen again */
|
||||
timeout = SmpDbMaxAttemptReached(pCcb->connId);
|
||||
|
||||
/* start wait interval timer */
|
||||
pCcb->waitTimer.msg.event = SMP_MSG_INT_WI_TIMEOUT;
|
||||
WsfTimerStartMs(&pCcb->waitTimer, timeout);
|
||||
|
||||
/* clear attempts count */
|
||||
pCcb->attempts = 0;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief A pairing attempt was received while in 'repeated attempts' state.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActAttemptRcvd(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* set that attempt was received */
|
||||
pCcb->attempts = 1;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Notify DM of a pairing failure due to max attempt failures.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActNotifyDmAttemptsFailure(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* notify DM of pairing failure */
|
||||
pMsg->hdr.status = SMP_ERR_ATTEMPTS;
|
||||
pMsg->hdr.event = DM_SEC_PAIR_FAIL_IND;
|
||||
DmSmpCbackExec((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Notify DM of a pairing failure due to response timeout.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActNotifyDmRspToFailure(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* notify DM of pairing failure */
|
||||
pMsg->hdr.status = SMP_ERR_TIMEOUT;
|
||||
pMsg->hdr.event = DM_SEC_PAIR_FAIL_IND;
|
||||
DmSmpCbackExec((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if any pairing attempts received when leaving 'repeated attempts' state.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActCheckAttempts(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* check if attempt was received */
|
||||
if (pCcb->attempts)
|
||||
{
|
||||
pCcb->attempts = 0;
|
||||
smpSendPairingFailed(pCcb, SMP_ERR_ATTEMPTS);
|
||||
|
||||
/* notify DM of pairing failure */
|
||||
smpActNotifyDmAttemptsFailure(pCcb, pMsg);
|
||||
|
||||
smpCleanup(pCcb);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Pairing completed successfully.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpActPairingCmpl(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
dmSecPairCmplIndEvt_t pairCmpl;
|
||||
|
||||
smpCleanup(pCcb);
|
||||
|
||||
/* set connection idle */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_SMP_PAIR, DM_CONN_IDLE);
|
||||
|
||||
pairCmpl.auth = pCcb->auth;
|
||||
pairCmpl.hdr.param = pCcb->connId;
|
||||
pairCmpl.hdr.event = DM_SEC_PAIR_CMPL_IND;
|
||||
DmSmpCbackExec((dmEvt_t *) &pairCmpl);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute the SMP state machine.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpSmExecute(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
smpTblEntry_t const *pTblEntry;
|
||||
smpSmIf_t const *pSmIf;
|
||||
|
||||
#if SMP_EXTRA_TRACE == TRUE
|
||||
if (smpCb.lescSupported)
|
||||
SMP_TRACE_INFO2("SMP Exe: evt=%s st=%s", smpEventStr(pMsg->hdr.event), smpStateStr(pCcb->state));
|
||||
else
|
||||
#endif
|
||||
SMP_TRACE_INFO2("smpSmExecute event=%d state=%d", pMsg->hdr.event, pCcb->state);
|
||||
|
||||
/* look up state table for state */
|
||||
pSmIf = DmConnRole(pCcb->connId) == DM_ROLE_SLAVE? smpCb.pSlave : smpCb.pMaster;
|
||||
pTblEntry = pSmIf->pStateTbl[pCcb->state];
|
||||
|
||||
/* run through state machine twice; once with state table for current state
|
||||
* and once with the state table for common events
|
||||
*/
|
||||
for(;;)
|
||||
{
|
||||
/* look for event match and execute action */
|
||||
do
|
||||
{
|
||||
/* if match */
|
||||
if ((*pTblEntry)[SMP_SM_POS_EVENT] == pMsg->hdr.event)
|
||||
{
|
||||
/* set next state */
|
||||
pCcb->state = (*pTblEntry)[SMP_SM_POS_NEXT_STATE];
|
||||
|
||||
/* execute action */
|
||||
(*pSmIf->pActionTbl[(*pTblEntry)[SMP_SM_POS_ACTION]])(pCcb, pMsg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* next entry */
|
||||
pTblEntry++;
|
||||
|
||||
/* while not at end */
|
||||
} while ((*pTblEntry)[SMP_SM_POS_EVENT] != 0);
|
||||
|
||||
/* if we've reached end of the common state table */
|
||||
if (pTblEntry == (pSmIf->pCommonTbl + SMP_STATE_TBL_COMMON_MAX - 1))
|
||||
{
|
||||
/* we're done */
|
||||
break;
|
||||
}
|
||||
/* else we haven't run through common state table yet */
|
||||
else
|
||||
{
|
||||
/* set it up */
|
||||
pTblEntry = pSmIf->pCommonTbl;
|
||||
}
|
||||
}
|
||||
}
|
||||
+433
@@ -0,0 +1,433 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP device database.
|
||||
*
|
||||
* Copyright (c) 2018-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Period in ms to service the database. */
|
||||
#define SMP_DB_SRV_MS 1000
|
||||
|
||||
/* Decrement a timer. */
|
||||
#define SMP_DB_DEC_TIMER(a) do { a = a > SMP_DB_SRV_MS ? a - SMP_DB_SRV_MS : 0;} while (0)
|
||||
|
||||
/* Device database indicies. */
|
||||
#define SMP_DB_COMMON_REC 0 /*! Common record used when database is full. */
|
||||
#define SMP_DB_FIRST_REC 1 /*! Index of first device specific record in database. */
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bdAddr_t peerAddr; /*! Peer address. */
|
||||
uint8_t addrType; /*! Peer address type. */
|
||||
uint8_t failCount; /*! Prior attempt failure count. */
|
||||
uint16_t attemptMult; /*! Attempts timeout multiplier (0 - record not used). */
|
||||
uint32_t lockMs; /*! Time remaining until device can attempt pairing. */
|
||||
uint32_t expDecrementMs; /*! Time remaining until attempt attemptMult decreases. */
|
||||
uint32_t failCountToMs; /*! Time remaining until failCount is cleared. */
|
||||
} smpDbDevice_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block. */
|
||||
static struct
|
||||
{
|
||||
smpDbDevice_t db[SMP_DB_MAX_DEVICES]; /*! Device database. */
|
||||
wsfTimer_t serviceTimer; /*! Timer to service database. */
|
||||
} smpDbCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Ensure the SMP DB service timer is running.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void smpDbStartServiceTimer(void)
|
||||
{
|
||||
if (smpDbCb.serviceTimer.isStarted == FALSE)
|
||||
{
|
||||
WsfTimerStartMs(&smpDbCb.serviceTimer, SMP_DB_SRV_MS);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if a database record is in use.
|
||||
*
|
||||
* \param pRec Pointer to the record.
|
||||
*
|
||||
* \return TRUE if record in use, else FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t smpDbRecordInUse(smpDbDevice_t *pRec)
|
||||
{
|
||||
/* When failCount, lockMs, and attemptMult are zero, the record can be used for another device. */
|
||||
if (pRec->failCount > 0)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (pRec->lockMs > 0)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (pRec->attemptMult > 0)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add a device to the database.
|
||||
*
|
||||
* \param pAddr Peer BD address.
|
||||
* \param addrType Peer BD address type.
|
||||
*
|
||||
* \return Pointer to DB record or NULL if database full.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static smpDbDevice_t *smpDbAddDevice(uint8_t *pAddr, uint8_t addrType)
|
||||
{
|
||||
smpDbDevice_t *pRec = &smpDbCb.db[SMP_DB_FIRST_REC];
|
||||
uint8_t i;
|
||||
|
||||
SMP_TRACE_INFO0("smpDbAddDevice");
|
||||
|
||||
for (i = SMP_DB_FIRST_REC; i < SMP_DB_MAX_DEVICES; i++, pRec++)
|
||||
{
|
||||
if (smpDbRecordInUse(pRec) == FALSE)
|
||||
{
|
||||
/* Reset record. */
|
||||
memset(pRec, 0, sizeof(smpDbDevice_t));
|
||||
|
||||
pRec->addrType = addrType;
|
||||
BdaCpy(pRec->peerAddr, pAddr);
|
||||
return pRec;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get a record in the database.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return Pointer to the record associated with the connection or the common record.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static smpDbDevice_t *smpDbGetRecord(dmConnId_t connId)
|
||||
{
|
||||
smpDbDevice_t *pRec = &smpDbCb.db[SMP_DB_FIRST_REC];
|
||||
uint8_t addrType = DmHostAddrType(DmConnPeerAddrType(connId));
|
||||
uint8_t *pAddr = DmConnPeerAddr(connId);
|
||||
uint8_t i;
|
||||
|
||||
SMP_TRACE_INFO2("smpDbGetRecord: connId: %d type: %d", connId, addrType);
|
||||
|
||||
for (i = SMP_DB_FIRST_REC; i < SMP_DB_MAX_DEVICES; i++, pRec++)
|
||||
{
|
||||
if (smpDbRecordInUse(pRec) && (pRec->addrType == addrType) && BdaCmp(pRec->peerAddr, pAddr))
|
||||
{
|
||||
return pRec;
|
||||
}
|
||||
}
|
||||
|
||||
/* Device is not in the database, add the device. */
|
||||
pRec = smpDbAddDevice(pAddr, addrType);
|
||||
|
||||
if (pRec == NULL)
|
||||
{
|
||||
SMP_TRACE_INFO0("smpDbGetRecord: common record");
|
||||
|
||||
/* Database is full, use the common record. */
|
||||
pRec = &smpDbCb.db[SMP_DB_COMMON_REC];
|
||||
}
|
||||
|
||||
return pRec;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the SMP Database.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDbInit(void)
|
||||
{
|
||||
/* Stop active service timer. */
|
||||
if (smpDbCb.serviceTimer.isStarted == TRUE)
|
||||
{
|
||||
WsfTimerStop(&smpDbCb.serviceTimer);
|
||||
}
|
||||
|
||||
/* Reset control block. */
|
||||
memset(&smpDbCb, 0, sizeof(smpDbCb));
|
||||
|
||||
/* Setup service timer. */
|
||||
smpDbCb.serviceTimer.handlerId = smpCb.handlerId;
|
||||
smpDbCb.serviceTimer.msg.event = SMP_DB_SERVICE_IND;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the time (msec) that pairing is disabled for a device.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return Time pairing is disabled (msec), or zero if pairing isn't disabled.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint32_t SmpDbGetPairingDisabledTime(dmConnId_t connId)
|
||||
{
|
||||
smpDbDevice_t *pRec = smpDbGetRecord(connId);
|
||||
|
||||
SMP_TRACE_INFO3("SmpDbGetPairingDisabledTime: connId: %d period: %d attemptMult: %d",
|
||||
connId, pRec->lockMs, pRec->attemptMult);
|
||||
|
||||
return pRec->lockMs;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the count of prior failures for a device.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param count Failure count.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDbSetFailureCount(dmConnId_t connId, uint8_t count)
|
||||
{
|
||||
smpDbDevice_t *pRec = smpDbGetRecord(connId);
|
||||
|
||||
SMP_TRACE_INFO2("SmpDbSetFailureCount: connId: %d count: %d", connId, count);
|
||||
|
||||
pRec->failCount = count;
|
||||
|
||||
if (count != 0)
|
||||
{
|
||||
pRec->failCountToMs = pSmpCfg->maxAttemptTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the count of prior failures for a device.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return The failure count.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t SmpDbGetFailureCount(dmConnId_t connId)
|
||||
{
|
||||
smpDbDevice_t *pRec = smpDbGetRecord(connId);
|
||||
|
||||
SMP_TRACE_INFO2("SmpDbGetFailureCount: connId: %d count: %d", connId, pRec->failCount);
|
||||
|
||||
return pRec->failCount;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Called to report max pairing attempts was reached for a device.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return Time until device can retry pairing.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint32_t SmpDbMaxAttemptReached(dmConnId_t connId)
|
||||
{
|
||||
smpDbDevice_t *pRec = smpDbGetRecord(connId);
|
||||
uint16_t multiplier;
|
||||
|
||||
SMP_TRACE_INFO1("SmpDbMaxAttemptReached: connId: %d", connId);
|
||||
|
||||
if (pRec->attemptMult == 0)
|
||||
{
|
||||
/* Due to a disconnection, a record exists but the attempt multipier hasn't been set. */
|
||||
multiplier = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
multiplier = (pRec->attemptMult * pSmpCfg->attemptExp);
|
||||
}
|
||||
|
||||
if ((pSmpCfg->attemptTimeout * multiplier) <= pSmpCfg->maxAttemptTimeout)
|
||||
{
|
||||
pRec->lockMs = pSmpCfg->attemptTimeout * multiplier;
|
||||
pRec->attemptMult = multiplier;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Exponential increase is greater than max timeout. */
|
||||
pRec->lockMs = pSmpCfg->maxAttemptTimeout;
|
||||
}
|
||||
|
||||
pRec->expDecrementMs = pSmpCfg->attemptDecTimeout;
|
||||
|
||||
/* Ensure the service timer is running. */
|
||||
smpDbStartServiceTimer();
|
||||
|
||||
return pRec->lockMs;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Called to report to the SMP DB that a pairing operation failed
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDbPairingFailed(dmConnId_t connId)
|
||||
{
|
||||
smpDbDevice_t *pRec = smpDbGetRecord(connId);
|
||||
|
||||
SMP_TRACE_INFO1("SmpDbPairingFailed: connId: %d", connId);
|
||||
|
||||
/* Reset exponent decrement timer. */
|
||||
pRec->expDecrementMs = pSmpCfg->attemptDecTimeout;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Service the device database timers
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDbService(void)
|
||||
{
|
||||
uint8_t i;
|
||||
smpDbDevice_t *pRec = smpDbCb.db;
|
||||
|
||||
/* Service device specific records. */
|
||||
for (i = 0; i < SMP_DB_MAX_DEVICES; i++, pRec++)
|
||||
{
|
||||
if (smpDbRecordInUse(pRec))
|
||||
{
|
||||
/* Decrement all time periods. */
|
||||
SMP_DB_DEC_TIMER(pRec->expDecrementMs);
|
||||
SMP_DB_DEC_TIMER(pRec->lockMs);
|
||||
SMP_DB_DEC_TIMER(pRec->failCountToMs);
|
||||
|
||||
/* Process expDecrementMs timeout. */
|
||||
if (pRec->expDecrementMs == 0)
|
||||
{
|
||||
/* Exponential decrease of multiplier. */
|
||||
pRec->attemptMult /= pSmpCfg->attemptExp;
|
||||
|
||||
if (pRec->attemptMult)
|
||||
{
|
||||
pRec->expDecrementMs = pSmpCfg->attemptDecTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
/* Process failCountToMs timeout. */
|
||||
if (pRec->failCountToMs == 0)
|
||||
{
|
||||
pRec->failCount = 0;
|
||||
}
|
||||
|
||||
/* If the record is in use, ensure the service timer is running. */
|
||||
if (smpDbRecordInUse(pRec))
|
||||
{
|
||||
smpDbStartServiceTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove all records from the SMP database.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDbRemoveAllDevices(void)
|
||||
{
|
||||
/* Stop active service timer. */
|
||||
if (smpDbCb.serviceTimer.isStarted == TRUE)
|
||||
{
|
||||
WsfTimerStop(&smpDbCb.serviceTimer);
|
||||
}
|
||||
|
||||
/* Reset database. */
|
||||
memset(&smpDbCb.db, 0, sizeof(smpDbCb.db));
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove a device with the given identity from the SMP database.
|
||||
*`
|
||||
* \param pAddr Pointer to BD Address.
|
||||
* \param addrType BD Address type.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDbRemoveDevice(uint8_t *pAddr, uint8_t addrType)
|
||||
{
|
||||
smpDbDevice_t *pRec = &smpDbCb.db[SMP_DB_FIRST_REC];
|
||||
uint8_t peerAddrType = DmHostAddrType(addrType);
|
||||
uint8_t i;
|
||||
|
||||
for (i = SMP_DB_FIRST_REC; i < SMP_DB_MAX_DEVICES; i++, pRec++)
|
||||
{
|
||||
if (smpDbRecordInUse(pRec) && (pRec->addrType == peerAddrType) && BdaCmp(pRec->peerAddr, pAddr))
|
||||
{
|
||||
/* Reset record. */
|
||||
memset(pRec, 0, sizeof(smpDbDevice_t));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
+862
@@ -0,0 +1,862 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP main module.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_math.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/calc128.h"
|
||||
#include "dm_api.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
#include "smp_handler.h"
|
||||
#include "sec_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* SMP packet length table */
|
||||
const uint8_t smpPktLenTbl[] =
|
||||
{
|
||||
0,
|
||||
SMP_PAIR_REQ_LEN,
|
||||
SMP_PAIR_RSP_LEN,
|
||||
SMP_PAIR_CNF_LEN,
|
||||
SMP_PAIR_RAND_LEN,
|
||||
SMP_PAIR_FAIL_LEN,
|
||||
SMP_ENC_INFO_LEN,
|
||||
SMP_MASTER_ID_LEN,
|
||||
SMP_ID_INFO_LEN,
|
||||
SMP_ID_ADDR_INFO_LEN,
|
||||
SMP_SIGN_INFO_LEN,
|
||||
SMP_SECURITY_REQ_LEN,
|
||||
SMP_PUB_KEY_MSG_LEN,
|
||||
SMP_DHKEY_CHECK_MSG_LEN,
|
||||
SMP_KEYPRESS_MSG_LEN
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
smpCb_t smpCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2C data callback for SMP.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void smpL2cDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t cmdCode;
|
||||
smpCcb_t *pCcb;
|
||||
|
||||
/* get connection control block for this handle, ignore packet if not found */
|
||||
if ((pCcb = smpCcbByHandle(handle)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse command code */
|
||||
cmdCode = *(pPacket + L2C_PAYLOAD_START);
|
||||
|
||||
/* verify length and that command is the expected command or pairing failed */
|
||||
if ((cmdCode >= SMP_CMD_PAIR_REQ && cmdCode < SMP_CMD_MAX) &&
|
||||
(len == smpPktLenTbl[cmdCode]) &&
|
||||
((cmdCode == pCcb->nextCmdCode) || (cmdCode == SMP_CMD_PAIR_FAIL)))
|
||||
{
|
||||
smpMsg_t msg;
|
||||
|
||||
/* send to state machine */
|
||||
if (cmdCode == SMP_CMD_PAIR_FAIL)
|
||||
{
|
||||
msg.hdr.event = SMP_MSG_CMD_PAIRING_FAILED;
|
||||
msg.hdr.status = *(pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.hdr.event = SMP_MSG_CMD_PKT;
|
||||
}
|
||||
|
||||
msg.hdr.param = pCcb->connId;
|
||||
msg.data.pPacket = pPacket;
|
||||
smpSmExecute(pCcb, &msg);
|
||||
}
|
||||
/* else ignore it */
|
||||
else
|
||||
{
|
||||
SMP_TRACE_WARN3("unexpected packet cmd:%d len:%d, expected:%d", cmdCode, len, pCcb->nextCmdCode);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2C control callback for SMP.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void smpL2cCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
smpCcb_t *pCcb;
|
||||
uint8_t *pPkt;
|
||||
|
||||
/* get connection control block */
|
||||
pCcb = smpCcbByConnId((dmConnId_t) pMsg->param);
|
||||
|
||||
/* verify connection is open */
|
||||
if (pCcb->connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
/* set flow */
|
||||
pCcb->flowDisabled = (pMsg->event == L2C_CTRL_FLOW_DISABLE_IND);
|
||||
|
||||
/* if data flow enabled */
|
||||
if (!pCcb->flowDisabled)
|
||||
{
|
||||
/* if packet in qeueue */
|
||||
if (pCcb->pQueued != NULL)
|
||||
{
|
||||
/* send queued packet */
|
||||
pPkt = pCcb->pQueued;
|
||||
pCcb->pQueued = NULL;
|
||||
smpSendPkt(pCcb, pPkt);
|
||||
}
|
||||
|
||||
/* if SMP state not idle */
|
||||
if (!smpStateIdle(pCcb))
|
||||
{
|
||||
/* trigger send of next key */
|
||||
pMsg->event = SMP_MSG_INT_SEND_NEXT_KEY;
|
||||
smpSmExecute(pCcb, (smpMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Called to resume attempts state if a disconnect and reconnect occured while in the
|
||||
* attempts state.
|
||||
*
|
||||
* \param connId Connection ID to prevent pairing.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void smpResumeAttemptsState(dmConnId_t connId)
|
||||
{
|
||||
smpCcb_t *pCcb = smpCcbByConnId(connId);
|
||||
uint32_t timeMs = SmpDbGetPairingDisabledTime(connId);
|
||||
|
||||
if (timeMs)
|
||||
{
|
||||
if (smpCb.lescSupported)
|
||||
{
|
||||
pCcb->state = DmConnRole(connId) == DM_ROLE_SLAVE? SMPR_SC_SM_ST_ATTEMPTS : SMPI_SC_SM_ST_ATTEMPTS;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCcb->state = DmConnRole(connId) == DM_ROLE_SLAVE? SMPR_SM_ST_ATTEMPTS : SMPI_SM_ST_ATTEMPTS;
|
||||
}
|
||||
|
||||
/* Start smp timer indicating the time to prevent pairing in the attempts state */
|
||||
pCcb->waitTimer.msg.event = SMP_MSG_INT_WI_TIMEOUT;
|
||||
WsfTimerStartMs(&pCcb->waitTimer, timeMs);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM connection callback for SMP.
|
||||
*
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void smpDmConnCback(dmEvt_t *pDmEvt)
|
||||
{
|
||||
smpCcb_t *pCcb;
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
pCcb = smpCcbByConnId((dmConnId_t) pDmEvt->hdr.param);
|
||||
|
||||
/* if new connection created */
|
||||
if (pDmEvt->hdr.event == DM_CONN_OPEN_IND)
|
||||
{
|
||||
/* set up state machine for master or slave */
|
||||
if (DmConnRole((dmConnId_t) pDmEvt->hdr.param) == DM_ROLE_MASTER)
|
||||
{
|
||||
pCcb->initiator = TRUE;
|
||||
pCcb->nextCmdCode = SMP_CMD_SECURITY_REQ;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCcb->initiator = FALSE;
|
||||
pCcb->nextCmdCode = SMP_CMD_PAIR_REQ;
|
||||
}
|
||||
|
||||
/* initialize control block */
|
||||
pCcb->handle = pDmEvt->connOpen.handle;
|
||||
pCcb->connId = (dmConnId_t) pDmEvt->hdr.param;
|
||||
pCcb->secReq = FALSE;
|
||||
pCcb->flowDisabled = FALSE;
|
||||
pCcb->attempts = SmpDbGetFailureCount((dmConnId_t) pDmEvt->hdr.param);
|
||||
pCcb->lastSentKey = 0;
|
||||
pCcb->state = 0;
|
||||
|
||||
/* Resume the attempts state if necessary */
|
||||
smpResumeAttemptsState((dmConnId_t) pDmEvt->hdr.param);
|
||||
}
|
||||
/* else if connection has been opened */
|
||||
else if (pCcb->connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
/* handle close */
|
||||
if (pDmEvt->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* store attempts count */
|
||||
SmpDbSetFailureCount((dmConnId_t) pDmEvt->hdr.param, pCcb->attempts);
|
||||
|
||||
/* send to state machine */
|
||||
hdr.param = pDmEvt->hdr.param;
|
||||
hdr.event = SMP_MSG_DM_CONN_CLOSE;
|
||||
hdr.status = pDmEvt->connClose.reason + DM_SEC_HCI_ERR_BASE;
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
|
||||
/* clear conn ID after handling event */
|
||||
pCcb->connId = DM_CONN_ID_NONE;
|
||||
|
||||
/* free queued packet buffer */
|
||||
if (pCcb->pQueued != NULL)
|
||||
{
|
||||
WsfMsgFree(pCcb->pQueued);
|
||||
pCcb->pQueued = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the connection control block for the given handle.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
*
|
||||
* \return Pointer to connection control block or NULL if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
smpCcb_t *smpCcbByHandle(uint16_t handle)
|
||||
{
|
||||
dmConnId_t connId;
|
||||
|
||||
if ((connId = DmConnIdByHandle(handle)) != DM_CONN_ID_NONE)
|
||||
{
|
||||
return &smpCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the connection control block for the connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to connection control block.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
smpCcb_t *smpCcbByConnId(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return &smpCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform the first part of SMP calculation C1.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pKey Encryption key parameter 'k'.
|
||||
* \param pRand Random value 'r'.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpCalcC1Part1(smpCcb_t *pCcb, uint8_t *pKey, uint8_t *pRand)
|
||||
{
|
||||
uint8_t buf[HCI_ENCRYPT_DATA_LEN];
|
||||
uint8_t *p;
|
||||
uint8_t i;
|
||||
uint8_t iAddrType;
|
||||
uint8_t rAddrType;
|
||||
|
||||
/* set initiator/responder address types */
|
||||
if (pCcb->initiator)
|
||||
{
|
||||
/* if local device's using RPA */
|
||||
if (!BdaIsZeros(DmConnLocalRpa(pCcb->connId)))
|
||||
{
|
||||
iAddrType = DM_ADDR_RANDOM;
|
||||
}
|
||||
else
|
||||
{
|
||||
iAddrType = DmConnLocalAddrType(pCcb->connId);
|
||||
}
|
||||
|
||||
/* if peer device's using RPA */
|
||||
if (!BdaIsZeros(DmConnPeerRpa(pCcb->connId)))
|
||||
{
|
||||
rAddrType = DM_ADDR_RANDOM;
|
||||
}
|
||||
else
|
||||
{
|
||||
rAddrType = DmConnPeerAddrType(pCcb->connId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if peer device's using RPA */
|
||||
if (!BdaIsZeros(DmConnPeerRpa(pCcb->connId)))
|
||||
{
|
||||
iAddrType = DM_ADDR_RANDOM;
|
||||
}
|
||||
else
|
||||
{
|
||||
iAddrType = DmConnPeerAddrType(pCcb->connId);
|
||||
}
|
||||
|
||||
/* if local device's using RPA */
|
||||
if (!BdaIsZeros(DmConnLocalRpa(pCcb->connId)))
|
||||
{
|
||||
rAddrType = DM_ADDR_RANDOM;
|
||||
}
|
||||
else
|
||||
{
|
||||
rAddrType = DmConnLocalAddrType(pCcb->connId);
|
||||
}
|
||||
}
|
||||
|
||||
/* note all numbers contained in byte arrays are little endian */
|
||||
|
||||
/* create parameter from xor of r and pres, preq, rat, and iat */
|
||||
p = buf;
|
||||
*p++ = iAddrType ^ *pRand++;
|
||||
*p++ = rAddrType ^ *pRand++;
|
||||
for (i = 0; i < SMP_PAIR_REQ_LEN; i++)
|
||||
{
|
||||
*p++ = pCcb->pairReq[i] ^ *pRand++;
|
||||
}
|
||||
for (i = 0; i < SMP_PAIR_RSP_LEN; i++)
|
||||
{
|
||||
*p++ = pCcb->pairRsp[i] ^ *pRand++;
|
||||
}
|
||||
|
||||
/* encrypt */
|
||||
pCcb->token = SecAes(pKey, buf, smpCb.handlerId, pCcb->connId, SMP_MSG_WSF_AES_CMPL);
|
||||
|
||||
if (pCcb->token == SEC_TOKEN_INVALID)
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
/* fail on invalid token */
|
||||
hdr.status = SMP_ERR_UNSPECIFIED;
|
||||
hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform the second part of SMP calculation C1.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pKey Encryption key parameter 'k'.
|
||||
* \param pPart1 Result from part 1.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpCalcC1Part2(smpCcb_t *pCcb, uint8_t *pKey, uint8_t *pPart1)
|
||||
{
|
||||
uint8_t buf[HCI_ENCRYPT_DATA_LEN];
|
||||
uint8_t *p;
|
||||
uint8_t i;
|
||||
uint8_t *pIaddr;
|
||||
uint8_t *pRaddr;
|
||||
|
||||
/* set initiator/responder addresss */
|
||||
if (pCcb->initiator)
|
||||
{
|
||||
/* use local device's RPA */
|
||||
pIaddr = DmConnLocalRpa(pCcb->connId);
|
||||
|
||||
/* if local device's not using RPA */
|
||||
if (BdaIsZeros(pIaddr))
|
||||
{
|
||||
/* use local device's address */
|
||||
pIaddr = DmConnLocalAddr(pCcb->connId);
|
||||
}
|
||||
|
||||
/* use peer device's RPA */
|
||||
pRaddr = DmConnPeerRpa(pCcb->connId);
|
||||
|
||||
/* if peer device's not using RPA */
|
||||
if (BdaIsZeros(pRaddr))
|
||||
{
|
||||
/* use peer device's address */
|
||||
pRaddr = DmConnPeerAddr(pCcb->connId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* use peer device's RPA */
|
||||
pIaddr = DmConnPeerRpa(pCcb->connId);
|
||||
|
||||
/* if peer device's not using RPA */
|
||||
if (BdaIsZeros(pIaddr))
|
||||
{
|
||||
/* use peer device's address */
|
||||
pIaddr = DmConnPeerAddr(pCcb->connId);
|
||||
}
|
||||
|
||||
/* use local device's RPA */
|
||||
pRaddr = DmConnLocalRpa(pCcb->connId);
|
||||
|
||||
/* if local device's not using RPA */
|
||||
if (BdaIsZeros(pRaddr))
|
||||
{
|
||||
/* use local device's address */
|
||||
pRaddr = DmConnLocalAddr(pCcb->connId);
|
||||
}
|
||||
}
|
||||
|
||||
/* note all numbers contained in byte arrays are little endian */
|
||||
|
||||
/* create parameter from xor of part 1 result with ia, ra, and pad */
|
||||
p = buf;
|
||||
for (i = BDA_ADDR_LEN; i > 0; i--)
|
||||
{
|
||||
*p++ = *pRaddr++ ^ *pPart1++;
|
||||
}
|
||||
for (i = BDA_ADDR_LEN; i > 0; i--)
|
||||
{
|
||||
*p++ = *pIaddr++ ^ *pPart1++;
|
||||
}
|
||||
*p++ = *pPart1++;
|
||||
*p++ = *pPart1++;
|
||||
*p++ = *pPart1++;
|
||||
*p++ = *pPart1++;
|
||||
|
||||
/* encrypt */
|
||||
pCcb->token = SecAes(pKey, buf, smpCb.handlerId, pCcb->connId, SMP_MSG_WSF_AES_CMPL);
|
||||
|
||||
if (pCcb->token == SEC_TOKEN_INVALID)
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
/* fail on invalid token */
|
||||
hdr.status = SMP_ERR_UNSPECIFIED;
|
||||
hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform calculation S1.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pKey Encryption key parameter 'k'.
|
||||
* \param pRand1 Random value 1.
|
||||
* \param pRand2 Random value 2.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpCalcS1(smpCcb_t *pCcb, uint8_t *pKey, uint8_t *pRand1, uint8_t *pRand2)
|
||||
{
|
||||
uint8_t buf[HCI_ENCRYPT_DATA_LEN];
|
||||
|
||||
/* note all numbers contained in byte arrays are little endian */
|
||||
|
||||
/* construct parameter r' from r1 and r2 */
|
||||
Calc128Cpy64(buf, pRand2);
|
||||
Calc128Cpy64(&buf[SMP_RAND8_LEN], pRand1);
|
||||
|
||||
/* encrypt */
|
||||
pCcb->token = SecAes(pKey, buf, smpCb.handlerId, pCcb->connId, SMP_MSG_WSF_AES_CMPL);
|
||||
|
||||
if (pCcb->token == SEC_TOKEN_INVALID)
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
/* fail on invalid token */
|
||||
hdr.status = SMP_ERR_UNSPECIFIED;
|
||||
hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Generate LTK, EDIV, and RAND.
|
||||
*
|
||||
* \param pScr Pointer to scratch buffer containing input and output data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpGenerateLtk(smpCcb_t *pCcb)
|
||||
{
|
||||
uint8_t *p;
|
||||
smpScratch_t *pScr = pCcb->pScr;
|
||||
|
||||
/* generated results are stored in scratch buffer */
|
||||
p = pScr->keyInd.keyData.ltk.key;
|
||||
|
||||
/* generate LTK from random number */
|
||||
SecRand(p, pScr->keyInd.encKeyLen);
|
||||
p += pScr->keyInd.encKeyLen;
|
||||
|
||||
/* set remaining key bytes to zero */
|
||||
memset(p, 0, (SMP_KEY_LEN - pScr->keyInd.encKeyLen));
|
||||
|
||||
/* use existing random number stored in scratch buf b4 for EDIV and RAND */
|
||||
BYTES_TO_UINT16(pScr->keyInd.keyData.ltk.ediv, pScr->buf.b4);
|
||||
memcpy(pScr->keyInd.keyData.ltk.rand, &pScr->buf.b4[2], SMP_RAND8_LEN);
|
||||
|
||||
/* pass key to app via DM */
|
||||
pScr->keyInd.type = DM_KEY_LOCAL_LTK;
|
||||
pScr->keyInd.secLevel = (pCcb->auth & SMP_AUTH_MITM_FLAG) ? DM_SEC_LEVEL_ENC_AUTH : DM_SEC_LEVEL_ENC;
|
||||
pScr->keyInd.hdr.event = DM_SEC_KEY_IND;
|
||||
DmSmpCbackExec((dmEvt_t *) &pScr->keyInd);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an SMP command packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pPkt Buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpSendPkt(smpCcb_t *pCcb, uint8_t *pPkt)
|
||||
{
|
||||
/* if flow disabled */
|
||||
if (pCcb->flowDisabled)
|
||||
{
|
||||
/* if packet already queued discard it and replace it with this new packet */
|
||||
if (pCcb->pQueued != NULL)
|
||||
{
|
||||
SMP_TRACE_WARN1("smpSendPkt packet discarded cmd:%d", pCcb->pQueued[L2C_PAYLOAD_START]);
|
||||
WsfMsgFree(pCcb->pQueued);
|
||||
}
|
||||
|
||||
/* queue packet */
|
||||
pCcb->pQueued = pPkt;
|
||||
}
|
||||
/* else send it to L2CAP */
|
||||
else
|
||||
{
|
||||
L2cDataReq(L2C_CID_SMP, pCcb->handle, smpPktLenTbl[pPkt[L2C_PAYLOAD_START]], pPkt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if SMP connection is in idle state.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return TRUE if in idle state, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t smpStateIdle(smpCcb_t *pCcb)
|
||||
{
|
||||
return (pCcb->state == 0);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate an SMP data message buffer to be used for the SMP protocol messages.
|
||||
*
|
||||
* \param len Message length in bytes.
|
||||
*
|
||||
* \return Pointer to data message buffer or NULL if allocation failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void *smpMsgAlloc(uint16_t len)
|
||||
{
|
||||
return WsfMsgDataAlloc(len, HCI_TX_DATA_TAILROOM);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by DM to send a message to SMP.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDmMsgSend(smpDmMsg_t *pMsg)
|
||||
{
|
||||
WsfMsgSend(smpCb.handlerId, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This function is called by DM to notify SMP of encrypted link status.
|
||||
*
|
||||
* \param pMsg Pointer to HCI message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpDmEncryptInd(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* set event to SMP event type */
|
||||
pMsg->event = (pMsg->status == HCI_SUCCESS) ?
|
||||
SMP_MSG_DM_ENCRYPT_CMPL : SMP_MSG_DM_ENCRYPT_FAILED;
|
||||
|
||||
/* pass event to handler */
|
||||
SmpHandler(0, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate Secure Connections security level.
|
||||
* Note: calculation assumes Secure Connections was used. This function cannot be used to
|
||||
* calculate a legacy pairing's security level.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return Security level.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t smpGetScSecLevel(smpCcb_t *pCcb)
|
||||
{
|
||||
uint8_t secLevel;
|
||||
|
||||
if (pCcb->auth & SMP_AUTH_MITM_FLAG)
|
||||
{
|
||||
if (WSF_MIN(pCcb->pairReq[SMP_MAXKEY_POS], pCcb->pairRsp[SMP_MAXKEY_POS]) == SMP_KEY_SIZE_MAX)
|
||||
{
|
||||
secLevel = DM_SEC_LEVEL_ENC_LESC;
|
||||
}
|
||||
else
|
||||
{
|
||||
secLevel = DM_SEC_LEVEL_ENC_AUTH;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
secLevel = DM_SEC_LEVEL_ENC;
|
||||
}
|
||||
|
||||
return secLevel;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the STK for the given connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param pSecLevel Returns the security level of pairing when STK was created.
|
||||
*
|
||||
* \return Pointer to STK or NULL if not available.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *SmpDmGetStk(dmConnId_t connId, uint8_t *pSecLevel)
|
||||
{
|
||||
smpCcb_t *pCcb;
|
||||
|
||||
/* get connection control block */
|
||||
pCcb = smpCcbByConnId(connId);
|
||||
|
||||
if (smpCb.lescSupported && pCcb->pScCcb->lescEnabled && (pCcb->pScCcb->pLtk != NULL))
|
||||
{
|
||||
/* set security level */
|
||||
*pSecLevel = smpGetScSecLevel(pCcb);
|
||||
|
||||
/* return buffer containing STK */
|
||||
return pCcb->pScCcb->pLtk->ltk_t;
|
||||
}
|
||||
else if (pCcb->pScr != NULL)
|
||||
{
|
||||
/* set security level */
|
||||
*pSecLevel = (pCcb->auth & SMP_AUTH_MITM_FLAG) ? DM_SEC_LEVEL_ENC_AUTH : DM_SEC_LEVEL_ENC;
|
||||
|
||||
/* return buffer containing STK */
|
||||
return pCcb->pScr->buf.b3;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the LTK for the given connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return Pointer to STK or NULL if not available.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *SmpDmGetLtk(dmConnId_t connId)
|
||||
{
|
||||
smpCcb_t *pCcb;
|
||||
|
||||
/* get connection control block */
|
||||
pCcb = smpCcbByConnId(connId);
|
||||
|
||||
if (smpCb.lescSupported)
|
||||
{
|
||||
/* return buffer containing STK */
|
||||
return pCcb->pScCcb->pLtk->ltk_t;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief SMP handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for SMP.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
uint8_t i;
|
||||
smpCcb_t *pCcb;
|
||||
|
||||
/* store handler ID */
|
||||
smpCb.handlerId = handlerId;
|
||||
|
||||
/* Initialize the SMP device database */
|
||||
SmpDbInit();
|
||||
|
||||
/* Initialize control block CCBs */
|
||||
for (i = 0, pCcb = smpCb.ccb; i < DM_CONN_MAX; i++, pCcb++)
|
||||
{
|
||||
/* initialize response timer */
|
||||
pCcb->rspTimer.handlerId = handlerId;
|
||||
pCcb->rspTimer.msg.param = i + 1; /* param stores the conn id */
|
||||
|
||||
/* initialize wait interval timer */
|
||||
pCcb->waitTimer.handlerId = handlerId;
|
||||
pCcb->waitTimer.msg.param = i + 1; /* param stores the conn id */
|
||||
}
|
||||
|
||||
/* Register with L2C */
|
||||
L2cRegister(L2C_CID_SMP, smpL2cDataCback, smpL2cCtrlCback);
|
||||
|
||||
/* Register with DM */
|
||||
DmConnRegister(DM_CLIENT_ID_SMP, smpDmConnCback);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
*
|
||||
* \brief WSF event handler for SMP.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
smpCcb_t *pCcb;
|
||||
|
||||
/* Handle message */
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
if (pMsg->event == SMP_DB_SERVICE_IND)
|
||||
{
|
||||
SmpDbService();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pMsg->event == SMP_MSG_WSF_CMAC_CMPL)
|
||||
{
|
||||
secCmacMsg_t *pCmac = (secCmacMsg_t *) pMsg;
|
||||
|
||||
/* Free the plain text buffer that was allocated and passed into SecCmac */
|
||||
if (pCmac->pPlainText)
|
||||
{
|
||||
WsfBufFree(pCmac->pPlainText);
|
||||
}
|
||||
}
|
||||
|
||||
/* get connection control block */
|
||||
pCcb = smpCcbByConnId((dmConnId_t) pMsg->param);
|
||||
|
||||
/* verify connection is open */
|
||||
if (pCcb->connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
/* if AES result verify it is not stale */
|
||||
if (pMsg->event == SMP_MSG_WSF_AES_CMPL && pCcb->token != pMsg->status)
|
||||
{
|
||||
SMP_TRACE_WARN2("AES token mismatch: %d %d", pCcb->token, pMsg->status);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* send to state machine */
|
||||
smpSmExecute(pCcb, (smpMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Handle events */
|
||||
else if (event)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
+419
@@ -0,0 +1,419 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP main module.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef SMP_MAIN_H
|
||||
#define SMP_MAIN_H
|
||||
|
||||
#include "sec_api.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "l2c_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "smp_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* State machine table constants */
|
||||
#define SMP_SM_POS_EVENT 0 /* Column position for event */
|
||||
#define SMP_SM_POS_NEXT_STATE 1 /* Column position for next state */
|
||||
#define SMP_SM_POS_ACTION 2 /* Column position for action */
|
||||
#define SMP_SM_NUM_COLS 3 /* Number of columns in state table */
|
||||
#define SMP_STATE_TBL_COMMON_MAX 5 /* Number of entries in common state table */
|
||||
|
||||
/* Position of parameters in pairing request/response */
|
||||
#define SMP_IO_POS 1
|
||||
#define SMP_OOB_POS 2
|
||||
#define SMP_AUTHREQ_POS 3
|
||||
#define SMP_MAXKEY_POS 4
|
||||
#define SMP_IKEYDIST_POS 5
|
||||
#define SMP_RKEYDIST_POS 6
|
||||
|
||||
/* Position of parameters in public key message */
|
||||
#define SMP_PUB_KEY_X_POS 1
|
||||
#define SMP_PUB_KEY_Y_POS 33
|
||||
|
||||
/* LESC Authorization Types */
|
||||
#define SMP_AUTH_TYPE_UNKNOWN 0
|
||||
#define SMP_AUTH_TYPE_JUST_WORKS 1
|
||||
#define SMP_AUTH_TYPE_OOB 2
|
||||
#define SMP_AUTH_TYPE_PASSKEY 3
|
||||
#define SMP_AUTH_TYPE_NUM_COMP 4
|
||||
|
||||
/*! smpi_sm state machine states */
|
||||
enum
|
||||
{
|
||||
SMPI_SM_ST_IDLE, /*!< Idle */
|
||||
SMPI_SM_ST_PAIR_RSP, /*!< Wait for pairing response */
|
||||
SMPI_SM_ST_PIN, /*!< Wait for PIN */
|
||||
SMPI_SM_ST_CNF_CALC_1, /*!< Wait for confirm calc 1 */
|
||||
SMPI_SM_ST_CNF_CALC_2, /*!< Wait for confirm calc 2 */
|
||||
SMPI_SM_ST_PAIR_CNF, /*!< Wait for pairing confirm */
|
||||
SMPI_SM_ST_PAIR_RAND, /*!< Wait for pairing random */
|
||||
SMPI_SM_ST_CNF_VER_CALC_1, /*!< Wait for confirm verify calc 1 */
|
||||
SMPI_SM_ST_CNF_VER_CALC_2, /*!< Wait for confirm verify calc 2 */
|
||||
SMPI_SM_ST_STK_CALC, /*!< Wait for STK calc */
|
||||
SMPI_SM_ST_ENCRYPT, /*!< Wait for link encryption */
|
||||
SMPI_SM_ST_KEY_DIST, /*!< Key distribution */
|
||||
SMPI_SM_ST_ATTEMPTS, /*!< Repeated attempts */
|
||||
SMPI_SM_ST_RSP_TO /*!< Response timeout state */
|
||||
};
|
||||
|
||||
/*! smpr_sm state machine states */
|
||||
enum
|
||||
{
|
||||
SMPR_SM_ST_IDLE, /*!< Idle */
|
||||
SMPR_SM_ST_API_PAIR_REQ, /*!< Wait for API pairing request */
|
||||
SMPR_SM_ST_API_PAIR_RSP, /*!< Wait for API pairing response */
|
||||
SMPR_SM_ST_PIN_PAIR_1, /*!< Wait for PIN or pairing confirm 1 */
|
||||
SMPR_SM_ST_PIN_PAIR_2, /*!< Wait for PIN or pairing confirm 2 */
|
||||
SMPR_SM_ST_CNF_CALC_1, /*!< Wait for confirm calc 1 */
|
||||
SMPR_SM_ST_CNF_CALC_2, /*!< Wait for confirm calc 2 */
|
||||
SMPR_SM_ST_PAIR_RAND, /*!< Wait for pairing random */
|
||||
SMPR_SM_ST_CNF_VER_CALC_1, /*!< Wait for confirm verify calc 1 */
|
||||
SMPR_SM_ST_CNF_VER_CALC_2, /*!< Wait for confirm verify calc 2 */
|
||||
SMPR_SM_ST_STK_CALC, /*!< Wait for STK calc */
|
||||
SMPR_SM_ST_ENCRYPT, /*!< Wait for link encryption */
|
||||
SMPR_SM_ST_KEY_DIST, /*!< Key distribution */
|
||||
SMPR_SM_ST_ATTEMPTS, /*!< Repeated attempts */
|
||||
SMPR_SM_ST_RSP_TO /*!< Response timeout state */
|
||||
};
|
||||
|
||||
/*! smpi_sc_sm state machine states */
|
||||
enum
|
||||
{
|
||||
SMPI_SC_SM_ST_IDLE, /*!< Idle */
|
||||
SMPI_SC_SM_ST_PAIR_RSP, /*!< Wait for pairing response */
|
||||
SMPI_SC_SM_ST_MODE_SELECT, /*!< Select the security mode (LESC or Legacy) */
|
||||
SMPI_SC_SM_ST_LESC_PIN, /*!< Wait for PIN in LESC */
|
||||
SMPI_SC_SM_ST_PUB_KEY, /*!< Wait for public key from peer */
|
||||
SMPI_SC_SM_ST_AUTH_SELECT, /*!< Select the type of LESC pairing (Just Works, Passkey, or OOB) */
|
||||
|
||||
SMPI_SC_SM_ST_JWNC_WAIT_CNF, /*!< Wait for confirm in Just Works/Numeric Comparison Pairing */
|
||||
SMPI_SC_SM_ST_JWNC_RAND, /*!< Wait for rand in Just Works/Numeric Comparison Pairing */
|
||||
SMPI_SC_SM_ST_JWNC_CHECK_1, /*!< Calculate confirm value in Just Works/Numeric Comparison Pairing */
|
||||
SMPI_SC_SM_ST_JWNC_CHECK_2, /*!< Calculate user validate value in Just Works/Numeric Comparison Pairing */
|
||||
SMPI_SC_SM_ST_JWNC_WAIT_USER, /* Wait for user to validate the confirm value */
|
||||
|
||||
SMPI_SC_SM_ST_PK_KEYPRESS, /*!< Process a keypress command in passkey pairing */
|
||||
SMPI_SC_SM_ST_PK_CALC, /*!< Calculate the confirm in passkey pairing */
|
||||
SMPI_SC_SM_ST_PK_CNF, /*!< Wait for confirm in passkey pairing */
|
||||
SMPI_SC_SM_ST_PK_RAND, /*!< Wait for rand in passkey pairing */
|
||||
SMPI_SC_SM_ST_PK_CHECK, /*!< Check the conform value in passkey pairing */
|
||||
SMPI_SC_SM_ST_PK_REPEAT, /*!< Repeat or complete the passkey pairing */
|
||||
|
||||
SMPI_SC_SM_ST_OOB_SEND_RAND, /*!< Send the rand in OOB pairing */
|
||||
SMPI_SC_SM_ST_OOB_WAIT_RAND, /*!< Wait for a rand in OOB pairing */
|
||||
|
||||
SMPI_SC_SM_ST_CALC_DHKEY, /*!< Calculate the DHKEY shared secret */
|
||||
SMPI_SC_SM_ST_CALC_F5_TKEY, /*!< Calculate the DHKEY T Key */
|
||||
SMPI_SC_SM_ST_CALC_F5_MACKEY, /*!< Calculate the DHKEY MAC Key */
|
||||
SMPI_SC_SM_ST_CALC_F5_LTK, /*!< Calculate the DHKEY LTK Key */
|
||||
SMPI_SC_SM_ST_CALC_F6_EA, /*!< Calculate the DHKEY Ea Key */
|
||||
SMPI_SC_SM_ST_CALC_F6_EB, /*!< Calculate the DHKEY Eb Key */
|
||||
SMPI_SC_SM_ST_VERIFY_DH_CHECK, /*!< Verify the DHKEY check value */
|
||||
|
||||
SMPI_SC_SM_ST_LEGACY_PIN, /*!< Wait for PIN */
|
||||
SMPI_SC_SM_ST_CNF_CALC_1, /*!< Wait for confirm calc 1 */
|
||||
SMPI_SC_SM_ST_CNF_CALC_2, /*!< Wait for confirm calc 2 */
|
||||
SMPI_SC_SM_ST_PAIR_CNF, /*!< Wait for pairing confirm */
|
||||
SMPI_SC_SM_ST_PAIR_RAND, /*!< Wait for pairing random */
|
||||
SMPI_SC_SM_ST_CNF_VER_CALC_1, /*!< Wait for confirm verify calc 1 */
|
||||
SMPI_SC_SM_ST_CNF_VER_CALC_2, /*!< Wait for confirm verify calc 2 */
|
||||
SMPI_SC_SM_ST_STK_CALC, /*!< Wait for STK calc */
|
||||
SMPI_SC_SM_ST_ENCRYPT, /*!< Wait for link encryption */
|
||||
SMPI_SC_SM_ST_KEY_DIST, /*!< Key distribution */
|
||||
SMPI_SC_SM_ST_ATTEMPTS, /*!< Repeated attempts */
|
||||
SMPI_SC_SM_ST_RSP_TO /*!< Response timeout state */
|
||||
};
|
||||
|
||||
/*! smpr_sc_sm state machine states */
|
||||
enum
|
||||
{
|
||||
SMPR_SC_SM_ST_IDLE, /*!< Idle */
|
||||
SMPR_SC_SM_ST_API_PAIR_REQ, /*!< Wait for API pairing request */
|
||||
SMPR_SC_SM_ST_API_PAIR_RSP, /*!< Wait for API pairing response */
|
||||
SMPR_SC_SM_ST_MODE_SELECT, /*!< Select the security mode (LESC or Legacy) */
|
||||
SMPR_SC_SM_ST_PUB_KEY, /*!< Wait for public key from peer */
|
||||
SMPR_SC_SM_ST_LESC_PIN, /*!< Wait for pin in LESC */
|
||||
SMPR_SC_SM_ST_AUTH_SELECT, /*!< Select the type of LESC pairing (Just Works, Passkey, or OOB) */
|
||||
|
||||
SMPR_SC_SM_ST_JWNC_SETUP, /*!< Prepare for Just Works/Numeric Comparison Pairing */
|
||||
SMPR_SC_SM_ST_JWNC_WAIT_RAND, /*!< Wait for Rand in Just Works/Numeric Comparison Pairing */
|
||||
SMPR_SC_SM_ST_JWNC_CALC_G2, /*!< Calculate the user validate value in Just Works/Numeric Comparison Pairing */
|
||||
SMPR_SC_SM_ST_JWNC_WAIT_USER, /*!< Wait for user to validate the confirm value */
|
||||
SMPR_SC_SM_ST_JWNC_WAIT_USER_DH_CHECK_RCVD, /*!< Wait for user to validate the confirm value with a received DH Key Check */
|
||||
|
||||
SMPR_SC_SM_ST_PK_KEYPRESS, /*!< Process a keypress command in passkey pairing */
|
||||
SMPR_SC_SM_ST_PK_WAIT_AUTH, /*!< The confirm was received before the auth from the API, wait for the auth */
|
||||
SMPR_SC_SM_ST_PK_WAIT_CNF, /*!< Wait for the confirm command in passkey pairing */
|
||||
SMPR_SC_SM_ST_PK_CALC, /*!< Calculate the confirm in passkey pairing */
|
||||
SMPR_SC_SM_ST_PK_RAND, /*!< Send the rand command in passkey pairing */
|
||||
SMPR_SC_SM_ST_PK_CHECK, /*!< Check the confirm in passkey pairing */
|
||||
SMPR_SC_SM_ST_PK_REPEAT, /*!< Repeat or complete the passkey pairing */
|
||||
|
||||
SMPR_SC_SM_ST_OOB_SEND_RAND, /*!< Send the rand in OOB pairing */
|
||||
SMPR_SC_SM_ST_OOB_WAIT_RAND, /*!< Wait for a rand in OOB pairing */
|
||||
|
||||
SMPR_SC_SM_ST_WAIT_DH_CHECK, /*!< Wait for the DH Check command */
|
||||
SMPR_SC_SM_ST_CALC_DHKEY, /*!< Calculate the DHKEY shared secret */
|
||||
SMPR_SC_SM_ST_CALC_F5_TKEY, /*!< Calculate the DHKEY T Key */
|
||||
SMPR_SC_SM_ST_CALC_F5_MACKEY, /*!< Calculate the DHKEY MAC Key */
|
||||
SMPR_SC_SM_ST_CALC_F5_LTK, /*!< Calculate the DHKEY LTK Key */
|
||||
SMPR_SC_SM_ST_CALC_F6_EA, /*!< Calculate the DHKEY Ea Key */
|
||||
SMPR_SC_SM_ST_CALC_F6_EB, /*!< Calculate the DHKEY Eb Key */
|
||||
|
||||
SMPR_SC_SM_ST_PIN_PAIR_1, /*!< Wait for PIN or pairing confirm 1 */
|
||||
SMPR_SC_SM_ST_PIN_PAIR_2, /*!< Wait for PIN or pairing confirm 2 */
|
||||
SMPR_SC_SM_ST_CNF_CALC_1, /*!< Wait for confirm calc 1 */
|
||||
SMPR_SC_SM_ST_CNF_CALC_2, /*!< Wait for confirm calc 2 */
|
||||
SMPR_SC_SM_ST_PAIR_RAND, /*!< Wait for pairing random */
|
||||
SMPR_SC_SM_ST_CNF_VER_CALC_1, /*!< Wait for confirm verify calc 1 */
|
||||
SMPR_SC_SM_ST_CNF_VER_CALC_2, /*!< Wait for confirm verify calc 2 */
|
||||
SMPR_SC_SM_ST_STK_CALC, /*!< Wait for STK calc */
|
||||
SMPR_SC_SM_ST_ENCRYPT, /*!< Wait for link encryption */
|
||||
SMPR_SC_SM_ST_KEY_DIST, /*!< Key distribution */
|
||||
SMPR_SC_SM_ST_ATTEMPTS, /*!< Repeated attempts */
|
||||
SMPR_SC_SM_ST_RSP_TO /*!< Response timeout state */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Data type for state machine table entry */
|
||||
typedef uint8_t smpTblEntry_t[SMP_SM_NUM_COLS];
|
||||
|
||||
/* Pairing request/response parameters */
|
||||
typedef uint8_t smpPair_t[SMP_PAIR_REQ_LEN];
|
||||
|
||||
/* Temporary storage structure for calculations and other data.
|
||||
* Note that keyInd overlaps b1 through b3 so these will be overwritten
|
||||
* when key exchange occurs.
|
||||
*
|
||||
* Notes on usage from start of pairing to encryption with STK:
|
||||
* buf.b1: PIN or OOB data
|
||||
* buf.b2: RAND from peer
|
||||
* buf.b3: pairing cnf received from peer, then STK (if responder)
|
||||
* buf.b4: RAND used in pairing calc
|
||||
*
|
||||
*/
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint8_t b1[SMP_KEY_LEN];
|
||||
uint8_t b2[SMP_KEY_LEN];
|
||||
uint8_t b3[SMP_KEY_LEN];
|
||||
uint8_t b4[SMP_KEY_LEN];
|
||||
} buf;
|
||||
dmSecKeyIndEvt_t keyInd;
|
||||
} smpScratch_t;
|
||||
|
||||
/* SMP message handling function type */
|
||||
typedef void (*smpMsgHandler_t)(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/* SMP connection callback type */
|
||||
typedef void (*smpConnCback_t)(dmEvt_t *pDmEvt);
|
||||
|
||||
/* SMP data message type */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr; /* Header structure */
|
||||
uint8_t *pPacket; /* Pointer to buffer containing L2CAP packet */
|
||||
} smpDataMsg_t;
|
||||
|
||||
/* Union of event handler data types */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr; /* Header structure */
|
||||
smpDmMsg_t dm; /* Union SMP DM message data types */
|
||||
secAes_t aes; /* AES Security callback parameters structure */
|
||||
smpDataMsg_t data; /* SMP data message */
|
||||
} smpMsg_t;
|
||||
|
||||
/* LE LTK Calc Scratch buffer */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t mac[SEC_CMAC_KEY_LEN]; /* MAC Key */
|
||||
uint8_t ltk_t[SEC_CMAC_KEY_LEN]; /* LTK Key or T value of MAC/LTK calculation */
|
||||
} smpScLtk_t;
|
||||
|
||||
/* ECC DH Key */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t pubKeyX[SMP_PUB_KEY_LEN]; /* Public Key X */
|
||||
uint8_t pubKeyY[SMP_PUB_KEY_LEN]; /* Public Key Y */
|
||||
} smpScPubKey_t;
|
||||
|
||||
/* LESC Scratch Buffers */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t Na_Ea[SMP_RAND_LEN]; /* Initiator Na or Ea */
|
||||
uint8_t Nb_Eb[SMP_RAND_LEN]; /* Responder Nb or Eb */
|
||||
uint8_t Ra[SMP_RAND_LEN]; /* Initiator Ra */
|
||||
uint8_t Rb[SMP_RAND_LEN]; /* Responder Nb */
|
||||
uint8_t PeerCb[SMP_RAND_LEN]; /* Peer Responder Confirm */
|
||||
uint8_t PeerCa_Ea[SMP_RAND_LEN]; /* Peer Responder Confirm or DH Key Check */
|
||||
} smpScScratch_t;
|
||||
|
||||
/* SMP LESC Control Block */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t lescEnabled; /* TRUE when LE Secure Connection pairing in use */
|
||||
uint8_t authType; /* Type of authentication (Just Works, Numeric Comparison, Passkey, or OOB) */
|
||||
uint8_t kpNotify; /* TRUE when Keypress notification in use */
|
||||
uint8_t pkPos; /* Current passkey bit position */
|
||||
bool_t display; /* Passkey display setting */
|
||||
smpScPubKey_t *pPeerPublicKey; /* Peer device's ECC Key */
|
||||
smpScPubKey_t *pLocalPublicKey; /* Local device's public ECC Key */
|
||||
uint8_t *pPrivateKey; /* Local device's private ECC Key */
|
||||
smpScScratch_t *pScratch; /* Scratch Buffer */
|
||||
smpScLtk_t *pLtk; /* LTK calculation control */
|
||||
} smpScCcb_t;
|
||||
|
||||
/* SMP connection control block */
|
||||
typedef struct
|
||||
{
|
||||
wsfTimer_t rspTimer; /* Response timer */
|
||||
wsfTimer_t waitTimer; /* Wait interval timer after max attempts */
|
||||
smpPair_t pairReq; /* Pair request parameters */
|
||||
smpPair_t pairRsp; /* Pair response parameters */
|
||||
smpScratch_t *pScr; /* Pointer to scratchpad buffer */
|
||||
uint8_t *pQueued; /* Pointer to queued packet buffer */
|
||||
uint16_t handle; /* Connection handle */
|
||||
bool_t initiator; /* TRUE if device is initiator */
|
||||
bool_t secReq; /* TRUE if security request sent or received */
|
||||
bool_t flowDisabled; /* TRUE if data flow disabled */
|
||||
dmConnId_t connId; /* DM connection ID */
|
||||
uint8_t state; /* State machine state */
|
||||
uint8_t nextCmdCode; /* Command code of next expected packet */
|
||||
uint8_t auth; /* Resulting authentication and bonding flags */
|
||||
uint8_t token; /* AES transaction token */
|
||||
uint8_t attempts; /* Failed pairing attempts */
|
||||
uint8_t lastSentKey; /* Command code of last sent key */
|
||||
smpScCcb_t *pScCcb; /* LE Secure Connection control blocks */
|
||||
} smpCcb_t;
|
||||
|
||||
/* State machine action function type */
|
||||
typedef void (*smpAct_t)(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
|
||||
/* SMP process pairing function */
|
||||
typedef bool_t smpProcPairing_t(smpCcb_t *pCcb, uint8_t *pOob, uint8_t *pDisplay);
|
||||
|
||||
/* SMP process pairing function */
|
||||
typedef void smpProcAuthReq_t(smpCcb_t *pCcb, uint8_t oob, uint8_t display);
|
||||
|
||||
/* State machine interface type */
|
||||
typedef struct
|
||||
{
|
||||
smpTblEntry_t const * const *pStateTbl; /* Pointer to state table */
|
||||
smpAct_t const *pActionTbl; /* Pointer to action table */
|
||||
smpTblEntry_t const *pCommonTbl; /* Pointer to common action table */
|
||||
} smpSmIf_t;
|
||||
|
||||
/* SMP main control block */
|
||||
typedef struct
|
||||
{
|
||||
smpCcb_t ccb[DM_CONN_MAX]; /* Legacy Connection control blocks */
|
||||
smpSmIf_t const *pSlave; /* Slave state machine interface */
|
||||
smpSmIf_t const *pMaster; /* Master state machine interface */
|
||||
wsfHandlerId_t handlerId; /* WSF handler ID */
|
||||
smpProcPairing_t *procPairing; /* Pointer to process pairing function */
|
||||
smpProcAuthReq_t *procAuthReq; /* Pointer to process auth request function */
|
||||
bool_t lescSupported; /* TRUE if LE Secure Connections is supported */
|
||||
} smpCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* SMP packet length table */
|
||||
extern const uint8_t smpPktLenTbl[];
|
||||
|
||||
/* Control block */
|
||||
extern smpCb_t smpCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* utility functions from smp main */
|
||||
smpCcb_t *smpCcbByHandle(uint16_t handle);
|
||||
smpCcb_t *smpCcbByConnId(dmConnId_t connId);
|
||||
void smpCalcC1Part1(smpCcb_t *pCcb, uint8_t *pKey, uint8_t *pRand);
|
||||
void smpCalcC1Part2(smpCcb_t *pCcb, uint8_t *pKey, uint8_t *pPart1);
|
||||
void smpCalcS1(smpCcb_t *pCcb, uint8_t *pKey, uint8_t *pRand1, uint8_t *pRand2);
|
||||
void smpGenerateLtk(smpCcb_t *pCcb);
|
||||
void smpSendPkt(smpCcb_t *pCcb, uint8_t *pPkt);
|
||||
bool_t smpStateIdle(smpCcb_t *pCcb);
|
||||
void *smpMsgAlloc(uint16_t len);
|
||||
uint8_t smpGetScSecLevel(smpCcb_t *pCcb);
|
||||
|
||||
/* action functions and utility functions from smp act */
|
||||
void smpStartRspTimer(smpCcb_t *pCcb);
|
||||
void smpActNone(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpCleanup(smpCcb_t *pCcb);
|
||||
void smpActCleanup(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpSendPairingFailed(smpCcb_t *pCcb, uint8_t reason);
|
||||
void smpActPairingFailed(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActPairingCancel(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActStorePin(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
bool_t smpProcPairing(smpCcb_t *pCcb, uint8_t *pOob, uint8_t *pDisplay);
|
||||
void smpAuthReq(smpCcb_t *pCcb, uint8_t oob, uint8_t display);
|
||||
void smpActPairCnfCalc1(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActPairCnfCalc2(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActSendPairCnf(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActPairCnfVerCalc1(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActPairCnfVerCalc2(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
bool_t smpSendKey(smpCcb_t *pCcb, uint8_t keyDist);
|
||||
bool_t smpProcRcvKey(smpCcb_t *pCcb, dmSecKeyIndEvt_t *pKeyInd, uint8_t *pBuf, uint8_t keyDist);
|
||||
void smpActMaxAttempts(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActAttemptRcvd(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActCheckAttempts(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActNotifyDmAttemptsFailure(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActNotifyDmRspToFailure(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpActPairingCmpl(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpSmExecute(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
|
||||
/* diagnostic functions */
|
||||
uint8_t *smpEventStr(uint8_t eventId);
|
||||
uint8_t *smpStateStr(uint8_t state);
|
||||
|
||||
/* SMP DB functions */
|
||||
uint32_t SmpDbMaxAttemptReached(dmConnId_t connId);
|
||||
void SmpDbPairingFailed(dmConnId_t connId);
|
||||
uint32_t SmpDbGetPairingDisabledTime(dmConnId_t connId);
|
||||
void SmpDbService(void);
|
||||
uint8_t SmpDbGetFailureCount(dmConnId_t connId);
|
||||
void SmpDbSetFailureCount(dmConnId_t connId, uint8_t count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* SMP_MAIN_H */
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Simple implementation of SMP when not supported.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "dm_api.h"
|
||||
#include "l2c_api.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2C control callback for SMP.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void smpNonL2cCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2CAP data callback for SMP when SMP not supported.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpNonL2cDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pRsp;
|
||||
uint8_t *p;
|
||||
uint8_t role;
|
||||
dmConnId_t connId;
|
||||
|
||||
if ((connId = DmConnIdByHandle(handle)) == DM_CONN_ID_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
role = DmConnRole(connId);
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START;
|
||||
|
||||
/* SMP is not supported so fail gracefully */
|
||||
|
||||
/* if slave and pairing request received, or master and security request received */
|
||||
if ((role == DM_ROLE_SLAVE && *p == SMP_CMD_PAIR_REQ) ||
|
||||
(role == DM_ROLE_MASTER && *p == SMP_CMD_SECURITY_REQ))
|
||||
{
|
||||
/* send pairing failed */
|
||||
if ((pRsp = smpMsgAlloc(L2C_PAYLOAD_START + SMP_PAIR_FAIL_LEN)) != NULL)
|
||||
{
|
||||
p = pRsp + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_FAIL);
|
||||
UINT8_TO_BSTREAM(p, SMP_ERR_PAIRING_NOT_SUP);
|
||||
|
||||
L2cDataReq(L2C_CID_SMP, handle, SMP_PAIR_FAIL_LEN, pRsp);
|
||||
}
|
||||
}
|
||||
|
||||
/* all other messages are ignored */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Use this SMP init function when SMP is not supported.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpNonInit(void)
|
||||
{
|
||||
/* Register with L2C */
|
||||
L2cRegister(L2C_CID_SMP, smpNonL2cDataCback, smpNonL2cCtrlCback);
|
||||
}
|
||||
Vendored
+1004
File diff suppressed because it is too large
Load Diff
Vendored
+758
@@ -0,0 +1,758 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP Secure Connections main module and utility functions.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "sec_api.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/calc128.h"
|
||||
#include "util/wstr.h"
|
||||
#include "dm_api.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_defs.h"
|
||||
#include "smp_main.h"
|
||||
#include "smp_sc_main.h"
|
||||
#include "smp_handler.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* LE Secure Connections Control Block (TBD: Make these dynamic) */
|
||||
smpScCcb_t SMP_ScCcb[DM_CONN_MAX];
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate LESC Scratch buffers
|
||||
*
|
||||
* \param pCcb SMP control block.
|
||||
*
|
||||
* \return TRUE if success, else FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t SmpScAllocScratchBuffers(smpCcb_t *pCcb)
|
||||
{
|
||||
if (pCcb->pScCcb->pScratch == NULL)
|
||||
{
|
||||
pCcb->pScCcb->pScratch = WsfBufAlloc(sizeof(smpScScratch_t));
|
||||
}
|
||||
|
||||
if (pCcb->pScCcb->pPeerPublicKey == NULL)
|
||||
{
|
||||
pCcb->pScCcb->pPeerPublicKey = WsfBufAlloc(sizeof(smpScPubKey_t));
|
||||
}
|
||||
|
||||
if (pCcb->pScCcb->pLtk == NULL)
|
||||
{
|
||||
pCcb->pScCcb->pLtk = WsfBufAlloc(sizeof(smpScLtk_t));
|
||||
}
|
||||
|
||||
if (pCcb->pScCcb->pLocalPublicKey == NULL)
|
||||
{
|
||||
pCcb->pScCcb->pLocalPublicKey = WsfBufAlloc(sizeof(smpScPubKey_t));
|
||||
}
|
||||
|
||||
if (pCcb->pScCcb->pPrivateKey == NULL)
|
||||
{
|
||||
pCcb->pScCcb->pPrivateKey = WsfBufAlloc(SMP_PRIVATE_KEY_LEN);
|
||||
}
|
||||
|
||||
if (pCcb->pScCcb->pScratch && pCcb->pScCcb->pPeerPublicKey && pCcb->pScCcb->pLtk &&
|
||||
pCcb->pScCcb->pLocalPublicKey && pCcb->pScCcb->pPrivateKey)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Free LESC Scratch buffers
|
||||
*
|
||||
* \param pCcb SMP control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpScFreeScratchBuffers(smpCcb_t *pCcb)
|
||||
{
|
||||
/* free scratch buffer */
|
||||
if (pCcb->pScCcb->pScratch != NULL)
|
||||
{
|
||||
WsfBufFree(pCcb->pScCcb->pScratch);
|
||||
pCcb->pScCcb->pScratch = NULL;
|
||||
}
|
||||
|
||||
/* free scratch peer public key buffer */
|
||||
if (pCcb->pScCcb->pPeerPublicKey != NULL)
|
||||
{
|
||||
WsfBufFree(pCcb->pScCcb->pPeerPublicKey);
|
||||
pCcb->pScCcb->pPeerPublicKey = NULL;
|
||||
}
|
||||
|
||||
/* free scratch LTK buffer */
|
||||
if (pCcb->pScCcb->pLtk != NULL)
|
||||
{
|
||||
WsfBufFree(pCcb->pScCcb->pLtk);
|
||||
pCcb->pScCcb->pLtk = NULL;
|
||||
}
|
||||
|
||||
/* free scratch local public key buffer */
|
||||
if (pCcb->pScCcb->pLocalPublicKey != NULL)
|
||||
{
|
||||
WsfBufFree(pCcb->pScCcb->pLocalPublicKey);
|
||||
pCcb->pScCcb->pLocalPublicKey = NULL;
|
||||
}
|
||||
|
||||
/* free scratch private key buffer */
|
||||
if (pCcb->pScCcb->pPrivateKey != NULL)
|
||||
{
|
||||
WsfBufFree(pCcb->pScCcb->pPrivateKey);
|
||||
pCcb->pScCcb->pPrivateKey = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform CMAC calculation or send SMP failure
|
||||
*
|
||||
* \param pKey CMAC Key
|
||||
* \param pText Pointer to message text.
|
||||
* \param textLen Length of pText in bytes.
|
||||
* \param pCcb SMP control block.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpScCmac(const uint8_t *pKey, uint8_t *pText, uint8_t textLen, smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
if (SecCmac(pKey, pText, textLen, smpCb.handlerId, pCcb->connId, SMP_MSG_WSF_CMAC_CMPL) == FALSE)
|
||||
{
|
||||
WsfBufFree(pText);
|
||||
pMsg->hdr.status = SMP_ERR_UNSPECIFIED;
|
||||
pMsg->hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
smpSmExecute(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate a buffer or send SMP failure
|
||||
*
|
||||
* \param size Size of buffer to allocate.
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return Allocated buffer.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *SmpScAlloc(uint8_t size, smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pBuf = WsfBufAlloc(size);
|
||||
|
||||
if (pBuf == NULL)
|
||||
{
|
||||
pMsg->hdr.status = SMP_ERR_UNSPECIFIED;
|
||||
pMsg->hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
smpSmExecute(pCcb, pMsg);
|
||||
}
|
||||
|
||||
return pBuf;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Free a buffer allocated with SmpScAlloc.
|
||||
*
|
||||
* \param pBuf Pointer to buffer to free.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpScFree(uint8_t *pBuf)
|
||||
{
|
||||
WsfBufFree(pBuf);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate cryptographic toolkit function F4.
|
||||
*
|
||||
* \param pCcb SMP control block
|
||||
* \param pMsg WSF message.
|
||||
* \param pU Pointer to U parameter of F4 function.
|
||||
* \param pV Pointer to V parameter of F4 function.
|
||||
* \param z Byte with z parameter of F4 function.
|
||||
* \param pX Pointer to X parameter of F4 function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpScCalcF4(smpCcb_t *pCcb, smpMsg_t *pMsg, uint8_t *pU, uint8_t *pV, uint8_t z, uint8_t *pX)
|
||||
{
|
||||
uint8_t *pCmacText;
|
||||
|
||||
/* f4(pU, pV, pX, z) where f4(U, V, x, Z) = AES-CMACx (U || V || Z) */
|
||||
if ((pCmacText = SmpScAlloc(SMP_F4_TEXT_LEN, pCcb, pMsg)) != NULL)
|
||||
{
|
||||
uint8_t *pCatBuf = pCmacText;
|
||||
|
||||
/* Concatinate pU, pV, z */
|
||||
pCatBuf = SmpScCat(pCatBuf, pU, SMP_PUB_KEY_LEN);
|
||||
pCatBuf = SmpScCat(pCatBuf, pV, SMP_PUB_KEY_LEN);
|
||||
*pCatBuf = z;
|
||||
|
||||
/* Execute CMAC with Nb as the key */
|
||||
SmpScCmac(pX, pCmacText, SMP_F4_TEXT_LEN, pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize SMP for LESC security.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpScInit()
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i=0; i<DM_CONN_MAX; i++)
|
||||
{
|
||||
smpCb.ccb[i].pScCcb = &SMP_ScCcb[i];
|
||||
}
|
||||
|
||||
smpCb.procPairing = smpScProcPairing;
|
||||
smpCb.procAuthReq = smpScAuthReq;
|
||||
smpCb.lescSupported = TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Concatinate a buffer and return a pointer to the next byte after concatination.
|
||||
*
|
||||
* \param pDst Pointer to destination.
|
||||
* \param pSrc Pointer to source buffer.
|
||||
* \param len Length of pSrc in bytes.
|
||||
*
|
||||
* \return Pointer to next byte after concatination.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *SmpScCat(uint8_t *pDst, const uint8_t *pSrc, uint8_t len)
|
||||
{
|
||||
memcpy(pDst, pSrc, len);
|
||||
return pDst + len;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Concatinate a 128 bit buffer and return a pointer to the next byte after concatination.
|
||||
*
|
||||
* \param pDst Pointer to destination.
|
||||
* \param pSrc Pointer to source buffer.
|
||||
*
|
||||
* \return Pointer to next byte after concatination.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *SmpScCat128(uint8_t *pDst, uint8_t *pSrc)
|
||||
{
|
||||
Calc128Cpy(pDst, pSrc);
|
||||
return pDst + 16;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send public key to peer.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpScSendPubKey(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* Transmit the public key */
|
||||
uint8_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* set connection busy */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_SMP_PAIR, DM_CONN_BUSY);
|
||||
|
||||
/* start smp response timer */
|
||||
smpStartRspTimer(pCcb);
|
||||
|
||||
/* allocate packet buffer */
|
||||
if ((pPkt = smpMsgAlloc(SMP_PUB_KEY_MSG_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* build packet */
|
||||
p = pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_PUBLIC_KEY);
|
||||
|
||||
/* Store Public Key X data in LSB first format */
|
||||
WStrReverseCpy(p, pCcb->pScCcb->pLocalPublicKey->pubKeyX, SMP_PUB_KEY_LEN);
|
||||
|
||||
/* Store Public Key Y data in LSB first format */
|
||||
WStrReverseCpy(p+SMP_PUB_KEY_LEN, pCcb->pScCcb->pLocalPublicKey->pubKeyY, SMP_PUB_KEY_LEN);
|
||||
|
||||
/* send packet */
|
||||
smpSendPkt(pCcb, pPkt);
|
||||
}
|
||||
else
|
||||
{
|
||||
pMsg->hdr.status = SMP_ERR_UNSPECIFIED;
|
||||
pMsg->hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
smpSmExecute(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send the DH Key check command to the peer.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
* \param pCheck Check data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpScSendDHKeyCheck(smpCcb_t *pCcb, smpMsg_t *pMsg, uint8_t *pCheck)
|
||||
{
|
||||
uint8_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* set connection busy */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_SMP_PAIR, DM_CONN_BUSY);
|
||||
|
||||
/* start smp response timer */
|
||||
smpStartRspTimer(pCcb);
|
||||
|
||||
/* allocate packet buffer */
|
||||
if ((pPkt = smpMsgAlloc(SMP_DHKEY_CHECK_MSG_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* build packet */
|
||||
p = pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_DHKEY_CHECK);
|
||||
|
||||
/* DH Key Check data is result of last CMAC operation (LSB first) */
|
||||
WStrReverseCpy(p, pCheck, SMP_DHKEY_CHECK_LEN);
|
||||
|
||||
/* send packet */
|
||||
smpSendPkt(pCcb, pPkt);
|
||||
}
|
||||
else
|
||||
{
|
||||
pMsg->hdr.status = SMP_ERR_UNSPECIFIED;
|
||||
pMsg->hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
smpSmExecute(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a Pair Rand command to the peer
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
* \param pRand Pointer to rand data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpScSendRand(smpCcb_t *pCcb, smpMsg_t *pMsg, uint8_t *pRand)
|
||||
{
|
||||
/* Transmit the Pair Rand */
|
||||
uint8_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* set connection busy */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_SMP_PAIR, DM_CONN_BUSY);
|
||||
|
||||
/* start smp response timer */
|
||||
smpStartRspTimer(pCcb);
|
||||
|
||||
/* allocate packet buffer */
|
||||
if ((pPkt = smpMsgAlloc(SMP_PAIR_RAND_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* build packet */
|
||||
p = pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_RAND);
|
||||
|
||||
/* Store Random data (LSB first) */
|
||||
WStrReverseCpy(p, pRand, SMP_RAND_LEN);
|
||||
|
||||
/* send packet */
|
||||
smpSendPkt(pCcb, pPkt);
|
||||
}
|
||||
else
|
||||
{
|
||||
pMsg->hdr.status = SMP_ERR_UNSPECIFIED;
|
||||
pMsg->hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
smpSmExecute(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a pairing confirm packet to the peer
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
* \param pCnf Point to confirm data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpScSendPairCnf(smpCcb_t *pCcb, smpMsg_t *pMsg, uint8_t *pCnf)
|
||||
{
|
||||
uint8_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* set connection busy */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_SMP_PAIR, DM_CONN_BUSY);
|
||||
|
||||
/* start smp response timer */
|
||||
smpStartRspTimer(pCcb);
|
||||
|
||||
/* allocate packet buffer */
|
||||
if ((pPkt = smpMsgAlloc(SMP_PAIR_CNF_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* build packet */
|
||||
p = pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_CNF);
|
||||
|
||||
/* Store Confirm data (LSB first) */
|
||||
WStrReverseCpy(p, pCnf, SMP_CONFIRM_LEN);
|
||||
|
||||
/* send packet */
|
||||
smpSendPkt(pCcb, pPkt);
|
||||
}
|
||||
else
|
||||
{
|
||||
pMsg->hdr.status = SMP_ERR_UNSPECIFIED;
|
||||
pMsg->hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
smpSmExecute(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the next bit used in Passkey calculations.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t smpGetPkBit(smpCcb_t *pCcb)
|
||||
{
|
||||
smpScCcb_t *pScCb = pCcb->pScCcb;
|
||||
uint8_t indx = (SMP_RAND_LEN - 1) - pScCb->pkPos / 8;
|
||||
uint8_t bit = pScCb->pkPos % 8;
|
||||
|
||||
if (pScCb->pScratch->Ra[indx] & 1<<bit)
|
||||
return 0x81;
|
||||
|
||||
return 0x80;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get a pointer to the connection's Peer Public Key for LESC pairing
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Peer public Key.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
smpScPubKey_t *smpGetPeerPublicKey(dmConnId_t connId)
|
||||
{
|
||||
smpCcb_t *pCcb = smpCcbByConnId(connId);
|
||||
|
||||
if (pCcb->pScCcb)
|
||||
{
|
||||
return pCcb->pScCcb->pPeerPublicKey;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the connection's Peer Public Key for LESC pairing
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
* \param pKey A Pointer to the peer's public key.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpSetPeerPublicKey(dmConnId_t connId, smpScPubKey_t *pKey)
|
||||
{
|
||||
smpCcb_t *pCcb = smpCcbByConnId(connId);
|
||||
|
||||
if (pCcb->pScCcb)
|
||||
{
|
||||
memcpy(pCcb->pScCcb->pPeerPublicKey, pKey, sizeof(smpScPubKey_t));
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the OOB Confirm value
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
* \param pCnf OOB Configuration.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpScSetOobCfg(dmConnId_t connId, dmSecLescOobCfg_t *pConfig)
|
||||
{
|
||||
smpCcb_t *pCcb = smpCcbByConnId(connId);
|
||||
|
||||
WSF_ASSERT(pCcb->pScCcb->pScratch);
|
||||
|
||||
SMP_TRACE_128("OOB Peer Confirm", pConfig->peerConfirm);
|
||||
SMP_TRACE_128("OOB Peer Random", pConfig->peerRandom);
|
||||
SMP_TRACE_128("OOB Local Confirm", pConfig->localConfirm);
|
||||
SMP_TRACE_128("OOB Local Random", pConfig->localRandom);
|
||||
|
||||
if (pCcb->initiator)
|
||||
{
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->PeerCa_Ea, pConfig->localConfirm);
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Ra, pConfig->localRandom);
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->PeerCb, pConfig->peerConfirm);
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Rb, pConfig->peerRandom);
|
||||
}
|
||||
else
|
||||
{
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->PeerCb, pConfig->localConfirm);
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Rb, pConfig->localRandom);
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->PeerCa_Ea, pConfig->peerConfirm);
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Ra, pConfig->peerRandom);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Format a cancel message with consideration for the attempts counter
|
||||
*
|
||||
* \param connId Connection Id.
|
||||
* \param pHdr Pointer to header of message to fill.
|
||||
* \param status Status to include.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpScGetCancelMsgWithReattempt(dmConnId_t connId, wsfMsgHdr_t *pHdr, uint8_t status)
|
||||
{
|
||||
smpCcb_t *pCcb = smpCcbByConnId(connId);
|
||||
|
||||
SMP_TRACE_INFO1("SmpScGetCancelMsgWithReattempt: %d", pCcb->attempts);
|
||||
|
||||
/* update repeated attempts count */
|
||||
pCcb->attempts++;
|
||||
|
||||
pHdr->param = connId;
|
||||
pHdr->status = status;
|
||||
|
||||
SmpDbPairingFailed(connId);
|
||||
|
||||
if (pCcb->attempts == pSmpCfg->maxAttempts)
|
||||
{
|
||||
/* max attempts reached */
|
||||
pHdr->event = SMP_MSG_INT_MAX_ATTEMPTS;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* else just fail */
|
||||
pHdr->event = SMP_MSG_API_CANCEL_REQ;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process failure and check attempt count
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
*
|
||||
* \return none.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpScFailWithReattempt(smpCcb_t *pCcb)
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
SmpScGetCancelMsgWithReattempt(pCcb->connId, &hdr, SMP_ERR_CONFIRM_VALUE);
|
||||
|
||||
smpSmExecute(pCcb, (smpMsg_t *)&hdr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Convert event into string for diagnostics.
|
||||
*
|
||||
* \param eventId Event ID
|
||||
*
|
||||
* \return Event string.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *smpEventStr(uint8_t eventId)
|
||||
{
|
||||
switch(eventId)
|
||||
{
|
||||
case SMP_MSG_API_PAIR_REQ: return (uint8_t*) "API_PAIR_REQ";
|
||||
case SMP_MSG_API_PAIR_RSP: return (uint8_t*) "API_PAIR_RSP";
|
||||
case SMP_MSG_API_CANCEL_REQ: return (uint8_t*) "API_CANCEL_REQ";
|
||||
case SMP_MSG_API_AUTH_RSP: return (uint8_t*) "API_AUTH_RSP";
|
||||
case SMP_MSG_API_SECURITY_REQ: return (uint8_t*) "API_SECURITY_REQ";
|
||||
case SMP_MSG_CMD_PKT: return (uint8_t*) "CMD_PKT";
|
||||
case SMP_MSG_CMD_PAIRING_FAILED: return (uint8_t*) "CMD_PAIRING_FAILED";
|
||||
case SMP_MSG_DM_ENCRYPT_CMPL: return (uint8_t*) "DM_ENCRYPT_CMPL";
|
||||
case SMP_MSG_DM_ENCRYPT_FAILED: return (uint8_t*) "DM_ENCRYPT_FAILED";
|
||||
case SMP_MSG_DM_CONN_CLOSE: return (uint8_t*) "DM_CONN_CLOSE";
|
||||
case SMP_MSG_WSF_AES_CMPL: return (uint8_t*) "WSF_AES_CMPL";
|
||||
case SMP_MSG_INT_SEND_NEXT_KEY: return (uint8_t*) "INT_SEND_NEXT_KEY";
|
||||
case SMP_MSG_INT_MAX_ATTEMPTS: return (uint8_t*) "INT_MAX_ATTEMPTS";
|
||||
case SMP_MSG_INT_PAIRING_CMPL: return (uint8_t*) "INT_PAIRING_CMPL";
|
||||
case SMP_MSG_INT_RSP_TIMEOUT: return (uint8_t*) "INT_RSP_TIMEOUT";
|
||||
case SMP_MSG_INT_WI_TIMEOUT: return (uint8_t*) "INT_WI_TIMEOUT";
|
||||
case SMP_MSG_INT_LESC: return (uint8_t*) "INT_LESC";
|
||||
case SMP_MSG_INT_LEGACY: return (uint8_t*) "INT_LEGACY";
|
||||
case SMP_MSG_INT_JW_NC: return (uint8_t*) "INT_JW_NC";
|
||||
case SMP_MSG_INT_PASSKEY: return (uint8_t*) "INT_PASSKEY";
|
||||
case SMP_MSG_INT_OOB: return (uint8_t*) "INT_OOB";
|
||||
case SMP_MSG_API_USER_CONFIRM: return (uint8_t*) "API_USER_CONFIRM";
|
||||
case SMP_MSG_API_USER_KEYPRESS: return (uint8_t*) "API_USER_KEYPRESS";
|
||||
case SMP_MSG_API_KEYPRESS_CMPL: return (uint8_t*) "API_KEYPRESS_CMPL";
|
||||
case SMP_MSG_WSF_ECC_CMPL: return (uint8_t*) "WSF_ECC_CMPL";
|
||||
case SMP_MSG_INT_PK_NEXT: return (uint8_t*) "INT_PK_NEXT";
|
||||
case SMP_MSG_INT_PK_CMPL: return (uint8_t*) "INT_PK_CMPL";
|
||||
case SMP_MSG_WSF_CMAC_CMPL: return (uint8_t*) "WSF_CMAC_CMPL";
|
||||
case SMP_MSG_DH_CHECK_FAILURE: return (uint8_t*) "DH_CHECK_FAILURE";
|
||||
|
||||
default: return (uint8_t*) "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Convert state into string for diagnostics.
|
||||
*
|
||||
* \param state State ID
|
||||
*
|
||||
* \return State string.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *smpStateStr(uint8_t state)
|
||||
{
|
||||
uint8_t initiator = smpCb.ccb[0].initiator;
|
||||
|
||||
if (initiator)
|
||||
{
|
||||
return smpiStateStr(state);
|
||||
}
|
||||
|
||||
return smprStateStr(state);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Write an array of bytes to the log.
|
||||
*
|
||||
* \param str Prefix string printed before the byte array
|
||||
* \param pArray Array of bytes
|
||||
* \param len Length of pArray in bytes
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpLogByteArray(char *str, uint8_t *pArray, uint8_t len)
|
||||
{
|
||||
#if WSF_TOKEN_ENABLED == TRUE || WSF_TRACE_ENABLED == TRUE
|
||||
|
||||
char buffer[512];
|
||||
int i, j=0, pos=0;
|
||||
|
||||
SMP_TRACE_INFO0(str);
|
||||
|
||||
while (j < len)
|
||||
{
|
||||
int count = 16;
|
||||
|
||||
if (len-j < count)
|
||||
count = j;
|
||||
|
||||
buffer[pos++] = '[';
|
||||
|
||||
for (i=0; i<count; i++, j++)
|
||||
{
|
||||
uint8_t quad;
|
||||
|
||||
if (i && i % 4 == 0)
|
||||
buffer[pos++] = ' ';
|
||||
|
||||
quad = (pArray[j] >> 4) & 0xf;
|
||||
|
||||
if (quad < 10)
|
||||
buffer[pos++] = '0' + quad;
|
||||
else
|
||||
buffer[pos++] = 'a' + quad - 10;
|
||||
|
||||
quad = pArray[j] & 0xf;
|
||||
|
||||
if (quad < 10)
|
||||
buffer[pos++] = '0' + quad;
|
||||
else
|
||||
buffer[pos++] = 'a' + quad - 10;
|
||||
}
|
||||
|
||||
buffer[pos++] = ']';
|
||||
buffer[pos++] = '\0';
|
||||
SMP_TRACE_INFO0(buffer);
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
if (pos)
|
||||
{
|
||||
buffer[pos++] = ']';
|
||||
buffer[pos++] = '\0';
|
||||
|
||||
SMP_TRACE_INFO0(buffer);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Vendored
+151
@@ -0,0 +1,151 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP main module header file.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef SMP_SC_MAIN_H
|
||||
#define SMP_SC_MAIN_H
|
||||
|
||||
#include "smp_main.h"
|
||||
#include "smp_defs.h"
|
||||
#include "smp_api.h"
|
||||
#include "wsf_trace.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
#if WSF_TOKEN_ENABLED == TRUE || WSF_TRACE_ENABLED == TRUE
|
||||
#define SMP_TRACE_128(msg, ptr) smpLogByteArray(msg, ptr, 16)
|
||||
#define SMP_TRACE_256(msg, ptr) smpLogByteArray(msg, ptr, 32)
|
||||
#else
|
||||
#define SMP_TRACE_128(msg, ptr)
|
||||
#define SMP_TRACE_256(msg, ptr)
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Constants
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Number of bits in the Passkey */
|
||||
#define SMP_PK_BIT_COUNT 20
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* common state machine action functions */
|
||||
void smpScActCleanup(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScActPairingFailed(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScActPairingCancel(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScActAuthSelect(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScActPkSetup(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScActJwncCalcF4(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScActJwncCalcG2(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScActJwncDisplay(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScActPkKeypress(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScActPkSendKeypress(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScActCalcSharedSecret(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScActCalcF5TKey(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScActCalcF5MacKey(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScActCalcF5Ltk(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScActDHKeyCalcF6Ea(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScActDHKeyCalcF6Eb(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
|
||||
/* initiator state machine action functions */
|
||||
void smpiScActSendPubKey(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiScActAuthSelect(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiScActJwncSetup(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiScActJwncSendRand(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiScActJwncCalcF4(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiScActJwncCalcG2(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiScActPkCalcCa(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiScActPkCalcCb(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiScActPkSendCnf(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiScActPkSendRand(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiScActPkCheck(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiScActOobCalcCb(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiScActOobSendRand(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiScActOobProcRand(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiScActDHKeyCheckSend(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiScActDHKeyCheckVerify(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
|
||||
/* responder state machine action functions */
|
||||
void smprScActStoreLescPin(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActSendPubKey(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActJwncSetup(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActJwncCalcG2(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActJwncDisplay(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActJwncSendCnf(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActPkStoreCnf(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActPkStoreCnfAndCalcCb(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActPkStorePinAndCalcCb(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActPkCalcCb(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActPkSendCnf(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActPkCalcCa(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActPkSendRand(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActOobSetup(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActOobCalcCa(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActOobSendRand(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActStoreDhCheck(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActWaitDhCheck(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActDHKeyCheckSend(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprScActCalcDHKey(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
|
||||
/* common functions */
|
||||
bool_t SmpScAllocScratchBuffers(smpCcb_t *pCcb);
|
||||
void SmpScFreeScratchBuffers(smpCcb_t *pCcb);
|
||||
bool_t smpScProcPairing(smpCcb_t *pCcb, uint8_t *pOob, uint8_t *pDisplay);
|
||||
void SmpReverseCpy(uint8_t *pBuf1, uint8_t *pBuf2, uint8_t len);
|
||||
void SmpScCmac(const uint8_t *pKey, uint8_t *pText, uint8_t textLen, smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
uint8_t *SmpScAlloc(uint8_t size, smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void SmpScFree(uint8_t *pBuf);
|
||||
void SmpScCalcF4(smpCcb_t *pCcb, smpMsg_t *pMsg, uint8_t *pU, uint8_t *pV, uint8_t z, uint8_t *pX);
|
||||
uint8_t *SmpScCat(uint8_t *pDst, const uint8_t *pSrc, uint8_t len);
|
||||
uint8_t *SmpScCat128(uint8_t *pDst, uint8_t *pSrc);
|
||||
uint8_t smpGetPkBit(smpCcb_t *pCcb);
|
||||
smpScPubKey_t *smpGetPeerPublicKey(dmConnId_t connId);
|
||||
void smpSetPeerPublicKey(dmConnId_t connId, smpScPubKey_t *pKey);
|
||||
void SmpScSetOobCfg(dmConnId_t connId, dmSecLescOobCfg_t *pConfig);
|
||||
void smpScAuthReq(smpCcb_t *pCcb, uint8_t oob, uint8_t display);
|
||||
void smpScFailWithReattempt(smpCcb_t *pCcb);
|
||||
void SmpScInit(void);
|
||||
|
||||
/* common command send functoins */
|
||||
void smpScSendPubKey(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpScSendDHKeyCheck(smpCcb_t *pCcb, smpMsg_t *pMsg, uint8_t *pCheck);
|
||||
void smpScSendRand(smpCcb_t *pCcb, smpMsg_t *pMsg, uint8_t *pRand);
|
||||
void smpScSendPairCnf(smpCcb_t *pCcb, smpMsg_t *pMsg, uint8_t *pCnf);
|
||||
|
||||
/* diagnostics utility functions */
|
||||
void smpLogByteArray(char *str, uint8_t *pArray, uint8_t len);
|
||||
uint8_t *smpiStateStr(uint8_t state);
|
||||
uint8_t *smprStateStr(uint8_t state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* SMP_SC_MAIN_H */
|
||||
+394
@@ -0,0 +1,394 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP initiator state machine action functions.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/calc128.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
#include "smpi_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate a pairing request.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiActPairReq(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* set connection busy */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_SMP_PAIR, DM_CONN_BUSY);
|
||||
|
||||
/* set next expected packet */
|
||||
pCcb->nextCmdCode = SMP_CMD_PAIR_RSP;
|
||||
|
||||
/* start smp response timer */
|
||||
smpStartRspTimer(pCcb);
|
||||
|
||||
/* allocate scratch buffer */
|
||||
pCcb->pScr = WsfBufAlloc(sizeof(smpScratch_t));
|
||||
/* handle alloc failure */
|
||||
|
||||
/* allocate packet buffer */
|
||||
if ((pPkt = smpMsgAlloc(SMP_PAIR_REQ_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* build packet */
|
||||
p = pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_REQ);
|
||||
UINT8_TO_BSTREAM(p, pSmpCfg->ioCap);
|
||||
UINT8_TO_BSTREAM(p, pMsg->dm.pair.oob);
|
||||
UINT8_TO_BSTREAM(p, pMsg->dm.pair.auth);
|
||||
UINT8_TO_BSTREAM(p, pSmpCfg->maxKeyLen);
|
||||
UINT8_TO_BSTREAM(p, pMsg->dm.pair.iKeyDist);
|
||||
UINT8_TO_BSTREAM(p, pMsg->dm.pair.rKeyDist);
|
||||
|
||||
/* store pair req data */
|
||||
memcpy(pCcb->pairReq, pPkt + L2C_PAYLOAD_START, SMP_PAIR_REQ_LEN);
|
||||
|
||||
/* send packet */
|
||||
smpSendPkt(pCcb, pPkt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if a security request has been received when pairing is cancelled.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiActCheckSecurityReq(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* if security req received send pairing failed */
|
||||
if (pCcb->secReq)
|
||||
{
|
||||
pCcb->secReq = FALSE;
|
||||
smpSendPairingFailed(pCcb, pMsg->hdr.status);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a security request packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiActProcSecurityReq(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
dmSecSlaveIndEvt_t slaveInd;
|
||||
|
||||
pCcb->secReq = TRUE;
|
||||
|
||||
/* parse packet */
|
||||
slaveInd.auth = *(pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN);
|
||||
|
||||
/* pass to DM */
|
||||
slaveInd.hdr.param = pCcb->connId;
|
||||
slaveInd.hdr.event = DM_SEC_SLAVE_REQ_IND;
|
||||
DmSmpCbackExec((dmEvt_t *) &slaveInd);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a pairing response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiActProcPairRsp(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *p;
|
||||
uint8_t oob;
|
||||
uint8_t display;
|
||||
|
||||
/* go to start of packet */
|
||||
p = pMsg->data.pPacket + L2C_PAYLOAD_START;
|
||||
|
||||
/* store packet parameters */
|
||||
memcpy(pCcb->pairRsp, p, SMP_PAIR_RSP_LEN);
|
||||
|
||||
/* verify no new key distribution bits are set */
|
||||
if (((~(pCcb->pairReq[SMP_IKEYDIST_POS]) & p[SMP_IKEYDIST_POS]) != 0) ||
|
||||
((~(pCcb->pairReq[SMP_RKEYDIST_POS]) & p[SMP_RKEYDIST_POS]) != 0))
|
||||
{
|
||||
/* invalid parameters; cancel pairing */
|
||||
pMsg->hdr.status = SMP_ERR_INVALID_PARAM;
|
||||
pMsg->hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
smpSmExecute(pCcb, pMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* proceed to process pairing */
|
||||
if (smpCb.procPairing(pCcb, &oob, &display))
|
||||
{
|
||||
smpCb.procAuthReq(pCcb, oob, display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a pairing confirm packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiActProcPairCnf(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* go to start of packet */
|
||||
p = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;
|
||||
|
||||
/* store confirm value */
|
||||
memcpy(pCcb->pScr->buf.b3, p, SMP_CONFIRM_LEN);
|
||||
|
||||
/* set next expected packet */
|
||||
pCcb->nextCmdCode = SMP_CMD_PAIR_RAND;
|
||||
|
||||
/* start smp response timer */
|
||||
smpStartRspTimer(pCcb);
|
||||
|
||||
/* allocate packet buffer and send pairing random packet */
|
||||
if ((pPkt = smpMsgAlloc(SMP_PAIR_RAND_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* build packet */
|
||||
p = pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_RAND);
|
||||
memcpy(p, pCcb->pScr->buf.b4, SMP_RAND_LEN);
|
||||
|
||||
/* send packet */
|
||||
smpSendPkt(pCcb, pPkt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Verify the calculated confirm value. If ok, proceed with STK calculcation.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiActCnfVerify(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* compare calculated confirm value with value received earlier */
|
||||
if (memcmp(pMsg->aes.pCiphertext, pCcb->pScr->buf.b3, SMP_CONFIRM_LEN) != 0)
|
||||
{
|
||||
pMsg->hdr.status = SMP_ERR_CONFIRM_VALUE;
|
||||
|
||||
/* confirm values don't match; update repeated attempts count */
|
||||
pCcb->attempts++;
|
||||
SmpDbPairingFailed(pCcb->connId);
|
||||
|
||||
if (pCcb->attempts == pSmpCfg->maxAttempts)
|
||||
{
|
||||
/* max attempts reached */
|
||||
pMsg->hdr.event = SMP_MSG_INT_MAX_ATTEMPTS;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* else just fail */
|
||||
pMsg->hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
}
|
||||
|
||||
smpSmExecute(pCcb, pMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* do STK calculation: key, responder rand, initiator rand */
|
||||
smpCalcS1(pCcb, pCcb->pScr->buf.b1, pCcb->pScr->buf.b2, pCcb->pScr->buf.b4);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Encrypt link with STK.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiActStkEncrypt(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t buf[SMP_KEY_LEN];
|
||||
uint8_t encKeyLen;
|
||||
uint8_t secLevel;
|
||||
|
||||
encKeyLen = (pCcb->pairReq[SMP_MAXKEY_POS] < pCcb->pairRsp[SMP_MAXKEY_POS]) ?
|
||||
pCcb->pairReq[SMP_MAXKEY_POS] : pCcb->pairRsp[SMP_MAXKEY_POS];
|
||||
|
||||
/* adjust key based on max key length */
|
||||
memcpy(buf, pMsg->aes.pCiphertext, encKeyLen);
|
||||
memset((buf + encKeyLen), 0, (SMP_KEY_LEN - encKeyLen));
|
||||
|
||||
secLevel = (pCcb->auth & SMP_AUTH_MITM_FLAG) ? DM_SEC_LEVEL_ENC_AUTH : DM_SEC_LEVEL_ENC;
|
||||
DmSmpEncryptReq(pCcb->connId, secLevel, buf);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up key distribution.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiActSetupKeyDist(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t rKeyDist;
|
||||
|
||||
/* start smp response timer once for entire key distribution phase */
|
||||
smpStartRspTimer(pCcb);
|
||||
|
||||
/* initialize parameters in key ind struct */
|
||||
pCcb->pScr->keyInd.hdr.param = pCcb->connId;
|
||||
pCcb->pScr->keyInd.secLevel = (pCcb->auth & SMP_AUTH_MITM_FLAG) ?
|
||||
DM_SEC_LEVEL_ENC_AUTH : DM_SEC_LEVEL_ENC;
|
||||
pCcb->pScr->keyInd.encKeyLen =
|
||||
(pCcb->pairReq[SMP_MAXKEY_POS] < pCcb->pairRsp[SMP_MAXKEY_POS]) ?
|
||||
pCcb->pairReq[SMP_MAXKEY_POS] : pCcb->pairRsp[SMP_MAXKEY_POS];
|
||||
|
||||
pCcb->nextCmdCode = 0;
|
||||
|
||||
/* get negotiated responder key distribution */
|
||||
rKeyDist = pCcb->pairReq[SMP_RKEYDIST_POS] & pCcb->pairRsp[SMP_RKEYDIST_POS];
|
||||
|
||||
/* set up to receive first key distribution packet */
|
||||
if (rKeyDist & SMP_KEY_DIST_ENC)
|
||||
{
|
||||
if (smpCb.lescSupported && pCcb->pScCcb->lescEnabled)
|
||||
{
|
||||
if (rKeyDist & SMP_KEY_DIST_ID)
|
||||
{
|
||||
pCcb->nextCmdCode = SMP_CMD_ID_INFO;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pCcb->nextCmdCode = SMP_CMD_ENC_INFO;
|
||||
}
|
||||
}
|
||||
else if (rKeyDist & SMP_KEY_DIST_ID)
|
||||
{
|
||||
pCcb->nextCmdCode = SMP_CMD_ID_INFO;
|
||||
}
|
||||
else if (rKeyDist & SMP_KEY_DIST_SIGN)
|
||||
{
|
||||
pCcb->nextCmdCode = SMP_CMD_SIGN_INFO;
|
||||
}
|
||||
|
||||
if (pCcb->nextCmdCode == 0)
|
||||
{
|
||||
/* no responder keys to be distributed; start sending keys */
|
||||
pMsg->hdr.event = SMP_MSG_INT_SEND_NEXT_KEY;
|
||||
smpSmExecute(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Receive a key.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiActRcvKey(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t keyDist;
|
||||
|
||||
/* get responder key distribution */
|
||||
keyDist = pCcb->pairReq[SMP_RKEYDIST_POS] & pCcb->pairRsp[SMP_RKEYDIST_POS];
|
||||
|
||||
/* process received key */
|
||||
if (smpProcRcvKey(pCcb, &pCcb->pScr->keyInd, pMsg->data.pPacket, keyDist))
|
||||
{
|
||||
/* no responder keys to be distributed; start sending keys */
|
||||
pCcb->nextCmdCode = 0;
|
||||
|
||||
pMsg->hdr.event = SMP_MSG_INT_SEND_NEXT_KEY;
|
||||
smpSmExecute(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a key.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiActSendKey(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t keyDist;
|
||||
|
||||
/* get initiator key distribution */
|
||||
keyDist = pCcb->pairReq[SMP_IKEYDIST_POS] & pCcb->pairRsp[SMP_IKEYDIST_POS];
|
||||
|
||||
/* send next key */
|
||||
if ((pCcb->nextCmdCode == 0) && smpSendKey(pCcb, keyDist))
|
||||
{
|
||||
/* done sending keys; send ourselves pairing complete msg */
|
||||
pMsg->hdr.event = SMP_MSG_INT_PAIRING_CMPL;
|
||||
smpSmExecute(pCcb, pMsg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Vendored
+58
@@ -0,0 +1,58 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP initiator main module.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef SMPI_MAIN_H
|
||||
#define SMPI_MAIN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* state machine interface */
|
||||
extern const smpSmIf_t smpiSmIf;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/* state machine action functions */
|
||||
void smpiActPairReq(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiActCheckSecurityReq(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiActProcSecurityReq(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiActProcPairRsp(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiActProcPairCnf(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiActCnfVerify(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiActStkEncrypt(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiActSetupKeyDist(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiActRcvKey(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smpiActSendKey(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* SMPI_MAIN_H */
|
||||
Vendored
+497
@@ -0,0 +1,497 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP Secure Connections initiator state machine action functions.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/calc128.h"
|
||||
#include "util/wstr.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
#include "smpi_main.h"
|
||||
#include "smp_sc_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a public key and perform common auth select actions
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActAuthSelect(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* Execute Common Auth Select actions */
|
||||
smpScActAuthSelect(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send public key to peer.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActSendPubKey(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* Next command is the Public Key from the responder */
|
||||
pCcb->nextCmdCode = SMP_CMD_PUBLIC_KEY;
|
||||
|
||||
/* Send the public key */
|
||||
smpScSendPubKey(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Prepare for the Just Works/Numeric Comparison Use Case
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActJwncSetup(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* Select Random Na (128-bits) */
|
||||
SecRand(pCcb->pScCcb->pScratch->Na_Ea, SMP_RAND_LEN);
|
||||
|
||||
SMP_TRACE_128("Rand Na", pCcb->pScCcb->pScratch->Na_Ea);
|
||||
|
||||
/* Set Ra and Rb to sero */
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Ra, (uint8_t*) calc128Zeros);
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Rb, (uint8_t*) calc128Zeros);
|
||||
|
||||
/* Next command is a Pair Confirm from Responder */
|
||||
pCcb->nextCmdCode = SMP_CMD_PAIR_CNF;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send the rand value to the responder for Just Works use case
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActJwncSendRand(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pCb = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;
|
||||
|
||||
/* Cb from responder is in Confirm from rsponder */
|
||||
WStrReverseCpy(pCcb->pScCcb->pScratch->PeerCb, pCb, SMP_CONFIRM_LEN);
|
||||
|
||||
SMP_TRACE_128("Peer Cb", pCcb->pScCcb->pScratch->PeerCb);
|
||||
|
||||
/* Next command is a Pair Random from Responder */
|
||||
pCcb->nextCmdCode = SMP_CMD_PAIR_RAND;
|
||||
|
||||
/* Send the Pair Rand */
|
||||
smpScSendRand(pCcb, pMsg, pCcb->pScCcb->pScratch->Na_Ea);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate the confirm value for just works use case
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActJwncCalcF4(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pNb = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;
|
||||
|
||||
/* Nb from responder is in pPacket */
|
||||
WStrReverseCpy(pCcb->pScCcb->pScratch->Nb_Eb, pNb, SMP_RAND_LEN);
|
||||
|
||||
smpScActJwncCalcF4(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate the verify value for the just works use case
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActJwncCalcG2(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
secCmacMsg_t *pCmac = (secCmacMsg_t *) pMsg;
|
||||
|
||||
SMP_TRACE_128("Local Cb", pCmac->pCiphertext);
|
||||
|
||||
/* Check the result of the F4 confirm calculation */
|
||||
if (memcmp(pCcb->pScCcb->pScratch->PeerCb, pCmac->pCiphertext, SMP_CONFIRM_LEN))
|
||||
{
|
||||
smpScFailWithReattempt(pCcb);
|
||||
}
|
||||
else
|
||||
{
|
||||
smpScActJwncCalcG2(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate the Cai for the passkey use case using toolkit function F4
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActPkCalcCa(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* Record the passkey on the first confirm */
|
||||
if (pCcb->pScCcb->pkPos == 0)
|
||||
{
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Ra, (uint8_t *)calc128Zeros);
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Rb, (uint8_t *)calc128Zeros);
|
||||
|
||||
if (pMsg->dm.authRsp.authDataLen <= 3)
|
||||
{
|
||||
WStrReverseCpy(&pCcb->pScCcb->pScratch->Ra[13], pMsg->dm.authRsp.authData, pMsg->dm.authRsp.authDataLen);
|
||||
WStrReverseCpy(&pCcb->pScCcb->pScratch->Rb[13], pMsg->dm.authRsp.authData, pMsg->dm.authRsp.authDataLen);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get random Nai */
|
||||
SecRand(pCcb->pScCcb->pScratch->Na_Ea, SMP_RAND_LEN);
|
||||
|
||||
SMP_TRACE_128("Rand Na", pCcb->pScCcb->pScratch->Na_Ea);
|
||||
|
||||
/* Ca = f4(PKax, PKbx, Nai, Rai) where f4(U, V, x, Z) = AES-CMACx (U || V || Z) */
|
||||
SmpScCalcF4(pCcb, pMsg,
|
||||
pCcb->pScCcb->pLocalPublicKey->pubKeyX,
|
||||
pCcb->pScCcb->pPeerPublicKey->pubKeyX,
|
||||
smpGetPkBit(pCcb), pCcb->pScCcb->pScratch->Na_Ea);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate the Cbi for the passkey use case using toolkit function F4
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActPkCalcCb(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pNb = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;
|
||||
|
||||
/* Record the Nbi */
|
||||
WStrReverseCpy(pCcb->pScCcb->pScratch->Nb_Eb, pNb, SMP_RAND_LEN);
|
||||
|
||||
/* Cb = f4(PKbx, PKax, Nbi, Rai) where f4(U, V, x, Z) = AES-CMACx (U || V || Z) */
|
||||
SmpScCalcF4(pCcb, pMsg,
|
||||
pCcb->pScCcb->pPeerPublicKey->pubKeyX,
|
||||
pCcb->pScCcb->pLocalPublicKey->pubKeyX,
|
||||
smpGetPkBit(pCcb), pCcb->pScCcb->pScratch->Nb_Eb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send the Cai Confirm for the passkey use case command for passkey pairing
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActPkSendCnf(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
SMP_TRACE_128("Cai", pMsg->aes.pCiphertext);
|
||||
|
||||
/* Send the Cai to the peer */
|
||||
smpScSendPairCnf(pCcb, pMsg, pMsg->aes.pCiphertext);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send the Nai Random command for passkey pairing
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActPkSendRand(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pCb = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;
|
||||
|
||||
/* Record the Cbi from the responder */
|
||||
WStrReverseCpy(pCcb->pScCcb->pScratch->PeerCb, pCb, SMP_CONFIRM_LEN);
|
||||
|
||||
/* Next command is the Pair Random */
|
||||
pCcb->nextCmdCode = SMP_CMD_PAIR_RAND;
|
||||
|
||||
/* Send the Nai */
|
||||
smpScSendRand(pCcb, pMsg, pCcb->pScCcb->pScratch->Na_Ea);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check the Cbi from the responder against the calculated Cbi for the passkey use case
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActPkCheck(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
SMP_TRACE_128("Cbi", pMsg->aes.pCiphertext);
|
||||
|
||||
/* Verify the Calculated Cbi to previously received Cbi */
|
||||
if (memcmp(pCcb->pScCcb->pScratch->PeerCb, pMsg->aes.pCiphertext, SMP_RAND_LEN))
|
||||
{
|
||||
smpScFailWithReattempt(pCcb);
|
||||
}
|
||||
else
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
/* Increment the bit position */
|
||||
if (++pCcb->pScCcb->pkPos >= SMP_PK_BIT_COUNT)
|
||||
{
|
||||
hdr.event = SMP_MSG_INT_PK_CMPL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Next command is the Pair Confirm */
|
||||
pCcb->nextCmdCode = SMP_CMD_PAIR_CNF;
|
||||
|
||||
hdr.event = SMP_MSG_INT_PK_NEXT;
|
||||
}
|
||||
|
||||
/* Post an event to move to the next passkey confirm or complete the process */
|
||||
hdr.param = pCcb->connId;
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate the Cb to compare against the Cb sent via OOB methods
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActOobCalcCb(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* If the peer device's OOB data flag does not indicate remote OOB data has been received,
|
||||
clear Ra. */
|
||||
if (pCcb->pairRsp[SMP_OOB_POS] != SMP_OOB_DATA_PRESENT)
|
||||
{
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Ra, (uint8_t*) calc128Zeros);
|
||||
}
|
||||
|
||||
/* If we indicated the presence of remote OOB data has been received, calculate Cb. */
|
||||
if (pCcb->pairReq[SMP_OOB_POS] == SMP_OOB_DATA_PRESENT)
|
||||
{
|
||||
/* Calculate Cb using Toolkit function F4 */
|
||||
SmpScCalcF4(pCcb, pMsg,
|
||||
pCcb->pScCcb->pPeerPublicKey->pubKeyX,
|
||||
pCcb->pScCcb->pPeerPublicKey->pubKeyX,
|
||||
0, pCcb->pScCcb->pScratch->Rb);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Simulate the cb calculation is complete and clear rb */
|
||||
secCmacMsg_t msg;
|
||||
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Rb, (uint8_t*)calc128Zeros);
|
||||
|
||||
msg.hdr.param = pCcb->connId;
|
||||
msg.hdr.event = SMP_MSG_WSF_CMAC_CMPL;
|
||||
msg.pPlainText = NULL;
|
||||
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &msg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send random Na to responder in OOB pairing
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActOobSendRand(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* Only compare Cb if we indicated that we received OOB data. */
|
||||
if (pCcb->pairReq[SMP_OOB_POS] == SMP_OOB_DATA_PRESENT)
|
||||
{
|
||||
SMP_TRACE_128("Initiator Cb", pMsg->aes.pCiphertext);
|
||||
|
||||
/* Verify the Cb matches the value passed from the responder via OOB methods */
|
||||
if (memcmp(pCcb->pScCcb->pScratch->PeerCb, pMsg->aes.pCiphertext, SMP_CONFIRM_LEN))
|
||||
{
|
||||
smpScFailWithReattempt(pCcb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Next command is a Pair Rand from Responder */
|
||||
pCcb->nextCmdCode = SMP_CMD_PAIR_RAND;
|
||||
|
||||
/* Calculate a the Na */
|
||||
SecRand(pCcb->pScCcb->pScratch->Na_Ea, SMP_RAND_LEN);
|
||||
|
||||
SMP_TRACE_128("Rand Na", pCcb->pScCcb->pScratch->Na_Ea);
|
||||
|
||||
/* Send the Na to the responder */
|
||||
smpScSendRand(pCcb, pMsg, pCcb->pScCcb->pScratch->Na_Ea);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process random Nb from responder in OOB pairing
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActOobProcRand(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pNb = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;
|
||||
|
||||
/* Copy the Nb from the responder */
|
||||
WStrReverseCpy(pCcb->pScCcb->pScratch->Nb_Eb, pNb, SMP_RAND_LEN);
|
||||
|
||||
/* Initiate the DH Check */
|
||||
smpScActCalcSharedSecret(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send the DH Key check.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActDHKeyCheckSend(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
SMP_TRACE_128("DHKey Eb", pMsg->aes.pCiphertext);
|
||||
|
||||
/* Copy Eb from the smpScActDHKeyCalcF6Eb in LSB first format (as it will be received from peer) */
|
||||
WStrReverseCpy(pCcb->pScCcb->pScratch->Nb_Eb, pMsg->aes.pCiphertext, SMP_RAND_LEN);
|
||||
|
||||
/* Next cmd message is the DH Key Check from the responder */
|
||||
pCcb->nextCmdCode = SMP_CMD_DHKEY_CHECK;
|
||||
|
||||
/* Send the DH Key check with Ea to the responder */
|
||||
smpScSendDHKeyCheck(pCcb, pMsg, pCcb->pScCcb->pScratch->Na_Ea);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Verify the DH Key Check.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smpiScActDHKeyCheckVerify(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pEbPeer = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;;
|
||||
|
||||
/* Verify the DH Key Check Eb with the value received from the responder */
|
||||
if (memcmp(pEbPeer, pCcb->pScCcb->pScratch->Nb_Eb, SMP_RAND_LEN) == 0)
|
||||
{
|
||||
uint8_t buf[SMP_KEY_LEN];
|
||||
uint8_t encKeyLen;
|
||||
|
||||
encKeyLen = (pCcb->pairReq[SMP_MAXKEY_POS] < pCcb->pairRsp[SMP_MAXKEY_POS]) ?
|
||||
pCcb->pairReq[SMP_MAXKEY_POS] : pCcb->pairRsp[SMP_MAXKEY_POS];
|
||||
|
||||
/* Adjust key based on max key length */
|
||||
memcpy(buf, pCcb->pScCcb->pLtk->ltk_t, encKeyLen);
|
||||
memset((buf + encKeyLen), 0, (SMP_KEY_LEN - encKeyLen));
|
||||
|
||||
/* Initiate encryption */
|
||||
DmSmpEncryptReq(pCcb->connId, smpGetScSecLevel(pCcb), buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* DH Key check failed */
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
hdr.param = pCcb->connId;
|
||||
hdr.status = SMP_ERR_DH_KEY_CHECK;
|
||||
|
||||
/* update repeated attempts count */
|
||||
pCcb->attempts++;
|
||||
SmpDbPairingFailed(pCcb->connId);
|
||||
|
||||
if (pCcb->attempts == pSmpCfg->maxAttempts)
|
||||
{
|
||||
/* max attempts reached */
|
||||
hdr.event = SMP_MSG_INT_MAX_ATTEMPTS;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* else just fail */
|
||||
hdr.event = SMP_MSG_DH_CHECK_FAILURE;
|
||||
}
|
||||
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
}
|
||||
}
|
||||
Vendored
+643
@@ -0,0 +1,643 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP Secure Connections initiator state machine.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
#include "smpi_main.h"
|
||||
#include "smp_sc_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Action function enumeration */
|
||||
enum
|
||||
{
|
||||
SMPI_SC_ACT_NONE, /*!< No Action */
|
||||
SMPI_SC_ACT_CLEANUP, /*!< Process Pairing Cleanup */
|
||||
SMPI_SC_ACT_PAIRING_FAILED, /*!< Process Pairing Failed */
|
||||
SMPI_SC_ACT_PAIRING_CANCEL, /*!< Process Pairing Canceled */
|
||||
SMPI_SC_ACT_PAIR_CNF_CALC_1, /*!< Process Confirm Value Calculation 1 */
|
||||
SMPI_SC_ACT_PAIR_CNF_CALC_2, /*!< Process Confirm Value Calculation 2 */
|
||||
SMPI_SC_ACT_SEND_PAIR_CNF, /*!< Process Send Confirm Value */
|
||||
SMPI_SC_ACT_PAIR_CNF_VER_CALC_1, /*!< Process Received Confirm Value Verification Calculation 1 */
|
||||
SMPI_SC_ACT_PAIR_CNF_VER_CALC_2, /*!< Process Received Confirm Value Verification Calculation 2 */
|
||||
SMPI_SC_ACT_MAX_ATTEMPTS, /*!< Process Maximum Attempts */
|
||||
SMPI_SC_ACT_ATTEMPT_RCVD, /*!< Process Attempts Received */
|
||||
SMPI_SC_ACT_CHECK_ATTEMPTS, /*!< Process Check Attempts */
|
||||
SMPI_SC_ACT_NOTIFY_DM_ATTEMPTS, /*!< Process Notify DM of Attempts Failure */
|
||||
SMPI_SC_ACT_NOTIFY_DM_RSP_TO, /*!< Process Notify DM of Response Timeout Failure */
|
||||
SMPI_SC_ACT_PAIRING_CMPL, /*!< Process Pairing Complete */
|
||||
SMPI_SC_ACT_PAIR_REQ, /*!< Process Send Pairing Request */
|
||||
SMPI_SC_ACT_CHECK_SECURITY_REQ, /*!< Process Check Slave Security Request */
|
||||
SMPI_SC_ACT_PROC_SECURITY_REQ, /*!< Process Slave Security Request */
|
||||
SMPI_SC_ACT_PROC_PAIR_RSP, /*!< Process Pairing Response */
|
||||
SMPI_SC_ACT_PROC_PAIR_CNF, /*!< Process Pairing Confirmation */
|
||||
SMPI_SC_ACT_CNF_VERIFY, /*!< Process Verify Received Confirm Value */
|
||||
SMPI_SC_ACT_STK_ENCRYPT, /*!< Process STK Encryption */
|
||||
SMPI_SC_ACT_SETUP_KEY_DIST, /*!< Process Setup Key Distribution */
|
||||
SMPI_SC_ACT_RCV_KEY, /*!< Process Received Key */
|
||||
SMPI_SC_ACT_SEND_KEY, /*!< Process Send Key */
|
||||
|
||||
SMPI_SC_ACT_SEND_PUB_KEY, /*!< Process Send Public Key */
|
||||
SMPI_SC_ACT_SC_AUTH_SELECT, /*!< Process Select Authentication Method */
|
||||
|
||||
SMPI_SC_ACT_JWNC_SETUP, /*!< Process Just Works/Numeric Comparison Setup */
|
||||
SMPI_SC_ACT_JWNC_SEND_RAND, /*!< Process JW/NC Send Random Value */
|
||||
SMPI_SC_ACT_JWNC_CALC_F4, /*!< Process JW/NC Calculate F4 */
|
||||
SMPI_SC_ACT_JWNC_CALC_G2, /*!< Process JW/NC Calculate G2 */
|
||||
SMPI_SC_ACT_JWNC_DISPLAY, /*!< Process Display Numeric Comparison */
|
||||
|
||||
SMPI_SC_ACT_PK_SETUP, /*!< Process Passkey Setup */
|
||||
SMPI_SC_ACT_PK_KEYPRESS, /*!< Process Passkey Keypress */
|
||||
SMPI_SC_ACT_PK_SEND_KEYPRESS, /*!< Process Passkey Send Keypress */
|
||||
SMPI_SC_ACT_PK_CALC_CA, /*!< Process Passkey Calcuate Ca */
|
||||
SMPI_SC_ACT_PK_CALC_CB, /*!< Process Passkey Calculate Cb */
|
||||
SMPI_SC_ACT_PK_SEND_CNF, /*!< Process Passkey Send Confirm Value */
|
||||
SMPI_SC_ACT_PK_SEND_RAND, /*!< Process Passkey Send Random Value */
|
||||
SMPI_SC_ACT_PK_CHECK, /*!< Process Passkey Check Passkey Complete */
|
||||
|
||||
SMPI_SC_ACT_OOB_CALC_CB, /*!< Process OOB Calculate Cb */
|
||||
SMPI_SC_ACT_OOB_SEND_RAND, /*!< Process OOB Send Random Value */
|
||||
SMPI_SC_ACT_OOB_PROC_RAND, /*!< Process OOB Process Received Random Value */
|
||||
|
||||
SMPI_SC_ACT_CALC_DHKEY, /*!< Process DH Key Calculation */
|
||||
SMPI_SC_ACT_CALC_F5_TKEY, /*!< Process Calculate F5 Temporary Key */
|
||||
SMPI_SC_ACT_CALC_F5_MACKEY, /*!< Process Calculate F5 MAC Key */
|
||||
SMPI_SC_ACT_CALC_F5_LTK, /*!< Process Calculate F5 LTK */
|
||||
SMPI_SC_ACT_CALC_F6_EA, /*!< Process Calculate F6 Ea */
|
||||
SMPI_SC_ACT_CALC_F6_EB, /*!< Process Calculate F6 Eb */
|
||||
SMPI_SC_ACT_SEND_DH_CHECK, /*!< Process Send DH Key Check */
|
||||
SMPI_SC_ACT_VERIFY_DH_CHECK, /*!< Process Verify Received DH Key Check */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Static Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Action function table; order matches action function enumeration */
|
||||
static const smpAct_t smpiScActionTbl[] =
|
||||
{
|
||||
smpActNone,
|
||||
smpScActCleanup,
|
||||
smpScActPairingFailed,
|
||||
smpScActPairingCancel,
|
||||
smpActPairCnfCalc1,
|
||||
smpActPairCnfCalc2,
|
||||
smpActSendPairCnf,
|
||||
smpActPairCnfVerCalc1,
|
||||
smpActPairCnfVerCalc2,
|
||||
smpActMaxAttempts,
|
||||
smpActAttemptRcvd,
|
||||
smpActCheckAttempts,
|
||||
smpActNotifyDmAttemptsFailure,
|
||||
smpActNotifyDmRspToFailure,
|
||||
smpActPairingCmpl,
|
||||
smpiActPairReq,
|
||||
smpiActCheckSecurityReq,
|
||||
smpiActProcSecurityReq,
|
||||
smpiActProcPairRsp,
|
||||
smpiActProcPairCnf,
|
||||
smpiActCnfVerify,
|
||||
smpiActStkEncrypt,
|
||||
smpiActSetupKeyDist,
|
||||
smpiActRcvKey,
|
||||
smpiActSendKey,
|
||||
|
||||
smpiScActSendPubKey,
|
||||
smpiScActAuthSelect,
|
||||
|
||||
smpiScActJwncSetup,
|
||||
smpiScActJwncSendRand,
|
||||
smpiScActJwncCalcF4,
|
||||
smpiScActJwncCalcG2,
|
||||
smpScActJwncDisplay,
|
||||
|
||||
smpScActPkSetup,
|
||||
smpScActPkKeypress,
|
||||
smpScActPkSendKeypress,
|
||||
smpiScActPkCalcCa,
|
||||
smpiScActPkCalcCb,
|
||||
smpiScActPkSendCnf,
|
||||
smpiScActPkSendRand,
|
||||
smpiScActPkCheck,
|
||||
|
||||
smpiScActOobCalcCb,
|
||||
smpiScActOobSendRand,
|
||||
smpiScActOobProcRand,
|
||||
|
||||
smpScActCalcSharedSecret,
|
||||
smpScActCalcF5TKey,
|
||||
smpScActCalcF5MacKey,
|
||||
smpScActCalcF5Ltk,
|
||||
smpScActDHKeyCalcF6Ea,
|
||||
smpScActDHKeyCalcF6Eb,
|
||||
smpiScActDHKeyCheckSend,
|
||||
smpiScActDHKeyCheckVerify,
|
||||
};
|
||||
|
||||
/*! State table for common actions */
|
||||
static const smpTblEntry_t smpiScStateTblCommon[SMP_STATE_TBL_COMMON_MAX] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPI_SC_SM_ST_IDLE, SMPI_SC_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPI_SC_SM_ST_IDLE, SMPI_SC_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPI_SC_SM_ST_IDLE, SMPI_SC_ACT_PAIRING_CANCEL},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPI_SC_SM_ST_RSP_TO, SMPI_SC_ACT_PAIRING_FAILED},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
|
||||
/*! State table for IDLE */
|
||||
static const smpTblEntry_t smpiScStateTblIdle[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_PAIR_REQ, SMPI_SC_SM_ST_PAIR_RSP, SMPI_SC_ACT_PAIR_REQ},
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPI_SC_SM_ST_IDLE, SMPI_SC_ACT_CLEANUP},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPI_SC_SM_ST_IDLE, SMPI_SC_ACT_CHECK_SECURITY_REQ},
|
||||
{SMP_MSG_CMD_PKT, SMPI_SC_SM_ST_IDLE, SMPI_SC_ACT_PROC_SECURITY_REQ},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPI_SC_SM_ST_IDLE, SMPI_SC_ACT_NONE},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPI_SC_SM_ST_IDLE, SMPI_SC_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for PAIR_RSP */
|
||||
static const smpTblEntry_t smpiScStateTblPairRsp[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SC_SM_ST_MODE_SELECT, SMPI_SC_ACT_PROC_PAIR_RSP},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_MODE_SELECT */
|
||||
static const smpTblEntry_t smpiScStateTblModeSelect[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_INT_LESC, SMPI_SC_SM_ST_LESC_PIN, SMPI_SC_ACT_NONE},
|
||||
{SMP_MSG_INT_LEGACY, SMPI_SC_SM_ST_LEGACY_PIN, SMPI_SC_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_LESC_PIN */
|
||||
static const smpTblEntry_t smpiScStateTblLescPin[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_AUTH_RSP, SMPI_SC_SM_ST_PUB_KEY, SMPI_SC_ACT_SEND_PUB_KEY},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_PUB_KEY */
|
||||
static const smpTblEntry_t smpiScStateTblPubKey[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SC_SM_ST_AUTH_SELECT, SMPI_SC_ACT_SC_AUTH_SELECT},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_AUTH_SELECT */
|
||||
static const smpTblEntry_t smpiScStateTblAuthSelect[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_INT_JW_NC, SMPI_SC_SM_ST_JWNC_WAIT_CNF, SMPI_SC_ACT_JWNC_SETUP},
|
||||
{SMP_MSG_INT_PASSKEY, SMPI_SC_SM_ST_PK_KEYPRESS, SMPI_SC_ACT_PK_SETUP},
|
||||
{SMP_MSG_INT_OOB, SMPI_SC_SM_ST_OOB_SEND_RAND, SMPI_SC_ACT_OOB_CALC_CB},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_JWNC_WAIT_CNF */
|
||||
static const smpTblEntry_t smpiScStateTblJwNcWaitCnf[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SC_SM_ST_JWNC_RAND, SMPI_SC_ACT_JWNC_SEND_RAND},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_JWNC_RAND */
|
||||
static const smpTblEntry_t smpiScStateTblJwNcRand[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SC_SM_ST_JWNC_CHECK_1, SMPI_SC_ACT_JWNC_CALC_F4},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_JWNC_CHECK_1 */
|
||||
static const smpTblEntry_t smpiScStateTblJwNcCheck1[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPI_SC_SM_ST_JWNC_CHECK_2, SMPI_SC_ACT_JWNC_CALC_G2},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_JWNC_CHECK_2 */
|
||||
static const smpTblEntry_t smpiScStateTblJwNcCheck2[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPI_SC_SM_ST_JWNC_WAIT_USER, SMPI_SC_ACT_JWNC_DISPLAY},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPI_SC_SM_ST_ATTEMPTS, SMPI_SC_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_JWNC_WAIT_USER */
|
||||
static const smpTblEntry_t smpiScStateTblJwNcWaitUser[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_USER_CONFIRM, SMPI_SC_SM_ST_CALC_DHKEY, SMPI_SC_ACT_CALC_DHKEY},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPI_SC_SM_ST_ATTEMPTS, SMPI_SC_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_PK_KEYPRESS */
|
||||
static const smpTblEntry_t smprScStateTblPasskeyKeypress[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SC_SM_ST_PK_KEYPRESS, SMPI_SC_ACT_PK_KEYPRESS},
|
||||
{SMP_MSG_API_USER_KEYPRESS, SMPI_SC_SM_ST_PK_KEYPRESS, SMPI_SC_ACT_PK_SEND_KEYPRESS},
|
||||
{SMP_MSG_API_AUTH_RSP, SMPI_SC_SM_ST_PK_CALC, SMPI_SC_ACT_PK_CALC_CA},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_PK_CALC */
|
||||
static const smpTblEntry_t smpiScStateTblPasskeyCalc[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPI_SC_SM_ST_PK_CNF, SMPI_SC_ACT_PK_SEND_CNF},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_PK_CNF */
|
||||
static const smpTblEntry_t smpiScStateTblPasskeyCnf[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SC_SM_ST_PK_RAND, SMPI_SC_ACT_PK_SEND_RAND},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_PK_RAND */
|
||||
static const smpTblEntry_t smpiScStateTblPasskeyRand[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SC_SM_ST_PK_CHECK, SMPI_SC_ACT_PK_CALC_CB},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_PK_CHECK */
|
||||
static const smpTblEntry_t smpiScStateTblPasskeyCheck[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPI_SC_SM_ST_PK_REPEAT, SMPI_SC_ACT_PK_CHECK},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_PK_REPEAT */
|
||||
static const smpTblEntry_t smpiScStateTblPasskeyRepeat[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_INT_PK_NEXT, SMPI_SC_SM_ST_PK_CALC, SMPI_SC_ACT_PK_CALC_CA},
|
||||
{SMP_MSG_INT_PK_CMPL, SMPI_SC_SM_ST_CALC_DHKEY, SMPI_SC_ACT_CALC_DHKEY},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPI_SC_SM_ST_ATTEMPTS, SMPI_SC_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_OOB_SEND_RAND */
|
||||
static const smpTblEntry_t smpiScStateTblOobSendRand[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPI_SC_SM_ST_OOB_WAIT_RAND, SMPI_SC_ACT_OOB_SEND_RAND},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_OOB_WAIT_RAND */
|
||||
static const smpTblEntry_t smpiScStateTblOobWaitRand[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SC_SM_ST_CALC_DHKEY, SMPI_SC_ACT_OOB_PROC_RAND},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPI_SC_SM_ST_ATTEMPTS, SMPI_SC_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_CALC_DHKEY */
|
||||
static const smpTblEntry_t smpiScStateTblCalcDHKey[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_ECC_CMPL, SMPI_SC_SM_ST_CALC_F5_TKEY, SMPI_SC_ACT_CALC_F5_TKEY},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_CALC_F5_TKEY */
|
||||
static const smpTblEntry_t smpiScStateTblCalcF5TKey[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPI_SC_SM_ST_CALC_F5_MACKEY, SMPI_SC_ACT_CALC_F5_MACKEY},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPI_SC_SM_ST_ATTEMPTS, SMPI_SC_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_CALC_F5_MACKEY */
|
||||
static const smpTblEntry_t smpiScStateTblCalcF5MacKey[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPI_SC_SM_ST_CALC_F5_LTK, SMPI_SC_ACT_CALC_F5_LTK},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_CALC_F5_LTK */
|
||||
static const smpTblEntry_t smpiScStateTblCalcF5LTK[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPI_SC_SM_ST_CALC_F6_EA, SMPI_SC_ACT_CALC_F6_EA},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_CALC_F6_EA */
|
||||
static const smpTblEntry_t smpiScStateTblDhCalcF6Ea[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPI_SC_SM_ST_CALC_F6_EB, SMPI_SC_ACT_CALC_F6_EB},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_CALC_F6_EB */
|
||||
static const smpTblEntry_t smpiScStateTblDhCalcF6Eb[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPI_SC_SM_ST_VERIFY_DH_CHECK, SMPI_SC_ACT_SEND_DH_CHECK},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPI_SC_SM_ST_VERIFY_DH_CHECK */
|
||||
static const smpTblEntry_t smpiScStateTblVerifyDHCheck[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SC_SM_ST_ENCRYPT, SMPI_SC_ACT_VERIFY_DH_CHECK},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for Legacy SMPI_SC_SM_ST_LEGACY_PIN */
|
||||
static const smpTblEntry_t smpiScStateTblLegacyPin[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_AUTH_RSP, SMPI_SC_SM_ST_CNF_CALC_1, SMPI_SC_ACT_PAIR_CNF_CALC_1},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for Legacy CNF_CALC_1 */
|
||||
static const smpTblEntry_t smpiScStateTblCnfCalc1[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPI_SC_SM_ST_CNF_CALC_2, SMPI_SC_ACT_PAIR_CNF_CALC_2},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for Legacy CNF_CALC_2 */
|
||||
static const smpTblEntry_t smpiScStateTblCnfCalc2[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPI_SC_SM_ST_PAIR_CNF, SMPI_SC_ACT_SEND_PAIR_CNF},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for Legacy PAIR_CNF */
|
||||
static const smpTblEntry_t smpiScStateTblPairCnf[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SC_SM_ST_PAIR_RAND, SMPI_SC_ACT_PROC_PAIR_CNF},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for Legacy PAIR_RAND */
|
||||
static const smpTblEntry_t smpiScStateTblPairRand[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SC_SM_ST_CNF_VER_CALC_1, SMPI_SC_ACT_PAIR_CNF_VER_CALC_1},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for Legacy CNF_VER_CALC_1 */
|
||||
static const smpTblEntry_t smpiScStateTblCnfVerCalc1[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPI_SC_SM_ST_CNF_VER_CALC_2, SMPI_SC_ACT_PAIR_CNF_VER_CALC_2},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for Legacy CNF_VER_CALC_2 */
|
||||
static const smpTblEntry_t smpiScStateTblCnfVerCalc2[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPI_SC_SM_ST_STK_CALC, SMPI_SC_ACT_CNF_VERIFY},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for Legacy STK_CALC */
|
||||
static const smpTblEntry_t smpiScStateTblStkCalc[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPI_SC_SM_ST_ENCRYPT, SMPI_SC_ACT_STK_ENCRYPT},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPI_SC_SM_ST_ATTEMPTS, SMPI_SC_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for ENCRYPT */
|
||||
static const smpTblEntry_t smpiScStateTblEncrypt[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_DM_ENCRYPT_CMPL, SMPI_SC_SM_ST_KEY_DIST, SMPI_SC_ACT_SETUP_KEY_DIST},
|
||||
{SMP_MSG_DM_ENCRYPT_FAILED, SMPI_SC_SM_ST_IDLE, SMPI_SC_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPI_SC_SM_ST_ENCRYPT, SMPI_SC_ACT_NONE},
|
||||
{SMP_MSG_DH_CHECK_FAILURE, SMPI_SC_SM_ST_IDLE, SMPI_SC_ACT_PAIRING_CANCEL},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPI_SC_SM_ST_ATTEMPTS, SMPI_SC_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for KEY_DIST */
|
||||
static const smpTblEntry_t smpiScStateTblKeyDist[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SC_SM_ST_KEY_DIST, SMPI_SC_ACT_RCV_KEY},
|
||||
{SMP_MSG_INT_SEND_NEXT_KEY, SMPI_SC_SM_ST_KEY_DIST, SMPI_SC_ACT_SEND_KEY},
|
||||
{SMP_MSG_INT_PAIRING_CMPL, SMPI_SC_SM_ST_IDLE, SMPI_SC_ACT_PAIRING_CMPL},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPI_SC_SM_ST_KEY_DIST, SMPI_SC_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for ATTEMPTS */
|
||||
static const smpTblEntry_t smpiScStateTblAttempts[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_INT_WI_TIMEOUT, SMPI_SC_SM_ST_IDLE, SMPI_SC_ACT_CHECK_ATTEMPTS},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPI_SC_SM_ST_RSP_TO, SMPI_SC_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_CMD_PKT, SMPI_SC_SM_ST_ATTEMPTS, SMPI_SC_ACT_ATTEMPT_RCVD},
|
||||
{SMP_MSG_API_PAIR_REQ, SMPI_SC_SM_ST_PAIR_RSP, SMPI_SC_ACT_NOTIFY_DM_ATTEMPTS},
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPI_SC_SM_ST_IDLE, SMPI_SC_ACT_CLEANUP},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPI_SC_SM_ST_IDLE, SMPI_SC_ACT_CLEANUP},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPI_SC_SM_ST_ATTEMPTS, SMPI_SC_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for RSP_TO */
|
||||
static const smpTblEntry_t smpiScStateTblRspTo[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPI_SC_SM_ST_IDLE, SMPI_SC_ACT_CLEANUP},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPI_SC_SM_ST_RSP_TO, SMPI_SC_ACT_NONE},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPI_SC_SM_ST_RSP_TO, SMPI_SC_ACT_NONE},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPI_SC_SM_ST_RSP_TO, SMPI_SC_ACT_NONE},
|
||||
{SMP_MSG_API_PAIR_REQ, SMPI_SC_SM_ST_RSP_TO, SMPI_SC_ACT_NOTIFY_DM_RSP_TO},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! Table of individual state tables */
|
||||
static const smpTblEntry_t * const smpiScStateTbl[] =
|
||||
{
|
||||
smpiScStateTblIdle,
|
||||
smpiScStateTblPairRsp,
|
||||
smpiScStateTblModeSelect,
|
||||
smpiScStateTblLescPin,
|
||||
smpiScStateTblPubKey,
|
||||
smpiScStateTblAuthSelect,
|
||||
smpiScStateTblJwNcWaitCnf,
|
||||
smpiScStateTblJwNcRand,
|
||||
smpiScStateTblJwNcCheck1,
|
||||
smpiScStateTblJwNcCheck2,
|
||||
smpiScStateTblJwNcWaitUser,
|
||||
|
||||
smprScStateTblPasskeyKeypress,
|
||||
smpiScStateTblPasskeyCalc,
|
||||
smpiScStateTblPasskeyCnf,
|
||||
smpiScStateTblPasskeyRand,
|
||||
smpiScStateTblPasskeyCheck,
|
||||
smpiScStateTblPasskeyRepeat,
|
||||
|
||||
smpiScStateTblOobSendRand,
|
||||
smpiScStateTblOobWaitRand,
|
||||
|
||||
smpiScStateTblCalcDHKey,
|
||||
smpiScStateTblCalcF5TKey,
|
||||
smpiScStateTblCalcF5MacKey,
|
||||
smpiScStateTblCalcF5LTK,
|
||||
smpiScStateTblDhCalcF6Ea,
|
||||
smpiScStateTblDhCalcF6Eb,
|
||||
smpiScStateTblVerifyDHCheck,
|
||||
|
||||
smpiScStateTblLegacyPin,
|
||||
smpiScStateTblCnfCalc1,
|
||||
smpiScStateTblCnfCalc2,
|
||||
smpiScStateTblPairCnf,
|
||||
smpiScStateTblPairRand,
|
||||
smpiScStateTblCnfVerCalc1,
|
||||
smpiScStateTblCnfVerCalc2,
|
||||
smpiScStateTblStkCalc,
|
||||
smpiScStateTblEncrypt,
|
||||
smpiScStateTblKeyDist,
|
||||
smpiScStateTblAttempts,
|
||||
smpiScStateTblRspTo
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! state machine interface */
|
||||
const smpSmIf_t smpiScSmIf =
|
||||
{
|
||||
smpiScStateTbl,
|
||||
smpiScActionTbl,
|
||||
smpiScStateTblCommon
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize SMP initiator role.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpiScInit(void)
|
||||
{
|
||||
/* set up callback interface */
|
||||
smpCb.pMaster = &smpiScSmIf;
|
||||
|
||||
/* General SMP LESC Initialization */
|
||||
SmpScInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Convert state into string for diagnostics.
|
||||
*
|
||||
* \param state State ID
|
||||
*
|
||||
* \return State string.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *smpiStateStr(uint8_t state)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case SMPI_SC_SM_ST_IDLE: return (uint8_t*) "I_IDLE";
|
||||
case SMPI_SC_SM_ST_PAIR_RSP: return (uint8_t*) "I_PAIR_RSP";
|
||||
case SMPI_SC_SM_ST_MODE_SELECT: return (uint8_t*) "I_MODE_SELECT";
|
||||
case SMPI_SC_SM_ST_LESC_PIN: return (uint8_t*) "I_LESC_PIN";
|
||||
case SMPI_SC_SM_ST_PUB_KEY: return (uint8_t*) "I_PUB_KEY";
|
||||
case SMPI_SC_SM_ST_AUTH_SELECT: return (uint8_t*) "I_AUTH_SELECT";
|
||||
case SMPI_SC_SM_ST_JWNC_WAIT_CNF: return (uint8_t*) "I_JWNC_WAIT_CNF";
|
||||
case SMPI_SC_SM_ST_JWNC_RAND: return (uint8_t*) "I_JWNC_RAND";
|
||||
case SMPI_SC_SM_ST_JWNC_CHECK_1: return (uint8_t*) "I_JWNC_CHECK_1";
|
||||
case SMPI_SC_SM_ST_JWNC_CHECK_2: return (uint8_t*) "I_JWNC_CHECK_2";
|
||||
case SMPI_SC_SM_ST_JWNC_WAIT_USER: return (uint8_t*) "I_JWNC_WAIT_USER";
|
||||
case SMPI_SC_SM_ST_PK_KEYPRESS: return (uint8_t*) "I_PK_KEYPRESS";
|
||||
case SMPI_SC_SM_ST_PK_CALC: return (uint8_t*) "I_PK_CALC";
|
||||
case SMPI_SC_SM_ST_PK_CNF: return (uint8_t*) "I_PK_CNF";
|
||||
case SMPI_SC_SM_ST_PK_RAND: return (uint8_t*) "I_PK_RAND";
|
||||
case SMPI_SC_SM_ST_PK_CHECK: return (uint8_t*) "I_PK_CHECK";
|
||||
case SMPI_SC_SM_ST_PK_REPEAT: return (uint8_t*) "I_PK_REPEAT";
|
||||
|
||||
case SMPI_SC_SM_ST_OOB_SEND_RAND: return (uint8_t*) "I_OOB_SEND_RAND";
|
||||
case SMPI_SC_SM_ST_OOB_WAIT_RAND: return (uint8_t*) "I_OOB_WAIT_RAND";
|
||||
|
||||
case SMPI_SC_SM_ST_CALC_DHKEY: return (uint8_t*) "I_CALC_DHKEY";
|
||||
case SMPI_SC_SM_ST_CALC_F5_TKEY: return (uint8_t*) "I_CALC_F5_TKEY";
|
||||
case SMPI_SC_SM_ST_CALC_F5_MACKEY: return (uint8_t*) "I_CALC_F5_MACKEY";
|
||||
case SMPI_SC_SM_ST_CALC_F5_LTK: return (uint8_t*) "I_CALC_F5_LTK";
|
||||
case SMPI_SC_SM_ST_CALC_F6_EA: return (uint8_t*) "I_CALC_F6_EA";
|
||||
case SMPI_SC_SM_ST_CALC_F6_EB: return (uint8_t*) "I_CALC_F6_EB";
|
||||
case SMPI_SC_SM_ST_VERIFY_DH_CHECK: return (uint8_t*) "I_VERIFY_DH_CHECK";
|
||||
|
||||
case SMPI_SC_SM_ST_LEGACY_PIN: return (uint8_t*) "I_LEGACY_PIN";
|
||||
case SMPI_SC_SM_ST_CNF_CALC_1: return (uint8_t*) "I_CNF_CALC_1";
|
||||
case SMPI_SC_SM_ST_CNF_CALC_2: return (uint8_t*) "I_CNF_CALC_2";
|
||||
case SMPI_SC_SM_ST_PAIR_CNF: return (uint8_t*) "I_PAIR_CNF";
|
||||
case SMPI_SC_SM_ST_PAIR_RAND: return (uint8_t*) "I_PAIR_RAND";
|
||||
case SMPI_SC_SM_ST_CNF_VER_CALC_1: return (uint8_t*) "I_CNF_VER_CALC_1";
|
||||
case SMPI_SC_SM_ST_CNF_VER_CALC_2: return (uint8_t*) "I_CNF_VER_CALC_2";
|
||||
case SMPI_SC_SM_ST_STK_CALC: return (uint8_t*) "I_STK_CALC";
|
||||
case SMPI_SC_SM_ST_ENCRYPT: return (uint8_t*) "I_ENCRYPT";
|
||||
case SMPI_SC_SM_ST_KEY_DIST: return (uint8_t*) "I_KEY_DIST";
|
||||
case SMPI_SC_SM_ST_ATTEMPTS: return (uint8_t*) "I_ATTEMPTS";
|
||||
case SMPI_SC_SM_ST_RSP_TO: return (uint8_t*) "I_RSP_TO";
|
||||
|
||||
default: return (uint8_t*) "I_Unknown";
|
||||
}
|
||||
}
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP initiator state machine.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
#include "smpi_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Action function enumeration */
|
||||
enum
|
||||
{
|
||||
SMPI_ACT_NONE, /*!< No Action */
|
||||
SMPI_ACT_CLEANUP, /*!< Process Pairing Cleanup */
|
||||
SMPI_ACT_PAIRING_FAILED, /*!< Process Pairing Failed */
|
||||
SMPI_ACT_PAIRING_CANCEL, /*!< Process Pairing Canceled */
|
||||
SMPI_ACT_PAIR_CNF_CALC_1, /*!< Process Confirm Value Calculation 1 */
|
||||
SMPI_ACT_PAIR_CNF_CALC_2, /*!< Process Confirm Value Calculation 2 */
|
||||
SMPI_ACT_SEND_PAIR_CNF, /*!< Process Send Confirm Value */
|
||||
SMPI_ACT_PAIR_CNF_VER_CALC_1, /*!< Process Received Confirm Value Verification Calculation 1 */
|
||||
SMPI_ACT_PAIR_CNF_VER_CALC_2, /*!< Process Received Confirm Value Verification Calculation 2 */
|
||||
SMPI_ACT_MAX_ATTEMPTS, /*!< Process Maximum Attempts */
|
||||
SMPI_ACT_ATTEMPT_RCVD, /*!< Process Attempts Received */
|
||||
SMPI_ACT_CHECK_ATTEMPTS, /*!< Process Check Attempts */
|
||||
SMPI_ACT_NOTIFY_DM_ATTEMPTS, /*!< Process Notify DM of Attempts Failure */
|
||||
SMPI_ACT_NOTIFY_DM_RSP_TO, /*!< Process Notify DM of Response Timeout Failure */
|
||||
SMPI_ACT_PAIRING_CMPL, /*!< Process Pairing Complete */
|
||||
SMPI_ACT_PAIR_REQ, /*!< Process Send Pairing Request */
|
||||
SMPI_ACT_CHECK_SECURITY_REQ, /*!< Process Check Security Request */
|
||||
SMPI_ACT_PROC_SECURITY_REQ, /*!< Process Security Request */
|
||||
SMPI_ACT_PROC_PAIR_RSP, /*!< Process Pairing Response */
|
||||
SMPI_ACT_PROC_PAIR_CNF, /*!< Process Pairing Confirmation */
|
||||
SMPI_ACT_CNF_VERIFY, /*!< Process Verify Received Confirm Value */
|
||||
SMPI_ACT_STK_ENCRYPT, /*!< Process STK Encryption */
|
||||
SMPI_ACT_SETUP_KEY_DIST, /*!< Process Setup Key Distribution */
|
||||
SMPI_ACT_RCV_KEY, /*!< Process Received Key */
|
||||
SMPI_ACT_SEND_KEY /*!< Process Send Key */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Static Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Action function table; order matches action function enumeration */
|
||||
static const smpAct_t smpiActionTbl[] =
|
||||
{
|
||||
smpActNone,
|
||||
smpActCleanup,
|
||||
smpActPairingFailed,
|
||||
smpActPairingCancel,
|
||||
smpActPairCnfCalc1,
|
||||
smpActPairCnfCalc2,
|
||||
smpActSendPairCnf,
|
||||
smpActPairCnfVerCalc1,
|
||||
smpActPairCnfVerCalc2,
|
||||
smpActMaxAttempts,
|
||||
smpActAttemptRcvd,
|
||||
smpActCheckAttempts,
|
||||
smpActNotifyDmAttemptsFailure,
|
||||
smpActNotifyDmRspToFailure,
|
||||
smpActPairingCmpl,
|
||||
smpiActPairReq,
|
||||
smpiActCheckSecurityReq,
|
||||
smpiActProcSecurityReq,
|
||||
smpiActProcPairRsp,
|
||||
smpiActProcPairCnf,
|
||||
smpiActCnfVerify,
|
||||
smpiActStkEncrypt,
|
||||
smpiActSetupKeyDist,
|
||||
smpiActRcvKey,
|
||||
smpiActSendKey
|
||||
};
|
||||
|
||||
/*! State table for common actions */
|
||||
static const smpTblEntry_t smpiStateTblCommon[SMP_STATE_TBL_COMMON_MAX] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPI_SM_ST_IDLE, SMPI_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPI_SM_ST_IDLE, SMPI_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPI_SM_ST_IDLE, SMPI_ACT_PAIRING_CANCEL},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPI_SM_ST_RSP_TO, SMPI_ACT_PAIRING_FAILED},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
|
||||
/*! State table for IDLE */
|
||||
static const smpTblEntry_t smpiStateTblIdle[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_PAIR_REQ, SMPI_SM_ST_PAIR_RSP, SMPI_ACT_PAIR_REQ},
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPI_SM_ST_IDLE, SMPI_ACT_CLEANUP},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPI_SM_ST_IDLE, SMPI_ACT_CHECK_SECURITY_REQ},
|
||||
{SMP_MSG_CMD_PKT, SMPI_SM_ST_IDLE, SMPI_ACT_PROC_SECURITY_REQ},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPI_SM_ST_IDLE, SMPI_ACT_NONE},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPI_SM_ST_IDLE, SMPI_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for PAIR_RSP */
|
||||
static const smpTblEntry_t smpiStateTblPairRsp[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SM_ST_PIN, SMPI_ACT_PROC_PAIR_RSP},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for PIN */
|
||||
static const smpTblEntry_t smpiStateTblPin[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_AUTH_RSP, SMPI_SM_ST_CNF_CALC_1, SMPI_ACT_PAIR_CNF_CALC_1},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for CNF_CALC_1 */
|
||||
static const smpTblEntry_t smpiStateTblCnfCalc1[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPI_SM_ST_CNF_CALC_2, SMPI_ACT_PAIR_CNF_CALC_2},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for CNF_CALC_2 */
|
||||
static const smpTblEntry_t smpiStateTblCnfCalc2[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPI_SM_ST_PAIR_CNF, SMPI_ACT_SEND_PAIR_CNF},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for PAIR_CNF */
|
||||
static const smpTblEntry_t smpiStateTblPairCnf[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SM_ST_PAIR_RAND, SMPI_ACT_PROC_PAIR_CNF},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for PAIR_RAND */
|
||||
static const smpTblEntry_t smpiStateTblPairRand[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SM_ST_CNF_VER_CALC_1, SMPI_ACT_PAIR_CNF_VER_CALC_1},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for CNF_VER_CALC_1 */
|
||||
static const smpTblEntry_t smpiStateTblCnfVerCalc1[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPI_SM_ST_CNF_VER_CALC_2, SMPI_ACT_PAIR_CNF_VER_CALC_2},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for CNF_VER_CALC_2 */
|
||||
static const smpTblEntry_t smpiStateTblCnfVerCalc2[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPI_SM_ST_STK_CALC, SMPI_ACT_CNF_VERIFY},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for STK_CALC */
|
||||
static const smpTblEntry_t smpiStateTblStkCalc[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPI_SM_ST_ENCRYPT, SMPI_ACT_STK_ENCRYPT},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPI_SM_ST_ATTEMPTS, SMPI_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for ENCRYPT */
|
||||
static const smpTblEntry_t smpiStateTblEncrypt[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_DM_ENCRYPT_CMPL, SMPI_SM_ST_KEY_DIST, SMPI_ACT_SETUP_KEY_DIST},
|
||||
{SMP_MSG_DM_ENCRYPT_FAILED, SMPI_SM_ST_IDLE, SMPI_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPI_SM_ST_ENCRYPT, SMPI_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for KEY_DIST */
|
||||
static const smpTblEntry_t smpiStateTblKeyDist[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPI_SM_ST_KEY_DIST, SMPI_ACT_RCV_KEY},
|
||||
{SMP_MSG_INT_SEND_NEXT_KEY, SMPI_SM_ST_KEY_DIST, SMPI_ACT_SEND_KEY},
|
||||
{SMP_MSG_INT_PAIRING_CMPL, SMPI_SM_ST_IDLE, SMPI_ACT_PAIRING_CMPL},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPI_SM_ST_KEY_DIST, SMPI_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for ATTEMPTS */
|
||||
static const smpTblEntry_t smpiStateTblAttempts[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_INT_WI_TIMEOUT, SMPI_SM_ST_IDLE, SMPI_ACT_CHECK_ATTEMPTS},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPI_SM_ST_RSP_TO, SMPI_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_CMD_PKT, SMPI_SM_ST_ATTEMPTS, SMPI_ACT_ATTEMPT_RCVD},
|
||||
{SMP_MSG_API_PAIR_REQ, SMPI_SM_ST_PAIR_RSP, SMPI_ACT_NOTIFY_DM_ATTEMPTS},
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPI_SM_ST_IDLE, SMPI_ACT_CLEANUP},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPI_SM_ST_IDLE, SMPI_ACT_CLEANUP},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPI_SM_ST_ATTEMPTS, SMPI_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for RSP_TO */
|
||||
static const smpTblEntry_t smpiStateTblRspTo[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPI_SM_ST_IDLE, SMPI_ACT_CLEANUP},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPI_SM_ST_RSP_TO, SMPI_ACT_NONE},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPI_SM_ST_RSP_TO, SMPI_ACT_NONE},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPI_SM_ST_RSP_TO, SMPI_ACT_NONE},
|
||||
{SMP_MSG_API_PAIR_REQ, SMPI_SM_ST_RSP_TO, SMPI_ACT_NOTIFY_DM_RSP_TO},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! Table of individual state tables */
|
||||
static const smpTblEntry_t * const smpiStateTbl[] =
|
||||
{
|
||||
smpiStateTblIdle,
|
||||
smpiStateTblPairRsp,
|
||||
smpiStateTblPin,
|
||||
smpiStateTblCnfCalc1,
|
||||
smpiStateTblCnfCalc2,
|
||||
smpiStateTblPairCnf,
|
||||
smpiStateTblPairRand,
|
||||
smpiStateTblCnfVerCalc1,
|
||||
smpiStateTblCnfVerCalc2,
|
||||
smpiStateTblStkCalc,
|
||||
smpiStateTblEncrypt,
|
||||
smpiStateTblKeyDist,
|
||||
smpiStateTblAttempts,
|
||||
smpiStateTblRspTo
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! state machine interface */
|
||||
const smpSmIf_t smpiSmIf =
|
||||
{
|
||||
smpiStateTbl,
|
||||
smpiActionTbl,
|
||||
smpiStateTblCommon
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize SMP initiator role.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmpiInit(void)
|
||||
{
|
||||
/* set up callback interface */
|
||||
smpCb.pMaster = &smpiSmIf;
|
||||
smpCb.procPairing = smpProcPairing;
|
||||
smpCb.procAuthReq = smpAuthReq;
|
||||
}
|
||||
|
||||
+418
@@ -0,0 +1,418 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP responder state machine action functions.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/calc128.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
#include "smpr_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a slave security request.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprActSendSecurityReq(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* start smp response timer */
|
||||
smpStartRspTimer(pCcb);
|
||||
|
||||
/* allocate packet buffer */
|
||||
if ((pPkt = smpMsgAlloc(SMP_SECURITY_REQ_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* build packet */
|
||||
p = pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_SECURITY_REQ);
|
||||
UINT8_TO_BSTREAM(p, pMsg->dm.securityReq.auth);
|
||||
|
||||
/* send packet */
|
||||
smpSendPkt(pCcb, pPkt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a pairing request packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprActProcPairReq(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
dmSecPairIndEvt_t pairInd;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate scratch buffer */
|
||||
if (pCcb->pScr == NULL)
|
||||
{
|
||||
if ((pCcb->pScr = WsfBufAlloc(sizeof(smpScratch_t))) == NULL)
|
||||
{
|
||||
/* alloc failed; cancel pairing */
|
||||
pMsg->hdr.status = SMP_ERR_UNSPECIFIED;
|
||||
pMsg->hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
smpSmExecute(pCcb, pMsg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* should not happen */
|
||||
SMP_TRACE_ERR0("pScr already allocated");
|
||||
}
|
||||
|
||||
/* set connection busy */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_SMP_PAIR, DM_CONN_BUSY);
|
||||
|
||||
p = pMsg->data.pPacket + L2C_PAYLOAD_START;
|
||||
|
||||
/* store packet for later */
|
||||
memcpy(pCcb->pairReq, p, SMP_PAIR_REQ_LEN);
|
||||
|
||||
/* parse packet to callback event structure */
|
||||
p++; /* skip command code */
|
||||
p++; /* skip IO capabilities */
|
||||
BSTREAM_TO_UINT8(pairInd.oob, p);
|
||||
BSTREAM_TO_UINT8(pairInd.auth, p);
|
||||
p++; /* skip max key len */
|
||||
BSTREAM_TO_UINT8(pairInd.iKeyDist, p);
|
||||
BSTREAM_TO_UINT8(pairInd.rKeyDist, p);
|
||||
|
||||
/* call app callback */
|
||||
pairInd.hdr.param = pCcb->connId;
|
||||
pairInd.hdr.event = DM_SEC_PAIR_IND;
|
||||
DmSmpCbackExec((dmEvt_t *) &pairInd);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a pairing response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprActSendPairRsp(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pPkt;
|
||||
uint8_t *p;
|
||||
uint8_t oob;
|
||||
uint8_t display;
|
||||
|
||||
/* build packet to pairing response buffer in ccb */
|
||||
p = pCcb->pairRsp;
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_RSP);
|
||||
UINT8_TO_BSTREAM(p, pSmpCfg->ioCap);
|
||||
UINT8_TO_BSTREAM(p, pMsg->dm.pair.oob);
|
||||
UINT8_TO_BSTREAM(p, pMsg->dm.pair.auth);
|
||||
UINT8_TO_BSTREAM(p, pSmpCfg->maxKeyLen);
|
||||
UINT8_TO_BSTREAM(p, pMsg->dm.pair.iKeyDist);
|
||||
UINT8_TO_BSTREAM(p, pMsg->dm.pair.rKeyDist);
|
||||
|
||||
/* process pairing request and response data */
|
||||
if (smpCb.procPairing(pCcb, &oob, &display))
|
||||
{
|
||||
/* set next expected packet */
|
||||
if ((pCcb->pairReq[SMP_AUTHREQ_POS] & pMsg->dm.pair.auth & SMP_AUTH_SC_FLAG) == SMP_AUTH_SC_FLAG)
|
||||
{
|
||||
pCcb->nextCmdCode = SMP_CMD_PUBLIC_KEY;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCcb->nextCmdCode = SMP_CMD_PAIR_CNF;
|
||||
}
|
||||
|
||||
/* start smp response timer */
|
||||
smpStartRspTimer(pCcb);
|
||||
|
||||
/* send pairing response; allocate packet buffer */
|
||||
if ((pPkt = smpMsgAlloc(SMP_PAIR_RSP_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* build packet from pairing response buffer */
|
||||
memcpy(pPkt + L2C_PAYLOAD_START, pCcb->pairRsp, SMP_PAIR_RSP_LEN);
|
||||
|
||||
/* send packet */
|
||||
smpSendPkt(pCcb, pPkt);
|
||||
}
|
||||
|
||||
/* request authentication data */
|
||||
smpCb.procAuthReq(pCcb, oob, display);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Store pairing confirm value.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprActProcPairCnf(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *p;
|
||||
|
||||
/* go to start of packet */
|
||||
p = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;
|
||||
|
||||
/* store confirm value */
|
||||
memcpy(pCcb->pScr->buf.b3, p, SMP_CONFIRM_LEN);
|
||||
|
||||
/* discard any packets received erroneously at this point */
|
||||
pCcb->nextCmdCode = 0;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Store pairing confirm value and perform first part of pairing confirm calculation.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprActProcPairCnfCalc1(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
smprActProcPairCnf(pCcb, pMsg);
|
||||
|
||||
/* get random number to scratchpad */
|
||||
SecRand(pCcb->pScr->buf.b4, SMP_RAND_LEN);
|
||||
|
||||
/* execute calculation */
|
||||
smpCalcC1Part1(pCcb, pCcb->pScr->buf.b1, pCcb->pScr->buf.b4);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Verify the calculated confirm value. If ok, proceed with STK calculcation.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprActCnfVerify(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* compare calculated confirm value with value received earlier */
|
||||
if (memcmp(pMsg->aes.pCiphertext, pCcb->pScr->buf.b3, SMP_CONFIRM_LEN) != 0)
|
||||
{
|
||||
/* confirm values don't match; update repeated attempts count */
|
||||
pCcb->attempts++;
|
||||
SmpDbPairingFailed(pCcb->connId);
|
||||
pMsg->hdr.status = SMP_ERR_CONFIRM_VALUE;
|
||||
|
||||
if (pCcb->attempts == pSmpCfg->maxAttempts)
|
||||
{
|
||||
/* max attempts reached */
|
||||
pMsg->hdr.event = SMP_MSG_INT_MAX_ATTEMPTS;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* else just fail */
|
||||
pMsg->hdr.event = SMP_MSG_API_CANCEL_REQ;
|
||||
}
|
||||
|
||||
smpSmExecute(pCcb, pMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* do STK calculation: key, responder rand, initiator rand */
|
||||
smpCalcS1(pCcb, pCcb->pScr->buf.b1, pCcb->pScr->buf.b4, pCcb->pScr->buf.b2);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Store STK and then send a pairing random packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprActSendPairRandom(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pPkt;
|
||||
uint8_t *p;
|
||||
uint8_t encKeyLen;
|
||||
|
||||
/* get max STK length */
|
||||
encKeyLen = (pCcb->pairReq[SMP_MAXKEY_POS] < pCcb->pairRsp[SMP_MAXKEY_POS]) ?
|
||||
pCcb->pairReq[SMP_MAXKEY_POS] : pCcb->pairRsp[SMP_MAXKEY_POS];
|
||||
|
||||
/* store STK and adjust based on max key length */
|
||||
memcpy(pCcb->pScr->buf.b3, pMsg->aes.pCiphertext, encKeyLen);
|
||||
memset((pCcb->pScr->buf.b3 + encKeyLen), 0, (SMP_KEY_LEN - encKeyLen));
|
||||
|
||||
/* start smp response timer */
|
||||
smpStartRspTimer(pCcb);
|
||||
|
||||
/* allocate packet buffer and send pairing random packet */
|
||||
if ((pPkt = smpMsgAlloc(SMP_PAIR_RAND_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* build packet */
|
||||
p = pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_RAND);
|
||||
memcpy(p, pCcb->pScr->buf.b4, SMP_RAND_LEN);
|
||||
|
||||
/* send packet */
|
||||
smpSendPkt(pCcb, pPkt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up key distribution.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprActSetupKeyDist(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* don't receive anything yet */
|
||||
pCcb->nextCmdCode = 0;
|
||||
|
||||
/* start smp response timer once for entire key distribution phase */
|
||||
smpStartRspTimer(pCcb);
|
||||
|
||||
/* initialize parameters in key ind struct */
|
||||
pCcb->pScr->keyInd.hdr.param = pCcb->connId;
|
||||
pCcb->pScr->keyInd.secLevel = (pCcb->auth & SMP_AUTH_MITM_FLAG) ?
|
||||
DM_SEC_LEVEL_ENC_AUTH : DM_SEC_LEVEL_ENC;
|
||||
pCcb->pScr->keyInd.encKeyLen =
|
||||
(pCcb->pairReq[SMP_MAXKEY_POS] < pCcb->pairRsp[SMP_MAXKEY_POS]) ?
|
||||
pCcb->pairReq[SMP_MAXKEY_POS] : pCcb->pairRsp[SMP_MAXKEY_POS];
|
||||
|
||||
/* start key distribution */
|
||||
smprActSendKey(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a key.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprActSendKey(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t keyDist;
|
||||
|
||||
/* get responder key distribution */
|
||||
keyDist = pCcb->pairReq[SMP_RKEYDIST_POS] & pCcb->pairRsp[SMP_RKEYDIST_POS];
|
||||
|
||||
/* send next key; if done sending keys set up to receive keys */
|
||||
if ((pCcb->nextCmdCode == 0) && smpSendKey(pCcb, keyDist))
|
||||
{
|
||||
pCcb->nextCmdCode = 0;
|
||||
|
||||
/* get initiator key distribution */
|
||||
keyDist = pCcb->pairReq[SMP_IKEYDIST_POS] & pCcb->pairRsp[SMP_IKEYDIST_POS];
|
||||
|
||||
/* set up to receive first key distribution packet */
|
||||
if (keyDist & SMP_KEY_DIST_ENC)
|
||||
{
|
||||
if (smpCb.lescSupported && pCcb->pScCcb->lescEnabled)
|
||||
{
|
||||
if (keyDist & SMP_KEY_DIST_ID)
|
||||
{
|
||||
pCcb->nextCmdCode = SMP_CMD_ID_INFO;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pCcb->nextCmdCode = SMP_CMD_ENC_INFO;
|
||||
}
|
||||
}
|
||||
else if (keyDist & SMP_KEY_DIST_ID)
|
||||
{
|
||||
pCcb->nextCmdCode = SMP_CMD_ID_INFO;
|
||||
}
|
||||
else if (keyDist & SMP_KEY_DIST_SIGN)
|
||||
{
|
||||
pCcb->nextCmdCode = SMP_CMD_SIGN_INFO;
|
||||
}
|
||||
|
||||
if (pCcb->nextCmdCode == 0)
|
||||
{
|
||||
/* no keys to receive; send ourselves pairing complete msg */
|
||||
pMsg->hdr.event = SMP_MSG_INT_PAIRING_CMPL;
|
||||
smpSmExecute(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Receive a key.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprActRcvKey(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t keyDist;
|
||||
|
||||
/* get initiator key distribution */
|
||||
keyDist = pCcb->pairReq[SMP_IKEYDIST_POS] & pCcb->pairRsp[SMP_IKEYDIST_POS];
|
||||
|
||||
/* process received key */
|
||||
if (smpProcRcvKey(pCcb, &pCcb->pScr->keyInd, pMsg->data.pPacket, keyDist))
|
||||
{
|
||||
/* no more keys to receive; send ourselves pairing complete msg */
|
||||
pMsg->hdr.event = SMP_MSG_INT_PAIRING_CMPL;
|
||||
smpSmExecute(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP responder main module.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef SMPR_MAIN_H
|
||||
#define SMPR_MAIN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* state machine interface */
|
||||
extern const smpSmIf_t smprSmIf;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
void smprActSendSecurityReq(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprActProcPairReq(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprActSendPairRsp(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprActProcPairCnf(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprActProcPairCnfCalc1(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprActCnfVerify(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprActSendPairRandom(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprActSetupKeyDist(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprActSendKey(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
void smprActRcvKey(smpCcb_t *pCcb, smpMsg_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* SMPR_MAIN_H */
|
||||
Vendored
+559
@@ -0,0 +1,559 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP Secure Connections responder state machine action functions.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/calc128.h"
|
||||
#include "util/wstr.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
#include "smpi_main.h"
|
||||
#include "smp_sc_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Store the pin.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActStoreLescPin(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
if (pCcb->pScCcb->authType == SMP_AUTH_TYPE_PASSKEY)
|
||||
{
|
||||
/* Store the pin */
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Ra, (uint8_t *)calc128Zeros);
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Rb, (uint8_t *)calc128Zeros);
|
||||
|
||||
if (pMsg->dm.authRsp.authDataLen <= 3)
|
||||
{
|
||||
WStrReverseCpy(&pCcb->pScCcb->pScratch->Ra[13], pMsg->dm.authRsp.authData, pMsg->dm.authRsp.authDataLen);
|
||||
WStrReverseCpy(&pCcb->pScCcb->pScratch->Rb[13], pMsg->dm.authRsp.authData, pMsg->dm.authRsp.authDataLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Responder public key exchange.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActSendPubKey(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* Execute Common Auth Select actions */
|
||||
smpScActAuthSelect(pCcb, pMsg);
|
||||
|
||||
/* Send our public key */
|
||||
smpScSendPubKey(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Prepare for the Just Works/Numeric Comparison Use Case
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActJwncSetup(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* Select Random Na (128-bits) */
|
||||
SecRand(pCcb->pScCcb->pScratch->Nb_Eb, SMP_RAND_LEN);
|
||||
|
||||
SMP_TRACE_128("Rand Nb", pCcb->pScCcb->pScratch->Nb_Eb);
|
||||
|
||||
/* Set Ra and Rb to zero */
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Ra, (uint8_t*) calc128Zeros);
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Rb, (uint8_t*) calc128Zeros);
|
||||
|
||||
/* Next command is a Pair Rand from Initiator */
|
||||
pCcb->nextCmdCode = SMP_CMD_PAIR_RAND;
|
||||
|
||||
/* Perform F4 Calculation of Cb */
|
||||
smpScActJwncCalcF4(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send the confirm to the initiator for Just Works/Numeric Comparison pairing
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActJwncSendCnf(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
SMP_TRACE_128("JWNC Confirm", pMsg->aes.pCiphertext);
|
||||
|
||||
smpScSendPairCnf(pCcb, pMsg, pMsg->aes.pCiphertext);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate Toolkit function G2 for Just Works pairing
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActJwncCalcG2(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pNa = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;
|
||||
|
||||
/* Na from initiator is in Random Cmd from initiator */
|
||||
WStrReverseCpy(pCcb->pScCcb->pScratch->Na_Ea, pNa, SMP_RAND_LEN);
|
||||
|
||||
/* Calculate Vb using G2 */
|
||||
smpScActJwncCalcG2(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Notify the application of the verify value calculated with Just Works pairing
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActJwncDisplay(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* Next command is a DH Key Check */
|
||||
pCcb->nextCmdCode = SMP_CMD_DHKEY_CHECK;
|
||||
|
||||
/* Send Pair Rand Nb to the Initiator */
|
||||
smpScSendRand(pCcb, pMsg, pCcb->pScCcb->pScratch->Nb_Eb);
|
||||
|
||||
/* Send Numeric Comparison to application, if applicable */
|
||||
smpScActJwncDisplay(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Store the confirm value from the initiator
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActPkStoreCnf(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pCa = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;
|
||||
|
||||
/* Store the Cai from the initiator */
|
||||
WStrReverseCpy(pCcb->pScCcb->pScratch->PeerCa_Ea, pCa, SMP_CONFIRM_LEN);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Store the Confirm and Calculate the Cbi using toolkit function F4 for passkey pairing
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActPkStoreCnfAndCalcCb(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
smprScActPkStoreCnf(pCcb, pMsg);
|
||||
smprScActPkCalcCb(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Store the Pin and Calculate the Cbi using toolkit function F4 for passkey pairing
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActPkStorePinAndCalcCb(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
smprScActStoreLescPin(pCcb, pMsg);
|
||||
smprScActPkCalcCb(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate the Cbi using toolkit function F4 for passkey pairing
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActPkCalcCb(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* Get random Nbi */
|
||||
SecRand(pCcb->pScCcb->pScratch->Nb_Eb, SMP_RAND_LEN);
|
||||
|
||||
SMP_TRACE_128("Rand Nb", pCcb->pScCcb->pScratch->Nb_Eb);
|
||||
|
||||
/* Next command is the Pair Random */
|
||||
pCcb->nextCmdCode = SMP_CMD_PAIR_RAND;
|
||||
|
||||
/* Cb = f4(PKbx, PKax, Nbi, Rbi) where f4(U, V, x, Z) = AES-CMACx (U || V || Z) */
|
||||
SmpScCalcF4(pCcb, pMsg,
|
||||
pCcb->pScCcb->pLocalPublicKey->pubKeyX,
|
||||
pCcb->pScCcb->pPeerPublicKey->pubKeyX,
|
||||
smpGetPkBit(pCcb), pCcb->pScCcb->pScratch->Nb_Eb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send the Cbi to the initiator in a Confirm Command Message in passkey pairing
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActPkSendCnf(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
SMP_TRACE_128("Cbi", pMsg->aes.pCiphertext);
|
||||
|
||||
/* Send the Cbi to the peer */
|
||||
smpScSendPairCnf(pCcb, pMsg, pMsg->aes.pCiphertext);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate the Cai to be checked against the confirm value from the initiator
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActPkCalcCa(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pNa = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;
|
||||
|
||||
/* Copy the Nai from the initiator */
|
||||
WStrReverseCpy(pCcb->pScCcb->pScratch->Na_Ea, pNa, SMP_RAND_LEN);
|
||||
|
||||
/* Cai = f4(PKax, PKbx, Nbi, Rbi) where f4(U, V, x, Z) = AES-CMACx (U || V || Z) */
|
||||
SmpScCalcF4(pCcb, pMsg,
|
||||
pCcb->pScCcb->pPeerPublicKey->pubKeyX,
|
||||
pCcb->pScCcb->pLocalPublicKey->pubKeyX,
|
||||
smpGetPkBit(pCcb), pCcb->pScCcb->pScratch->Na_Ea);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send the Nai in a Random Command Message in passkey pairing
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActPkSendRand(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
SMP_TRACE_128("Ca", pMsg->aes.pCiphertext);
|
||||
SMP_TRACE_128("Ca Peer", pCcb->pScCcb->pScratch->PeerCa_Ea);
|
||||
|
||||
/* Verify the Calculated Cai to previously received Cai */
|
||||
if (memcmp(pCcb->pScCcb->pScratch->PeerCa_Ea, pMsg->aes.pCiphertext, SMP_RAND_LEN))
|
||||
{
|
||||
smpScFailWithReattempt(pCcb);
|
||||
}
|
||||
else
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
/* Increment the bit position */
|
||||
if (++pCcb->pScCcb->pkPos >= SMP_PK_BIT_COUNT)
|
||||
{
|
||||
hdr.event = SMP_MSG_INT_PK_CMPL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Next command is the Pair Confirm */
|
||||
pCcb->nextCmdCode = SMP_CMD_PAIR_CNF;
|
||||
hdr.event = SMP_MSG_INT_PK_NEXT;
|
||||
|
||||
/* Send the Nbi */
|
||||
smpScSendRand(pCcb, pMsg, pCcb->pScCcb->pScratch->Nb_Eb);
|
||||
}
|
||||
|
||||
/* Post an event to move to the next passkey confirm or complete the process */
|
||||
hdr.param = pCcb->connId;
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Setup for OOB pairing
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActOobSetup(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* The next command is the Pair Rand from the initiator */
|
||||
pCcb->nextCmdCode = SMP_CMD_PAIR_RAND;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check the confirm passed via OOB methods
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActOobCalcCa(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pNa = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;
|
||||
|
||||
/* Copy the Na from the initiator */
|
||||
WStrReverseCpy(pCcb->pScCcb->pScratch->Na_Ea, pNa, SMP_CONFIRM_LEN);
|
||||
|
||||
/* If the peer device's OOB data flag does not indicate remote OOB data has been received,
|
||||
clear Rb. */
|
||||
if (pCcb->pairReq[SMP_OOB_POS] != SMP_OOB_DATA_PRESENT)
|
||||
{
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Rb, (uint8_t*) calc128Zeros);
|
||||
}
|
||||
|
||||
/* If we indicated the presence of remote OOB data has been received, calculate Ca. */
|
||||
if (pCcb->pairRsp[SMP_OOB_POS] == SMP_OOB_DATA_PRESENT)
|
||||
{
|
||||
/* Calculate Ca using Toolkit function F4 */
|
||||
SmpScCalcF4(pCcb, pMsg,
|
||||
pCcb->pScCcb->pPeerPublicKey->pubKeyX,
|
||||
pCcb->pScCcb->pPeerPublicKey->pubKeyX,
|
||||
0, pCcb->pScCcb->pScratch->Ra);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Simulate the ca calculation is complete and clear ra */
|
||||
secCmacMsg_t msg;
|
||||
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Ra, (uint8_t*)calc128Zeros);
|
||||
|
||||
msg.hdr.param = pCcb->connId;
|
||||
msg.hdr.event = SMP_MSG_WSF_CMAC_CMPL;
|
||||
msg.pPlainText = NULL;
|
||||
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &msg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send the random value to the initiator when using OOB pairing
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActOobSendRand(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* Only compare Ca if we indicated that we received OOB data. */
|
||||
if (pCcb->pairRsp[SMP_OOB_POS] == SMP_OOB_DATA_PRESENT)
|
||||
{
|
||||
SMP_TRACE_128("Ca", pMsg->aes.pCiphertext);
|
||||
|
||||
/* Check that the Ca value passed via OOB methods match expectations */
|
||||
if (memcmp(pCcb->pScCcb->pScratch->PeerCa_Ea, pMsg->aes.pCiphertext, SMP_CONFIRM_LEN))
|
||||
{
|
||||
smpScFailWithReattempt(pCcb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Next command is a DK Key Check from initiator */
|
||||
pCcb->nextCmdCode = SMP_CMD_DHKEY_CHECK;
|
||||
|
||||
/* Get a random Nb */
|
||||
SecRand(pCcb->pScCcb->pScratch->Nb_Eb, SMP_RAND_LEN);
|
||||
|
||||
SMP_TRACE_128("Rand Nb", pCcb->pScCcb->pScratch->Nb_Eb);
|
||||
|
||||
/* Send the rand Nb to the initiator */
|
||||
smpScSendRand(pCcb, pMsg, pCcb->pScCcb->pScratch->Nb_Eb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Store DH Key Check from Initiator and wait for user input on Numeric Comparison
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActStoreDhCheck(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
uint8_t *pEa = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;
|
||||
|
||||
/* Signal that not further commands are expected until Key Distribution phase (if applicable) */
|
||||
pCcb->nextCmdCode = SMP_CMD_MAX;
|
||||
|
||||
/* The Ea from the peer is in the cmd message (copy as MSB First) */
|
||||
WStrReverseCpy(pCcb->pScCcb->pScratch->PeerCa_Ea, pEa, SMP_CONFIRM_LEN);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Prepare to wait for the DH Key Check Cmd from the initiator
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActWaitDhCheck(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
/* Next command is a DH Key Check */
|
||||
pCcb->nextCmdCode = SMP_CMD_DHKEY_CHECK;
|
||||
|
||||
if (pCcb->pScCcb->authType == SMP_AUTH_TYPE_PASSKEY)
|
||||
{
|
||||
/* Send the Pair Rand Nb */
|
||||
smpScSendRand(pCcb, pMsg, pCcb->pScCcb->pScratch->Nb_Eb);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate the DHKey, for Cryptographic Function 5
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActCalcDHKey(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
if (pCcb->nextCmdCode == SMP_CMD_DHKEY_CHECK)
|
||||
{
|
||||
uint8_t *pEa = pMsg->data.pPacket + L2C_PAYLOAD_START + SMP_HDR_LEN;
|
||||
|
||||
/* The Ea from the peer is in the cmd message (copy as MSB First) */
|
||||
WStrReverseCpy(pCcb->pScCcb->pScratch->PeerCa_Ea, pEa, SMP_CONFIRM_LEN);
|
||||
}
|
||||
|
||||
/* Complete the calculation */
|
||||
smpScActCalcSharedSecret(pCcb, pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send the DH Key check.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pMsg State machine message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void smprScActDHKeyCheckSend(smpCcb_t *pCcb, smpMsg_t *pMsg)
|
||||
{
|
||||
SMP_TRACE_128("DHKey Eb", pMsg->aes.pCiphertext);
|
||||
|
||||
/* The Eb from the CMAC calculation */
|
||||
Calc128Cpy(pCcb->pScCcb->pScratch->Nb_Eb, pMsg->aes.pCiphertext);
|
||||
|
||||
/* Verify the DH Key Check Ea with the value received from the initiator */
|
||||
if (memcmp(pCcb->pScCcb->pScratch->PeerCa_Ea, pCcb->pScCcb->pScratch->Na_Ea, SMP_RAND_LEN) == 0)
|
||||
{
|
||||
/* Adjust key based on max key length */
|
||||
uint8_t encKeyLen = (pCcb->pairReq[SMP_MAXKEY_POS] < pCcb->pairRsp[SMP_MAXKEY_POS]) ?
|
||||
pCcb->pairReq[SMP_MAXKEY_POS] : pCcb->pairRsp[SMP_MAXKEY_POS];
|
||||
|
||||
memset((pCcb->pScCcb->pLtk->ltk_t + encKeyLen), 0, (SMP_KEY_LEN - encKeyLen));
|
||||
|
||||
/* Send the DH Key check Eb to the initiator */
|
||||
smpScSendDHKeyCheck(pCcb, pMsg, pCcb->pScCcb->pScratch->Nb_Eb);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* DH Key check failed */
|
||||
wsfMsgHdr_t hdr;
|
||||
|
||||
hdr.param = pCcb->connId;
|
||||
hdr.status = SMP_ERR_DH_KEY_CHECK;
|
||||
|
||||
/* update repeated attempts count */
|
||||
pCcb->attempts++;
|
||||
SmpDbPairingFailed(pCcb->connId);
|
||||
|
||||
if (pCcb->attempts == pSmpCfg->maxAttempts)
|
||||
{
|
||||
/* max attempts reached */
|
||||
hdr.event = SMP_MSG_INT_MAX_ATTEMPTS;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* else just fail */
|
||||
hdr.event = SMP_MSG_DH_CHECK_FAILURE;
|
||||
}
|
||||
|
||||
smpSmExecute(pCcb, (smpMsg_t *) &hdr);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+685
@@ -0,0 +1,685 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP Secure Connections initiator state machine.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
#include "smpr_main.h"
|
||||
#include "smp_sc_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Action function enumeration */
|
||||
enum
|
||||
{
|
||||
SMPR_SC_ACT_NONE, /*!< No Action */
|
||||
SMPR_SC_ACT_CLEANUP, /*!< Process Pairing Cleanup */
|
||||
SMPR_SC_ACT_PAIRING_FAILED, /*!< Process Pairing Failed */
|
||||
SMPR_SC_ACT_PAIRING_CANCEL, /*!< Process Pairing Canceled */
|
||||
SMPR_SC_ACT_SEND_PUB_KEY, /*!< Process Send Public Key */
|
||||
SMPR_SC_ACT_STORE_LEGACY_PIN, /*!< Process Store Legacy Pin */
|
||||
SMPR_SC_ACT_STORE_LESC_PIN, /*!< Process Store LESC Pin */
|
||||
|
||||
SMPR_SC_ACT_PAIR_CNF_CALC_1, /*!< Process Confirm Value Calculation 1 */
|
||||
SMPR_SC_ACT_PAIR_CNF_CALC_2, /*!< Process Confirm Value Calculation 2 */
|
||||
SMPR_SC_ACT_SEND_PAIR_CNF, /*!< Process Send Confirm Value */
|
||||
SMPR_SC_ACT_PAIR_CNF_VER_CALC_1, /*!< Process Received Confirm Value Verification Calculation 1 */
|
||||
SMPR_SC_ACT_PAIR_CNF_VER_CALC_2, /*!< Process Received Confirm Value Verification Calculation 2 */
|
||||
SMPR_SC_ACT_MAX_ATTEMPTS, /*!< Process Maximum Attempts */
|
||||
SMPR_SC_ACT_PAIRING_CMPL, /*!< Process Pairing Complete */
|
||||
SMPR_SC_ACT_CHECK_ATTEMPTS, /*!< Process Check Attempts */
|
||||
SMPR_SC_ACT_NOTIFY_DM_ATTEMPTS, /*!< Process Notify DM of Attempts Failure */
|
||||
SMPR_SC_ACT_NOTIFY_DM_RSP_TO, /*!< Process Notify DM of Response Timeout Failure */
|
||||
SMPR_SC_ACT_ATTEMPT_RCVD, /*!< Process Attempts Received */
|
||||
|
||||
SMPR_SC_ACT_SEND_SECURITY_REQ, /*!< Process Send Slave Security Request */
|
||||
SMPR_SC_ACT_PROC_PAIR_REQ, /*!< Process Pairing Request */
|
||||
SMPR_SC_ACT_SEND_PAIR_RSP, /*!< Process Send Pairing Response */
|
||||
SMPR_SC_ACT_PROC_PAIR_CNF, /*!< Process Received Confirm Value */
|
||||
|
||||
SMPR_SC_ACT_JWNC_SETUP, /*!< Process Just Works/Numeric Comparison Setup */
|
||||
SMPR_SC_ACT_JWNC_SEND_CNF, /*!< Process JW/NC Send Confirm Value */
|
||||
SMPR_SC_ACT_JWNC_CALC_G2, /*!< Process JW/NC Calculate G2 */
|
||||
SMPR_SC_ACT_JWNC_DISPLAY, /*!< Process JW/NC Display Numeric Comparison */
|
||||
|
||||
SMPR_SC_ACT_PK_SETUP, /*!< Process Passkey Setup */
|
||||
SMPR_SC_ACT_PK_KEYPRESS, /*!< Process Passkey Keypress */
|
||||
SMPR_SC_ACT_PK_SEND_KEYPRESS, /*!< Process Passkey Send Keypress */
|
||||
SMPR_SC_ACT_PK_STORE_CNF, /*!< Process Passkey Store Received Confirm Value */
|
||||
SMPR_SC_ACT_PK_STORE_CNF_CALC_CB, /*!< Process Passkey Store Confirm Value Calculation Cb */
|
||||
SMPR_SC_ACT_PK_STORE_PIN_CALC_CB, /*!< Process Passkey Store Pin Value Calculation Cb */
|
||||
SMPR_SC_ACT_PK_CALC_CB, /*!< Process Passkey Calculate Cb */
|
||||
SMPR_SC_ACT_PK_SEND_CNF, /*!< Process Passkey Send Confirm Value */
|
||||
SMPR_SC_ACT_PK_CALC_CA, /*!< Process Passkey Calculate Ca */
|
||||
SMPR_SC_ACT_PK_SEND_RAND, /*!< Process Passkey Send Random Value */
|
||||
|
||||
SMPR_SC_ACT_OOB_SETUP, /*!< Process OOB Setup */
|
||||
SMPR_SC_ACT_OOB_CALC_CA, /*!< Process OOB Calculate Ca */
|
||||
SMPR_SC_ACT_OOB_SEND_RAND, /*!< Process OOB Send Random Value */
|
||||
|
||||
SMPR_SC_ACT_STORE_DH_CHECK, /*!< Process Store DH Key Check */
|
||||
SMPR_SC_ACT_WAIT_DH_CHECK, /*!< Process Wait DH Key Check */
|
||||
SMPR_SC_ACT_CALC_DHKEY, /*!< Process Calculate DH Key Check */
|
||||
SMPR_SC_ACT_CALC_F5_TKEY, /*!< Process Calculate F5 Temporary Key */
|
||||
SMPR_SC_ACT_CALC_F5_MACKEY, /*!< Process Calculate F5 MAC Key */
|
||||
SMPR_SC_ACT_CALC_F5_LTK, /*!< Process Calculate LTK */
|
||||
SMPR_SC_ACT_CALC_F6_EA, /*!< Process Calculate Ea */
|
||||
SMPR_SC_ACT_CALC_F6_EB, /*!< Process Calculate Eb */
|
||||
SMPR_SC_ACT_SEND_DH_CHECK, /*!< Process Send DH Key Check */
|
||||
|
||||
SMPR_SC_ACT_PROC_PAIR_CNF_CALC_1, /*!< Process Confirm Value Calcuation 1 */
|
||||
SMPR_SC_ACT_CNF_VERIFY, /*!< Process Confirm Value Verification */
|
||||
SMPR_SC_ACT_SEND_PAIR_RANDOM, /*!< Process Send Random Value */
|
||||
SMPR_SC_ACT_SETUP_KEY_DIST, /*!< Processs Setup Key Distribution */
|
||||
SMPR_SC_ACT_RCV_KEY, /*!< Process Received Key */
|
||||
SMPR_SC_ACT_SEND_KEY /*!< Process Send Key */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Static Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Action function table; order matches action function enumeration */
|
||||
static const smpAct_t smprScActionTbl[] =
|
||||
{
|
||||
smpActNone,
|
||||
smpScActCleanup,
|
||||
smpScActPairingFailed,
|
||||
smpScActPairingCancel,
|
||||
smprScActSendPubKey,
|
||||
smpActStorePin,
|
||||
smprScActStoreLescPin,
|
||||
|
||||
smpActPairCnfCalc1,
|
||||
smpActPairCnfCalc2,
|
||||
smpActSendPairCnf,
|
||||
smpActPairCnfVerCalc1,
|
||||
smpActPairCnfVerCalc2,
|
||||
smpActMaxAttempts,
|
||||
smpActPairingCmpl,
|
||||
smpActCheckAttempts,
|
||||
smpActNotifyDmAttemptsFailure,
|
||||
smpActNotifyDmRspToFailure,
|
||||
smpActAttemptRcvd,
|
||||
|
||||
smprActSendSecurityReq,
|
||||
smprActProcPairReq,
|
||||
smprActSendPairRsp,
|
||||
smprActProcPairCnf,
|
||||
|
||||
smprScActJwncSetup,
|
||||
smprScActJwncSendCnf,
|
||||
smprScActJwncCalcG2,
|
||||
smprScActJwncDisplay,
|
||||
|
||||
smpScActPkSetup,
|
||||
smpScActPkKeypress,
|
||||
smpScActPkSendKeypress,
|
||||
smprScActPkStoreCnf,
|
||||
smprScActPkStoreCnfAndCalcCb,
|
||||
smprScActPkStorePinAndCalcCb,
|
||||
smprScActPkCalcCb,
|
||||
smprScActPkSendCnf,
|
||||
smprScActPkCalcCa,
|
||||
smprScActPkSendRand,
|
||||
|
||||
smprScActOobSetup,
|
||||
smprScActOobCalcCa,
|
||||
smprScActOobSendRand,
|
||||
|
||||
smprScActStoreDhCheck,
|
||||
smprScActWaitDhCheck,
|
||||
smprScActCalcDHKey,
|
||||
smpScActCalcF5TKey,
|
||||
smpScActCalcF5MacKey,
|
||||
smpScActCalcF5Ltk,
|
||||
smpScActDHKeyCalcF6Ea,
|
||||
smpScActDHKeyCalcF6Eb,
|
||||
smprScActDHKeyCheckSend,
|
||||
|
||||
smprActProcPairCnfCalc1,
|
||||
smprActCnfVerify,
|
||||
smprActSendPairRandom,
|
||||
smprActSetupKeyDist,
|
||||
smprActRcvKey,
|
||||
smprActSendKey
|
||||
};
|
||||
|
||||
/*! State table for common actions */
|
||||
static const smpTblEntry_t smprScStateTblCommon[SMP_STATE_TBL_COMMON_MAX] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_PAIRING_CANCEL},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPR_SC_SM_ST_RSP_TO, SMPR_SC_ACT_PAIRING_FAILED},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_IDLE */
|
||||
static const smpTblEntry_t smprScStateTblIdle[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_SECURITY_REQ, SMPR_SC_SM_ST_API_PAIR_REQ, SMPR_SC_ACT_SEND_SECURITY_REQ},
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_CLEANUP},
|
||||
{SMP_MSG_CMD_PKT, SMPR_SC_SM_ST_API_PAIR_RSP, SMPR_SC_ACT_PROC_PAIR_REQ},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_NONE},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_CLEANUP},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_API_PAIR_REQ */
|
||||
static const smpTblEntry_t smprScStateTblApiPairReq[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_CLEANUP},
|
||||
{SMP_MSG_CMD_PKT, SMPR_SC_SM_ST_API_PAIR_RSP, SMPR_SC_ACT_PROC_PAIR_REQ},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_CLEANUP},
|
||||
{SMP_MSG_DM_ENCRYPT_CMPL, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_CLEANUP},
|
||||
{SMP_MSG_DM_ENCRYPT_FAILED, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_CLEANUP},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_API_PAIR_RSP */
|
||||
static const smpTblEntry_t smprScStateTblApiPairRsp[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_PAIR_RSP, SMPR_SC_SM_ST_MODE_SELECT, SMPR_SC_ACT_SEND_PAIR_RSP},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_MODE_SELECT */
|
||||
static const smpTblEntry_t smprScStateTblModeSelect[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_INT_LESC, SMPR_SC_SM_ST_LESC_PIN, SMPR_SC_ACT_NONE},
|
||||
{SMP_MSG_INT_LEGACY, SMPR_SC_SM_ST_PIN_PAIR_1, SMPR_SC_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for LESC SMPR_SC_SM_ST_LESC_PIN */
|
||||
static const smpTblEntry_t smprScStateTblLescPin[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_AUTH_RSP, SMPR_SC_SM_ST_PUB_KEY, SMPR_SC_ACT_STORE_LESC_PIN},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for LESC SMPR_SC_SM_ST_PUB_KEY */
|
||||
static const smpTblEntry_t smprScStateTblPubKey[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPR_SC_SM_ST_AUTH_SELECT, SMPR_SC_ACT_SEND_PUB_KEY},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_AUTH_SELECT */
|
||||
static const smpTblEntry_t smprScStateTblAuthSelect[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_INT_JW_NC, SMPR_SC_SM_ST_JWNC_SETUP, SMPR_SC_ACT_JWNC_SETUP},
|
||||
{SMP_MSG_INT_PASSKEY, SMPR_SC_SM_ST_PK_KEYPRESS, SMPR_SC_ACT_PK_SETUP},
|
||||
{SMP_MSG_INT_OOB, SMPR_SC_SM_ST_OOB_WAIT_RAND, SMPR_SC_ACT_OOB_SETUP},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_JWNC_SETUP */
|
||||
static const smpTblEntry_t smprScStateTblJwNcSetup[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPR_SC_SM_ST_JWNC_WAIT_RAND, SMPR_SC_ACT_JWNC_SEND_CNF},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_JWNC_WAIT_RAND */
|
||||
static const smpTblEntry_t smprScStateTblJwNcWaitRand[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPR_SC_SM_ST_JWNC_CALC_G2, SMPR_SC_ACT_JWNC_CALC_G2},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_JWNC_CALC_G2 */
|
||||
static const smpTblEntry_t smprScStateTblJwNcCalcG2[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPR_SC_SM_ST_JWNC_WAIT_USER, SMPR_SC_ACT_JWNC_DISPLAY},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_JWNC_WAIT_USER */
|
||||
static const smpTblEntry_t smprScStateTblJwNcWaitUser[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_USER_CONFIRM, SMPR_SC_SM_ST_WAIT_DH_CHECK, SMPR_SC_ACT_WAIT_DH_CHECK},
|
||||
{SMP_MSG_CMD_PKT, SMPR_SC_SM_ST_JWNC_WAIT_USER_DH_CHECK_RCVD, SMPR_SC_ACT_STORE_DH_CHECK},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPR_SC_SM_ST_ATTEMPTS, SMPR_SC_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_JWNC_WAIT_USER_DH_CHECK_RCVD */
|
||||
static const smpTblEntry_t smprScStateTblJwNcWaitUserDhCheckRcvd[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_USER_CONFIRM, SMPR_SC_SM_ST_CALC_DHKEY, SMPR_SC_ACT_CALC_DHKEY},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPR_SC_SM_ST_ATTEMPTS, SMPR_SC_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_PK_KEYPRESS */
|
||||
static const smpTblEntry_t smprScStateTblPassKeyKeypress[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPR_SC_SM_ST_PK_KEYPRESS, SMPR_SC_ACT_PK_KEYPRESS},
|
||||
{SMP_MSG_EARLY_CNF, SMPR_SC_SM_ST_PK_WAIT_AUTH, SMPR_SC_ACT_PK_STORE_CNF},
|
||||
{SMP_MSG_API_USER_KEYPRESS, SMPR_SC_SM_ST_PK_KEYPRESS, SMPR_SC_ACT_PK_SEND_KEYPRESS},
|
||||
{SMP_MSG_API_AUTH_RSP, SMPR_SC_SM_ST_PK_WAIT_CNF, SMPR_SC_ACT_STORE_LESC_PIN},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_PK_WAIT_AUTH */
|
||||
static const smpTblEntry_t smprScStateTblPassWaitAuthRsp[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_AUTH_RSP, SMPR_SC_SM_ST_PK_CALC, SMPR_SC_ACT_PK_STORE_PIN_CALC_CB},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_PK_WAIT_CNF */
|
||||
static const smpTblEntry_t smprScStateTblPasskeyWaitCnf[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPR_SC_SM_ST_PK_CALC, SMPR_SC_ACT_PK_STORE_CNF_CALC_CB},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_PK_CALC */
|
||||
static const smpTblEntry_t smprScStateTblPasskeyCalc[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPR_SC_SM_ST_PK_RAND, SMPR_SC_ACT_PK_SEND_CNF},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_PK_RAND */
|
||||
static const smpTblEntry_t smprScStateTblPasskeyRand[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPR_SC_SM_ST_PK_CHECK, SMPR_SC_ACT_PK_CALC_CA},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_PK_CHECK */
|
||||
static const smpTblEntry_t smprScStateTblPasskeyCheck[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPR_SC_SM_ST_PK_REPEAT, SMPR_SC_ACT_PK_SEND_RAND},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_PK_REPEAT */
|
||||
static const smpTblEntry_t smprScStateTblPasskeyRepeat[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_INT_PK_NEXT, SMPR_SC_SM_ST_PK_WAIT_CNF, SMPR_SC_ACT_NONE},
|
||||
{SMP_MSG_INT_PK_CMPL, SMPR_SC_SM_ST_WAIT_DH_CHECK, SMPR_SC_ACT_WAIT_DH_CHECK},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPR_SC_SM_ST_ATTEMPTS, SMPR_SC_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_OOB_WAIT_RAND */
|
||||
static const smpTblEntry_t smprScStateTblOobWaitRand[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPR_SC_SM_ST_OOB_SEND_RAND, SMPR_SC_ACT_OOB_CALC_CA},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_OOB_SEND_RAND */
|
||||
static const smpTblEntry_t smprScStateTblOobSendRand[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPR_SC_SM_ST_WAIT_DH_CHECK, SMPR_SC_ACT_OOB_SEND_RAND},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_WAIT_DH_CHECK */
|
||||
static const smpTblEntry_t smprScStateTblWaitDhCheck[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPR_SC_SM_ST_CALC_DHKEY, SMPR_SC_ACT_CALC_DHKEY},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPR_SC_SM_ST_ATTEMPTS, SMPR_SC_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_CALC_DHKEY */
|
||||
static const smpTblEntry_t smprScStateTblCalcDHKey[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_ECC_CMPL, SMPR_SC_SM_ST_CALC_F5_TKEY, SMPR_SC_ACT_CALC_F5_TKEY},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_CALC_F5_TKEY */
|
||||
static const smpTblEntry_t smprScStateTblCalcF5TKey[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPR_SC_SM_ST_CALC_F5_MACKEY, SMPR_SC_ACT_CALC_F5_MACKEY},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPR_SC_SM_ST_ATTEMPTS, SMPR_SC_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_CALC_F5_MACKEY */
|
||||
static const smpTblEntry_t smprScStateTblCalcF5MacKey[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPR_SC_SM_ST_CALC_F5_LTK, SMPR_SC_ACT_CALC_F5_LTK},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_CALC_F5_LTK */
|
||||
static const smpTblEntry_t smprScStateTblCalcF5LTK[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPR_SC_SM_ST_CALC_F6_EA, SMPR_SC_ACT_CALC_F6_EA},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_CALC_F6_EA */
|
||||
static const smpTblEntry_t smprScStateTblDhCalcF6Ea[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPR_SC_SM_ST_CALC_F6_EB, SMPR_SC_ACT_CALC_F6_EB},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for SMPR_SC_SM_ST_CALC_F6_EB */
|
||||
static const smpTblEntry_t smprScStateTblDhCalcF6Eb[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_CMAC_CMPL, SMPR_SC_SM_ST_ENCRYPT, SMPR_SC_ACT_SEND_DH_CHECK},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for Legacy PIN_PAIR_1 */
|
||||
static const smpTblEntry_t smprStateTblPinPair1[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_AUTH_RSP, SMPR_SC_SM_ST_PIN_PAIR_2, SMPR_SC_ACT_STORE_LEGACY_PIN},
|
||||
{SMP_MSG_CMD_PKT, SMPR_SC_SM_ST_PIN_PAIR_2, SMPR_SC_ACT_PROC_PAIR_CNF},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for Legacy PIN_PAIR_2 */
|
||||
static const smpTblEntry_t smprStateTblPinPair2[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_AUTH_RSP, SMPR_SC_SM_ST_CNF_CALC_1, SMPR_SC_ACT_PAIR_CNF_CALC_1},
|
||||
{SMP_MSG_CMD_PKT, SMPR_SC_SM_ST_CNF_CALC_1, SMPR_SC_ACT_PROC_PAIR_CNF_CALC_1},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for Legacy CNF_CALC_1 */
|
||||
static const smpTblEntry_t smprStateTblCnfCalc1[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPR_SC_SM_ST_CNF_CALC_2, SMPR_SC_ACT_PAIR_CNF_CALC_2},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for Legacy CNF_CALC_2 */
|
||||
static const smpTblEntry_t smprStateTblCnfCalc2[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPR_SC_SM_ST_PAIR_RAND, SMPR_SC_ACT_SEND_PAIR_CNF},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
|
||||
/*! State table for Legacy PAIR_RAND */
|
||||
static const smpTblEntry_t smprStateTblPairRand[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPR_SC_SM_ST_CNF_VER_CALC_1, SMPR_SC_ACT_PAIR_CNF_VER_CALC_1},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for Legacy CNF_VER_CALC_1 */
|
||||
static const smpTblEntry_t smprStateTblCnfVerCalc1[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPR_SC_SM_ST_CNF_VER_CALC_2, SMPR_SC_ACT_PAIR_CNF_VER_CALC_2},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for Legacy CNF_VER_CALC_2 */
|
||||
static const smpTblEntry_t smprStateTblCnfVerCalc2[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPR_SC_SM_ST_STK_CALC, SMPR_SC_ACT_CNF_VERIFY},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for Legacy STK_CALC */
|
||||
static const smpTblEntry_t smprScStateTblStkCalc[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPR_SC_SM_ST_ENCRYPT, SMPR_SC_ACT_SEND_PAIR_RANDOM},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPR_SC_SM_ST_ATTEMPTS, SMPR_SC_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for ENCRYPT */
|
||||
static const smpTblEntry_t smprScStateTblEncrypt[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_DM_ENCRYPT_CMPL, SMPR_SC_SM_ST_KEY_DIST, SMPR_SC_ACT_SETUP_KEY_DIST},
|
||||
{SMP_MSG_DM_ENCRYPT_FAILED, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPR_SC_SM_ST_ENCRYPT, SMPR_SC_ACT_NONE},
|
||||
{SMP_MSG_DH_CHECK_FAILURE, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_PAIRING_CANCEL},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPR_SC_SM_ST_ATTEMPTS, SMPR_SC_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for KEY_DIST */
|
||||
static const smpTblEntry_t smprScStateTblKeyDist[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPR_SC_SM_ST_KEY_DIST, SMPR_SC_ACT_RCV_KEY},
|
||||
{SMP_MSG_INT_SEND_NEXT_KEY, SMPR_SC_SM_ST_KEY_DIST, SMPR_SC_ACT_SEND_KEY},
|
||||
{SMP_MSG_INT_PAIRING_CMPL, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_PAIRING_CMPL},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPR_SC_SM_ST_KEY_DIST, SMPR_SC_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for ATTEMPTS */
|
||||
static const smpTblEntry_t smprScStateTblAttempts[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_INT_WI_TIMEOUT, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_CHECK_ATTEMPTS},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPR_SC_SM_ST_RSP_TO, SMPR_SC_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_CMD_PKT, SMPR_SC_SM_ST_ATTEMPTS, SMPR_SC_ACT_ATTEMPT_RCVD},
|
||||
{SMP_MSG_API_SECURITY_REQ, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_NOTIFY_DM_ATTEMPTS},
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_CLEANUP},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_CLEANUP},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPR_SC_SM_ST_ATTEMPTS, SMPR_SC_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for RSP_TO */
|
||||
static const smpTblEntry_t smprScStateTblRspTo[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPR_SC_SM_ST_IDLE, SMPR_SC_ACT_CLEANUP},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPR_SC_SM_ST_RSP_TO, SMPR_SC_ACT_NONE},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPR_SC_SM_ST_RSP_TO, SMPR_SC_ACT_NONE},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPR_SC_SM_ST_RSP_TO, SMPR_SC_ACT_NONE},
|
||||
{SMP_MSG_API_SECURITY_REQ, SMPR_SC_SM_ST_RSP_TO, SMPR_SC_ACT_NOTIFY_DM_RSP_TO},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! Table of individual state tables */
|
||||
static const smpTblEntry_t * const smprScStateTbl[] =
|
||||
{
|
||||
smprScStateTblIdle,
|
||||
smprScStateTblApiPairReq,
|
||||
smprScStateTblApiPairRsp,
|
||||
smprScStateTblModeSelect,
|
||||
smprScStateTblPubKey,
|
||||
smprScStateTblLescPin,
|
||||
smprScStateTblAuthSelect,
|
||||
|
||||
smprScStateTblJwNcSetup,
|
||||
smprScStateTblJwNcWaitRand,
|
||||
smprScStateTblJwNcCalcG2,
|
||||
smprScStateTblJwNcWaitUser,
|
||||
smprScStateTblJwNcWaitUserDhCheckRcvd,
|
||||
|
||||
smprScStateTblPassKeyKeypress,
|
||||
smprScStateTblPassWaitAuthRsp,
|
||||
smprScStateTblPasskeyWaitCnf,
|
||||
smprScStateTblPasskeyCalc,
|
||||
smprScStateTblPasskeyRand,
|
||||
smprScStateTblPasskeyCheck,
|
||||
smprScStateTblPasskeyRepeat,
|
||||
|
||||
smprScStateTblOobSendRand,
|
||||
smprScStateTblOobWaitRand,
|
||||
|
||||
smprScStateTblWaitDhCheck,
|
||||
smprScStateTblCalcDHKey,
|
||||
smprScStateTblCalcF5TKey,
|
||||
smprScStateTblCalcF5MacKey,
|
||||
smprScStateTblCalcF5LTK,
|
||||
smprScStateTblDhCalcF6Ea,
|
||||
smprScStateTblDhCalcF6Eb,
|
||||
|
||||
smprStateTblPinPair1,
|
||||
smprStateTblPinPair2,
|
||||
smprStateTblCnfCalc1,
|
||||
smprStateTblCnfCalc2,
|
||||
smprStateTblPairRand,
|
||||
smprStateTblCnfVerCalc1,
|
||||
smprStateTblCnfVerCalc2,
|
||||
|
||||
smprScStateTblStkCalc,
|
||||
smprScStateTblEncrypt,
|
||||
smprScStateTblKeyDist,
|
||||
smprScStateTblAttempts,
|
||||
smprScStateTblRspTo
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! state machine interface */
|
||||
const smpSmIf_t smprScSmIf =
|
||||
{
|
||||
smprScStateTbl,
|
||||
smprScActionTbl,
|
||||
smprScStateTblCommon
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize SMP initiator role.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmprScInit(void)
|
||||
{
|
||||
/* set up callback interface */
|
||||
smpCb.pSlave = &smprScSmIf;
|
||||
|
||||
/* General SMP LESC Initialization */
|
||||
SmpScInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Convert state into string for diagnostics.
|
||||
*
|
||||
* \param state State ID
|
||||
*
|
||||
* \return State string.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *smprStateStr(uint8_t state)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case SMPR_SC_SM_ST_IDLE: return (uint8_t*) "R_IDLE";
|
||||
case SMPR_SC_SM_ST_API_PAIR_REQ: return (uint8_t*) "R_API_PAIR_REQ";
|
||||
case SMPR_SC_SM_ST_API_PAIR_RSP: return (uint8_t*) "R_API_PAIR_RSP";
|
||||
case SMPR_SC_SM_ST_MODE_SELECT: return (uint8_t*) "R_MODE_SELECT";
|
||||
case SMPR_SC_SM_ST_PUB_KEY: return (uint8_t*) "R_PUB_KEY";
|
||||
case SMPR_SC_SM_ST_LESC_PIN: return (uint8_t*) "R_LESC_PIN";
|
||||
case SMPR_SC_SM_ST_AUTH_SELECT: return (uint8_t*) "R_AUTH_SELECT";
|
||||
case SMPR_SC_SM_ST_JWNC_SETUP: return (uint8_t*) "R_JWNC_SETUP";
|
||||
case SMPR_SC_SM_ST_JWNC_WAIT_RAND: return (uint8_t*) "R_JWNC_WAIT_RAND";
|
||||
case SMPR_SC_SM_ST_JWNC_CALC_G2: return (uint8_t*) "R_JWNC_CALC_G2";
|
||||
case SMPR_SC_SM_ST_JWNC_WAIT_USER: return (uint8_t*) "R_JWNC_WAIT_USER";
|
||||
case SMPR_SC_SM_ST_JWNC_WAIT_USER_DH_CHECK_RCVD: return (uint8_t*) "R_JWNC_WAIT_USER_DH";
|
||||
|
||||
case SMPR_SC_SM_ST_PK_KEYPRESS: return (uint8_t*) "R_PK_KEYPRESS";
|
||||
case SMPR_SC_SM_ST_PK_WAIT_AUTH: return (uint8_t*) "R_PK_WAIT_AUTH";
|
||||
case SMPR_SC_SM_ST_PK_WAIT_CNF: return (uint8_t*) "R_PK_WAIT_CNF";
|
||||
case SMPR_SC_SM_ST_PK_CALC: return (uint8_t*) "R_PK_CALC";
|
||||
case SMPR_SC_SM_ST_PK_RAND: return (uint8_t*) "R_PK_RAND";
|
||||
case SMPR_SC_SM_ST_PK_CHECK: return (uint8_t*) "R_PK_CHECK";
|
||||
case SMPR_SC_SM_ST_PK_REPEAT: return (uint8_t*) "R_PK_REPEAT";
|
||||
|
||||
case SMPR_SC_SM_ST_OOB_SEND_RAND: return (uint8_t*) "R_OOB_SEND_RAND";
|
||||
case SMPR_SC_SM_ST_OOB_WAIT_RAND: return (uint8_t*) "R_OOB_WAIT_RAND";
|
||||
|
||||
case SMPR_SC_SM_ST_WAIT_DH_CHECK: return (uint8_t*) "R_WAIT_DH_CHECK";
|
||||
case SMPR_SC_SM_ST_CALC_DHKEY: return (uint8_t*) "R_CALC_DHKEY";
|
||||
case SMPR_SC_SM_ST_CALC_F5_TKEY: return (uint8_t*) "R_CALC_F5_TKEY";
|
||||
case SMPR_SC_SM_ST_CALC_F5_MACKEY: return (uint8_t*) "R_CALC_F5_MACKEY";
|
||||
case SMPR_SC_SM_ST_CALC_F5_LTK: return (uint8_t*) "R_CALC_F5_LTK";
|
||||
case SMPR_SC_SM_ST_CALC_F6_EA: return (uint8_t*) "R_CALC_F6_EA";
|
||||
case SMPR_SC_SM_ST_CALC_F6_EB: return (uint8_t*) "R_CALC_F6_EB";
|
||||
case SMPR_SC_SM_ST_PIN_PAIR_1: return (uint8_t*) "R_PIN_PAIR_1";
|
||||
case SMPR_SC_SM_ST_PIN_PAIR_2: return (uint8_t*) "R_PIN_PAIR_2";
|
||||
case SMPR_SC_SM_ST_CNF_CALC_1: return (uint8_t*) "R_CNF_CALC_1";
|
||||
case SMPR_SC_SM_ST_CNF_CALC_2: return (uint8_t*) "R_CNF_CALC_2";
|
||||
case SMPR_SC_SM_ST_PAIR_RAND: return (uint8_t*) "R_PAIR_RAND";
|
||||
case SMPR_SC_SM_ST_CNF_VER_CALC_1: return (uint8_t*) "R_CNF_VER_CALC_1";
|
||||
case SMPR_SC_SM_ST_CNF_VER_CALC_2: return (uint8_t*) "R_CNF_VER_CALC_2";
|
||||
case SMPR_SC_SM_ST_STK_CALC: return (uint8_t*) "R_STK_CALC";
|
||||
case SMPR_SC_SM_ST_ENCRYPT: return (uint8_t*) "R_ENCRYPT";
|
||||
case SMPR_SC_SM_ST_KEY_DIST: return (uint8_t*) "R_KEY_DIST";
|
||||
case SMPR_SC_SM_ST_ATTEMPTS: return (uint8_t*) "R_ATTEMPTS";
|
||||
case SMPR_SC_SM_ST_RSP_TO: return (uint8_t*) "R_RSP_TO";
|
||||
|
||||
default: return (uint8_t*) "R_Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
+310
@@ -0,0 +1,310 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief SMP responder state machine.
|
||||
*
|
||||
* Copyright (c) 2010-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* 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 "wsf_types.h"
|
||||
#include "smp_api.h"
|
||||
#include "smp_main.h"
|
||||
#include "smpr_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Action function enumeration */
|
||||
enum
|
||||
{
|
||||
SMPR_ACT_NONE, /*!< No Action */
|
||||
SMPR_ACT_CLEANUP, /*!< Process Pairing Cleanup */
|
||||
SMPR_ACT_PAIRING_FAILED, /*!< Process Pairing Failed */
|
||||
SMPR_ACT_PAIRING_CANCEL, /*!< Process Pairing Canceled */
|
||||
SMPR_ACT_STORE_PIN, /*!< Process Store Pin */
|
||||
SMPR_ACT_PAIR_CNF_CALC_1, /*!< Process Confirm Value Calculation 1 */
|
||||
SMPR_ACT_PAIR_CNF_CALC_2, /*!< Process Confirm Value Calculation 2 */
|
||||
SMPR_ACT_SEND_PAIR_CNF, /*!< Process Send Confirm Value */
|
||||
SMPR_ACT_PAIR_CNF_VER_CALC_1, /*!< Process Received Confirm Value Verification Calculation 1 */
|
||||
SMPR_ACT_PAIR_CNF_VER_CALC_2, /*!< Process Received Confirm Value Verification Calculation 2 */
|
||||
SMPR_ACT_MAX_ATTEMPTS, /*!< Process Maximum Attempts */
|
||||
SMPR_ACT_ATTEMPT_RCVD, /*!< Process Attempts Received */
|
||||
SMPR_ACT_CHECK_ATTEMPTS, /*!< Process Check Attempts */
|
||||
SMPR_ACT_NOTIFY_DM_ATTEMPTS, /*!< Process Notify DM of Attempts Failure */
|
||||
SMPR_ACT_NOTIFY_DM_RSP_TO, /*!< Process Notify DM of Response Timeout Failure */
|
||||
SMPR_ACT_PAIRING_CMPL, /*!< Process Pairing Complete */
|
||||
SMPR_ACT_SEND_SECURITY_REQ, /*!< Process Send Slave Security Request */
|
||||
SMPR_ACT_PROC_PAIR_REQ, /*!< Process Pairing Request */
|
||||
SMPR_ACT_SEND_PAIR_RSP, /*!< Process Send Pairing Response */
|
||||
SMPR_ACT_PROC_PAIR_CNF, /*!< Process Pair Confirm Value */
|
||||
SMPR_ACT_PROC_PAIR_CNF_CALC_1, /*!< Process Received Confirm Value */
|
||||
SMPR_ACT_CNF_VERIFY, /*!< Process Recevied Confirm Value Verification */
|
||||
SMPR_ACT_SEND_PAIR_RANDOM, /*!< Process Send Random Value */
|
||||
SMPR_ACT_SETUP_KEY_DIST, /*!< Process Setup Key Distribution */
|
||||
SMPR_ACT_SEND_KEY, /*!< Process Send Key */
|
||||
SMPR_ACT_RCV_KEY, /*!< Process Received Key */
|
||||
};
|
||||
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Static Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Action function table; order matches action function enumeration */
|
||||
static const smpAct_t smprActionTbl[] =
|
||||
{
|
||||
smpActNone,
|
||||
smpActCleanup,
|
||||
smpActPairingFailed,
|
||||
smpActPairingCancel,
|
||||
smpActStorePin,
|
||||
smpActPairCnfCalc1,
|
||||
smpActPairCnfCalc2,
|
||||
smpActSendPairCnf,
|
||||
smpActPairCnfVerCalc1,
|
||||
smpActPairCnfVerCalc2,
|
||||
smpActMaxAttempts,
|
||||
smpActAttemptRcvd,
|
||||
smpActCheckAttempts,
|
||||
smpActNotifyDmAttemptsFailure,
|
||||
smpActNotifyDmRspToFailure,
|
||||
smpActPairingCmpl,
|
||||
smprActSendSecurityReq,
|
||||
smprActProcPairReq,
|
||||
smprActSendPairRsp,
|
||||
smprActProcPairCnf,
|
||||
smprActProcPairCnfCalc1,
|
||||
smprActCnfVerify,
|
||||
smprActSendPairRandom,
|
||||
smprActSetupKeyDist,
|
||||
smprActSendKey,
|
||||
smprActRcvKey
|
||||
};
|
||||
|
||||
/*! State table for common actions */
|
||||
static const smpTblEntry_t smprStateTblCommon[SMP_STATE_TBL_COMMON_MAX] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPR_SM_ST_IDLE, SMPR_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPR_SM_ST_IDLE, SMPR_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPR_SM_ST_IDLE, SMPR_ACT_PAIRING_CANCEL},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPR_SM_ST_RSP_TO, SMPR_ACT_PAIRING_FAILED},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for IDLE */
|
||||
static const smpTblEntry_t smprStateTblIdle[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_SECURITY_REQ, SMPR_SM_ST_API_PAIR_REQ, SMPR_ACT_SEND_SECURITY_REQ},
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPR_SM_ST_IDLE, SMPR_ACT_CLEANUP},
|
||||
{SMP_MSG_CMD_PKT, SMPR_SM_ST_API_PAIR_RSP, SMPR_ACT_PROC_PAIR_REQ},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPR_SM_ST_IDLE, SMPR_ACT_NONE},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPR_SM_ST_IDLE, SMPR_ACT_CLEANUP},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPR_SM_ST_IDLE, SMPR_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for API_PAIR_REQ */
|
||||
static const smpTblEntry_t smprStateTblApiPairReq[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPR_SM_ST_IDLE, SMPR_ACT_CLEANUP},
|
||||
{SMP_MSG_CMD_PKT, SMPR_SM_ST_API_PAIR_RSP, SMPR_ACT_PROC_PAIR_REQ},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPR_SM_ST_IDLE, SMPR_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPR_SM_ST_IDLE, SMPR_ACT_CLEANUP},
|
||||
{SMP_MSG_DM_ENCRYPT_CMPL, SMPR_SM_ST_IDLE, SMPR_ACT_CLEANUP},
|
||||
{SMP_MSG_DM_ENCRYPT_FAILED, SMPR_SM_ST_IDLE, SMPR_ACT_CLEANUP},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for API_PAIR_RSP */
|
||||
static const smpTblEntry_t smprStateTblApiPairRsp[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_PAIR_RSP, SMPR_SM_ST_PIN_PAIR_1, SMPR_ACT_SEND_PAIR_RSP},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for PIN_PAIR_1 */
|
||||
static const smpTblEntry_t smprStateTblPinPair1[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_AUTH_RSP, SMPR_SM_ST_PIN_PAIR_2, SMPR_ACT_STORE_PIN},
|
||||
{SMP_MSG_CMD_PKT, SMPR_SM_ST_PIN_PAIR_2, SMPR_ACT_PROC_PAIR_CNF},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for PIN_PAIR_2 */
|
||||
static const smpTblEntry_t smprStateTblPinPair2[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_API_AUTH_RSP, SMPR_SM_ST_CNF_CALC_1, SMPR_ACT_PAIR_CNF_CALC_1},
|
||||
{SMP_MSG_CMD_PKT, SMPR_SM_ST_CNF_CALC_1, SMPR_ACT_PROC_PAIR_CNF_CALC_1},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for CNF_CALC_1 */
|
||||
static const smpTblEntry_t smprStateTblCnfCalc1[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPR_SM_ST_CNF_CALC_2, SMPR_ACT_PAIR_CNF_CALC_2},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for CNF_CALC_2 */
|
||||
static const smpTblEntry_t smprStateTblCnfCalc2[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPR_SM_ST_PAIR_RAND, SMPR_ACT_SEND_PAIR_CNF},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
|
||||
/*! State table for PAIR_RAND */
|
||||
static const smpTblEntry_t smprStateTblPairRand[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_CMD_PKT, SMPR_SM_ST_CNF_VER_CALC_1, SMPR_ACT_PAIR_CNF_VER_CALC_1},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for CNF_VER_CALC_1 */
|
||||
static const smpTblEntry_t smprStateTblCnfVerCalc1[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPR_SM_ST_CNF_VER_CALC_2, SMPR_ACT_PAIR_CNF_VER_CALC_2},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for CNF_VER_CALC_2 */
|
||||
static const smpTblEntry_t smprStateTblCnfVerCalc2[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPR_SM_ST_STK_CALC, SMPR_ACT_CNF_VERIFY},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for STK_CALC */
|
||||
static const smpTblEntry_t smprStateTblStkCalc[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_WSF_AES_CMPL, SMPR_SM_ST_ENCRYPT, SMPR_ACT_SEND_PAIR_RANDOM},
|
||||
{SMP_MSG_INT_MAX_ATTEMPTS, SMPR_SM_ST_ATTEMPTS, SMPR_ACT_MAX_ATTEMPTS},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for ENCRYPT */
|
||||
static const smpTblEntry_t smprStateTblEncrypt[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_DM_ENCRYPT_CMPL, SMPR_SM_ST_KEY_DIST, SMPR_ACT_SETUP_KEY_DIST},
|
||||
{SMP_MSG_DM_ENCRYPT_FAILED, SMPR_SM_ST_IDLE, SMPR_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPR_SM_ST_ENCRYPT, SMPR_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for KEY_DIST */
|
||||
static const smpTblEntry_t smprStateTblKeyDist[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_INT_SEND_NEXT_KEY, SMPR_SM_ST_KEY_DIST, SMPR_ACT_SEND_KEY},
|
||||
{SMP_MSG_CMD_PKT, SMPR_SM_ST_KEY_DIST, SMPR_ACT_RCV_KEY},
|
||||
{SMP_MSG_INT_PAIRING_CMPL, SMPR_SM_ST_IDLE, SMPR_ACT_PAIRING_CMPL},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPR_SM_ST_KEY_DIST, SMPR_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for ATTEMPTS */
|
||||
static const smpTblEntry_t smprStateTblAttempts[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_INT_WI_TIMEOUT, SMPR_SM_ST_IDLE, SMPR_ACT_CHECK_ATTEMPTS},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPR_SM_ST_RSP_TO, SMPR_ACT_PAIRING_FAILED},
|
||||
{SMP_MSG_CMD_PKT, SMPR_SM_ST_ATTEMPTS, SMPR_ACT_ATTEMPT_RCVD},
|
||||
{SMP_MSG_API_SECURITY_REQ, SMPR_SM_ST_IDLE, SMPR_ACT_NOTIFY_DM_ATTEMPTS},
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPR_SM_ST_IDLE, SMPR_ACT_CLEANUP},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPR_SM_ST_IDLE, SMPR_ACT_CLEANUP},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPR_SM_ST_ATTEMPTS, SMPR_ACT_NONE},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! State table for RSP_TO */
|
||||
static const smpTblEntry_t smprStateTblRspTo[] =
|
||||
{
|
||||
/* Event Next state Action */
|
||||
{SMP_MSG_DM_CONN_CLOSE, SMPR_SM_ST_IDLE, SMPR_ACT_CLEANUP},
|
||||
{SMP_MSG_CMD_PAIRING_FAILED, SMPR_SM_ST_RSP_TO, SMPR_ACT_NONE},
|
||||
{SMP_MSG_API_CANCEL_REQ, SMPR_SM_ST_RSP_TO, SMPR_ACT_NONE},
|
||||
{SMP_MSG_INT_RSP_TIMEOUT, SMPR_SM_ST_RSP_TO, SMPR_ACT_NONE},
|
||||
{SMP_MSG_API_SECURITY_REQ, SMPR_SM_ST_RSP_TO, SMPR_ACT_NOTIFY_DM_RSP_TO},
|
||||
{0, 0, 0}
|
||||
};
|
||||
|
||||
/*! Table of individual state tables */
|
||||
const smpTblEntry_t * const smprStateTbl[] =
|
||||
{
|
||||
smprStateTblIdle,
|
||||
smprStateTblApiPairReq,
|
||||
smprStateTblApiPairRsp,
|
||||
smprStateTblPinPair1,
|
||||
smprStateTblPinPair2,
|
||||
smprStateTblCnfCalc1,
|
||||
smprStateTblCnfCalc2,
|
||||
smprStateTblPairRand,
|
||||
smprStateTblCnfVerCalc1,
|
||||
smprStateTblCnfVerCalc2,
|
||||
smprStateTblStkCalc,
|
||||
smprStateTblEncrypt,
|
||||
smprStateTblKeyDist,
|
||||
smprStateTblAttempts,
|
||||
smprStateTblRspTo
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! state machine interface */
|
||||
const smpSmIf_t smprSmIf =
|
||||
{
|
||||
smprStateTbl,
|
||||
smprActionTbl,
|
||||
smprStateTblCommon
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize SMP responder role.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SmprInit(void)
|
||||
{
|
||||
/* set up state machine interface */
|
||||
smpCb.pSlave = &smprSmIf;
|
||||
smpCb.procPairing = smpProcPairing;
|
||||
smpCb.procAuthReq = smpAuthReq;
|
||||
}
|
||||
Reference in New Issue
Block a user