initial commit

This commit is contained in:
2022-10-23 23:45:43 -07:00
commit e190fa5193
6450 changed files with 8626944 additions and 0 deletions
@@ -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);
}
@@ -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 */
@@ -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 */
@@ -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)};
@@ -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);
}
@@ -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;
}
@@ -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 */
@@ -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);
}
}
}
@@ -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);
}
}
@@ -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;
}
@@ -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);
}
}
@@ -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;
}
@@ -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;
}
@@ -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);
}
}
@@ -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);
}
@@ -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;
}
@@ -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 */
@@ -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);
}
}
@@ -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);
}
}
@@ -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;
}
@@ -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);
}
}
}
@@ -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;
}
@@ -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 */
@@ -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;
}
@@ -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 */
File diff suppressed because it is too large Load Diff
@@ -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;
}
File diff suppressed because it is too large Load Diff
@@ -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 */
@@ -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();
}
@@ -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();
}
@@ -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;
}
@@ -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;
}
@@ -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);
}
@@ -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;
}
@@ -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;
}
@@ -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);
}
}
@@ -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);
}
@@ -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 */
@@ -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);
}
}
@@ -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);
}
@@ -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 */
@@ -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);
}
}
@@ -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;
}
@@ -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 */
@@ -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);
}
}
@@ -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 */
@@ -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();
}
@@ -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 */
@@ -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;
}
@@ -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;
}
@@ -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();
}
@@ -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 */
@@ -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;
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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();
}
@@ -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);
}
@@ -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 */
File diff suppressed because it is too large Load Diff
@@ -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);
}
@@ -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 */
@@ -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);
}
}
@@ -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)
{
}
}
@@ -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;
}
}
}
@@ -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;
}
}
}
@@ -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)
{
}
}
@@ -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 */
@@ -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);
}
File diff suppressed because it is too large Load Diff
@@ -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
}
@@ -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 */
@@ -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;
}
}
@@ -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 */
@@ -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);
}
}
@@ -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";
}
}
@@ -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;
}
@@ -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);
}
}
@@ -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 */
@@ -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);
}
}
@@ -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";
}
}
@@ -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;
}