initial commit
This commit is contained in:
+552
@@ -0,0 +1,552 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_math.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* indexes into base UUID for 16-to-128 bit UUID conversion */
|
||||
#define ATT_BASE_UUID_POS_0 12
|
||||
#define ATT_BASE_UUID_POS_1 13
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
static uint8_t attBaseUuid[] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
|
||||
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Default component function inteface */
|
||||
const attFcnIf_t attFcnDefault =
|
||||
{
|
||||
attEmptyDataCback,
|
||||
(l2cCtrlCback_t) attEmptyHandler,
|
||||
(attMsgHandler_t) attEmptyHandler,
|
||||
attEmptyConnCback
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
attCb_t attCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2C data callback for ATT.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attL2cDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t pduType;
|
||||
|
||||
/* parse PDU type */
|
||||
pduType = *(pPacket + L2C_PAYLOAD_START);
|
||||
|
||||
/* if from server */
|
||||
if ((pduType & ATT_PDU_MASK_SERVER) != 0)
|
||||
{
|
||||
/* call client data callback */
|
||||
(*attCb.pClient->dataCback)(handle, len, pPacket);
|
||||
}
|
||||
/* else from client */
|
||||
else
|
||||
{
|
||||
/* call server data callback */
|
||||
(*attCb.pServer->dataCback)(handle, len, pPacket);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2C control callback for ATT.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attL2cCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
attCcb_t *pCcb;
|
||||
|
||||
/* get connection control block */
|
||||
pCcb = attCcbByConnId((dmConnId_t) pMsg->param);
|
||||
|
||||
/* verify connection is open */
|
||||
if (pCcb->connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
if (pMsg->event == L2C_CTRL_FLOW_DISABLE_IND)
|
||||
{
|
||||
/* flow disabled */
|
||||
pCcb->control |= ATT_CCB_STATUS_FLOW_DISABLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* flow enabled */
|
||||
pCcb->control &= ~ATT_CCB_STATUS_FLOW_DISABLED;
|
||||
|
||||
/* call server control callback */
|
||||
(*attCb.pServer->ctrlCback)(pMsg);
|
||||
|
||||
/* check flow again; could be changed recursively */
|
||||
if (!(pCcb->control & ATT_CCB_STATUS_FLOW_DISABLED))
|
||||
{
|
||||
/* call client control callback */
|
||||
(*attCb.pClient->ctrlCback)(pMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief DM connection callback for ATT.
|
||||
*
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attDmConnCback(dmEvt_t *pDmEvt)
|
||||
{
|
||||
attCcb_t *pCcb;
|
||||
|
||||
pCcb = attCcbByConnId((dmConnId_t) pDmEvt->hdr.param);
|
||||
|
||||
/* if new connection created */
|
||||
if (pDmEvt->hdr.event == DM_CONN_OPEN_IND)
|
||||
{
|
||||
/* initialize control block before handling event */
|
||||
pCcb->handle = pDmEvt->connOpen.handle;
|
||||
pCcb->mtu = ATT_DEFAULT_MTU;
|
||||
pCcb->connId = (dmConnId_t) pDmEvt->hdr.param;
|
||||
pCcb->control = 0;
|
||||
pCcb->pPendDbHashRsp = NULL;
|
||||
}
|
||||
|
||||
/* if connection has been opened */
|
||||
if (pCcb->connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
/* pass event to server */
|
||||
(*attCb.pServer->connCback)(pCcb, pDmEvt);
|
||||
|
||||
/* pass event to client */
|
||||
(*attCb.pClient->connCback)(pCcb, pDmEvt);
|
||||
|
||||
/* if connection closed */
|
||||
if (pDmEvt->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* clear control block after handling event */
|
||||
pCcb->connId = DM_CONN_ID_NONE;
|
||||
|
||||
if (pCcb->pPendDbHashRsp)
|
||||
{
|
||||
WsfBufFree(pCcb->pPendDbHashRsp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* execute ATT connection callback */
|
||||
if (attCb.connCback != NULL)
|
||||
{
|
||||
(*attCb.connCback)(pDmEvt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief ATT empty event handler.
|
||||
*
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attEmptyHandler(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Empty connection callback for ATT.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attEmptyConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Empty data callback for ATT.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attEmptyDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the connection control block for the given handle.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
*
|
||||
* \return Pointer to connection control block or NULL if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
attCcb_t *attCcbByHandle(uint16_t handle)
|
||||
{
|
||||
dmConnId_t connId;
|
||||
|
||||
if ((connId = DmConnIdByHandle(handle)) != DM_CONN_ID_NONE)
|
||||
{
|
||||
return &attCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the connection control block for the connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to connection control block.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
attCcb_t *attCcbByConnId(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return &attCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Compare a 16 bit UUID to a 128 bit UUID.
|
||||
*
|
||||
* \param pUuid16 Pointer to 16 bit UUID.
|
||||
* \param pUuid128 Pointer to 128 bit UUID.
|
||||
*
|
||||
* \return TRUE of UUIDs match, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t attUuidCmp16to128(const uint8_t *pUuid16, const uint8_t *pUuid128)
|
||||
{
|
||||
attBaseUuid[ATT_BASE_UUID_POS_0] = pUuid16[0];
|
||||
attBaseUuid[ATT_BASE_UUID_POS_1] = pUuid16[1];
|
||||
|
||||
return (memcmp(attBaseUuid, pUuid128, ATT_128_UUID_LEN) == 0);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the attribute protocol MTU of a connection.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param peerMtu Peer MTU.
|
||||
* \param localMtu Local MTU.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attSetMtu(attCcb_t *pCcb, uint16_t peerMtu, uint16_t localMtu)
|
||||
{
|
||||
uint16_t mtu;
|
||||
|
||||
/* set negotiated mtu for the connection to the lesser of ours and theirs */
|
||||
mtu = WSF_MIN(peerMtu, localMtu);
|
||||
|
||||
/* if current mtu is not the same as the negotiated value */
|
||||
if (pCcb->mtu != mtu)
|
||||
{
|
||||
/* set mtu to the new value */
|
||||
pCcb->mtu = mtu;
|
||||
|
||||
/* notify app about the new value */
|
||||
attExecCallback(pCcb->connId, ATT_MTU_UPDATE_IND, 0, ATT_SUCCESS, mtu);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute application callback function.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param event Callback event ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param status Callback event status.
|
||||
* \param mtu Negotiated MTU value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attExecCallback(dmConnId_t connId, uint8_t event, uint16_t handle, uint8_t status, uint16_t mtu)
|
||||
{
|
||||
if (attCb.cback)
|
||||
{
|
||||
attEvt_t evt;
|
||||
|
||||
evt.hdr.param = connId;
|
||||
evt.hdr.event = event;
|
||||
evt.hdr.status = status;
|
||||
evt.valueLen = 0;
|
||||
evt.handle = handle;
|
||||
evt.continuing = 0;
|
||||
evt.mtu = mtu;
|
||||
|
||||
(*attCb.cback)(&evt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate an ATT data message buffer to be used for the ATT attribute protocol messages.
|
||||
*
|
||||
* \param len Message length in bytes.
|
||||
*
|
||||
* \return Pointer to data message buffer or NULL if allocation failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void *attMsgAlloc(uint16_t len)
|
||||
{
|
||||
return WsfMsgDataAlloc(len, HCI_TX_DATA_TAILROOM);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief ATT handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for ATT.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
/* store handler ID */
|
||||
attCb.handlerId = handlerId;
|
||||
|
||||
/* initialize control block */
|
||||
attCb.pClient = &attFcnDefault;
|
||||
attCb.pServer = &attFcnDefault;
|
||||
|
||||
/* Register with L2C */
|
||||
L2cRegister(L2C_CID_ATT, attL2cDataCback, attL2cCtrlCback);
|
||||
|
||||
/* Register with DM */
|
||||
DmConnRegister(DM_CLIENT_ID_ATT, attDmConnCback);
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for ATT.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* Handle message */
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
if (pMsg->event >= ATTS_MSG_START)
|
||||
{
|
||||
/* pass event to server */
|
||||
(*attCb.pServer->msgCback)(pMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* pass event to client */
|
||||
(*attCb.pClient->msgCback)(pMsg);
|
||||
}
|
||||
}
|
||||
/* Handle events */
|
||||
else if (event)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback with ATT.
|
||||
*
|
||||
* \param cback Client callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttRegister(attCback_t cback)
|
||||
{
|
||||
attCb.cback = cback;
|
||||
|
||||
/* if configured MTU size is larger than maximum RX PDU length */
|
||||
if (pAttCfg->mtu > (HciGetMaxRxAclLen() - L2C_HDR_LEN))
|
||||
{
|
||||
/* notify app about MTU misconfiguration */
|
||||
attExecCallback(0, DM_ERROR_IND, 0, DM_ERR_ATT_RX_PDU_LEN_EXCEEDED, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a connection callback with ATT. The callback is typically used to
|
||||
* manage the attribute server database.
|
||||
*
|
||||
* \param cback Client callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttConnRegister(dmCback_t cback)
|
||||
{
|
||||
attCb.connCback = cback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the attribute protocol MTU of a connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return MTU of the connection.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t AttGetMtu(dmConnId_t connId)
|
||||
{
|
||||
return (attCcbByConnId(connId)->mtu);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate an ATT message buffer to be sent with the ATT attribute protocol
|
||||
* zero-copy APIs.
|
||||
*
|
||||
* \param len Message length in bytes.
|
||||
* \param opcode Opcode for ATT message.
|
||||
*
|
||||
* \return Pointer to message buffer or NULL if allocation failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void *AttMsgAlloc(uint16_t len, uint8_t opcode)
|
||||
{
|
||||
uint8_t *pMsg;
|
||||
uint8_t hdrLen;
|
||||
|
||||
WSF_ASSERT((opcode == ATT_PDU_VALUE_IND) || (opcode == ATT_PDU_VALUE_NTF));
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case ATT_PDU_VALUE_IND:
|
||||
case ATT_PDU_VALUE_NTF:
|
||||
hdrLen = ATT_VALUE_IND_NTF_BUF_LEN;
|
||||
break;
|
||||
|
||||
default:
|
||||
hdrLen = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hdrLen > 0)
|
||||
{
|
||||
pMsg = attMsgAlloc(hdrLen + len);
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
/* return pointer to attribute value buffer */
|
||||
return (pMsg + hdrLen);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Free an ATT message buffer allocated with AttMsgAlloc().
|
||||
*
|
||||
* \param pMsg Pointer to message buffer.
|
||||
* \param opcode Opcode for ATT message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttMsgFree(void *pMsg, uint8_t opcode)
|
||||
{
|
||||
uint8_t hdrLen;
|
||||
|
||||
WSF_ASSERT((opcode == ATT_PDU_VALUE_IND) || (opcode == ATT_PDU_VALUE_NTF));
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case ATT_PDU_VALUE_IND:
|
||||
case ATT_PDU_VALUE_NTF:
|
||||
hdrLen = ATT_VALUE_IND_NTF_BUF_LEN;
|
||||
break;
|
||||
|
||||
default:
|
||||
hdrLen = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
WsfMsgFree(((uint8_t *)pMsg) - hdrLen);
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef ATT_MAIN_H
|
||||
#define ATT_MAIN_H
|
||||
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "l2c_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* ATT protocol methods */
|
||||
#define ATT_METHOD_ERR 0 /* Error response */
|
||||
#define ATT_METHOD_MTU 1 /* Exchange mtu */
|
||||
#define ATT_METHOD_FIND_INFO 2 /* Find information */
|
||||
#define ATT_METHOD_FIND_TYPE 3 /* Find by type value */
|
||||
#define ATT_METHOD_READ_TYPE 4 /* Read by type value */
|
||||
#define ATT_METHOD_READ 5 /* Read */
|
||||
#define ATT_METHOD_READ_BLOB 6 /* Read long */
|
||||
#define ATT_METHOD_READ_MULTIPLE 7 /* Read multiple */
|
||||
#define ATT_METHOD_READ_GROUP_TYPE 8 /* Read group type */
|
||||
#define ATT_METHOD_WRITE 9 /* Write */
|
||||
#define ATT_METHOD_WRITE_CMD 10 /* Write command */
|
||||
#define ATT_METHOD_PREPARE_WRITE 11 /* Prepare write */
|
||||
#define ATT_METHOD_EXECUTE_WRITE 12 /* Execute write */
|
||||
#define ATT_METHOD_VALUE_NTF 13 /* Handle value notification */
|
||||
#define ATT_METHOD_VALUE_IND 14 /* Handle value indication */
|
||||
#define ATT_METHOD_VALUE_CNF 15 /* Handle value confirm */
|
||||
#define ATT_METHOD_SIGNED_WRITE_CMD 16 /* Signed write command */
|
||||
|
||||
/* Convert opcode to method */
|
||||
#define ATT_OPCODE_2_METHOD(op) (((op) & ~ATT_PDU_MASK_SERVER) / 2)
|
||||
|
||||
/* Client and server message macros */
|
||||
#define ATTC_MSG_START 0x00
|
||||
#define ATTS_MSG_START 0x20
|
||||
#define ATT_MSG_MASK(msg) ((msg) & 0x1F)
|
||||
|
||||
/* Buffer lengths for messages */
|
||||
#define ATT_VALUE_IND_NTF_BUF_LEN (ATT_VALUE_NTF_LEN + L2C_PAYLOAD_START)
|
||||
|
||||
/* attCcb_t control bits */
|
||||
#define ATT_CCB_STATUS_MTU_SENT (1<<0) /* MTU req or rsp sent */
|
||||
#define ATT_CCB_STATUS_FLOW_DISABLED (1<<1) /* Data flow disabled */
|
||||
#define ATT_CCB_STATUS_TX_TIMEOUT (1<<2) /* ATT transaction timed out */
|
||||
#define ATT_CCB_STATUS_RSP_PENDING (1<<3) /* ATTS write rsp pending */
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t startHandle; /* Start handle of the requested operation. Used if an Error Response is sent. */
|
||||
uint16_t handle; /* Attribute handle of pending response. */
|
||||
} attPendDbHashRsp_t;
|
||||
|
||||
/* Connection control block */
|
||||
typedef struct
|
||||
{
|
||||
wsfQueue_t prepWriteQueue; /* prepare write queue */
|
||||
wsfTimer_t idleTimer; /* service discovery idle timer */
|
||||
uint16_t handle; /* connection handle */
|
||||
uint16_t mtu; /* connection mtu */
|
||||
dmConnId_t connId; /* DM connection ID */
|
||||
uint8_t control; /* Control bitfield */
|
||||
attPendDbHashRsp_t *pPendDbHashRsp; /* Pending ATT Response information. */
|
||||
} attCcb_t;
|
||||
|
||||
/* ATT message handling function type */
|
||||
typedef void (*attMsgHandler_t)(void *pMsg);
|
||||
|
||||
/* ATT connection callback type */
|
||||
typedef void (*attConnCback_t)(attCcb_t *pCcb, dmEvt_t *pDmEvt);
|
||||
|
||||
/* Callback interface for client and server */
|
||||
typedef struct
|
||||
{
|
||||
l2cDataCback_t dataCback; /* Data callback */
|
||||
l2cCtrlCback_t ctrlCback; /* Control callback */
|
||||
attMsgHandler_t msgCback; /* Message handling callback */
|
||||
attConnCback_t connCback; /* Connection callback */
|
||||
} attFcnIf_t;
|
||||
|
||||
/* Main control block of the ATT subsystem */
|
||||
typedef struct
|
||||
{
|
||||
attCcb_t ccb[DM_CONN_MAX]; /* Connection control blocks */
|
||||
attFcnIf_t const *pClient; /* Client callback interface */
|
||||
attFcnIf_t const *pServer; /* Server callback interface */
|
||||
attCback_t cback; /* ATT callback function */
|
||||
dmCback_t connCback; /* ATT connection callback function */
|
||||
wsfHandlerId_t handlerId; /* WSF handler ID */
|
||||
uint8_t errTest; /* Status code for error testing */
|
||||
} attCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Default component function inteface */
|
||||
extern const attFcnIf_t attFcnDefault;
|
||||
|
||||
/* Control block */
|
||||
extern attCb_t attCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
void attEmptyHandler(wsfMsgHdr_t *pMsg);
|
||||
void attEmptyDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket);
|
||||
void attEmptyConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt);
|
||||
|
||||
attCcb_t *attCcbByHandle(uint16_t handle);
|
||||
attCcb_t *attCcbByConnId(dmConnId_t connId);
|
||||
|
||||
bool_t attUuidCmp16to128(const uint8_t *pUuid16, const uint8_t *pUuid128);
|
||||
void attSetMtu(attCcb_t *pCcb, uint16_t peerMtu, uint16_t localMtu);
|
||||
void attExecCallback(dmConnId_t connId, uint8_t event, uint16_t handle, uint8_t status, uint16_t mtu);
|
||||
void *attMsgAlloc(uint16_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* ATT_MAIN_H */
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT optional signed PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef ATT_SIGN_H
|
||||
#define ATT_SIGN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* CMAC algorithm subkey length */
|
||||
#define ATT_CMAC_SUBKEY_LEN 16
|
||||
|
||||
/* CMAC algorithm block length */
|
||||
#define ATT_CMAC_BLOCK_LEN 16
|
||||
|
||||
/* CMAC constant Rb */
|
||||
#define ATT_CMAC_RB 0x87
|
||||
|
||||
/* CMAC signature result length */
|
||||
#define ATT_CMAC_RESULT_LEN 8
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* ATT_SIGN_H */
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT UUID constants.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_uuid.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Service UUIDs */
|
||||
const uint8_t attGapSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GAP_SERVICE)};
|
||||
const uint8_t attGattSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GATT_SERVICE)};
|
||||
const uint8_t attIasSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_IMMEDIATE_ALERT_SERVICE)};
|
||||
const uint8_t attLlsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_LINK_LOSS_SERVICE)};
|
||||
const uint8_t attTpsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TX_POWER_SERVICE)};
|
||||
const uint8_t attCtsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CURRENT_TIME_SERVICE)};
|
||||
const uint8_t attRtusSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_REF_TIME_UPDATE_SERVICE)};
|
||||
const uint8_t attNdcsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DST_CHANGE_SERVICE)};
|
||||
const uint8_t attGlsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GLUCOSE_SERVICE)};
|
||||
const uint8_t attHtsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HEALTH_THERM_SERVICE)};
|
||||
const uint8_t attDisSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)};
|
||||
const uint8_t attNwaSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_NETWORK_AVAIL_SERVICE)};
|
||||
const uint8_t attWdsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_WATCHDOG_SERVICE)};
|
||||
const uint8_t attHrsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HEART_RATE_SERVICE)};
|
||||
const uint8_t attPassSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PHONE_ALERT_SERVICE)};
|
||||
const uint8_t attBasSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BATTERY_SERVICE)};
|
||||
const uint8_t attBpsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BLOOD_PRESSURE_SERVICE)};
|
||||
const uint8_t attAnsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_NOTIF_SERVICE)};
|
||||
const uint8_t attHidSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_SERVICE)};
|
||||
const uint8_t attSpsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SCAN_PARAM_SERVICE)};
|
||||
const uint8_t attPlxsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PULSE_OXIMITER_SERVICE)};
|
||||
const uint8_t attUdsSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_USER_DATA_SERVICE)};
|
||||
const uint8_t attMprvSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PRV_SERVICE)};
|
||||
const uint8_t attMprxSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PROXY_SERVICE)};
|
||||
|
||||
/*! GATT UUIDs */
|
||||
const uint8_t attPrimSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PRIMARY_SERVICE)};
|
||||
const uint8_t attSecSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SECONDARY_SERVICE)};
|
||||
const uint8_t attIncUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_INCLUDE)};
|
||||
const uint8_t attChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CHARACTERISTIC)};
|
||||
|
||||
/*! Descriptor UUIDs */
|
||||
const uint8_t attChExtUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CHARACTERISTIC_EXT)};
|
||||
const uint8_t attChUserDescUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CHAR_USER_DESC)};
|
||||
const uint8_t attCliChCfgUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CLIENT_CHAR_CONFIG)};
|
||||
const uint8_t attSrvChCfgUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SERVER_CHAR_CONFIG)};
|
||||
const uint8_t attChPresFmtUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CHAR_PRES_FORMAT)};
|
||||
const uint8_t attAggFmtUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_AGGREGATE_FORMAT)};
|
||||
const uint8_t attValRangeUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_VALID_RANGE)};
|
||||
const uint8_t attHidErmUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_EXT_REPORT_MAPPING)};
|
||||
const uint8_t attHidRimUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_REPORT_ID_MAPPING)};
|
||||
|
||||
/*! Characteristic UUIDs */
|
||||
const uint8_t attDnChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DEVICE_NAME)};
|
||||
const uint8_t attApChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_APPEARANCE)};
|
||||
const uint8_t attPpfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PERIPH_PRIVACY_FLAG)};
|
||||
const uint8_t attRaChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RECONN_ADDR)};
|
||||
const uint8_t attPpcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PREF_CONN_PARAM)};
|
||||
const uint8_t attScChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SERVICE_CHANGED)};
|
||||
const uint8_t attAlChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_LEVEL)};
|
||||
const uint8_t attTxpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TX_POWER_LEVEL)};
|
||||
const uint8_t attDtChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DATE_TIME)};
|
||||
const uint8_t attDwChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DAY_OF_WEEK)};
|
||||
const uint8_t attDdtChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DAY_DATE_TIME)};
|
||||
const uint8_t attEt100ChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_EXACT_TIME_100)};
|
||||
const uint8_t attEt256ChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_EXACT_TIME_256)};
|
||||
const uint8_t attDstoChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DST_OFFSET)};
|
||||
const uint8_t attTzChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_ZONE)};
|
||||
const uint8_t attLtiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_LOCAL_TIME_INFO)};
|
||||
const uint8_t attStzChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SECONDARY_TIME_ZONE)};
|
||||
const uint8_t attTdstChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_WITH_DST)};
|
||||
const uint8_t attTaChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_ACCURACY)};
|
||||
const uint8_t attTsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_SOURCE)};
|
||||
const uint8_t attRtiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_REFERENCE_TIME_INFO)};
|
||||
const uint8_t attTbChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_BROADCAST)};
|
||||
const uint8_t attTucpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_UPDATE_CP)};
|
||||
const uint8_t attTusChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TIME_UPDATE_STATE)};
|
||||
const uint8_t attGlmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GLUCOSE_MEAS)};
|
||||
const uint8_t attBlChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BATTERY_LEVEL)};
|
||||
const uint8_t attBpsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BATTERY_POWER_STATE)};
|
||||
const uint8_t attBlsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BATTERY_LEVEL_STATE)};
|
||||
const uint8_t attTmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TEMP_MEAS)};
|
||||
const uint8_t attTtChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TEMP_TYPE)};
|
||||
const uint8_t attItChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_INTERMEDIATE_TEMP)};
|
||||
const uint8_t attTcelChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TEMP_C)};
|
||||
const uint8_t attTfahChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_TEMP_F)};
|
||||
const uint8_t attSidChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SYSTEM_ID)};
|
||||
const uint8_t attMnsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MODEL_NUMBER)};
|
||||
const uint8_t attSnsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SERIAL_NUMBER)};
|
||||
const uint8_t attFrsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_FIRMWARE_REV)};
|
||||
const uint8_t attHrsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HARDWARE_REV)};
|
||||
const uint8_t attSrsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SOFTWARE_REV)};
|
||||
const uint8_t attMfnsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MANUFACTURER_NAME)};
|
||||
const uint8_t attIeeeChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_11073_CERT_DATA)};
|
||||
const uint8_t attCtChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CURRENT_TIME)};
|
||||
const uint8_t attElChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ELEVATION)};
|
||||
const uint8_t attLatChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_LATITUDE)};
|
||||
const uint8_t attLongChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_LONGITUDE)};
|
||||
const uint8_t attP2dChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_POSITION_2D)};
|
||||
const uint8_t attP3dChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_POSITION_3D)};
|
||||
const uint8_t attVidChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_VENDOR_ID)};
|
||||
const uint8_t attHbmiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_BOOT_MOUSE_IN)};
|
||||
const uint8_t attGlmcChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GLUCOSE_MEAS_CONTEXT)};
|
||||
const uint8_t attBpmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BP_MEAS)};
|
||||
const uint8_t attIcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_INTERMEDIATE_BP)};
|
||||
const uint8_t attHrmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HR_MEAS)};
|
||||
const uint8_t attBslChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HR_SENSOR_LOC)};
|
||||
const uint8_t attHrcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HR_CP)};
|
||||
const uint8_t attRemChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_REMOVABLE)};
|
||||
const uint8_t attSrChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SERVICE_REQ)};
|
||||
const uint8_t attStcChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SCI_TEMP_C)};
|
||||
const uint8_t attStrChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_STRING)};
|
||||
const uint8_t attNwaChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_NETWORK_AVAIL)};
|
||||
const uint8_t attAsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_STATUS)};
|
||||
const uint8_t attRcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RINGER_CP)};
|
||||
const uint8_t attRsChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RINGER_SETTING)};
|
||||
const uint8_t attAcbmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_CAT_ID_MASK)};
|
||||
const uint8_t attAcChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_CAT_ID)};
|
||||
const uint8_t attAncpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_ALERT_NOTIF_CP)};
|
||||
const uint8_t attUasChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_UNREAD_ALERT_STATUS)};
|
||||
const uint8_t attNaChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_NEW_ALERT)};
|
||||
const uint8_t attSnacChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SUP_NEW_ALERT_CAT)};
|
||||
const uint8_t attSuacChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SUP_UNREAD_ALERT_CAT)};
|
||||
const uint8_t attBpfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_BP_FEATURE)};
|
||||
const uint8_t attHidBmiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MEAS_INTERVAL)};
|
||||
const uint8_t attHidBkiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_BOOT_KEYBOARD_IN)};
|
||||
const uint8_t attHidBkoChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_BOOT_KEYBOARD_OUT)};
|
||||
const uint8_t attHidiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_INFORMATION)};
|
||||
const uint8_t attHidRmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_REPORT_MAP)};
|
||||
const uint8_t attHidcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_CONTROL_POINT)};
|
||||
const uint8_t attHidRepChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_REPORT)};
|
||||
const uint8_t attHidPmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_HID_PROTOCOL_MODE)};
|
||||
const uint8_t attSiwChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SCAN_INT_WIND)};
|
||||
const uint8_t attPnpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PNP_ID)};
|
||||
const uint8_t attGlfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_GLUCOSE_FEATURE)};
|
||||
const uint8_t attRacpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RACP)};
|
||||
const uint8_t attCarChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CAR)};
|
||||
const uint8_t attRsfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RUNNING_SPEED_FEATURE)};
|
||||
const uint8_t attRsmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RUNNING_SPEED_MEASUREMENT)};
|
||||
const uint8_t attCpfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CYCLING_POWER_FEATURE)};
|
||||
const uint8_t attCpmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CYCLING_POWER_MEASUREMENT)};
|
||||
const uint8_t attCsfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CYCLING_SPEED_FEATURE)};
|
||||
const uint8_t attCsmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CYCLING_SPEED_MEASUREMENT)};
|
||||
const uint8_t attSlChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SENSOR_LOCATION)};
|
||||
const uint8_t attPlxfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PULSE_OX_FEATURES)};
|
||||
const uint8_t attPlxscmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PULSE_OX_SPOT_CHECK)};
|
||||
const uint8_t attPlxcmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PULSE_OX_CONTINUOUS)};
|
||||
const uint8_t attRpaoChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_RPAO)};
|
||||
const uint8_t attDbciChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DB_CHANGE_INCREMENT)};
|
||||
const uint8_t attUiChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_USER_INDEX)};
|
||||
const uint8_t attUcpChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_USER_CONTROL_POINT)};
|
||||
const uint8_t attMprvDinChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PRV_DATA_IN)};
|
||||
const uint8_t attMprvDoutChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PRV_DATA_OUT)};
|
||||
const uint8_t attMprxDinChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PROXY_DATA_IN)};
|
||||
const uint8_t attMprxDoutChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_MESH_PROXY_DATA_OUT)};
|
||||
const uint8_t attWssSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_WEIGHT_SCALE_SERVICE)};
|
||||
const uint8_t attWmChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_WEIGHT_MEAS)};
|
||||
const uint8_t attWsfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_WEIGHT_SCALE_FEATURE)};
|
||||
const uint8_t attGattCsfChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CLIENT_SUPPORTED_FEATURES)};
|
||||
const uint8_t attGattDbhChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_DATABASE_HASH)};
|
||||
const uint8_t attCteSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CONSTANT_TONE_SERVICE)};
|
||||
const uint8_t attCteEnChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CTE_ENABLE)};
|
||||
const uint8_t attCteMinLenChUuid[ATT_16_UUID_LEN] ={UINT16_TO_BYTES(ATT_UUID_CTE_MIN_LEN)};
|
||||
const uint8_t attCteTxCntChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CTE_TX_CNT)};
|
||||
const uint8_t attCteTxDurChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CTE_TX_DURATION)};
|
||||
const uint8_t attCteIntChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CTE_INTERVAL)};
|
||||
const uint8_t attCtePhyChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CTE_PHY)};
|
||||
Vendored
+706
@@ -0,0 +1,706 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client service and characteristic utility functions.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_uuid.h"
|
||||
#include "att_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
#define ATT_DISC_HDL_IDX_NONE 0xFF
|
||||
|
||||
#define ATT_SET_UUID_MASK ATTC_SET_UUID_128
|
||||
|
||||
/* Characteristic declaration lengths */
|
||||
#define ATT_CHAR_DECL_LEN_UUID16 5
|
||||
#define ATT_CHAR_DECL_LEN_UUID128 19
|
||||
|
||||
/* Read by type response lengths for characteristic discovery */
|
||||
#define ATT_READ_RSP_LEN_UUID16 (ATT_CHAR_DECL_LEN_UUID16 + 2)
|
||||
#define ATT_READ_RSP_LEN_UUID128 (ATT_CHAR_DECL_LEN_UUID128 + 2)
|
||||
|
||||
/* Find info response lengths */
|
||||
#define ATT_FIND_RSP_LEN_UUID16 (ATT_16_UUID_LEN + 2)
|
||||
#define ATT_FIND_RSP_LEN_UUID128 (ATT_128_UUID_LEN + 2)
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Compare two UUIDs.
|
||||
*
|
||||
* \param pChar Characteristic that we are looking for.
|
||||
* \param pUuid Pointer to peer UUID data.
|
||||
* \param settings Indicates 16 or 128 bit UUID.
|
||||
*
|
||||
* \return TRUE if UUIDs match.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t attcUuidCmp(attcDiscChar_t *pChar, uint8_t *pUuid, uint8_t settings)
|
||||
{
|
||||
/* if both uuids are the same length */
|
||||
if ((pChar->settings & ATT_SET_UUID_MASK) == settings)
|
||||
{
|
||||
/* simply compare the data */
|
||||
return (memcmp(pChar->pUuid, pUuid, (settings == 0) ? ATT_16_UUID_LEN : ATT_128_UUID_LEN) == 0);
|
||||
}
|
||||
/* if discovered UUID is 128 bit and our UUID is 16 bit */
|
||||
else if ((settings == ATTC_SET_UUID_128) && ((pChar->settings & ATTC_SET_UUID_128) == 0))
|
||||
{
|
||||
/* convert our UUID to 128 bit and compare */
|
||||
return attUuidCmp16to128(pChar->pUuid, pUuid);
|
||||
}
|
||||
/* else discovered UUID is 16 bit and our UUID is 128 bit */
|
||||
else
|
||||
{
|
||||
/* no match */
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Verify that required characterstics and descriptors have been discovered.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
*
|
||||
* \return ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attcDiscVerify(attcDiscCb_t *pCb)
|
||||
{
|
||||
attcDiscChar_t **pChar;
|
||||
uint8_t i;
|
||||
|
||||
/* for each characteristic */
|
||||
for (i = 0, pChar = pCb->pCharList; i < pCb->charListLen; i++, pChar++)
|
||||
{
|
||||
/* if characteristic required */
|
||||
if (((*pChar)->settings & ATTC_SET_REQUIRED) != 0)
|
||||
{
|
||||
/* verify handle was discovered */
|
||||
if (pCb->pHdlList[i] == ATT_HANDLE_NONE)
|
||||
{
|
||||
return ATT_ERR_REQ_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find next characteristic that requires descriptor discovery. If none found,
|
||||
* discovery is complete; verify that required characterstics have been discovered.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attcDiscDescriptors(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
attcDiscChar_t **pChar;
|
||||
uint16_t startHdl = ATT_HANDLE_NONE;
|
||||
uint16_t endHdl = ATT_HANDLE_NONE;
|
||||
|
||||
/* find next descriptor in list */
|
||||
pChar = pCb->pCharList + pCb->charListIdx;
|
||||
while (pCb->charListIdx < pCb->charListLen)
|
||||
{
|
||||
/* if this is a descriptor */
|
||||
if (((*pChar)->settings & ATTC_SET_DESCRIPTOR) != 0)
|
||||
{
|
||||
/* start handle is one greater than characteristic value handle,
|
||||
* which is stored in the previous entry in the list;
|
||||
* end handle is stored at current entry in the list
|
||||
*/
|
||||
startHdl = pCb->pHdlList[pCb->charListIdx - 1] + 1;
|
||||
endHdl = pCb->pHdlList[pCb->charListIdx];
|
||||
|
||||
/* clear temp end handle */
|
||||
pCb->pHdlList[pCb->charListIdx] = ATT_HANDLE_NONE;
|
||||
|
||||
/* if there are descriptors */
|
||||
if (startHdl <= endHdl)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we are looking for descriptors for this characteristic but
|
||||
* there aren't any;
|
||||
* skip over any other descriptors that follow in our list
|
||||
*/
|
||||
while (++pCb->charListIdx < pCb->charListLen)
|
||||
{
|
||||
pChar++;
|
||||
if ((*pChar)->settings & ATTC_SET_DESCRIPTOR)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* go to next in list */
|
||||
pChar++;
|
||||
pCb->charListIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
/* if no more descriptors to be discovered */
|
||||
if (pCb->charListIdx == pCb->charListLen)
|
||||
{
|
||||
/* we're done; verify required characteristics and descriptors were discovered */
|
||||
return attcDiscVerify(pCb);
|
||||
}
|
||||
/* else initiate characteristic descriptor discovery */
|
||||
else
|
||||
{
|
||||
AttcFindInfoReq(connId, startHdl, endHdl, TRUE);
|
||||
return ATT_CONTINUING;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a descriptor handle/UUID pair.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param settings Indicates 16 or 128 bit UUID.
|
||||
* \param pPair Pointer to handle/UUID pair.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcDiscProcDescPair(attcDiscCb_t *pCb, uint8_t settings, uint8_t *pPair)
|
||||
{
|
||||
attcDiscChar_t **pDesc;
|
||||
uint16_t hdl;
|
||||
uint8_t i;
|
||||
|
||||
/* parse handle */
|
||||
BSTREAM_TO_UINT16(hdl, pPair);
|
||||
|
||||
/* now pPair points to UUID; find descriptor with matching UUID */
|
||||
pDesc = &pCb->pCharList[pCb->charListIdx];
|
||||
for (i = pCb->charListIdx;
|
||||
(i < pCb->charListLen) && (((*pDesc)->settings & ATTC_SET_DESCRIPTOR) != 0);
|
||||
i++, pDesc++)
|
||||
{
|
||||
/* if characteristic not already found */
|
||||
if (pCb->pHdlList[i] == 0)
|
||||
{
|
||||
/* if UUIDs match */
|
||||
if (attcUuidCmp(*pDesc, pPair, settings))
|
||||
{
|
||||
/* match found; store handle */
|
||||
pCb->pHdlList[i] = hdl;
|
||||
|
||||
ATT_TRACE_INFO1("descriptor found handle:0x%x", hdl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a ATTC_FIND_INFO_RSP received while performing characteristic discovery.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param pMsg ATT callback event message.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attcDiscProcDesc(attcDiscCb_t *pCb, attEvt_t *pMsg)
|
||||
{
|
||||
attcDiscChar_t **pChar;
|
||||
uint8_t *p;
|
||||
uint8_t *pEnd;
|
||||
uint8_t format;
|
||||
uint8_t pairLen;
|
||||
uint8_t settings;
|
||||
|
||||
/* if find info successful */
|
||||
if (pMsg->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
p = pMsg->pValue;
|
||||
pEnd = pMsg->pValue + pMsg->valueLen;
|
||||
|
||||
/* determine UUID format */
|
||||
BSTREAM_TO_UINT8(format, p);
|
||||
if (format == ATT_FIND_HANDLE_16_UUID)
|
||||
{
|
||||
settings = 0;
|
||||
pairLen = ATT_FIND_RSP_LEN_UUID16;
|
||||
}
|
||||
else if (format == ATT_FIND_HANDLE_128_UUID)
|
||||
{
|
||||
settings = ATTC_SET_UUID_128;
|
||||
pairLen = ATT_FIND_RSP_LEN_UUID128;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATT_ERR_INVALID_RSP;
|
||||
}
|
||||
|
||||
/* for each handle/UUID pair */
|
||||
while (p < pEnd)
|
||||
{
|
||||
/* process descriptor handle/UUID pair */
|
||||
attcDiscProcDescPair(pCb, settings, p);
|
||||
|
||||
/* go to next */
|
||||
p += pairLen;
|
||||
}
|
||||
}
|
||||
|
||||
/* if descriptor discovery complete for this characteristic */
|
||||
if (pMsg->hdr.status != ATT_SUCCESS || pMsg->continuing == FALSE)
|
||||
{
|
||||
/* go to next entry in list */
|
||||
pChar = &pCb->pCharList[pCb->charListIdx];
|
||||
do
|
||||
{
|
||||
/* check if at end of list */
|
||||
pCb->charListIdx++;
|
||||
if (pCb->charListIdx == pCb->charListLen)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* skip over descriptors that follow current characteristic */
|
||||
pChar++;
|
||||
} while ((*pChar)->settings & ATTC_SET_DESCRIPTOR);
|
||||
|
||||
/* proceed with descriptor discovery for the next characteristic */
|
||||
return attcDiscDescriptors((dmConnId_t) pMsg->hdr.param, pCb);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* still more to do */
|
||||
return ATT_CONTINUING;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a characteristic declaration.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param settings Indicates 16 or 128 bit UUID.
|
||||
* \param pDecl Pointer to declaration.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcDiscProcCharDecl(attcDiscCb_t *pCb, uint8_t settings, uint8_t *pDecl)
|
||||
{
|
||||
attcDiscChar_t **pChar;
|
||||
uint16_t hdl;
|
||||
uint16_t declHdl;
|
||||
uint8_t i;
|
||||
|
||||
/* parse it */
|
||||
BSTREAM_TO_UINT16(declHdl, pDecl);
|
||||
pDecl++; /* skip properties field */
|
||||
BSTREAM_TO_UINT16(hdl, pDecl);
|
||||
|
||||
/* if looking for end handle of previous characteristic */
|
||||
if (pCb->endHdlIdx != ATT_DISC_HDL_IDX_NONE)
|
||||
{
|
||||
/* end handle of previous characteristic is one less than
|
||||
* the handle of the current characteristic declaration
|
||||
*/
|
||||
pCb->pHdlList[pCb->endHdlIdx] = declHdl - 1;
|
||||
pCb->endHdlIdx = ATT_DISC_HDL_IDX_NONE;
|
||||
}
|
||||
|
||||
/* check handle */
|
||||
if (hdl > declHdl && hdl <= pCb->svcEndHdl)
|
||||
{
|
||||
/* now pDecl points to UUID; search for UUID in characteristic list */
|
||||
for (i = 0, pChar = pCb->pCharList; i < pCb->charListLen; i++, pChar++)
|
||||
{
|
||||
/* if characteristic not already found */
|
||||
if (pCb->pHdlList[i] == 0)
|
||||
{
|
||||
/* if UUIDs match */
|
||||
if (attcUuidCmp(*pChar, pDecl, settings))
|
||||
{
|
||||
/* match found; store handle */
|
||||
pCb->pHdlList[i] = hdl;
|
||||
|
||||
/* if not at end of list and next in list is a descriptor */
|
||||
if (i < (pCb->charListLen - 1) &&
|
||||
((*(pChar + 1))->settings & ATTC_SET_DESCRIPTOR) == ATTC_SET_DESCRIPTOR)
|
||||
{
|
||||
/* characteristic has descriptors, we need to find end handle
|
||||
* store end handle temporarily in handle list location
|
||||
* for the first descriptor
|
||||
*/
|
||||
pCb->endHdlIdx = i + 1;
|
||||
}
|
||||
|
||||
ATT_TRACE_INFO1("characteristic found handle:0x%x", hdl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* invalid handle; skip this declaration */
|
||||
ATT_TRACE_WARN1("invalid handle:0x%x", hdl);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a ATTC_READ_BY_TYPE_RSP received while performing characteristic discovery.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param pMsg ATT callback event message.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attcDiscProcChar(attcDiscCb_t *pCb, attEvt_t *pMsg)
|
||||
{
|
||||
uint8_t *p;
|
||||
uint8_t *pEnd;
|
||||
uint8_t pairLen;
|
||||
uint8_t settings;
|
||||
|
||||
/* if read by type successful */
|
||||
if (pMsg->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
p = pMsg->pValue;
|
||||
pEnd = pMsg->pValue + pMsg->valueLen;
|
||||
|
||||
/* verify attribute-handle pair length and determine UUID length */
|
||||
BSTREAM_TO_UINT8(pairLen, p);
|
||||
if (pairLen == ATT_READ_RSP_LEN_UUID16)
|
||||
{
|
||||
settings = 0;
|
||||
}
|
||||
else if (pairLen == ATT_READ_RSP_LEN_UUID128)
|
||||
{
|
||||
settings = ATTC_SET_UUID_128;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATT_ERR_INVALID_RSP;
|
||||
}
|
||||
|
||||
/* Note that ATT already verifies response length and handle values */
|
||||
|
||||
/* for each characteristic declaration */
|
||||
while (p < pEnd)
|
||||
{
|
||||
/* process characteristic declaration */
|
||||
attcDiscProcCharDecl(pCb, settings, p);
|
||||
|
||||
/* go to next */
|
||||
p += pairLen;
|
||||
}
|
||||
}
|
||||
|
||||
/* if characteristic discovery complete */
|
||||
if (pMsg->hdr.status != ATT_SUCCESS || pMsg->continuing == FALSE)
|
||||
{
|
||||
/* check if characteristic end handle needs to be set */
|
||||
if (pCb->endHdlIdx != ATT_DISC_HDL_IDX_NONE)
|
||||
{
|
||||
/* end handle of characteristic declaration is end handle of service */
|
||||
pCb->pHdlList[pCb->endHdlIdx] = pCb->svcEndHdl;
|
||||
}
|
||||
|
||||
/* proceed with descriptor discovery */
|
||||
pCb->charListIdx = 0;
|
||||
return attcDiscDescriptors((dmConnId_t) pMsg->hdr.param, pCb);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* still more to do */
|
||||
return ATT_CONTINUING;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiates the next characteristic configuration procedure.
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param pMsg ATT callback event message.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and configuration procedure is continuing.
|
||||
* ATT_SUCCESS if configuration procedure completed successfully.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attcDiscConfigNext(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
attcDiscCfg_t *pCfg;
|
||||
|
||||
pCfg = pCb->pCfgList + pCb->charListIdx;
|
||||
|
||||
/* iterate over list */
|
||||
while (pCb->charListIdx < pCb->cfgListLen)
|
||||
{
|
||||
/* if handle was discovered */
|
||||
if (pCb->pHdlList[pCfg->hdlIdx] != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* if value present do write req */
|
||||
if (pCfg->valueLen != 0)
|
||||
{
|
||||
AttcWriteReq(connId, pCb->pHdlList[pCfg->hdlIdx], pCfg->valueLen, (uint8_t *) pCfg->pValue);
|
||||
}
|
||||
/* else do read */
|
||||
else
|
||||
{
|
||||
AttcReadReq(connId, pCb->pHdlList[pCfg->hdlIdx]);
|
||||
}
|
||||
|
||||
/* done for now */
|
||||
return ATT_CONTINUING;
|
||||
}
|
||||
|
||||
/* next in list */
|
||||
pCb->charListIdx++;
|
||||
pCfg++;
|
||||
}
|
||||
|
||||
/* nothing left to configure; we're done */
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function discovers the given service on a peer device. Function
|
||||
* AttcFindByTypeValueReq() is called to initiate the discovery procedure.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param uuidLen Length of UUID (2 or 16).
|
||||
* \param pUuid Pointer to UUID data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcDiscService(dmConnId_t connId, attcDiscCb_t *pCb, uint8_t uuidLen, uint8_t *pUuid)
|
||||
{
|
||||
AttcFindByTypeValueReq(connId, ATT_HANDLE_START, ATT_HANDLE_MAX, ATT_UUID_PRIMARY_SERVICE,
|
||||
uuidLen, pUuid, FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function processes a service discovery result. It should be called
|
||||
* when an ATTC_FIND_BY_TYPE_VALUE_RSP callback event is received after service
|
||||
* discovery is initiated by calling AttcDiscService().
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param pMsg ATT callback event message.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttcDiscServiceCmpl(attcDiscCb_t *pCb, attEvt_t *pMsg)
|
||||
{
|
||||
uint8_t *p;
|
||||
|
||||
/* verify callback event */
|
||||
if (pMsg->hdr.event != ATTC_FIND_BY_TYPE_VALUE_RSP)
|
||||
{
|
||||
ATT_TRACE_WARN1("unexpected callback event %d", pMsg->hdr.event);
|
||||
return ATT_ERR_UNDEFINED;
|
||||
}
|
||||
/* verify status */
|
||||
else if (pMsg->hdr.status != ATT_SUCCESS)
|
||||
{
|
||||
return pMsg->hdr.status;
|
||||
}
|
||||
/* verify result was found */
|
||||
else if (pMsg->valueLen == 0)
|
||||
{
|
||||
return ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* get handles of first returned service only; ATT has already performed error checking */
|
||||
p = pMsg->pValue;
|
||||
BSTREAM_TO_UINT16(pCb->svcStartHdl, p);
|
||||
BSTREAM_TO_UINT16(pCb->svcEndHdl, p);
|
||||
|
||||
ATT_TRACE_INFO2("found service startHdl=0x%x endHdl=0x%x", pCb->svcStartHdl, pCb->svcEndHdl);
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function starts characteristic and characteristic descriptor
|
||||
* discovery for a service on a peer device. The service must have been previously
|
||||
* discovered by calling AttcDiscService() and AttcDiscServiceCmpl().
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcDiscCharStart(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
/* initialize control block */
|
||||
pCb->charListIdx = 0;
|
||||
pCb->endHdlIdx = ATT_DISC_HDL_IDX_NONE;
|
||||
|
||||
AttcReadByTypeReq(connId, pCb->svcStartHdl, pCb->svcEndHdl, ATT_16_UUID_LEN,
|
||||
(uint8_t *) attChUuid, TRUE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function processes a characteristic discovery result. It should be
|
||||
* called when an ATTC_READ_BY_TYPE_RSP or ATTC_FIND_INFO_RSP callback event is
|
||||
* received after characteristic discovery is initiated by calling AttcDiscCharStart().
|
||||
*
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
* \param pMsg ATT callback event message.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and discovery procedure is continuing.
|
||||
* ATT_SUCCESS if discovery procedure completed successfully.
|
||||
* Otherwise the discovery procedure failed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttcDiscCharCmpl(attcDiscCb_t *pCb, attEvt_t *pMsg)
|
||||
{
|
||||
uint8_t status;
|
||||
|
||||
/* verify callback event */
|
||||
if (pMsg->hdr.event != ATTC_READ_BY_TYPE_RSP &&
|
||||
pMsg->hdr.event != ATTC_FIND_INFO_RSP)
|
||||
{
|
||||
ATT_TRACE_WARN1("unexpected callback event %d", pMsg->hdr.event);
|
||||
return ATT_ERR_UNDEFINED;
|
||||
}
|
||||
|
||||
/* if read by type (characteristic discovery) */
|
||||
if (pMsg->hdr.event == ATTC_READ_BY_TYPE_RSP)
|
||||
{
|
||||
status = attcDiscProcChar(pCb, pMsg);
|
||||
}
|
||||
/* else if find info (descriptor discovery) */
|
||||
else
|
||||
{
|
||||
status = attcDiscProcDesc(pCb, pMsg);
|
||||
}
|
||||
|
||||
/* if characteristic discovery failed clear any handles */
|
||||
if (status != ATT_SUCCESS && status != ATT_CONTINUING)
|
||||
{
|
||||
memset(pCb->pHdlList, 0, (pCb->charListLen * sizeof(uint16_t)));
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function starts characteristic configuration for characteristics on a
|
||||
* peer device. The characteristics must have been previously discovered by calling
|
||||
* AttcDiscCharStart() and AttcDisccharCont().
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to service discovery control block.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and configuration procedure is continuing.
|
||||
* ATT_SUCCESS if nothing to configure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttcDiscConfigStart(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
/* use char list index to iterate over config list */
|
||||
pCb->charListIdx = 0;
|
||||
|
||||
return attcDiscConfigNext(connId, pCb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function initiates the next characteristic configuration procedure.
|
||||
* It should be called when an ATTC_READ_RSP or ATTC_WRITE_RSP callback event is received
|
||||
* after characteristic configuration is initiated by calling AttcDiscConfigStart().
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to discovery control block.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and configuration procedure is continuing.
|
||||
* ATT_SUCCESS if configuration procedure completed successfully.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttcDiscConfigCmpl(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
/* go to next in list */
|
||||
pCb->charListIdx++;
|
||||
|
||||
return attcDiscConfigNext(connId, pCb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief This utility function resumes the characteristic configuration procedure. It can
|
||||
* be called when an ATTC_READ_RSP or ATTC_WRITE_RSP callback event is received
|
||||
* with failure status to attempt the read or write procedure again.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCb Pointer to discovery control block.
|
||||
*
|
||||
* \return ATT_CONTINUING if successful and configuration procedure is continuing.
|
||||
* ATT_SUCCESS if configuration procedure completed successfully.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttcDiscConfigResume(dmConnId_t connId, attcDiscCb_t *pCb)
|
||||
{
|
||||
return attcDiscConfigNext(connId, pCb);
|
||||
}
|
||||
Vendored
+840
@@ -0,0 +1,840 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_math.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "attc_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* type for send request function */
|
||||
typedef void (*attcSendReq_t)(attcCcb_t *pCcb);
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
static void attcDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket);
|
||||
static void attcCtrlCback(wsfMsgHdr_t *pMsg);
|
||||
static void attcConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt);
|
||||
static void attcMsgCback(attcApiMsg_t *pMsg);
|
||||
|
||||
static void attcSendSimpleReq(attcCcb_t *pCcb);
|
||||
static void attcSendContinuingReq(attcCcb_t *pCcb);
|
||||
static void attcSendMtuReq(attcCcb_t *pCcb);
|
||||
static void attcSendWriteCmd(attcCcb_t *pCcb);
|
||||
static void attcSendPrepWriteReq(attcCcb_t *pCcb);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Interface to ATT */
|
||||
static const attFcnIf_t attcFcnIf =
|
||||
{
|
||||
attcDataCback,
|
||||
attcCtrlCback,
|
||||
(attMsgHandler_t) attcMsgCback,
|
||||
attcConnCback
|
||||
};
|
||||
|
||||
/* Table of send request functions */
|
||||
static const attcSendReq_t attcSendReqTbl[] =
|
||||
{
|
||||
NULL, /* ATTC_MSG_API_NONE (unused) */
|
||||
attcSendMtuReq, /* ATTC_MSG_API_MTU */
|
||||
attcSendContinuingReq, /* ATTC_MSG_API_FIND_INFO */
|
||||
attcSendContinuingReq, /* ATTC_MSG_API_FIND_BY_TYPE_VALUE */
|
||||
attcSendContinuingReq, /* ATTC_MSG_API_READ_BY_TYPE */
|
||||
attcSendSimpleReq, /* ATTC_MSG_API_READ */
|
||||
attcSendContinuingReq, /* ATTC_MSG_API_READ_LONG */
|
||||
attcSendSimpleReq, /* ATTC_MSG_API_READ_MULTIPLE */
|
||||
attcSendContinuingReq, /* ATTC_MSG_API_READ_BY_GROUP_TYPE */
|
||||
attcSendSimpleReq, /* ATTC_MSG_API_WRITE */
|
||||
attcSendWriteCmd, /* ATTC_MSG_API_WRITE_CMD */
|
||||
attcSendPrepWriteReq, /* ATTC_MSG_API_PREP_WRITE */
|
||||
attcSendSimpleReq /* ATTC_MSG_API_EXEC_WRITE */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
attcCb_t attcCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if application callback is pending for a given write command, or the maximum
|
||||
* number of simultaneous write commands has been reached.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
* \param pMsg ATTC message.
|
||||
*
|
||||
* \return TRUE if app callback's pending or maximum number of simultaneous write commands reached.
|
||||
* FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t attcPendWriteCmd(attcCcb_t *pCcb, attcApiMsg_t *pMsg)
|
||||
{
|
||||
uint8_t pendRsp;
|
||||
uint8_t i;
|
||||
|
||||
/* initialize number of response callbacks pending */
|
||||
pendRsp = 0;
|
||||
|
||||
for (i = 0; i < ATT_NUM_SIMUL_WRITE_CMD; i++)
|
||||
{
|
||||
/* if callback pending for write command */
|
||||
if (pCcb->pendWriteCmdHandle[i] != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* if callback pending for this handle */
|
||||
if (pCcb->pendWriteCmdHandle[i] == pMsg->handle)
|
||||
{
|
||||
/* callback pending for this write command */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
pendRsp++;
|
||||
}
|
||||
}
|
||||
|
||||
/* no callback is pending for this write command but see if the maximum number of simultaneous
|
||||
write commands has been reached */
|
||||
return (pendRsp < ATT_NUM_SIMUL_WRITE_CMD) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set pending write command response callback for a given attribute handle.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSetPendWriteCmd(attcCcb_t *pCcb)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < ATT_NUM_SIMUL_WRITE_CMD; i++)
|
||||
{
|
||||
/* if entry free */
|
||||
if (pCcb->pendWriteCmdHandle[i] == ATT_HANDLE_NONE)
|
||||
{
|
||||
/* set pending write command handle */
|
||||
pCcb->pendWriteCmdHandle[i] = pCcb->outReq.handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Call pending write command response application callback.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCcb ATTC control block.
|
||||
* \param status Callback event status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcWriteCmdCallback(dmConnId_t connId, attcCcb_t *pCcb, uint8_t status)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* if any pending write command callback */
|
||||
for (i = 0; i < ATT_NUM_SIMUL_WRITE_CMD; i++)
|
||||
{
|
||||
if (pCcb->pendWriteCmdHandle[i] != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* call callback with status */
|
||||
attcExecCallback(connId, ATTC_WRITE_CMD_RSP, pCcb->pendWriteCmdHandle[i], status);
|
||||
pCcb->pendWriteCmdHandle[i] = ATT_HANDLE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send attribute client request that has already been built.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSendSimpleReq(attcCcb_t *pCcb)
|
||||
{
|
||||
attcPktParam_t *pPkt = pCcb->outReq.pPkt;
|
||||
|
||||
/* clear stored packet pointer */
|
||||
pCcb->outReq.pPkt = NULL;
|
||||
|
||||
/* start request timer (except for write command) */
|
||||
if (pCcb->outReq.hdr.event != ATTC_MSG_API_WRITE_CMD)
|
||||
{
|
||||
pCcb->outReqTimer.msg.event = ATTC_MSG_REQ_TIMEOUT;
|
||||
WsfTimerStartSec(&pCcb->outReqTimer, pAttCfg->transTimeout);
|
||||
}
|
||||
|
||||
/* send packet to L2CAP */
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, pPkt->len, (uint8_t *) pPkt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a request that may be continuing.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSendContinuingReq(attcCcb_t *pCcb)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* allocate new buffer */
|
||||
if ((pPkt = attMsgAlloc(pCcb->outReq.pPkt->len + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* copy stored packet to new */
|
||||
memcpy(pPkt, pCcb->outReq.pPkt, pCcb->outReq.pPkt->len + L2C_PAYLOAD_START);
|
||||
}
|
||||
/* else handle error case of allocation failure */
|
||||
else
|
||||
{
|
||||
/* free stored packet and call callback with failure status */
|
||||
attcReqClear(pCcb, &pCcb->outReq, ATT_ERR_MEMORY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* else not continuing */
|
||||
else
|
||||
{
|
||||
/* send the stored packet */
|
||||
pPkt = pCcb->outReq.pPkt;
|
||||
|
||||
/* clear stored packet pointer */
|
||||
pCcb->outReq.pPkt = NULL;
|
||||
}
|
||||
|
||||
/* build remaining fields of packet from stored parameters */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_READ_LONG)
|
||||
{
|
||||
/* build offset field */
|
||||
p += sizeof(uint16_t);
|
||||
UINT16_TO_BSTREAM(p, pCcb->outReqParams.o.offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Find Info, Find By Type Value, Read By Type, and Read By Group Type
|
||||
* request packets all begin with a start handle and end handle.
|
||||
*/
|
||||
UINT16_TO_BSTREAM(p, pCcb->outReqParams.h.startHandle);
|
||||
UINT16_TO_BSTREAM(p, pCcb->outReqParams.h.endHandle);
|
||||
}
|
||||
|
||||
/* start request timer */
|
||||
pCcb->outReqTimer.msg.event = ATTC_MSG_REQ_TIMEOUT;
|
||||
WsfTimerStartSec(&pCcb->outReqTimer, pAttCfg->transTimeout);
|
||||
|
||||
/* send packet to L2CAP */
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, pPkt->len, (uint8_t *) pPkt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send MTU request.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSendMtuReq(attcCcb_t *pCcb)
|
||||
{
|
||||
/* if MTU already exchanged */
|
||||
if (pCcb->pMainCcb->control & ATT_CCB_STATUS_MTU_SENT)
|
||||
{
|
||||
/* discard request */
|
||||
attcFreePkt(&pCcb->outReq);
|
||||
|
||||
/* clear out req */
|
||||
pCcb->outReq.hdr.event = ATTC_MSG_API_NONE;
|
||||
|
||||
ATT_TRACE_WARN0("MTU req discarded");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set MTU sent */
|
||||
pCcb->pMainCcb->control |= ATT_CCB_STATUS_MTU_SENT;
|
||||
|
||||
/* send packet */
|
||||
attcSendSimpleReq(pCcb);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send attribute client Write command.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSendWriteCmd(attcCcb_t *pCcb)
|
||||
{
|
||||
/* send packet */
|
||||
attcSendSimpleReq(pCcb);
|
||||
|
||||
/* if flow not disabled */
|
||||
if (!(pCcb->pMainCcb->control & ATT_CCB_STATUS_FLOW_DISABLED))
|
||||
{
|
||||
/* call callback */
|
||||
attcExecCallback(pCcb->pMainCcb->connId, ATTC_WRITE_CMD_RSP, pCcb->outReq.handle, ATT_SUCCESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set pending write command callback for this handle */
|
||||
attcSetPendWriteCmd(pCcb);
|
||||
}
|
||||
|
||||
/* clear out req */
|
||||
pCcb->outReq.hdr.event = ATTC_MSG_API_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send attribute client Prepare Write request.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSendPrepWriteReq(attcCcb_t *pCcb)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
uint16_t dataLen;
|
||||
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* determine size of buffer to allocate */
|
||||
if (pCcb->outReqParams.w.len < (pCcb->pMainCcb->mtu - ATT_PREP_WRITE_REQ_LEN))
|
||||
{
|
||||
dataLen = pCcb->outReqParams.w.len;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataLen = pCcb->pMainCcb->mtu - ATT_PREP_WRITE_REQ_LEN;
|
||||
}
|
||||
|
||||
/* allocate new buffer */
|
||||
if ((pPkt = attMsgAlloc(dataLen + ATT_PREP_WRITE_REQ_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
/* copy fixed fields */
|
||||
memcpy(pPkt, pCcb->outReq.pPkt, ATT_PREP_WRITE_REQ_LEN + L2C_PAYLOAD_START);
|
||||
|
||||
/* copy data */
|
||||
memcpy(((uint8_t *) pPkt + L2C_PAYLOAD_START + ATT_PREP_WRITE_REQ_LEN),
|
||||
pCcb->outReqParams.w.pValue, dataLen);
|
||||
|
||||
/* update length and data pointer */
|
||||
pCcb->outReqParams.w.pValue += dataLen;
|
||||
pCcb->outReqParams.w.len -= dataLen;
|
||||
}
|
||||
/* else handle error case of allocation failure */
|
||||
else
|
||||
{
|
||||
/* free stored packet and call callback with failure status */
|
||||
attcReqClear(pCcb, &pCcb->outReq, ATT_ERR_MEMORY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* else not continuing */
|
||||
else
|
||||
{
|
||||
dataLen = pCcb->outReqParams.w.len;
|
||||
|
||||
/* send the stored packet */
|
||||
pPkt = pCcb->outReq.pPkt;
|
||||
|
||||
/* clear stored packet pointer */
|
||||
pCcb->outReq.pPkt = NULL;
|
||||
}
|
||||
|
||||
/* build remaining fields of packet from stored parameters */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START + ATT_HDR_LEN + sizeof(uint16_t);
|
||||
UINT16_TO_BSTREAM(p, pCcb->outReqParams.w.offset);
|
||||
|
||||
/* update offset after building packet */
|
||||
pCcb->outReqParams.w.offset += dataLen;
|
||||
|
||||
/* start request timer */
|
||||
pCcb->outReqTimer.msg.event = ATTC_MSG_REQ_TIMEOUT;
|
||||
WsfTimerStartSec(&pCcb->outReqTimer, pAttCfg->transTimeout);
|
||||
|
||||
/* send packet to L2CAP */
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, dataLen + ATT_PREP_WRITE_REQ_LEN, (uint8_t *) pPkt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Lookup and execute function to build and send request.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcSendReq(attcCcb_t *pCcb)
|
||||
{
|
||||
(*attcSendReqTbl[pCcb->outReq.hdr.event])(pCcb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up and send an attribute client request.
|
||||
*
|
||||
* \param pCcb ATTC control block.
|
||||
* \param pMsg ATTC message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcSetupReq(attcCcb_t *pCcb, attcApiMsg_t *pMsg)
|
||||
{
|
||||
/* set out req to api message */
|
||||
pCcb->outReq = *pMsg;
|
||||
|
||||
/* store parameters */
|
||||
pCcb->outReqParams = *(pMsg->pPkt);
|
||||
|
||||
/* build and send request */
|
||||
attcSendReq(pCcb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Data callback for ATTC.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t opcode;
|
||||
attcCcb_t *pCcb;
|
||||
|
||||
/* get connection control block for this handle, ignore packet if not found */
|
||||
if ((pCcb = attcCcbByHandle(handle)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse opcode */
|
||||
opcode = *(pPacket + L2C_PAYLOAD_START);
|
||||
|
||||
/* if response */
|
||||
if (opcode <= ATT_PDU_EXEC_WRITE_RSP)
|
||||
{
|
||||
attcProcRsp(pCcb, len, pPacket);
|
||||
}
|
||||
/* else if indication or notification */
|
||||
else if ((opcode == ATT_PDU_VALUE_NTF) || (opcode == ATT_PDU_VALUE_IND))
|
||||
{
|
||||
attcProcInd(pCcb, len, pPacket);
|
||||
}
|
||||
/* else unknown opcode */
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN1("attc unknown opcode 0x%02x", opcode);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2CAP control callback.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
attcCcb_t *pCcb;
|
||||
|
||||
/* note this function is currently only called when flow is enabled */
|
||||
|
||||
/* get CCB */
|
||||
if ((pCcb = attcCcbByConnId((dmConnId_t) pMsg->param)) != NULL)
|
||||
{
|
||||
/* if confirmation pending try sending now */
|
||||
AttcIndConfirm((dmConnId_t) pMsg->param);
|
||||
|
||||
/* call pending write command callback */
|
||||
attcWriteCmdCallback((dmConnId_t) pMsg->param, pCcb, ATT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection callback for ATTC.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt)
|
||||
{
|
||||
attcCcb_t *pClient;
|
||||
uint16_t localMtu;
|
||||
uint8_t status;
|
||||
|
||||
/* if connection opened */
|
||||
if (pDmEvt->hdr.event == DM_CONN_OPEN_IND)
|
||||
{
|
||||
/* if we initiated connection send MTU request */
|
||||
if (DmConnRole(pCcb->connId) == DM_ROLE_MASTER)
|
||||
{
|
||||
localMtu = WSF_MIN(pAttCfg->mtu, (HciGetMaxRxAclLen() - L2C_HDR_LEN));
|
||||
|
||||
/* if desired MTU is not the default */
|
||||
if (localMtu != ATT_DEFAULT_MTU)
|
||||
{
|
||||
AttcMtuReq(pCcb->connId, localMtu);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* if connection closed */
|
||||
else if (pDmEvt->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* set status */
|
||||
if (pDmEvt->connClose.hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
status = pDmEvt->connClose.reason + ATT_HCI_ERR_BASE;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = pDmEvt->connClose.hdr.status + ATT_HCI_ERR_BASE;
|
||||
}
|
||||
|
||||
/* get client control block directly */
|
||||
pClient = &attcCb.ccb[pCcb->connId - 1];
|
||||
|
||||
/* free any out req */
|
||||
if (pClient->outReq.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
WsfTimerStop(&pClient->outReqTimer);
|
||||
attcReqClear(pClient, &pClient->outReq, status);
|
||||
}
|
||||
|
||||
/* free any req on deck */
|
||||
if (pClient->onDeck.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
attcReqClear(pClient, &pClient->onDeck, status);
|
||||
}
|
||||
|
||||
/* initialize other control block variables */
|
||||
pClient->flowDisabled = FALSE;
|
||||
pClient->cnfPending = FALSE;
|
||||
|
||||
/* pass to connection close callback for signed data */
|
||||
if (attcCb.pSign != NULL)
|
||||
{
|
||||
(*attcCb.pSign->closeCback)(pClient, status);
|
||||
}
|
||||
|
||||
/* call pending write command callback */
|
||||
attcWriteCmdCallback(pCcb->connId, pClient, status);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Message handler callback for ATTC.
|
||||
*
|
||||
* \param pMsg ATTC message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcMsgCback(attcApiMsg_t *pMsg)
|
||||
{
|
||||
attcCcb_t *pCcb;
|
||||
|
||||
/* get CCB and verify connection still in use */
|
||||
if ((pCcb = attcCcbByConnId((dmConnId_t) pMsg->hdr.param)) == NULL)
|
||||
{
|
||||
/* if message has a packet buffer */
|
||||
if (pMsg->hdr.event >= ATTC_MSG_API_MTU &&
|
||||
pMsg->hdr.event <= ATTC_MSG_API_SIGNED_WRITE_CMD)
|
||||
{
|
||||
/* free packet buffer */
|
||||
attcFreePkt(pMsg);
|
||||
}
|
||||
|
||||
/* ignore if connection not in use */
|
||||
return;
|
||||
}
|
||||
|
||||
/* if an API request to send packet (non-signed) */
|
||||
if (pMsg->hdr.event <= ATTC_MSG_API_EXEC_WRITE)
|
||||
{
|
||||
/* verify no API request already waiting on deck, in progress, or no pending write command
|
||||
already for this handle */
|
||||
if ((pCcb->onDeck.hdr.event != ATTC_MSG_API_NONE) ||
|
||||
(pCcb->outReq.hdr.event > ATTC_MSG_API_MTU) ||
|
||||
((pMsg->hdr.event == ATTC_MSG_API_WRITE_CMD) &&
|
||||
attcPendWriteCmd(pCcb, pMsg)))
|
||||
{
|
||||
/* free request and call callback with failure status */
|
||||
attcReqClear(pCcb, pMsg, ATT_ERR_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
|
||||
/* if MTU request in progress or flow controlled */
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_MTU || pCcb->flowDisabled)
|
||||
{
|
||||
/* put request "on deck" for processing later */
|
||||
pCcb->onDeck = *pMsg;
|
||||
}
|
||||
/* otherwise ready to send; set up request */
|
||||
else
|
||||
{
|
||||
attcSetupReq(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
/* else if signed data event */
|
||||
else if (pMsg->hdr.event >= ATTC_MSG_API_SIGNED_WRITE_CMD &&
|
||||
pMsg->hdr.event <= ATTC_MSG_CMAC_CMPL)
|
||||
{
|
||||
/* pass to message callback for signed data */
|
||||
if (attcCb.pSign != NULL)
|
||||
{
|
||||
(*attcCb.pSign->msgCback)(pCcb, pMsg);
|
||||
}
|
||||
}
|
||||
/* else if cancel request */
|
||||
else if (pMsg->hdr.event == ATTC_MSG_API_CANCEL)
|
||||
{
|
||||
/* free any out req (except mtu req) */
|
||||
if (pCcb->outReq.hdr.event != ATTC_MSG_API_NONE &&
|
||||
pCcb->outReq.hdr.event != ATTC_MSG_API_MTU)
|
||||
{
|
||||
WsfTimerStop(&pCcb->outReqTimer);
|
||||
attcReqClear(pCcb, &pCcb->outReq, ATT_ERR_CANCELLED);
|
||||
}
|
||||
/* else free any req on deck */
|
||||
else if (pCcb->onDeck.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
attcReqClear(pCcb, &pCcb->onDeck, ATT_ERR_CANCELLED);
|
||||
}
|
||||
}
|
||||
/* else if timeout */
|
||||
else if (pMsg->hdr.event == ATTC_MSG_REQ_TIMEOUT)
|
||||
{
|
||||
/* free any out req */
|
||||
if (pCcb->outReq.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
attcReqClear(pCcb, &pCcb->outReq, ATT_ERR_TIMEOUT);
|
||||
pCcb->pMainCcb->control |= ATT_CCB_STATUS_TX_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the ATTC connection control block connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to connection control block or NULL if not in use.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
attcCcb_t *attcCcbByConnId(dmConnId_t connId)
|
||||
{
|
||||
if (DmConnInUse(connId))
|
||||
{
|
||||
return &attcCb.ccb[connId - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN1("attc ccb not in use: %d", connId);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the connection control block for the given handle.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
*
|
||||
* \return Pointer to connection control block or NULL if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
attcCcb_t *attcCcbByHandle(uint16_t handle)
|
||||
{
|
||||
dmConnId_t connId;
|
||||
|
||||
if ((connId = DmConnIdByHandle(handle)) != DM_CONN_ID_NONE)
|
||||
{
|
||||
return &attcCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Free the packet buffer of an API message structure.
|
||||
*
|
||||
* \param pMsg Pointer to API message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcFreePkt(attcApiMsg_t *pMsg)
|
||||
{
|
||||
if (pMsg->pPkt != NULL)
|
||||
{
|
||||
WsfMsgFree(pMsg->pPkt);
|
||||
pMsg->pPkt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute application callback function.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param event Callback event ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param status Callback event status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcExecCallback(dmConnId_t connId, uint8_t event, uint16_t handle, uint8_t status)
|
||||
{
|
||||
if (event != ATT_METHOD_MTU)
|
||||
{
|
||||
attExecCallback(connId, event, handle, status, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear an outstanding request and execute the callback.
|
||||
*
|
||||
* \param pCcb Pointer to control block.
|
||||
* \param pMsg API message.
|
||||
* \param status Callback event status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcReqClear(attcCcb_t *pCcb, attcApiMsg_t *pMsg, uint8_t status)
|
||||
{
|
||||
attcFreePkt(pMsg);
|
||||
attcExecCallback(pCcb->pMainCcb->connId, pMsg->hdr.event, pMsg->handle, status);
|
||||
pMsg->hdr.event = ATTC_MSG_API_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set automatic Indication Confirmations sent from this ATT Client.
|
||||
*
|
||||
* \param enable \ref TRUE to enable automatic confirmations (default), \ref FALSE to disable.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcSetAutoConfirm(bool_t enable)
|
||||
{
|
||||
attcCb.autoCnf = enable;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATT client.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcInit(void)
|
||||
{
|
||||
uint8_t i;
|
||||
attcCcb_t *pCcb;
|
||||
|
||||
/* Initialize control block */
|
||||
attcCb.pSign = NULL;
|
||||
attcCb.autoCnf = TRUE;
|
||||
|
||||
/* Initialize control block CCBs */
|
||||
for (i = 0, pCcb = attcCb.ccb; i < DM_CONN_MAX; i++, pCcb++)
|
||||
{
|
||||
/* set pointer to main CCB */
|
||||
pCcb->pMainCcb = &attCb.ccb[i];
|
||||
|
||||
/* initialize timer */
|
||||
pCcb->outReqTimer.handlerId = attCb.handlerId;
|
||||
pCcb->outReqTimer.msg.param = i + 1; /* param stores the conn id */
|
||||
}
|
||||
|
||||
/* set up callback interface */
|
||||
attCb.pClient = &attcFcnIf;
|
||||
}
|
||||
Vendored
+208
@@ -0,0 +1,208 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef ATTC_MAIN_H
|
||||
#define ATTC_MAIN_H
|
||||
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "att_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Buffer lengths for requests */
|
||||
#define ATT_MTU_REQ_BUF_LEN (ATT_MTU_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_FIND_INFO_REQ_BUF_LEN (ATT_FIND_INFO_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_FIND_TYPE_REQ_BUF_LEN (ATT_FIND_TYPE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_READ_TYPE_REQ_BUF_LEN (ATT_READ_TYPE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_READ_REQ_BUF_LEN (ATT_READ_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_READ_BLOB_REQ_BUF_LEN (ATT_READ_BLOB_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_READ_MULT_REQ_BUF_LEN (ATT_READ_MULT_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_READ_GROUP_TYPE_REQ_BUF_LEN (ATT_READ_GROUP_TYPE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_WRITE_REQ_BUF_LEN (ATT_WRITE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_WRITE_CMD_BUF_LEN (ATT_WRITE_CMD_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_SIGNED_WRITE_CMD_BUF_LEN (ATT_SIGNED_WRITE_CMD_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_PREP_WRITE_REQ_BUF_LEN (ATT_PREP_WRITE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
#define ATT_EXEC_WRITE_REQ_BUF_LEN (ATT_EXEC_WRITE_REQ_LEN + L2C_PAYLOAD_START)
|
||||
|
||||
/* values for 'continuing' flag */
|
||||
#define ATTC_CONTINUING TRUE
|
||||
#define ATTC_NOT_CONTINUING FALSE
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* ATTC event handler messages */
|
||||
enum
|
||||
{
|
||||
/* messages from API; note these match method values */
|
||||
ATTC_MSG_API_NONE = ATTC_MSG_START,
|
||||
ATTC_MSG_API_MTU = ATT_METHOD_MTU,
|
||||
ATTC_MSG_API_FIND_INFO = ATT_METHOD_FIND_INFO,
|
||||
ATTC_MSG_API_FIND_BY_TYPE_VALUE = ATT_METHOD_FIND_TYPE,
|
||||
ATTC_MSG_API_READ_BY_TYPE = ATT_METHOD_READ_TYPE,
|
||||
ATTC_MSG_API_READ = ATT_METHOD_READ,
|
||||
ATTC_MSG_API_READ_LONG = ATT_METHOD_READ_BLOB,
|
||||
ATTC_MSG_API_READ_MULTIPLE = ATT_METHOD_READ_MULTIPLE,
|
||||
ATTC_MSG_API_READ_BY_GROUP_TYPE = ATT_METHOD_READ_GROUP_TYPE,
|
||||
ATTC_MSG_API_WRITE = ATT_METHOD_WRITE,
|
||||
ATTC_MSG_API_WRITE_CMD = ATT_METHOD_WRITE_CMD,
|
||||
ATTC_MSG_API_PREP_WRITE = ATT_METHOD_PREPARE_WRITE,
|
||||
ATTC_MSG_API_EXEC_WRITE = ATT_METHOD_EXECUTE_WRITE,
|
||||
ATTC_MSG_API_SIGNED_WRITE_CMD,
|
||||
ATTC_MSG_CMAC_CMPL,
|
||||
ATTC_MSG_API_CANCEL,
|
||||
ATTC_MSG_REQ_TIMEOUT
|
||||
};
|
||||
|
||||
/*!
|
||||
* Data buffer format for API request messages:
|
||||
*
|
||||
* | attcPktParam_t | ATT request data |
|
||||
* | bytes 0 to 7 | bytes 8 - |
|
||||
*/
|
||||
|
||||
/* Structure for API with offset parameter */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t len;
|
||||
uint16_t offset;
|
||||
} attcPktParamOffset_t;
|
||||
|
||||
/* Structure for API with start and end handle parameters */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t len;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
} attcPktParamHandles_t;
|
||||
|
||||
/* Structure for API with offset and value parameters */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t len;
|
||||
uint16_t offset;
|
||||
uint8_t *pValue;
|
||||
} attcPktParamPrepWrite_t;
|
||||
|
||||
/* union of API parameter types */
|
||||
typedef union
|
||||
{
|
||||
uint16_t len;
|
||||
attcPktParamOffset_t o;
|
||||
attcPktParamHandles_t h;
|
||||
attcPktParamPrepWrite_t w;
|
||||
} attcPktParam_t;
|
||||
|
||||
/* verify attcPktParam_t will work in data buffer format described above */
|
||||
WSF_CT_ASSERT(sizeof(attcPktParam_t) <= L2C_PAYLOAD_START);
|
||||
|
||||
/* API message structure */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
attcPktParam_t *pPkt;
|
||||
uint16_t handle;
|
||||
} attcApiMsg_t;
|
||||
|
||||
/* ATTC connection control block */
|
||||
typedef struct
|
||||
{
|
||||
attCcb_t *pMainCcb; /* Pointer to ATT main CCB */
|
||||
attcApiMsg_t onDeck; /* API message "on deck" waiting to be sent */
|
||||
attcApiMsg_t outReq; /* Outstanding request waiting for response */
|
||||
attcPktParam_t outReqParams; /* Parameters associated with outstanding request */
|
||||
wsfTimer_t outReqTimer; /* Outstanding request timer */
|
||||
bool_t flowDisabled; /* Data flow disabled */
|
||||
bool_t cnfPending; /* Handle value confirm packet waiting to be sent */
|
||||
uint16_t pendWriteCmdHandle[ATT_NUM_SIMUL_WRITE_CMD]; /* Callback to app pending for this write cmd handle */
|
||||
} attcCcb_t;
|
||||
|
||||
/* Signed data callbacks */
|
||||
typedef void (*attcSignMsgCback_t)(attcCcb_t *pCcb, attcApiMsg_t *pMsg);
|
||||
typedef void (*attcCloseCback_t)(attcCcb_t *pCcb, uint8_t status);
|
||||
|
||||
/* Signed data callback interface */
|
||||
typedef struct
|
||||
{
|
||||
attcSignMsgCback_t msgCback; /* Message handling callback */
|
||||
attcCloseCback_t closeCback; /* Connection close callback */
|
||||
} attcSignFcnIf_t;
|
||||
|
||||
/* Main control block of the ATTC subsystem */
|
||||
typedef struct
|
||||
{
|
||||
attcCcb_t ccb[DM_CONN_MAX];
|
||||
attcSignFcnIf_t const *pSign;
|
||||
bool_t autoCnf;
|
||||
} attcCb_t;
|
||||
|
||||
/* type for response processing functions */
|
||||
typedef void (*attcProcRsp_t)(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
extern attcCb_t attcCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
attcCcb_t *attcCcbByConnId(dmConnId_t connId);
|
||||
attcCcb_t *attcCcbByHandle(uint16_t handle);
|
||||
void attcFreePkt(attcApiMsg_t *pMsg);
|
||||
void attcExecCallback(dmConnId_t connId, uint8_t event, uint16_t handle, uint8_t status);
|
||||
void attcReqClear(attcCcb_t *pCcb, attcApiMsg_t *pMsg, uint8_t status);
|
||||
|
||||
void attcSetupReq(attcCcb_t *pCcb, attcApiMsg_t *pMsg);
|
||||
void attcSendReq(attcCcb_t *pCcb);
|
||||
void attcSendMsg(dmConnId_t connId, uint16_t handle, uint8_t msgId, attcPktParam_t *pPkt, bool_t continuing);
|
||||
|
||||
void attcProcRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attcProcInd(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
|
||||
void attcProcErrRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcMtuRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcFindOrReadRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcFindByTypeRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcReadRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcReadLongRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcWriteRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
void attcProcPrepWriteRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* ATTC_MAIN_H */
|
||||
Vendored
+699
@@ -0,0 +1,699 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client mandatory PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_math.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "attc_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Table of response processing functions */
|
||||
static const attcProcRsp_t attcProcRspTbl[] =
|
||||
{
|
||||
attcProcErrRsp, /* ATT_METHOD_ERR */
|
||||
attcProcMtuRsp, /* ATT_METHOD_MTU */
|
||||
attcProcFindOrReadRsp, /* ATT_METHOD_FIND_INFO */
|
||||
attcProcFindByTypeRsp, /* ATT_METHOD_FIND_TYPE */
|
||||
attcProcFindOrReadRsp, /* ATT_METHOD_READ_TYPE */
|
||||
attcProcReadRsp, /* ATT_METHOD_READ */
|
||||
attcProcReadLongRsp, /* ATT_METHOD_READ_BLOB */
|
||||
attcProcReadRsp, /* ATT_METHOD_READ_MULTIPLE */
|
||||
attcProcFindOrReadRsp, /* ATT_METHOD_READ_GROUP_TYPE */
|
||||
attcProcWriteRsp, /* ATT_METHOD_WRITE */
|
||||
NULL, /* ATT_METHOD_WRITE_CMD */
|
||||
attcProcPrepWriteRsp, /* ATT_METHOD_PREPARE_WRITE */
|
||||
attcProcWriteRsp /* ATT_METHOD_EXECUTE_WRITE */
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Error response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcErrRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
uint8_t *p;
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
|
||||
/* set callback event from stored method */
|
||||
pEvt->hdr.event = pCcb->outReq.hdr.event;
|
||||
|
||||
/* ignore request opcode in the error response */
|
||||
p++;
|
||||
|
||||
/* if request was a read or write with a specific handle */
|
||||
if (pEvt->hdr.event == ATTC_READ_RSP || pEvt->hdr.event == ATTC_READ_LONG_RSP ||
|
||||
pEvt->hdr.event == ATTC_WRITE_RSP || pEvt->hdr.event == ATTC_PREPARE_WRITE_RSP)
|
||||
{
|
||||
/* ignore handle in the error response; callback will use stored handle from request */
|
||||
p += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set handle from packet */
|
||||
BSTREAM_TO_UINT16(pEvt->handle, p);
|
||||
}
|
||||
|
||||
/* set status from error code in packet, but verify it's not 'success' */
|
||||
BSTREAM_TO_UINT8(pEvt->hdr.status, p);
|
||||
if (pEvt->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_UNDEFINED;
|
||||
}
|
||||
|
||||
/* no parameters so clear length */
|
||||
pEvt->valueLen = 0;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received MTU response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcMtuRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
uint16_t mtu;
|
||||
|
||||
BYTES_TO_UINT16(mtu, pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN);
|
||||
|
||||
/* verify */
|
||||
if (mtu < ATT_DEFAULT_MTU)
|
||||
{
|
||||
mtu = ATT_DEFAULT_MTU;
|
||||
}
|
||||
|
||||
/* set mtu for the connection */
|
||||
attSetMtu(pCcb->pMainCcb, mtu, WSF_MIN(pAttCfg->mtu, (HciGetMaxRxAclLen() - L2C_HDR_LEN)));
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Find Information, Read By Type, or Read By Group Type
|
||||
* response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcFindOrReadRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
uint8_t *p;
|
||||
uint8_t *pEnd;
|
||||
uint16_t handle;
|
||||
uint16_t nextHandle;
|
||||
uint16_t prevHandle;
|
||||
uint8_t paramLen;
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
pEnd = pPacket + L2C_PAYLOAD_START + len;
|
||||
|
||||
/* parameter length depends on packet type */
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_FIND_INFO)
|
||||
{
|
||||
/* length in find info response is coded by UUID */
|
||||
if (*p++ == ATT_FIND_HANDLE_16_UUID)
|
||||
{
|
||||
paramLen = ATT_16_UUID_LEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
paramLen = ATT_128_UUID_LEN;
|
||||
}
|
||||
}
|
||||
else if (pCcb->outReq.hdr.event == ATTC_MSG_API_READ_BY_TYPE)
|
||||
{
|
||||
/* length in read by type response is handle plus parameter length */
|
||||
paramLen = *p++ - sizeof(uint16_t);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* length in read by group type response is two handles plus parameter length */
|
||||
paramLen = *p++ - (2 * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
/* get and verify all handles */
|
||||
nextHandle = pCcb->outReqParams.h.startHandle;
|
||||
while (p < pEnd)
|
||||
{
|
||||
/* get and compare handle */
|
||||
BSTREAM_TO_UINT16(handle, p);
|
||||
if (handle == 0 || nextHandle == 0 || handle < nextHandle ||
|
||||
handle > pCcb->outReqParams.h.endHandle)
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_INVALID_RSP;
|
||||
break;
|
||||
}
|
||||
|
||||
/* if read by group type response get second handle */
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_READ_BY_GROUP_TYPE)
|
||||
{
|
||||
prevHandle = handle;
|
||||
BSTREAM_TO_UINT16(handle, p);
|
||||
if (handle == 0 || handle < prevHandle || handle < nextHandle ||
|
||||
handle > pCcb->outReqParams.h.endHandle)
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_INVALID_RSP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* set next expected handle, with special case for max handle */
|
||||
if (handle == ATT_HANDLE_MAX)
|
||||
{
|
||||
nextHandle = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextHandle = handle + 1;
|
||||
}
|
||||
|
||||
/* skip over parameter */
|
||||
p += paramLen;
|
||||
|
||||
/* check for truncated response */
|
||||
if (p > pEnd)
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_INVALID_RSP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if response was correct */
|
||||
if (pEvt->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* if all handles read */
|
||||
if (nextHandle == 0 || nextHandle == (pCcb->outReqParams.h.endHandle + 1))
|
||||
{
|
||||
/* we're done */
|
||||
pCcb->outReq.hdr.status = ATTC_NOT_CONTINUING;
|
||||
}
|
||||
/* else set up for next request */
|
||||
else
|
||||
{
|
||||
pCcb->outReqParams.h.startHandle = nextHandle;
|
||||
pCcb->outReq.handle = nextHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Read response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcReadRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
/* nothing to process */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Write response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcWriteRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
/* no parameters so clear length */
|
||||
pEvt->valueLen = 0;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
attEvt_t evt;
|
||||
|
||||
/* if no request in progress ignore response */
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* get method */
|
||||
evt.hdr.event = ATT_OPCODE_2_METHOD(*(pPacket + L2C_PAYLOAD_START));
|
||||
|
||||
/* if response method is not error and does not match stored method ignore response */
|
||||
if ((evt.hdr.event != ATT_METHOD_ERR) && (evt.hdr.event != pCcb->outReq.hdr.event))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* stop request timer */
|
||||
WsfTimerStop(&pCcb->outReqTimer);
|
||||
|
||||
/* initialize event structure then process response */
|
||||
evt.pValue = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
evt.valueLen = len - ATT_HDR_LEN;
|
||||
evt.handle = pCcb->outReq.handle;
|
||||
evt.hdr.status = ATT_SUCCESS;
|
||||
(*attcProcRspTbl[evt.hdr.event])(pCcb, len, pPacket, &evt);
|
||||
|
||||
/* if not continuing or status is not success */
|
||||
if ((pCcb->outReq.hdr.status == ATTC_NOT_CONTINUING) || (evt.hdr.status != ATT_SUCCESS))
|
||||
{
|
||||
/* we're not sending another request so clear the out req */
|
||||
pCcb->outReq.hdr.event = ATTC_MSG_API_NONE;
|
||||
attcFreePkt(&pCcb->outReq);
|
||||
}
|
||||
|
||||
/* call callback (if not mtu rsp) */
|
||||
if ((evt.hdr.event != ATT_METHOD_MTU) && attCb.cback)
|
||||
{
|
||||
/* set additional parameters and call callback */
|
||||
evt.continuing = pCcb->outReq.hdr.status; /* continuing flag */
|
||||
evt.hdr.param = pCcb->outReq.hdr.param; /* connId */
|
||||
(*attCb.cback)(&evt);
|
||||
}
|
||||
|
||||
/* if no flow control */
|
||||
if (!pCcb->flowDisabled)
|
||||
{
|
||||
/* if out req ready */
|
||||
if (pCcb->outReq.pPkt != NULL)
|
||||
{
|
||||
/* build and send request */
|
||||
attcSendReq(pCcb);
|
||||
}
|
||||
/* else if api is on deck */
|
||||
else if (pCcb->onDeck.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
/* set up and send request */
|
||||
attcSetupReq(pCcb, &pCcb->onDeck);
|
||||
|
||||
/* clear on deck */
|
||||
pCcb->onDeck.hdr.event = ATTC_MSG_API_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received indication or notification packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcInd(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
attEvt_t evt;
|
||||
uint8_t *p;
|
||||
uint8_t *pPkt;
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START;
|
||||
|
||||
/* parse packet and set callback event struct */
|
||||
evt.hdr.event = ATT_OPCODE_2_METHOD(*p++);
|
||||
BSTREAM_TO_UINT16(evt.handle, p);
|
||||
evt.pValue = p;
|
||||
evt.valueLen = len - ATT_HDR_LEN - sizeof(uint16_t);
|
||||
evt.hdr.param = pCcb->pMainCcb->connId;
|
||||
evt.hdr.status = ATT_SUCCESS;
|
||||
evt.continuing = FALSE;
|
||||
|
||||
/* verify handle and call callback */
|
||||
if ((evt.handle != 0) && attCb.cback)
|
||||
{
|
||||
(*attCb.cback)(&evt);
|
||||
}
|
||||
|
||||
/* if indication send confirm */
|
||||
if (attcCb.autoCnf && (evt.hdr.event == ATT_METHOD_VALUE_IND))
|
||||
{
|
||||
if (!pCcb->flowDisabled)
|
||||
{
|
||||
if ((pPkt = attMsgAlloc(ATT_VALUE_CNF_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
*(pPkt + L2C_PAYLOAD_START) = ATT_PDU_VALUE_CNF;
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, ATT_VALUE_CNF_LEN, pPkt);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* mark confirm as pending; will be sent when flow enabled or application sends it. */
|
||||
pCcb->cnfPending = TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Build and send a WSF message to ATTC.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param msgId Message ID.
|
||||
* \param pPkt Packet parameters.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcSendMsg(dmConnId_t connId, uint16_t handle, uint8_t msgId, attcPktParam_t *pPkt, bool_t continuing)
|
||||
{
|
||||
attcCcb_t *pCcb;
|
||||
uint16_t mtu;
|
||||
bool_t transTimedOut;
|
||||
|
||||
WsfTaskLock();
|
||||
|
||||
/* get CCB and verify connection still in use */
|
||||
if ((pCcb = attcCcbByConnId(connId)) != NULL)
|
||||
{
|
||||
/* get MTU size */
|
||||
mtu = pCcb->pMainCcb->mtu;
|
||||
transTimedOut = !!(pCcb->pMainCcb->control & ATT_CCB_STATUS_TX_TIMEOUT);
|
||||
}
|
||||
/* else connection not in use */
|
||||
else
|
||||
{
|
||||
/* MTU size unknown */
|
||||
mtu = 0;
|
||||
transTimedOut = FALSE;
|
||||
}
|
||||
|
||||
WsfTaskUnlock();
|
||||
|
||||
/* if MTU size known for connection */
|
||||
if (mtu > 0)
|
||||
{
|
||||
/* if no transaction's timed out */
|
||||
if (!transTimedOut)
|
||||
{
|
||||
uint16_t dataLen = 0;
|
||||
|
||||
/* if packet is not null then find out its length */
|
||||
if (pPkt != NULL)
|
||||
{
|
||||
/* if not prepare write request */
|
||||
if (msgId != ATTC_MSG_API_PREP_WRITE)
|
||||
{
|
||||
dataLen = pPkt->len;
|
||||
}
|
||||
/* else prepare write request */
|
||||
else
|
||||
{
|
||||
/* if not continuing */
|
||||
if (!continuing)
|
||||
{
|
||||
/* single prepare write request */
|
||||
dataLen = ATT_PREP_WRITE_REQ_LEN + pPkt->w.len;
|
||||
}
|
||||
/* else will be sent as multiple prepare write requests */
|
||||
}
|
||||
}
|
||||
|
||||
/* if packet length is less than or equal to negotiated MTU */
|
||||
if (dataLen <= mtu)
|
||||
{
|
||||
attcApiMsg_t *pMsg;
|
||||
|
||||
/* allocate message buffer */
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(attcApiMsg_t))) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->hdr.status = continuing;
|
||||
pMsg->hdr.event = msgId;
|
||||
pMsg->pPkt = pPkt;
|
||||
pMsg->handle = handle;
|
||||
|
||||
/* send message */
|
||||
WsfMsgSend(attCb.handlerId, pMsg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* else packet length exceeds MTU size */
|
||||
else
|
||||
{
|
||||
/* call callback with failure status */
|
||||
attcExecCallback(connId, msgId, handle, ATT_ERR_MTU_EXCEEDED);
|
||||
}
|
||||
}
|
||||
else
|
||||
/* transaction's timed out */
|
||||
{
|
||||
/* call callback with failure status */
|
||||
attcExecCallback(connId, msgId, handle, ATT_ERR_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/* alloc failed, transaction's timed out or packet length exceeded MTU size; free packet buffer */
|
||||
if (pPkt != NULL)
|
||||
{
|
||||
WsfMsgFree(pPkt);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Find Information Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param startHandle Attribute start handle.
|
||||
* \param endHandle Attribute end handle.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcFindInfoReq(dmConnId_t connId, uint16_t startHandle, uint16_t endHandle, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_FIND_INFO_REQ_BUF_LEN)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->len = ATT_FIND_INFO_REQ_LEN;
|
||||
pPkt->h.startHandle = startHandle;
|
||||
pPkt->h.endHandle = endHandle;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_FIND_INFO_REQ);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, startHandle, ATTC_MSG_API_FIND_INFO, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Read Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcReadReq(dmConnId_t connId, uint16_t handle)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_READ_REQ_BUF_LEN)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_READ_REQ_LEN;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_REQ);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_READ, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Write Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcWriteReq(dmConnId_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_WRITE_REQ_BUF_LEN + valueLen)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_WRITE_REQ_LEN + valueLen;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_WRITE_REQ);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pValue, valueLen);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_WRITE, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Cancel an attribute protocol request in progress.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcCancelReq(dmConnId_t connId)
|
||||
{
|
||||
attcSendMsg(connId, 0, ATTC_MSG_API_CANCEL, NULL, FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For internal use only.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param mtu Attribute protocol MTU.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcMtuReq(dmConnId_t connId, uint16_t mtu)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_MTU_REQ_BUF_LEN)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_MTU_REQ_LEN;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_MTU_REQ);
|
||||
UINT16_TO_BSTREAM(p, mtu);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, 0, ATTC_MSG_API_MTU, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol indication confirmation.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcIndConfirm(dmConnId_t connId)
|
||||
{
|
||||
attcCcb_t *pCcb;
|
||||
uint8_t *pPkt;
|
||||
|
||||
pCcb = attcCcbByHandle(connId - 1);
|
||||
|
||||
/* If confirmation is pending */
|
||||
if (pCcb && pCcb->cnfPending && !pCcb->flowDisabled)
|
||||
{
|
||||
if ((pPkt = attMsgAlloc(ATT_VALUE_CNF_LEN + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
pCcb->cnfPending = FALSE;
|
||||
|
||||
*(pPkt + L2C_PAYLOAD_START) = ATT_PDU_VALUE_CNF;
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, ATT_VALUE_CNF_LEN, pPkt);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+347
@@ -0,0 +1,347 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client optional read PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "attc_main.h"
|
||||
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Find By Type response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcFindByTypeRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
uint8_t *p;
|
||||
uint8_t *pEnd;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
uint16_t nextHandle;
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
pEnd = pPacket + L2C_PAYLOAD_START + len;
|
||||
|
||||
/* get and verify all handles */
|
||||
nextHandle = pCcb->outReqParams.h.startHandle;
|
||||
while (p < pEnd)
|
||||
{
|
||||
/* get handle pair */
|
||||
BSTREAM_TO_UINT16(startHandle, p);
|
||||
BSTREAM_TO_UINT16(endHandle, p);
|
||||
|
||||
/*
|
||||
* start handle of handle pair must be:
|
||||
* not greater than end handle of handle pair
|
||||
* not less than than start handle of request or end handle of previous handle pair
|
||||
* not greater than end handle of request
|
||||
* and no additional handle pairs following end handle = 0xFFFF
|
||||
*/
|
||||
if ((startHandle > endHandle) || (startHandle < nextHandle) ||
|
||||
(startHandle > pCcb->outReqParams.h.endHandle) || (nextHandle == 0))
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_INVALID_RSP;
|
||||
break;
|
||||
}
|
||||
|
||||
/* set next expected handle, with special case for max handle */
|
||||
if (endHandle == ATT_HANDLE_MAX)
|
||||
{
|
||||
nextHandle = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextHandle = endHandle + 1;
|
||||
}
|
||||
|
||||
/* check for truncated response */
|
||||
if (p > pEnd)
|
||||
{
|
||||
pEvt->hdr.status = ATT_ERR_INVALID_RSP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if response was correct */
|
||||
if (pEvt->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* if all handles read */
|
||||
if (nextHandle == 0 || nextHandle > pCcb->outReqParams.h.endHandle)
|
||||
{
|
||||
/* we're done */
|
||||
pCcb->outReq.hdr.status = ATTC_NOT_CONTINUING;
|
||||
}
|
||||
/* else set up for next request */
|
||||
else
|
||||
{
|
||||
pCcb->outReqParams.h.startHandle = nextHandle;
|
||||
pCcb->outReq.handle = nextHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Read Long response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcReadLongRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* length of response is less than mtu */
|
||||
if (len < pCcb->pMainCcb->mtu)
|
||||
{
|
||||
/* we're done */
|
||||
pCcb->outReq.hdr.status = ATTC_NOT_CONTINUING;
|
||||
}
|
||||
/* else set up for next request */
|
||||
else
|
||||
{
|
||||
pCcb->outReqParams.o.offset += pEvt->valueLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Find By Type Value Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param startHandle Attribute start handle.
|
||||
* \param endHandle Attribute end handle.
|
||||
* \param uuid16 16-bit UUID to find.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcFindByTypeValueReq(dmConnId_t connId, uint16_t startHandle, uint16_t endHandle,
|
||||
uint16_t uuid16, uint16_t valueLen, uint8_t *pValue, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_FIND_TYPE_REQ_BUF_LEN + valueLen)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->len = ATT_FIND_TYPE_REQ_LEN + valueLen;
|
||||
pPkt->h.startHandle = startHandle;
|
||||
pPkt->h.endHandle = endHandle;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_FIND_TYPE_REQ);
|
||||
/* skip start and end handle fields */
|
||||
p += (2 * sizeof(uint16_t));
|
||||
UINT16_TO_BSTREAM(p, uuid16);
|
||||
memcpy(p, pValue, valueLen);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, startHandle, ATTC_MSG_API_FIND_BY_TYPE_VALUE, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Read By Type Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param startHandle Attribute start handle.
|
||||
* \param endHandle Attribute end handle.
|
||||
* \param uuidLen Length of UUID (2 or 16).
|
||||
* \param pUuid Pointer to UUID data.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcReadByTypeReq(dmConnId_t connId, uint16_t startHandle, uint16_t endHandle,
|
||||
uint8_t uuidLen, uint8_t *pUuid, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_READ_TYPE_REQ_BUF_LEN + uuidLen)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->len = ATT_READ_TYPE_REQ_LEN + uuidLen;
|
||||
pPkt->h.startHandle = startHandle;
|
||||
pPkt->h.endHandle = endHandle;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_TYPE_REQ);
|
||||
/* skip start and end handle fields */
|
||||
p += (2 * sizeof(uint16_t));
|
||||
memcpy(p, pUuid, uuidLen);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, startHandle, ATTC_MSG_API_READ_BY_TYPE, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Read Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param offset Read attribute data starting at this offset.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcReadLongReq(dmConnId_t connId, uint16_t handle, uint16_t offset, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_READ_BLOB_REQ_BUF_LEN)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->len = ATT_READ_BLOB_REQ_LEN;
|
||||
pPkt->o.offset = offset;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_BLOB_REQ);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_READ_LONG, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Read Multiple Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param numHandles Number of handles in attribute handle list.
|
||||
* \param pHandles List of attribute handles.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcReadMultipleReq(dmConnId_t connId, uint8_t numHandles, uint16_t *pHandles)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
uint16_t handle;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_READ_MULT_REQ_BUF_LEN + (numHandles * sizeof(uint16_t)))) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_READ_MULT_REQ_LEN + (numHandles * sizeof(uint16_t));
|
||||
|
||||
/* save first handle */
|
||||
handle = pHandles[0];
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_MULT_REQ);
|
||||
while (numHandles--)
|
||||
{
|
||||
UINT16_TO_BSTREAM(p, *pHandles);
|
||||
pHandles++;
|
||||
}
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_READ_MULTIPLE, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Read By Group Type Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param startHandle Attribute start handle.
|
||||
* \param endHandle Attribute end handle.
|
||||
* \param uuidLen Length of UUID (2 or 16).
|
||||
* \param pUuid Pointer to UUID data.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcReadByGroupTypeReq(dmConnId_t connId, uint16_t startHandle, uint16_t endHandle,
|
||||
uint8_t uuidLen, uint8_t *pUuid, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_READ_GROUP_TYPE_REQ_BUF_LEN + uuidLen)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->len = ATT_READ_GROUP_TYPE_REQ_LEN + uuidLen;
|
||||
pPkt->h.startHandle = startHandle;
|
||||
pPkt->h.endHandle = endHandle;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_GROUP_TYPE_REQ);
|
||||
/* skip start and end handle fields */
|
||||
p += (2 * sizeof(uint16_t));
|
||||
memcpy(p, pUuid, uuidLen);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, startHandle, ATTC_MSG_API_READ_BY_GROUP_TYPE, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
Vendored
+303
@@ -0,0 +1,303 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client optional signed PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "sec_api.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/calc128.h"
|
||||
#include "util/wstr.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "attc_main.h"
|
||||
#include "att_sign.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* ATTC signed PDU control block */
|
||||
typedef struct
|
||||
{
|
||||
attcApiMsg_t msg;
|
||||
} attcSignCb_t;
|
||||
|
||||
/* Message parameters */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
attcApiMsg_t api;
|
||||
} attcSignMsg_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
static void attcSignCloseCback(attcCcb_t *pCcb, uint8_t status);
|
||||
static void attcSignMsgCback(attcCcb_t *pCcb, attcSignMsg_t *pMsg);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Interface to ATT */
|
||||
static const attcSignFcnIf_t attcSignFcnIf =
|
||||
{
|
||||
(attcSignMsgCback_t) attcSignMsgCback,
|
||||
attcSignCloseCback
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
static attcSignCb_t *pAttcSignCb[DM_CONN_MAX];
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate a signing control block.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to control block.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static attcSignCb_t *attcSignCbAlloc(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
WSF_ASSERT(pAttcSignCb[connId - 1] == NULL);
|
||||
|
||||
return (pAttcSignCb[connId - 1] = WsfBufAlloc(sizeof(attcSignCb_t)));
|
||||
}
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Free a signing control block.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSignCbFree(dmConnId_t connId)
|
||||
{
|
||||
WsfBufFree(pAttcSignCb[connId - 1]);
|
||||
pAttcSignCb[connId - 1] = NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the signing control block for the connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to control block.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static attcSignCb_t *attcSignCbByConnId(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return pAttcSignCb[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection close callback for ATTC signed PDU processing.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param status Connection close status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSignCloseCback(attcCcb_t *pCcb, uint8_t status)
|
||||
{
|
||||
attcSignCb_t *pCb;
|
||||
|
||||
/* if connection closed while processing in progress */
|
||||
if ((pCb = attcSignCbByConnId(pCcb->pMainCcb->connId)) != NULL)
|
||||
{
|
||||
|
||||
/* message free buffer */
|
||||
if (pCb->msg.hdr.event != ATTC_MSG_API_NONE)
|
||||
{
|
||||
attcReqClear(pCcb, &pCb->msg, status);
|
||||
}
|
||||
|
||||
attcSignCbFree(pCcb->pMainCcb->connId);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Message handler callback for ATTC signed PDU processing.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param pMsg ATTC message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attcSignMsgCback(attcCcb_t *pCcb, attcSignMsg_t *pMsg)
|
||||
{
|
||||
attcSignCb_t *pCb;
|
||||
|
||||
if (pMsg->hdr.event == ATTC_MSG_API_SIGNED_WRITE_CMD)
|
||||
{
|
||||
/* adjust msg event id for later use with app callback */
|
||||
pMsg->hdr.event = ATTC_MSG_API_WRITE_CMD;
|
||||
|
||||
/* verify no API request already waiting on deck or in progress,
|
||||
* and no signed write already in progress
|
||||
*/
|
||||
if ((pCcb->onDeck.hdr.event != ATTC_MSG_API_NONE) ||
|
||||
(pCcb->outReq.hdr.event > ATTC_MSG_API_MTU) ||
|
||||
(attcSignCbByConnId((dmConnId_t) pMsg->hdr.param) != NULL))
|
||||
{
|
||||
/* free request and call callback with failure status */
|
||||
attcReqClear(pCcb, &pMsg->api, ATT_ERR_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate signing control block */
|
||||
if ((pCb = attcSignCbAlloc((dmConnId_t) pMsg->hdr.param)) != NULL)
|
||||
{
|
||||
/* store message */
|
||||
pCb->msg = pMsg->api;
|
||||
|
||||
uint16_t cmacTxtLen = pCb->msg.pPkt->len - ATT_AUTH_SIG_LEN + sizeof(uint32_t);
|
||||
uint8_t *pCmacText = WsfBufAlloc(cmacTxtLen);
|
||||
|
||||
if (pCmacText)
|
||||
{
|
||||
uint8_t revLocalCsrk[SEC_CMAC_KEY_LEN] = {0};
|
||||
|
||||
WStrReverseCpy(revLocalCsrk, DmSecGetLocalCsrk(), SEC_CMAC_KEY_LEN);
|
||||
WStrReverseCpy(pCmacText, (uint8_t *)pCb->msg.pPkt + L2C_PAYLOAD_START, cmacTxtLen);
|
||||
|
||||
if (SecCmac(revLocalCsrk, pCmacText, cmacTxtLen, attCb.handlerId,
|
||||
(dmConnId_t) pMsg->hdr.param, ATTC_MSG_CMAC_CMPL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
WsfBufFree(pCmacText);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* allocation failure */
|
||||
attcReqClear(pCcb, &pMsg->api, ATT_ERR_UNDEFINED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (pMsg->hdr.event == ATTC_MSG_CMAC_CMPL)
|
||||
{
|
||||
secCmacMsg_t *pCmacMsg = (secCmacMsg_t*) pMsg;
|
||||
WsfBufFree(pCmacMsg->pPlainText);
|
||||
|
||||
if ((pCb = attcSignCbByConnId((dmConnId_t) pMsg->hdr.param)) != NULL)
|
||||
{
|
||||
/* append signature to packet */
|
||||
WStrReverseCpy((uint8_t *) pCb->msg.pPkt +
|
||||
(L2C_PAYLOAD_START + pCb->msg.pPkt->len - ATT_CMAC_RESULT_LEN),
|
||||
pCmacMsg->pCiphertext, ATT_CMAC_RESULT_LEN);
|
||||
|
||||
/* if MTU request in progress or flow controlled */
|
||||
if (pCcb->outReq.hdr.event == ATTC_MSG_API_MTU || pCcb->flowDisabled)
|
||||
{
|
||||
/* put request "on deck" for processing later */
|
||||
pCcb->onDeck = pCb->msg;
|
||||
}
|
||||
/* otherwise ready to send */
|
||||
else
|
||||
{
|
||||
attcSetupReq(pCcb, &pCb->msg);
|
||||
}
|
||||
|
||||
/* we're done-- free control block */
|
||||
attcSignCbFree((dmConnId_t) pMsg->hdr.param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol signed Write Command.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param signCounter Value of the sign counter.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcSignedWriteCmd(dmConnId_t connId, uint16_t handle, uint32_t signCounter,
|
||||
uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* if connection already encrypted with security mode 1 level 2 or higher */
|
||||
if (DmConnSecLevel(connId) > DM_SEC_LEVEL_NONE)
|
||||
{
|
||||
/* use write command instead */
|
||||
AttcWriteCmd(connId, handle, valueLen, pValue);
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_SIGNED_WRITE_CMD_BUF_LEN + valueLen)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_SIGNED_WRITE_CMD_LEN + valueLen;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_SIGNED_WRITE_CMD);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pValue, valueLen);
|
||||
p += valueLen;
|
||||
UINT32_TO_BSTREAM(p, signCounter);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_SIGNED_WRITE_CMD, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATT client for data signing.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcSignInit(void)
|
||||
{
|
||||
/* set up callback interface */
|
||||
attcCb.pSign = &attcSignFcnIf;
|
||||
}
|
||||
Vendored
+189
@@ -0,0 +1,189 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client optional write PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "attc_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received Prepare Write response packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
* \param pEvt Pointer to callback event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attcProcPrepWriteRsp(attcCcb_t *pCcb, uint16_t len, uint8_t *pPacket, attEvt_t *pEvt)
|
||||
{
|
||||
/* if continuing */
|
||||
if (pCcb->outReq.hdr.status == ATTC_CONTINUING)
|
||||
{
|
||||
/* if no more data to send */
|
||||
if (pCcb->outReqParams.w.len == 0)
|
||||
{
|
||||
/* we're done */
|
||||
pCcb->outReq.hdr.status = ATTC_NOT_CONTINUING;
|
||||
}
|
||||
}
|
||||
|
||||
/* adjust attribute value and its length (adjusted by ATT header length already) */
|
||||
pEvt->pValue += (ATT_PREP_WRITE_RSP_LEN - ATT_HDR_LEN);
|
||||
pEvt->valueLen -= (ATT_PREP_WRITE_RSP_LEN - ATT_HDR_LEN);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Write Command.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcWriteCmd(dmConnId_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_WRITE_CMD_BUF_LEN + valueLen)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_WRITE_CMD_LEN + valueLen;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_WRITE_CMD);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pValue, valueLen);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_WRITE_CMD, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Prepare Write Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param offset Write attribute data starting at this offset.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
* \param valueByRef TRUE if pValue data is accessed by reference rather than copied.
|
||||
* \param continuing TRUE if ATTC continues sending requests until complete.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcPrepareWriteReq(dmConnId_t connId, uint16_t handle, uint16_t offset, uint16_t valueLen,
|
||||
uint8_t *pValue, bool_t valueByRef, bool_t continuing)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
uint16_t bufLen;
|
||||
|
||||
if (continuing && valueByRef)
|
||||
{
|
||||
bufLen = ATT_PREP_WRITE_REQ_BUF_LEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
bufLen = ATT_PREP_WRITE_REQ_BUF_LEN + valueLen;
|
||||
}
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(bufLen)) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pPkt->w.len = valueLen;
|
||||
pPkt->w.offset = offset;
|
||||
|
||||
/* build partial packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_PREP_WRITE_REQ);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
/* skip over offset field */
|
||||
p += sizeof(uint16_t);
|
||||
|
||||
/* set value pointer and copy data to packet, if not valueByRef */
|
||||
if (continuing && valueByRef)
|
||||
{
|
||||
pPkt->w.pValue = pValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(p, pValue, valueLen);
|
||||
pPkt->w.pValue = p;
|
||||
}
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, handle, ATTC_MSG_API_PREP_WRITE, pPkt, continuing);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate an attribute protocol Execute Write Request.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param writeAll TRUE to write all queued writes, FALSE to cancel all queued writes.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttcExecuteWriteReq(dmConnId_t connId, bool_t writeAll)
|
||||
{
|
||||
attcPktParam_t *pPkt;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate packet and parameter buffer */
|
||||
if ((pPkt = attMsgAlloc(ATT_EXEC_WRITE_REQ_BUF_LEN)) != NULL)
|
||||
{
|
||||
/* set length */
|
||||
pPkt->len = ATT_EXEC_WRITE_REQ_LEN;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *) pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_EXEC_WRITE_REQ);
|
||||
UINT8_TO_BSTREAM(p, writeAll);
|
||||
|
||||
/* send message */
|
||||
attcSendMsg(connId, 0, ATTC_MSG_API_EXEC_WRITE, pPkt, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
+457
@@ -0,0 +1,457 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client characteristic configuration module.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "util/bstream.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t *pCccTbl[DM_CONN_MAX]; /* Pointer to descriptor value tables */
|
||||
attsCccSet_t *pSet; /* Array of CCC descriptor settings */
|
||||
attsCccCback_t cback; /* Client callback function */
|
||||
uint8_t setLen; /* Length of settings array */
|
||||
} AttsCccCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
static AttsCccCb_t attsCccCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute the client callback function.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param idx Index of descriptor in CCC descriptor handle table.
|
||||
* \param handle Attribute handle of the descriptor.
|
||||
* \param value Attribute value of the descriptor.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsCccCback(dmConnId_t connId, uint8_t idx, uint16_t handle, uint16_t value)
|
||||
{
|
||||
attsCccEvt_t evt;
|
||||
|
||||
evt.hdr.event = ATTS_CCC_STATE_IND;
|
||||
evt.hdr.param = connId;
|
||||
evt.idx = idx;
|
||||
evt.handle = handle;
|
||||
evt.value = value;
|
||||
|
||||
(*attsCccCb.cback)(&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate the CCC table for this connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return Pointer into the CCC table.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint16_t *attsCccAllocTbl(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
/* if not already allocated */
|
||||
if (attsCccCb.pCccTbl[connId - 1] == NULL)
|
||||
{
|
||||
WSF_ASSERT(attsCccCb.setLen > 0);
|
||||
|
||||
/* allocate new buffer */
|
||||
attsCccCb.pCccTbl[connId - 1] = WsfBufAlloc(attsCccCb.setLen * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
return attsCccCb.pCccTbl[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the pointer into the CCC table for this connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return Pointer into the CCC table.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint16_t *attsCccGetTbl(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return attsCccCb.pCccTbl[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Free the CCC table for this connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsCccFreeTbl(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
if (attsCccCb.pCccTbl[connId - 1] != NULL)
|
||||
{
|
||||
WsfBufFree(attsCccCb.pCccTbl[connId - 1]);
|
||||
attsCccCb.pCccTbl[connId - 1] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read the value of a client characteristic configuration descriptor. Note the
|
||||
* value is treated as a little endian byte array.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle of the descriptor.
|
||||
* \param pValue The attribute value of the descriptor is copied to this pointer.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attsCccReadValue(dmConnId_t connId, uint16_t handle, uint8_t *pValue)
|
||||
{
|
||||
attsCccSet_t *pSet;
|
||||
uint16_t *pTbl;
|
||||
uint8_t i;
|
||||
|
||||
/* find handle in handle array */
|
||||
for (pSet = attsCccCb.pSet, i = 0; i < attsCccCb.setLen; i++, pSet++)
|
||||
{
|
||||
if (pSet->handle == handle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if handle not found return error */
|
||||
if (i == attsCccCb.setLen)
|
||||
{
|
||||
return ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* get pointer to the table for this connection */
|
||||
if ((pTbl = attsCccGetTbl(connId)) != NULL)
|
||||
{
|
||||
/* read value */
|
||||
UINT16_TO_BSTREAM(pValue, pTbl[i]);
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Write the value of a client characteristic configuration descriptor. Note the
|
||||
* value is treated as a little endian byte array.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle of the descriptor.
|
||||
* \param pValue Pointer to the attribute value of the descriptor.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attsCccWriteValue(dmConnId_t connId, uint16_t handle, uint8_t *pValue)
|
||||
{
|
||||
attsCccSet_t *pSet;
|
||||
uint16_t *pTbl;
|
||||
uint8_t i;
|
||||
uint16_t value;
|
||||
uint16_t prevValue;
|
||||
|
||||
/* find handle in handle array */
|
||||
for (pSet = attsCccCb.pSet, i = 0; i < attsCccCb.setLen; i++, pSet++)
|
||||
{
|
||||
if (pSet->handle == handle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if handle not found return error */
|
||||
if (i == attsCccCb.setLen)
|
||||
{
|
||||
return ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
BYTES_TO_UINT16(value, pValue);
|
||||
|
||||
/* verify value range */
|
||||
if (((value != 0) && (value != ATT_CLIENT_CFG_NOTIFY) && (value != ATT_CLIENT_CFG_INDICATE)) ||
|
||||
((value != 0) && ((value & pSet->valueRange) == 0)))
|
||||
{
|
||||
return ATT_ERR_VALUE_RANGE;
|
||||
}
|
||||
|
||||
/* get pointer to the table for this connection */
|
||||
if ((pTbl = attsCccGetTbl(connId)) != NULL)
|
||||
{
|
||||
/* write value */
|
||||
prevValue = pTbl[i];
|
||||
pTbl[i] = value;
|
||||
|
||||
/* if value changed call callback */
|
||||
if (prevValue != value)
|
||||
{
|
||||
attsCccCback(connId, i, handle, value);
|
||||
}
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief CCC callback function executed by ATTS and CCC read or write.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param method Read or write.
|
||||
* \param handle Attribute handle of the descriptor.
|
||||
* \param pValue The attribute value of the descriptor is copied to this pointer.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attsCccMainCback(dmConnId_t connId, uint8_t method, uint16_t handle, uint8_t *pValue)
|
||||
{
|
||||
ATT_TRACE_INFO2("attsCccMainCback connId=%d handle=%d", connId, handle);
|
||||
|
||||
if (method == ATT_METHOD_READ)
|
||||
{
|
||||
return attsCccReadValue(connId, handle, pValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return attsCccWriteValue(connId, handle, pValue);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register the utility service for managing client characteristic
|
||||
* configuration descriptors. This function is typically called once on
|
||||
* system initialization.
|
||||
*
|
||||
* \param setLen Length of settings array.
|
||||
* \param pSet Array of CCC descriptor settings.
|
||||
* \param cback Client callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCccRegister(uint8_t setLen, attsCccSet_t *pSet, attsCccCback_t cback)
|
||||
{
|
||||
attsCccCb.setLen = setLen;
|
||||
attsCccCb.pSet = pSet;
|
||||
attsCccCb.cback = cback;
|
||||
|
||||
attsCb.cccCback = attsCccMainCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the client characteristic configuration descriptor value table for a
|
||||
* connection. This function is typically called when a connection is established
|
||||
* or when a device is bonded.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCccTbl Pointer to the descriptor value array. The length of the array
|
||||
* must equal the value of setLen passed to AttsCccRegister().
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCccInitTable(dmConnId_t connId, uint16_t *pCccTbl)
|
||||
{
|
||||
uint8_t i;
|
||||
uint16_t *pTbl;
|
||||
|
||||
ATT_TRACE_INFO1("AttsCccInitTable connId=%d", connId);
|
||||
|
||||
if ((pTbl = attsCccAllocTbl(connId)) != NULL)
|
||||
{
|
||||
/* if initializer table is passed in */
|
||||
if (pCccTbl != NULL)
|
||||
{
|
||||
/* initialize table */
|
||||
for (i = 0; i < attsCccCb.setLen; i++, pCccTbl++, pTbl++)
|
||||
{
|
||||
/* copy value */
|
||||
*pTbl = *pCccTbl;
|
||||
|
||||
/* execute callback for each nonzero entry in table */
|
||||
if (*pCccTbl != 0)
|
||||
{
|
||||
attsCccCback(connId, i, ATT_HANDLE_NONE, *pCccTbl);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* initialize table to zero */
|
||||
memset(pTbl, 0, (sizeof(uint16_t) * attsCccCb.setLen));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear and deallocate the client characteristic configuration descriptor value
|
||||
* table for a connection. This function must be called when a connection is closed.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCccClearTable(dmConnId_t connId)
|
||||
{
|
||||
ATT_TRACE_INFO1("AttsCccClearTable connId=%d", connId);
|
||||
|
||||
attsCccFreeTbl(connId);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the value of a client characteristic configuration descriptor by its index.
|
||||
* If not found, return zero.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param idx Index of descriptor in CCC descriptor handle table.
|
||||
*
|
||||
* \return Value of the descriptor.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t AttsCccGet(dmConnId_t connId, uint8_t idx)
|
||||
{
|
||||
uint16_t *pTbl;
|
||||
|
||||
WSF_ASSERT(idx < attsCccCb.setLen);
|
||||
|
||||
if ((pTbl = attsCccGetTbl(connId)) != NULL)
|
||||
{
|
||||
/* return value from table */
|
||||
return pTbl[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the value of a client characteristic configuration descriptor by its index.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param idx Index of descriptor in CCC descriptor handle table.
|
||||
* \param value Value of the descriptor.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCccSet(dmConnId_t connId, uint8_t idx, uint16_t value)
|
||||
{
|
||||
uint16_t *pTbl;
|
||||
|
||||
WSF_ASSERT(idx < attsCccCb.setLen);
|
||||
|
||||
if ((pTbl = attsCccGetTbl(connId)) != NULL)
|
||||
{
|
||||
pTbl[idx] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if a client characteristic configuration descriptor is enabled and if
|
||||
* the characteristic's security level has been met.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param idx Index of descriptor in CCC descriptor handle table.
|
||||
*
|
||||
* \return Value of the descriptor if security level is met, otherwise zero.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t AttsCccEnabled(dmConnId_t connId, uint8_t idx)
|
||||
{
|
||||
WSF_ASSERT(idx < attsCccCb.setLen);
|
||||
|
||||
/* check security level */
|
||||
if (DmConnSecLevel(connId) < attsCccCb.pSet[idx].secLevel)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get value */
|
||||
return AttsCccGet(connId, idx);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get number of CCC entries in table.
|
||||
*
|
||||
* \param None
|
||||
*
|
||||
* \return Number of CCC entries in table.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttsGetCccTableLen(void)
|
||||
{
|
||||
return attsCccCb.setLen;
|
||||
}
|
||||
+412
@@ -0,0 +1,412 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT client supported features module.
|
||||
*
|
||||
* Copyright (c) 2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
#include "util/bstream.h"
|
||||
#include "svc_core.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
attsCsfCb_t attsCsfCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set status of on-going database hash update operation.
|
||||
*
|
||||
* \param isUpdating \ref TRUE is updating, otherwise \ref FALSE;
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsCsfSetHashUpdateStatus(bool_t isUpdating)
|
||||
{
|
||||
if (attsCsfCb.isHashUpdating == isUpdating)
|
||||
{
|
||||
/* Already in the current state, nothing to do. */
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Update state. */
|
||||
attsCsfCb.isHashUpdating = isUpdating;
|
||||
}
|
||||
|
||||
/* Update complete.
|
||||
* Check if clients were pending on the hash value and fulfill their requests.
|
||||
*/
|
||||
if (isUpdating == FALSE)
|
||||
{
|
||||
ATT_TRACE_INFO0("Database hash calculation complete");
|
||||
|
||||
attsCheckPendDbHashReadRsp();
|
||||
|
||||
/* Note: Clients which were pending on a Database Hash read from a Read by Type Request are not
|
||||
* transitioned to the change-aware state here. The application is expected to initiate the
|
||||
* state transition of all clients when the new hash is set. If this is not done, the
|
||||
* state of pending Clients will be out of sync, and will be corrected on the next database
|
||||
* sync.
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_INFO0("Calculating database hash");
|
||||
|
||||
/* If the application, for whatever reason, previously recalculated the database hash over an
|
||||
* unchanged database and a client pended on a Read By Type Request of the database hash, then
|
||||
* that clients state may be out of step if the application did not initiate a state
|
||||
* transition. That state transition is forced here to keep handle next transition.
|
||||
*/
|
||||
for (uint8_t i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
if (attsCsfCb.attsCsfTable[i].changeAwareState == ATTS_CLIENT_CHANGE_AWARE_DB_READ_PENDING)
|
||||
{
|
||||
attsCsfCb.attsCsfTable[i].changeAwareState = ATTS_CLIENT_CHANGE_PENDING_AWARE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if database hash update is in progress.
|
||||
*
|
||||
* \return \ref TRUE if update in progress, \ref FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t attsCsfGetHashUpdateStatus(void)
|
||||
{
|
||||
return attsCsfCb.isHashUpdating;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check client awareness to database hash before sending notification or indication.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle ATT handle.
|
||||
*
|
||||
* \return \ref TRUE if client is aware, otherwise \ref FALSE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t attsCsfIsClientChangeAware(dmConnId_t connId, uint16_t handle)
|
||||
{
|
||||
if ((attsCsfCb.attsCsfTable[connId - 1].csf & ATTS_CSF_ROBUST_CACHING) &&
|
||||
(attsCsfCb.attsCsfTable[connId - 1].changeAwareState == ATTS_CLIENT_CHANGE_UNAWARE) &&
|
||||
(handle != GATT_SC_HDL))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Update client change-aware state based on protocol event.
|
||||
*
|
||||
* \param connId Connection handle.
|
||||
* \param opcode ATT PDU type.
|
||||
* \param pPacket Data packet from L2CAP.
|
||||
*
|
||||
* \return \ref ATT_SUCCESS if client is change-aware, else \ref ATT_ERR_DATABASE_OUT_OF_SYNC.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t attsCsfActClientState(uint16_t handle, uint8_t opcode, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
attsCsfRec_t *pRec;
|
||||
|
||||
/* PDU which do not operate on att handles are handled agnostically of the client's state. */
|
||||
if (opcode == ATT_PDU_MTU_REQ || opcode == ATT_PDU_VALUE_CNF)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
pRec = &attsCsfCb.attsCsfTable[handle];
|
||||
|
||||
/* If the client is change-unaware */
|
||||
if (pRec->changeAwareState == ATTS_CLIENT_CHANGE_UNAWARE)
|
||||
{
|
||||
/* If not a command */
|
||||
if ((opcode & ATT_PDU_MASK_COMMAND) == 0)
|
||||
{
|
||||
/* Note: there is no need to call back to the application here. The application only
|
||||
* needs to know when a transition to or from the change-aware state occurs.
|
||||
*/
|
||||
|
||||
/* Move client change-aware state to pending */
|
||||
pRec->changeAwareState = ATTS_CLIENT_CHANGE_PENDING_AWARE;
|
||||
|
||||
ATT_TRACE_INFO2("ConnId %d change aware state is %d", handle + 1,
|
||||
ATTS_CLIENT_CHANGE_PENDING_AWARE);
|
||||
}
|
||||
|
||||
/* If this is a command or the Client has indicated Robust Caching, set an error so that
|
||||
* this command or request is not processed.
|
||||
*/
|
||||
if ((opcode & ATT_PDU_MASK_COMMAND) ||
|
||||
(pRec->csf & ATTS_CSF_ROBUST_CACHING))
|
||||
{
|
||||
/* return a database out of sync error */
|
||||
err = ATT_ERR_DATABASE_OUT_OF_SYNC;
|
||||
}
|
||||
}
|
||||
else if (pRec->changeAwareState == ATTS_CLIENT_CHANGE_PENDING_AWARE)
|
||||
{
|
||||
/* If not a command */
|
||||
if ((opcode & ATT_PDU_MASK_COMMAND) == 0)
|
||||
{
|
||||
/* Move client change-aware state to aware */
|
||||
pRec->changeAwareState = ATTS_CLIENT_CHANGE_AWARE;
|
||||
|
||||
ATT_TRACE_INFO2("ConnId %d change aware state is %d", handle + 1, ATTS_CLIENT_CHANGE_AWARE);
|
||||
|
||||
/* Callback to application to store updated awareness, if bonded. */
|
||||
if (attsCsfCb.writeCback != NULL)
|
||||
{
|
||||
attsCsfCb.writeCback(handle + 1, pRec->changeAwareState, &pRec->csf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Return an error so that command is not processed. */
|
||||
err = ATT_ERR_DATABASE_OUT_OF_SYNC;
|
||||
}
|
||||
}
|
||||
|
||||
/* If this is Read by Type request */
|
||||
if (opcode == ATT_PDU_READ_TYPE_REQ)
|
||||
{
|
||||
uint16_t uuid;
|
||||
|
||||
/* Extract UUID: Skip L2C, ATT Header and 4 byte handle range */
|
||||
BYTES_TO_UINT16(uuid, (pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN + 4));
|
||||
|
||||
/* If this is a Read By Type Request of the Database Hash characteristic value */
|
||||
if (uuid == ATT_UUID_DATABASE_HASH)
|
||||
{
|
||||
err = ATT_SUCCESS;
|
||||
|
||||
/* Reading the hash during a hash update causes the new hash to be returned and counts
|
||||
* towards the peer's progression towards a change-aware state.
|
||||
*/
|
||||
if (attsCsfCb.isHashUpdating)
|
||||
{
|
||||
/* This read will not be processed until after the hash update completes, so this read
|
||||
* request shall be counted as a move from change-unaware to chang-aware pending.
|
||||
*/
|
||||
pRec->changeAwareState = ATTS_CLIENT_CHANGE_AWARE_DB_READ_PENDING;
|
||||
|
||||
ATT_TRACE_INFO2("ConnId %d change aware state is %d", handle + 1,
|
||||
ATTS_CLIENT_CHANGE_AWARE_DB_READ_PENDING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (err == ATT_ERR_DATABASE_OUT_OF_SYNC)
|
||||
{
|
||||
ATT_TRACE_INFO2("ConnId %d out of sync, PDU with opcode 0x%02x ignored!", handle + 1, opcode);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Update a client's state of awareness to a change in the database.
|
||||
*
|
||||
* \param connId DM connection ID. if \ref DM_CONN_ID_NONE, sets the state for all connected
|
||||
* clients.
|
||||
* \param state The state of awareness to a change, see ::attClientAwareStates.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note A callback to application is not needed as it is expected the caller (i.e. the
|
||||
* application) will have updated all persistent records prior to calling this function.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCsfSetClientsChangeAwarenessState(dmConnId_t connId, uint8_t state)
|
||||
{
|
||||
if (connId == DM_CONN_ID_NONE)
|
||||
{
|
||||
for (uint8_t i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
if (attsCsfCb.attsCsfTable[i].changeAwareState == ATTS_CLIENT_CHANGE_AWARE_DB_READ_PENDING)
|
||||
{
|
||||
attsCsfCb.attsCsfTable[i].changeAwareState = ATTS_CLIENT_CHANGE_PENDING_AWARE;
|
||||
}
|
||||
else
|
||||
{
|
||||
attsCsfCb.attsCsfTable[i].changeAwareState = state;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
attsCsfCb.attsCsfTable[connId - 1].changeAwareState = state;
|
||||
|
||||
ATT_TRACE_INFO2("ConnId %d change aware state is %d", connId, state);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the client supported features for a connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param changeAwareState The state of awareness to a change in the database.
|
||||
* \param pCsf Pointer to the client supported features value to cache. \ref NULL or
|
||||
* buffer of length \ref ATT_CSF_LEN.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCsfConnOpen(dmConnId_t connId, uint8_t changeAwareState, uint8_t *pCsf)
|
||||
{
|
||||
if (pCsf != NULL)
|
||||
{
|
||||
attsCsfCb.attsCsfTable[connId - 1].changeAwareState = changeAwareState;
|
||||
memcpy(&attsCsfCb.attsCsfTable[connId - 1].csf, pCsf, ATT_CSF_LEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Note: this set client to the change-aware state. */
|
||||
memset(&attsCsfCb.attsCsfTable[connId - 1], 0, sizeof(attsCsfRec_t));
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register callback.
|
||||
*
|
||||
* \param writeCback Application callback for when features or status change.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCsfRegister(attsCsfWriteCback_t writeCback)
|
||||
{
|
||||
attsCsfCb.writeCback = writeCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATTS client supported features module.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCsfInit(void)
|
||||
{
|
||||
attsCsfCb.isHashUpdating = FALSE;
|
||||
attsCsfCb.writeCback = NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief GATT write of client supported feature characteristic value.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param offset offset into csf characteristic.
|
||||
* \param valueLen length of write in bytes.
|
||||
* \param pValue Pointer to client's supported features characteristic value.
|
||||
*
|
||||
* \return \ref ATT_SUCCESS is successful, \ref ATT_ERR_VALUE_NOT_ALLOWED if any supported
|
||||
* features are flipped from 1 to 0.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttsCsfWriteFeatures(dmConnId_t connId, uint16_t offset, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attsCsfRec_t *pCsfRec = &attsCsfCb.attsCsfTable[connId - 1];
|
||||
|
||||
/* future parameter in case the client supported features characteristic becomes a multi-octet
|
||||
* structure.
|
||||
*/
|
||||
(void)offset;
|
||||
|
||||
if (valueLen > ATT_CSF_LEN)
|
||||
{
|
||||
return ATT_ERR_LENGTH;
|
||||
}
|
||||
|
||||
/* A client can not clear any bits it has set. */
|
||||
if ((pCsfRec->csf & *pValue) < pCsfRec->csf)
|
||||
{
|
||||
return ATT_ERR_VALUE_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
pCsfRec->csf = *pValue & ATTS_CSF_OCT0_FEATURES;
|
||||
|
||||
ATT_TRACE_INFO2("connId %d updated csf to 0x%02x", connId, pCsfRec->csf);
|
||||
|
||||
/* Callback to application to store updated features, if bonded. */
|
||||
if (attsCsfCb.writeCback != NULL)
|
||||
{
|
||||
attsCsfCb.writeCback(connId, pCsfRec->changeAwareState, &pCsfRec->csf);
|
||||
}
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get client supported feature record.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCsfOut Output parameter for client supported features buffer.
|
||||
* \param pCsfOutLen Length of output parameter buffer.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCsfGetFeatures(dmConnId_t connId, uint8_t *pCsfOut, uint8_t pCsfOutLen)
|
||||
{
|
||||
if (pCsfOutLen <= ATT_CSF_LEN)
|
||||
{
|
||||
memcpy(pCsfOut, &attsCsfCb.attsCsfTable[connId - 1].csf, pCsfOutLen);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get client state of awareness to a change in the database.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return Client's change-aware state.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttsCsfGetChangeAwareState(dmConnId_t connId)
|
||||
{
|
||||
return attsCsfCb.attsCsfTable[connId - 1].changeAwareState;
|
||||
}
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Dynamic ATT services and attributes.
|
||||
*
|
||||
* Copyright (c) 2017-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_math.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Configurable number of bytes in the Dynamic Service and Attribute Heap */
|
||||
#ifndef ATTS_DYN_HEAP_SIZE
|
||||
#define ATTS_DYN_HEAP_SIZE 1280
|
||||
#endif
|
||||
|
||||
/* Configurable memory byte alignment Dynamic Service and Attribute Heap */
|
||||
#ifndef ATTS_DYN_ALIGNMENT
|
||||
#define ATTS_DYN_ALIGNMENT 4
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Dynamic service and attributes control block */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t numServices;
|
||||
uint8_t *pNextBuffer;
|
||||
} attsDynCb_t;
|
||||
|
||||
/* Dynamic service group control block */
|
||||
typedef struct
|
||||
{
|
||||
attsGroup_t group;
|
||||
uint16_t currentAttr;
|
||||
} attsDynGroupCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Dynamic Service and Attribute Heap */
|
||||
static uint8_t attsDynHeap[ATTS_DYN_HEAP_SIZE];
|
||||
|
||||
/* Dynamic Service and Attribute control block */
|
||||
static attsDynCb_t attsDynCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Allocate memory from the Dynamic Service and Attribute Heap.
|
||||
*
|
||||
* \param size Size of buffer to allocate in bytes
|
||||
*
|
||||
* \return Allocated buffer or NULL if failed to allocate a buffer.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void *attsDynAlloc(uint16_t size)
|
||||
{
|
||||
uint8_t *pMem = attsDynCb.pNextBuffer;
|
||||
|
||||
/* Verify enough space in heap for buffer */
|
||||
if (pMem + size <= attsDynHeap + ATTS_DYN_HEAP_SIZE)
|
||||
{
|
||||
#if ATTS_DYN_ALIGNMENT > 1
|
||||
/* Increase size if size not a multiple of the memory alignment */
|
||||
if (size % ATTS_DYN_ALIGNMENT)
|
||||
{
|
||||
size += ATTS_DYN_ALIGNMENT - (size % ATTS_DYN_ALIGNMENT);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set the next buffer location */
|
||||
attsDynCb.pNextBuffer += size;
|
||||
|
||||
return pMem;
|
||||
}
|
||||
|
||||
/* Out of heap */
|
||||
WSF_ASSERT(FALSE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the Dynamic ATT Service subsystem.
|
||||
*
|
||||
* \param None
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsDynInit()
|
||||
{
|
||||
attsDynCb.numServices = 0;
|
||||
attsDynCb.pNextBuffer = attsDynHeap;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Dynamically create an ATT Service at runtime.
|
||||
*
|
||||
* \param startHandle Starting attribute handle in the service
|
||||
* \param endHandle Last attribute handle in the service
|
||||
*
|
||||
* \return Service Handle.
|
||||
*
|
||||
* \note It is recommended this function only be used when no connections are open and the
|
||||
* device is not in a connectable mode.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void *AttsDynCreateGroup(uint16_t startHandle, uint16_t endHandle)
|
||||
{
|
||||
attsDynGroupCb_t *pGroup = NULL;
|
||||
|
||||
/* Allocate memory for the service control block */
|
||||
pGroup = attsDynAlloc(sizeof(attsDynGroupCb_t));
|
||||
WSF_ASSERT(pGroup);
|
||||
|
||||
if (pGroup != NULL)
|
||||
{
|
||||
|
||||
/* Initialize the service group */
|
||||
pGroup->group.startHandle = startHandle;
|
||||
pGroup->group.endHandle = endHandle;
|
||||
pGroup->group.readCback = NULL;
|
||||
pGroup->group.writeCback = NULL;
|
||||
|
||||
/* Allocate memory for the attributes */
|
||||
pGroup->group.pAttr = attsDynAlloc(sizeof(attsAttr_t) * (endHandle - startHandle + 1));
|
||||
WSF_ASSERT(pGroup->group.pAttr);
|
||||
|
||||
pGroup->currentAttr = 0;
|
||||
attsDynCb.numServices++;
|
||||
}
|
||||
|
||||
return pGroup;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Dynamically delete an ATT Service at runtime.
|
||||
*
|
||||
* \param pSvcHandle Service handle returned by AttsDynCreateGroup
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note It is recommended this function only be used when no connections are open and the
|
||||
* device is not in a connectable mode.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsDynDeleteGroup(void *pSvcHandle)
|
||||
{
|
||||
attsDynGroupCb_t *pGroup = pSvcHandle;
|
||||
|
||||
WSF_ASSERT(attsDynCb.numServices);
|
||||
WSF_ASSERT(pGroup);
|
||||
|
||||
/* Remove the group */
|
||||
AttsRemoveGroup(pGroup->group.startHandle);
|
||||
|
||||
attsDynCb.numServices--;
|
||||
|
||||
/* If there are no more dynamic groups, initialize the subsystem to free the memory in the heap */
|
||||
if (attsDynCb.numServices == 0)
|
||||
{
|
||||
AttsDynInit();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register callback functions for a dynamic ATT Service at runtime.
|
||||
*
|
||||
* \param pSvcHandle Service handle returned by AttsDynCreateGroup
|
||||
* \param readCback Read callback function.
|
||||
* \param writeCback Write callback function.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note It is recommended this function only be used when no connections are open and the
|
||||
* device is not in a connectable mode.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsDynRegister(void *pSvcHandle, attsReadCback_t readCback, attsWriteCback_t writeCback)
|
||||
{
|
||||
attsDynGroupCb_t *pGroup = pSvcHandle;
|
||||
|
||||
WSF_ASSERT(pGroup);
|
||||
|
||||
pGroup->group.readCback = readCback;
|
||||
pGroup->group.writeCback = writeCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Dynamically add an attribute to a dynamic ATT Services at app initialization.
|
||||
*
|
||||
* \param pSvcHandle Service handle returned by AttsDynCreateGroup
|
||||
* \param pUuid Constant UUID
|
||||
* \param pValue Initial value of attribute (copied into attribute memory)
|
||||
* \param len Length of pValue in bytes
|
||||
* \param maxLen Maximum length of the attribute in bytes
|
||||
* \param settings Attribute settings
|
||||
* \param permissions Attribute permissions
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note It is recommended this function only be used when no connections are open and the
|
||||
* device is not in a connectable mode.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsDynAddAttr(void *pSvcHandle, const uint8_t *pUuid, const uint8_t *pValue, uint16_t len,
|
||||
const uint16_t maxLen, uint8_t settings, uint8_t permissions)
|
||||
{
|
||||
attsAttr_t *pAttr;
|
||||
attsDynGroupCb_t *pGroup = pSvcHandle;
|
||||
uint16_t handle = pGroup->group.startHandle + pGroup->currentAttr++;
|
||||
|
||||
/* Verify inputs */
|
||||
WSF_ASSERT(handle <= pGroup->group.endHandle);
|
||||
WSF_ASSERT(pUuid);
|
||||
WSF_ASSERT(len <= maxLen);
|
||||
|
||||
pAttr = pGroup->group.pAttr + (handle - pGroup->group.startHandle);
|
||||
|
||||
|
||||
/* Allocate a buffer for the length of the attribute */
|
||||
pAttr->pLen = attsDynAlloc(sizeof(uint16_t));
|
||||
WSF_ASSERT(pAttr->pLen);
|
||||
|
||||
if (pAttr->pLen == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate a buffer for the value of the attribute */
|
||||
pAttr->pValue = attsDynAlloc(maxLen);
|
||||
WSF_ASSERT(pAttr->pValue);
|
||||
|
||||
if (pAttr->pValue == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the attribute values */
|
||||
pAttr->pUuid = pUuid;
|
||||
pAttr->maxLen = maxLen;
|
||||
pAttr->settings = settings;
|
||||
pAttr->permissions = permissions;
|
||||
|
||||
|
||||
|
||||
if (pValue)
|
||||
{
|
||||
/* Copy the initial value */
|
||||
memcpy(pAttr->pValue, pValue, len);
|
||||
*pAttr->pLen = len;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No initial value, zero value and length */
|
||||
memset(pAttr->pValue, 0, maxLen);
|
||||
*pAttr->pLen = 0;
|
||||
}
|
||||
|
||||
/* Add the service when the last attribute has been added */
|
||||
if (handle == pGroup->group.endHandle)
|
||||
{
|
||||
AttsAddGroup(&pGroup->group);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Dynamically add an attribute with a constant value to a dynamic ATT Services.
|
||||
*
|
||||
* \param pSvcHandle Service handle returned by AttsDynCreateGroup
|
||||
* \param pUuid Constant UUID
|
||||
* \param pValue Pointer to constant attribute memory
|
||||
* \param len Length of pValue in bytes
|
||||
* \param settings Attribute settings
|
||||
* \param permissions Attribute permissions
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note It is recommended this function only be used when no connections are open and the
|
||||
* device is not in a connectable mode.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsDynAddAttrConst(void *pSvcHandle, const uint8_t *pUuid, const uint8_t *pValue,
|
||||
const uint16_t len, uint8_t settings, uint8_t permissions)
|
||||
{
|
||||
attsAttr_t *pAttr;
|
||||
attsDynGroupCb_t *pGroup = pSvcHandle;
|
||||
uint16_t handle = pGroup->group.startHandle + pGroup->currentAttr++;
|
||||
|
||||
/* Verify inputs */
|
||||
WSF_ASSERT(handle <= pGroup->group.endHandle);
|
||||
WSF_ASSERT(pValue);
|
||||
WSF_ASSERT(pUuid);
|
||||
|
||||
pAttr = pGroup->group.pAttr + (handle - pGroup->group.startHandle);
|
||||
|
||||
/* Allocate a buffer for the length of the attribute */
|
||||
pAttr->pLen = attsDynAlloc(sizeof(uint16_t));
|
||||
WSF_ASSERT(pAttr->pLen);
|
||||
|
||||
if (pAttr->pLen == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the attribute values */
|
||||
*pAttr->pLen = len;
|
||||
pAttr->pUuid = pUuid;
|
||||
pAttr->pValue = (uint8_t *) pValue;
|
||||
pAttr->maxLen = len;
|
||||
pAttr->settings = settings;
|
||||
pAttr->permissions = permissions;
|
||||
|
||||
/* Add the service when the last attribute has been added */
|
||||
if (handle == pGroup->group.endHandle)
|
||||
{
|
||||
AttsAddGroup(&pGroup->group);
|
||||
}
|
||||
}
|
||||
+654
@@ -0,0 +1,654 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server indication and notification functions.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
static void attsIndConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt);
|
||||
static void attsIndMsgCback(attsApiMsg_t *pMsg);
|
||||
static void attsIndCtrlCback(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Interface to ATT */
|
||||
static const attFcnIf_t attsIndFcnIf =
|
||||
{
|
||||
attEmptyDataCback,
|
||||
attsIndCtrlCback,
|
||||
(attMsgHandler_t) attsIndMsgCback,
|
||||
attsIndConnCback
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
attsIndCb_t attsIndCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the ATTS connection control block connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to connection control block or NULL if not in use.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static attsIndCcb_t *attsIndCcbByConnId(dmConnId_t connId)
|
||||
{
|
||||
if (DmConnInUse(connId))
|
||||
{
|
||||
return &attsIndCb.ccb[connId - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN1("atts ccb not in use: %d", connId);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if application callback is pending for indication or a given notification, or
|
||||
* the maximum number of simultaneous notifications has been reached.
|
||||
*
|
||||
* \param pCcb ATTS ind control block.
|
||||
* \param pPkt Pointer to packet.
|
||||
*
|
||||
* \return TRUE if app callback's pending or max number of simultaneous notifications reached.
|
||||
* FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t attsPendIndNtfHandle(attsIndCcb_t *pCcb, attsPktParam_t *pPkt)
|
||||
{
|
||||
uint8_t opcode;
|
||||
uint8_t pendNtfs;
|
||||
uint8_t i;
|
||||
|
||||
/* extract opcode */
|
||||
opcode = *(((uint8_t *) pPkt) + L2C_PAYLOAD_START);
|
||||
|
||||
/* if indication */
|
||||
if (opcode == ATT_PDU_VALUE_IND)
|
||||
{
|
||||
/* see if callback pending for indication */
|
||||
return (pCcb->pendIndHandle == ATT_HANDLE_NONE) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
/* initialize number of notification callbacks pending */
|
||||
pendNtfs = 0;
|
||||
|
||||
for (i = 0; i < ATT_NUM_SIMUL_NTF; i++)
|
||||
{
|
||||
/* if callback pending for notification */
|
||||
if (pCcb->pendNtfHandle[i] != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* if callback pending for this handle */
|
||||
if (pCcb->pendNtfHandle[i] == pPkt->handle)
|
||||
{
|
||||
/* callback pending for this notification */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
pendNtfs++;
|
||||
}
|
||||
}
|
||||
|
||||
/* no callback is pending for this notification but see if the maximum number of simultaneous
|
||||
notifications has been reached */
|
||||
return (pendNtfs < ATT_NUM_SIMUL_NTF) ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set pending notification callback for a given handle.
|
||||
*
|
||||
* \param pCcb ATTS ind control block.
|
||||
* \param handle Notification handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsSetPendNtfHandle(attsIndCcb_t *pCcb, uint16_t handle)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < ATT_NUM_SIMUL_NTF; i++)
|
||||
{
|
||||
/* if entry free */
|
||||
if (pCcb->pendNtfHandle[i] == ATT_HANDLE_NONE)
|
||||
{
|
||||
/* set pending notification handle */
|
||||
pCcb->pendNtfHandle[i] = handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute application callback function with confirmation event.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param status Callback event status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsExecCallback(dmConnId_t connId, uint16_t handle, uint8_t status)
|
||||
{
|
||||
attExecCallback(connId, ATTS_HANDLE_VALUE_CNF, handle, status, 0);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Call pending indication or/and notification(s) application callback.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCcb ATTS ind control block.
|
||||
* \param status Callback event status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsIndNtfCallback(dmConnId_t connId, attsIndCcb_t *pCcb, uint8_t status)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* if pending indication callback */
|
||||
if (pCcb->pendIndHandle != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* call indication callback with status */
|
||||
attsExecCallback(connId, pCcb->pendIndHandle, status);
|
||||
pCcb->pendIndHandle = ATT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
/* if any pending notification callback */
|
||||
for (i = 0; i < ATT_NUM_SIMUL_NTF; i++)
|
||||
{
|
||||
if (pCcb->pendNtfHandle[i] != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* call notification callback with status */
|
||||
attsExecCallback(connId, pCcb->pendNtfHandle[i], status);
|
||||
pCcb->pendNtfHandle[i] = ATT_HANDLE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up and send an attribute server indication or notification.
|
||||
*
|
||||
* \param pCcb ATTS ind control block.
|
||||
* \param connId DM connection ID.
|
||||
* \param pPkt Pointer to packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsSetupMsg(attsIndCcb_t *pCcb, dmConnId_t connId, attsPktParam_t *pPkt)
|
||||
{
|
||||
uint8_t opcode;
|
||||
uint16_t handle;
|
||||
|
||||
/* extract opcode */
|
||||
opcode = *(((uint8_t *) pPkt) + L2C_PAYLOAD_START);
|
||||
|
||||
/* copy handle (it may be overwritten in pPkt) */
|
||||
handle = pPkt->handle;
|
||||
|
||||
/* send pdu */
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->pMainCcb->handle, pPkt->len, (uint8_t *) pPkt);
|
||||
|
||||
/* if indication store handle and start timer */
|
||||
if (opcode == ATT_PDU_VALUE_IND)
|
||||
{
|
||||
pCcb->outIndHandle = pCcb->pendIndHandle = handle;
|
||||
pCcb->outIndTimer.msg.event = ATTS_MSG_IND_TIMEOUT;
|
||||
WsfTimerStartSec(&pCcb->outIndTimer, pAttCfg->transTimeout);
|
||||
}
|
||||
/* else if a notification and flow not disabled call callback now */
|
||||
else if (!(pCcb->pMainCcb->control & ATT_CCB_STATUS_FLOW_DISABLED))
|
||||
{
|
||||
attsExecCallback(connId, handle, ATT_SUCCESS);
|
||||
}
|
||||
/* else set pending notification callback for this handle */
|
||||
else
|
||||
{
|
||||
attsSetPendNtfHandle(pCcb, handle);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection callback for ATTS indications/notifications.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsIndConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt)
|
||||
{
|
||||
attsIndCcb_t *pIndCcb;
|
||||
uint8_t status;
|
||||
|
||||
/* if connection opened */
|
||||
if (pDmEvt->hdr.event == DM_CONN_OPEN_IND)
|
||||
{
|
||||
|
||||
}
|
||||
/* if connection closed */
|
||||
else if (pDmEvt->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* set status */
|
||||
if (pDmEvt->connClose.hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
status = pDmEvt->connClose.reason + ATT_HCI_ERR_BASE;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = pDmEvt->connClose.hdr.status + ATT_HCI_ERR_BASE;
|
||||
}
|
||||
|
||||
/* get server control block directly */
|
||||
pIndCcb = &attsIndCb.ccb[pCcb->connId - 1];
|
||||
|
||||
/* if outstanding indication */
|
||||
if (pIndCcb->outIndHandle != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* stop timer */
|
||||
WsfTimerStop(&pIndCcb->outIndTimer);
|
||||
pIndCcb->outIndHandle = ATT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
/* call pending indication and notification callback */
|
||||
attsIndNtfCallback(pCcb->connId, pIndCcb, status);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF message handler callback for ATTS indications/notifications.
|
||||
*
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsIndMsgCback(attsApiMsg_t *pMsg)
|
||||
{
|
||||
attsIndCcb_t *pCcb;
|
||||
|
||||
/* get CCB and verify connection still in use */
|
||||
if ((pCcb = attsIndCcbByConnId((dmConnId_t) pMsg->hdr.param)) == NULL)
|
||||
{
|
||||
/* if message has a packet buffer free packet buffer */
|
||||
if (pMsg->hdr.event == ATTS_MSG_API_VALUE_IND_NTF)
|
||||
{
|
||||
WsfMsgFree(pMsg->pPkt);
|
||||
}
|
||||
|
||||
/* ignore if connection not in use */
|
||||
return;
|
||||
}
|
||||
|
||||
/* if an API message to send packet */
|
||||
if (pMsg->hdr.event == ATTS_MSG_API_VALUE_IND_NTF)
|
||||
{
|
||||
/* verify no API message already pending */
|
||||
if (attsPendIndNtfHandle(pCcb, pMsg->pPkt))
|
||||
{
|
||||
/* call callback with failure status and free packet buffer */
|
||||
attsExecCallback((dmConnId_t) pMsg->hdr.param, pMsg->pPkt->handle, ATT_ERR_OVERFLOW);
|
||||
WsfMsgFree(pMsg->pPkt);
|
||||
}
|
||||
/* otherwise ready to send; set up request */
|
||||
else
|
||||
{
|
||||
attsSetupMsg(pCcb, (dmConnId_t) pMsg->hdr.param, pMsg->pPkt);
|
||||
}
|
||||
}
|
||||
/* else if indication timeout */
|
||||
else if (pMsg->hdr.event == ATTS_MSG_IND_TIMEOUT)
|
||||
{
|
||||
/* if outstanding indication */
|
||||
if (pCcb->outIndHandle != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* clear out handle */
|
||||
pCcb->outIndHandle = ATT_HANDLE_NONE;
|
||||
|
||||
/* call callback with timeout error */
|
||||
attsExecCallback((dmConnId_t) pMsg->hdr.param, pCcb->pendIndHandle, ATT_ERR_TIMEOUT);
|
||||
pCcb->pendIndHandle = ATT_HANDLE_NONE;
|
||||
pCcb->pMainCcb->control |= ATT_CCB_STATUS_TX_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2CAP control callback.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsIndCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
attsIndCcb_t *pCcb;
|
||||
|
||||
/* note this function is currently only called when flow is enabled */
|
||||
|
||||
/* get CCB */
|
||||
if ((pCcb = attsIndCcbByConnId((dmConnId_t) pMsg->param)) != NULL)
|
||||
{
|
||||
/* call pending indication and notification callback */
|
||||
attsIndNtfCallback((dmConnId_t) pMsg->param, pCcb, ATT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol Handle Value Indication or Notification.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
* \param opcode Opcode for notification or indication.
|
||||
* \param zeroCpy Whether or not to copy attribute value data into new buffer.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsHandleValueIndNtf(dmConnId_t connId, uint16_t handle, uint16_t valueLen,
|
||||
uint8_t *pValue, uint8_t opcode, bool_t zeroCpy)
|
||||
{
|
||||
attsIndCcb_t *pCcb;
|
||||
uint16_t mtu;
|
||||
bool_t transTimedOut;
|
||||
bool_t pktSent = FALSE;
|
||||
|
||||
WsfTaskLock();
|
||||
|
||||
/* get CCB and verify connection still in use */
|
||||
if ((pCcb = attsIndCcbByConnId(connId)) != NULL)
|
||||
{
|
||||
/* get MTU size */
|
||||
mtu = pCcb->pMainCcb->mtu;
|
||||
transTimedOut = !!(pCcb->pMainCcb->control & ATT_CCB_STATUS_TX_TIMEOUT);
|
||||
}
|
||||
/* else connection not in use */
|
||||
else
|
||||
{
|
||||
/* MTU size unknown */
|
||||
mtu = 0;
|
||||
transTimedOut = FALSE;
|
||||
}
|
||||
|
||||
WsfTaskUnlock();
|
||||
|
||||
/* if MTU size known for connection */
|
||||
if (mtu > 0)
|
||||
{
|
||||
/* if no transaction's timed out */
|
||||
if (!transTimedOut)
|
||||
{
|
||||
/* Only send notifications and indications if client is aware of any database changes. */
|
||||
if (attsCsfIsClientChangeAware(connId, handle))
|
||||
{
|
||||
/* if packet length is less than or equal to negotiated MTU */
|
||||
if ((valueLen + ATT_VALUE_NTF_LEN) <= mtu)
|
||||
{
|
||||
attsApiMsg_t *pMsg;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate message buffer */
|
||||
if ((pMsg = WsfMsgAlloc(sizeof(attsApiMsg_t))) != NULL)
|
||||
{
|
||||
/* set parameters */
|
||||
pMsg->hdr.param = connId;
|
||||
pMsg->hdr.event = ATTS_MSG_API_VALUE_IND_NTF;
|
||||
|
||||
if (zeroCpy)
|
||||
{
|
||||
/* use packet buffer provided */
|
||||
pMsg->pPkt = (attsPktParam_t *)(pValue - ATT_VALUE_IND_NTF_BUF_LEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* allocate packet buffer */
|
||||
pMsg->pPkt = attMsgAlloc(ATT_VALUE_IND_NTF_BUF_LEN + valueLen);
|
||||
}
|
||||
|
||||
if (pMsg->pPkt != NULL)
|
||||
{
|
||||
/* set data length and handle (ind and ntf have same header length) */
|
||||
pMsg->pPkt->len = ATT_VALUE_IND_LEN + valueLen;
|
||||
pMsg->pPkt->handle = handle;
|
||||
|
||||
/* build packet */
|
||||
p = (uint8_t *)pMsg->pPkt + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, opcode);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
if (!zeroCpy)
|
||||
{
|
||||
memcpy(p, pValue, valueLen);
|
||||
}
|
||||
|
||||
/* send message */
|
||||
WsfMsgSend(attCb.handlerId, pMsg);
|
||||
pktSent = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* free message buffer if packet buffer alloc failed */
|
||||
WsfMsgFree(pMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* packet length exceeds MTU size */
|
||||
else
|
||||
{
|
||||
/* call callback with failure status */
|
||||
attsExecCallback(connId, handle, ATT_ERR_MTU_EXCEEDED);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
/* transaction's timed out */
|
||||
{
|
||||
/* call callback with failure status */
|
||||
attsExecCallback(connId, handle, ATT_ERR_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/* if packet wasn't sent and it's a zero-copy packet */
|
||||
if (!pktSent && zeroCpy)
|
||||
{
|
||||
/* free packet buffer provided */
|
||||
AttMsgFree(pValue, opcode);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process received handle value confirm packet.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcValueCnf(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
attsIndCcb_t *pIndCcb;
|
||||
|
||||
/* get server indication CCB */
|
||||
if ((pIndCcb = attsIndCcbByConnId(pCcb->connId)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* if an outstanding indication */
|
||||
if (pIndCcb->outIndHandle != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* clear outstanding indication */
|
||||
pIndCcb->outIndHandle = ATT_HANDLE_NONE;
|
||||
|
||||
/* stop indication timer */
|
||||
WsfTimerStop(&pIndCcb->outIndTimer);
|
||||
|
||||
/* call callback if flow control permits */
|
||||
if (!(pCcb->control & ATT_CCB_STATUS_FLOW_DISABLED))
|
||||
{
|
||||
attsExecCallback(pCcb->connId, pIndCcb->pendIndHandle, ATT_SUCCESS);
|
||||
pIndCcb->pendIndHandle = ATT_HANDLE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATT server for indications/notifications.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsIndInit(void)
|
||||
{
|
||||
uint8_t i;
|
||||
attsIndCcb_t *pCcb;
|
||||
|
||||
/* Initialize control block CCBs */
|
||||
for (i = 0, pCcb = attsIndCb.ccb; i < DM_CONN_MAX; i++, pCcb++)
|
||||
{
|
||||
/* set pointer to main CCB */
|
||||
pCcb->pMainCcb = &attCb.ccb[i];
|
||||
|
||||
/* initialize timer */
|
||||
pCcb->outIndTimer.handlerId = attCb.handlerId;
|
||||
pCcb->outIndTimer.msg.param = i + 1; /* param stores the conn id */
|
||||
}
|
||||
|
||||
/* set up callback interface */
|
||||
attsCb.pInd = &attsIndFcnIf;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol Handle Value Indication.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsHandleValueInd(dmConnId_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attsHandleValueIndNtf(connId, handle, valueLen, pValue, ATT_PDU_VALUE_IND, FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol Handle Value Notification.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsHandleValueNtf(dmConnId_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attsHandleValueIndNtf(connId, handle, valueLen, pValue, ATT_PDU_VALUE_NTF, FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol Handle Value Indication without copying the attribute
|
||||
* value data.
|
||||
*
|
||||
* Note: attribute value buffer 'pValue' must be allocated with AttMsgAlloc().
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsHandleValueIndZeroCpy(dmConnId_t connId, uint16_t handle, uint16_t valueLen,
|
||||
uint8_t *pValue)
|
||||
{
|
||||
attsHandleValueIndNtf(connId, handle, valueLen, pValue, ATT_PDU_VALUE_IND, TRUE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an attribute protocol Handle Value Notification without copying the attribute
|
||||
* value data.
|
||||
*
|
||||
* Note: attribute value buffer 'pValue' must be allocated with AttMsgAlloc().
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsHandleValueNtfZeroCpy(dmConnId_t connId, uint16_t handle, uint16_t valueLen,
|
||||
uint8_t *pValue)
|
||||
{
|
||||
attsHandleValueIndNtf(connId, handle, valueLen, pValue, ATT_PDU_VALUE_NTF, TRUE);
|
||||
}
|
||||
Vendored
+881
@@ -0,0 +1,881 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/wstr.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
#include "att_uuid.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* ATTS control block */
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
static void attsDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket);
|
||||
static void attsConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt);
|
||||
static void attsMsgCback(wsfMsgHdr_t *pMsg);
|
||||
static void attsL2cCtrlCback(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Interface to ATT */
|
||||
static const attFcnIf_t attsFcnIf =
|
||||
{
|
||||
attsDataCback,
|
||||
attsL2cCtrlCback,
|
||||
(attMsgHandler_t) attsMsgCback,
|
||||
attsConnCback
|
||||
};
|
||||
|
||||
/* Minimum PDU lengths, indexed by method */
|
||||
static const uint8_t attsMinPduLen[] =
|
||||
{
|
||||
0, /* ATT_METHOD_ERR */
|
||||
ATT_MTU_REQ_LEN, /* ATT_METHOD_MTU */
|
||||
ATT_FIND_INFO_REQ_LEN, /* ATT_METHOD_FIND_INFO */
|
||||
ATT_FIND_TYPE_REQ_LEN, /* ATT_METHOD_FIND_TYPE */
|
||||
ATT_READ_TYPE_REQ_LEN, /* ATT_METHOD_READ_TYPE */
|
||||
ATT_READ_REQ_LEN, /* ATT_METHOD_READ */
|
||||
ATT_READ_BLOB_REQ_LEN, /* ATT_METHOD_READ_BLOB */
|
||||
ATT_READ_MULT_REQ_LEN + 4, /* ATT_METHOD_READ_MULTIPLE */
|
||||
ATT_READ_GROUP_TYPE_REQ_LEN, /* ATT_METHOD_READ_GROUP_TYPE */
|
||||
ATT_WRITE_REQ_LEN, /* ATT_METHOD_WRITE */
|
||||
ATT_WRITE_CMD_LEN, /* ATT_METHOD_WRITE_CMD */
|
||||
ATT_PREP_WRITE_REQ_LEN, /* ATT_METHOD_PREPARE_WRITE */
|
||||
ATT_EXEC_WRITE_REQ_LEN, /* ATT_METHOD_EXECUTE_WRITE */
|
||||
0, /* ATT_METHOD_VALUE_NTF */
|
||||
0, /* ATT_METHOD_VALUE_IND */
|
||||
ATT_VALUE_CNF_LEN, /* ATT_METHOD_VALUE_CNF */
|
||||
ATT_SIGNED_WRITE_CMD_LEN /* ATT_METHOD_SIGNED_WRITE_CMD */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* PDU processing function lookup table, indexed by method */
|
||||
attsProcFcn_t attsProcFcnTbl[ATT_METHOD_SIGNED_WRITE_CMD+1] =
|
||||
{
|
||||
NULL, /* ATT_METHOD_ERR */
|
||||
attsProcMtuReq, /* ATT_METHOD_MTU */
|
||||
attsProcFindInfoReq, /* ATT_METHOD_FIND_INFO */
|
||||
attsProcFindTypeReq, /* ATT_METHOD_FIND_TYPE */
|
||||
attsProcReadTypeReq, /* ATT_METHOD_READ_TYPE */
|
||||
attsProcReadReq, /* ATT_METHOD_READ */
|
||||
attsProcReadBlobReq, /* ATT_METHOD_READ_BLOB */
|
||||
attsProcReadMultReq, /* ATT_METHOD_READ_MULT */
|
||||
attsProcReadGroupTypeReq, /* ATT_METHOD_READ_GROUP_TYPE */
|
||||
attsProcWrite, /* ATT_METHOD_WRITE */
|
||||
attsProcWrite, /* ATT_METHOD_WRITE_CMD */
|
||||
attsProcPrepWriteReq, /* ATT_METHOD_PREP_WRITE */
|
||||
attsProcExecWriteReq, /* ATT_METHOD_EXEC_WRITE */
|
||||
NULL, /* ATT_METHOD_VALUE_NTF */
|
||||
NULL, /* ATT_METHOD_VALUE_IND */
|
||||
attsProcValueCnf, /* ATT_METHOD_VALUE_CNF */
|
||||
NULL /* ATT_METHOD_SIGNED_WRITE_CMD */
|
||||
};
|
||||
|
||||
/* Control block */
|
||||
attsCb_t attsCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Data callback for ATTS.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsDataCback(uint16_t handle, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t opcode;
|
||||
uint8_t method;
|
||||
uint8_t err;
|
||||
attsProcFcn_t procFcn;
|
||||
attCcb_t *pCcb;
|
||||
uint16_t attHandle;
|
||||
|
||||
/* get connection cb for this handle */
|
||||
if ((pCcb = attCcbByHandle(handle)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse opcode */
|
||||
opcode = *(pPacket + L2C_PAYLOAD_START);
|
||||
|
||||
/* get method */
|
||||
if ((opcode <= ATT_PDU_WRITE_REQ) ||
|
||||
((opcode >= ATT_PDU_PREP_WRITE_REQ) && (opcode <= ATT_PDU_VALUE_CNF)))
|
||||
{
|
||||
method = ATT_OPCODE_2_METHOD(opcode);
|
||||
}
|
||||
else if (opcode == ATT_PDU_WRITE_CMD)
|
||||
{
|
||||
method = ATT_METHOD_WRITE_CMD;
|
||||
}
|
||||
else if (opcode == ATT_PDU_SIGNED_WRITE_CMD)
|
||||
{
|
||||
method = ATT_METHOD_SIGNED_WRITE_CMD;
|
||||
}
|
||||
else
|
||||
{
|
||||
method = ATT_METHOD_ERR;
|
||||
}
|
||||
|
||||
/* ignore packet if write response is pending. */
|
||||
if (pCcb->control & ATT_CCB_STATUS_RSP_PENDING)
|
||||
{
|
||||
if (method != ATT_METHOD_VALUE_CNF)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* check client's status to see if server is allowed to process this PDU. */
|
||||
err = attsCsfActClientState(handle, opcode, pPacket);
|
||||
if (err)
|
||||
{
|
||||
BYTES_TO_UINT16(attHandle, pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
attHandle = ATT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
#if defined(ATTS_ERROR_TEST) && (ATTS_ERROR_TEST == TRUE)
|
||||
if (attCb.errTest != ATT_SUCCESS)
|
||||
{
|
||||
BYTES_TO_UINT16(attHandle, pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN);
|
||||
attsErrRsp(handle, opcode, attHandle, attCb.errTest);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* if no error process request */
|
||||
if (!err)
|
||||
{
|
||||
/* look up processing function */
|
||||
procFcn = attsProcFcnTbl[method];
|
||||
|
||||
/* if method is supported */
|
||||
if (procFcn != NULL)
|
||||
{
|
||||
/* verify length */
|
||||
if (len >= attsMinPduLen[method])
|
||||
{
|
||||
/* execute processing function */
|
||||
(*procFcn)(pCcb, len, pPacket);
|
||||
err = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* invalid PDU length */
|
||||
err = ATT_ERR_INVALID_PDU;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* PDU not supported */
|
||||
err = ATT_ERR_NOT_SUP;
|
||||
}
|
||||
}
|
||||
|
||||
/* if there's an error and an error response can be sent for this opcode */
|
||||
if (err && (opcode != ATT_PDU_MTU_REQ) && (opcode != ATT_PDU_VALUE_CNF) &&
|
||||
((opcode & ATT_PDU_MASK_COMMAND) == 0))
|
||||
{
|
||||
attsErrRsp(handle, opcode, attHandle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Connection callback for ATTS.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsConnCback(attCcb_t *pCcb, dmEvt_t *pDmEvt)
|
||||
{
|
||||
/* if connection closed */
|
||||
if (pDmEvt->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* clear prepare write queue */
|
||||
attsClearPrepWrites(pCcb);
|
||||
|
||||
/* stop service discovery idle timer, if running */
|
||||
if (DmConnCheckIdle(pCcb->connId) & DM_IDLE_ATTS_DISC)
|
||||
{
|
||||
WsfTimerStop(&pCcb->idleTimer);
|
||||
}
|
||||
}
|
||||
|
||||
/* pass event to indication interface */
|
||||
(*attsCb.pInd->connCback)(pCcb, pDmEvt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF message handler callback for ATTS.
|
||||
*
|
||||
* \param pMsg DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsMsgCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* handle service discovery idle timeout */
|
||||
if (pMsg->event == ATTS_MSG_IDLE_TIMEOUT)
|
||||
{
|
||||
/* set channel as idle */
|
||||
DmConnSetIdle((dmConnId_t) pMsg->param, DM_IDLE_ATTS_DISC, DM_CONN_IDLE);
|
||||
}
|
||||
/* pass event to indication interface */
|
||||
else if (pMsg->event <= ATTS_MSG_IND_TIMEOUT)
|
||||
{
|
||||
(*attsCb.pInd->msgCback)(pMsg);
|
||||
}
|
||||
/* pass event to signed data interface */
|
||||
else if (pMsg->event == ATTS_MSG_SIGN_CMAC_CMPL)
|
||||
{
|
||||
(*attsCb.signMsgCback)(pMsg);
|
||||
}
|
||||
else if (pMsg->event == ATTS_MSG_DBH_CMAC_CMPL)
|
||||
{
|
||||
/* handle database hash update */
|
||||
attsProcessDatabaseHashUpdate((secCmacMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief L2C control callback for ATTS.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsL2cCtrlCback(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* pass event to indication interface */
|
||||
(*attsCb.pInd->ctrlCback)(pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send an error response PDU.
|
||||
*
|
||||
* \param handle The connection handle.
|
||||
* \param opcode Opcode of the request that generated this error.
|
||||
* \param attHandle Attribute handle in request, if applicable.
|
||||
* \param reason Error reason.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsErrRsp(uint16_t handle, uint8_t opcode, uint16_t attHandle, uint8_t reason)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
/* allocate buffer */
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_ERR_RSP_LEN)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_ERR_RSP);
|
||||
UINT8_TO_BSTREAM(p, opcode);
|
||||
UINT16_TO_BSTREAM(p, attHandle);
|
||||
UINT8_TO_BSTREAM(p, reason);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, handle, ATT_ERR_RSP_LEN, pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear the prepared write queue.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsClearPrepWrites(attCcb_t *pCcb)
|
||||
{
|
||||
void *pBuf;
|
||||
|
||||
while ((pBuf = WsfQueueDeq(&pCcb->prepWriteQueue)) != NULL)
|
||||
{
|
||||
WsfBufFree(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set connection idle status to busy for service discovery.
|
||||
*
|
||||
* \param pCcb ATT control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsDiscBusy(attCcb_t *pCcb)
|
||||
{
|
||||
if (pAttCfg->discIdleTimeout > 0)
|
||||
{
|
||||
/* set channel as busy */
|
||||
DmConnSetIdle(pCcb->connId, DM_IDLE_ATTS_DISC, DM_CONN_BUSY);
|
||||
|
||||
/* start service discovery idle timer */
|
||||
pCcb->idleTimer.handlerId = attCb.handlerId;
|
||||
pCcb->idleTimer.msg.event = ATTS_MSG_IDLE_TIMEOUT;
|
||||
pCcb->idleTimer.msg.param = pCcb->connId;
|
||||
WsfTimerStartSec(&pCcb->idleTimer, pAttCfg->discIdleTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process updated database hash.
|
||||
*
|
||||
* \param pMsg Event containing the new database hash.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcessDatabaseHashUpdate(secCmacMsg_t *pMsg)
|
||||
{
|
||||
attEvt_t evt;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t dbhCharHandle;
|
||||
|
||||
/* send to application */
|
||||
evt.hdr.event = ATTS_DB_HASH_CALC_CMPL_IND;
|
||||
evt.hdr.status = ATT_SUCCESS;
|
||||
evt.hdr.param = DM_CONN_ID_NONE;
|
||||
|
||||
evt.valueLen = ATT_DATABASE_HASH_LEN;
|
||||
evt.handle = ATT_HANDLE_NONE;
|
||||
evt.continuing = FALSE;
|
||||
evt.mtu = 0;
|
||||
|
||||
/* free plain text buffer */
|
||||
if (pMsg->pPlainText != NULL)
|
||||
{
|
||||
WsfBufFree(pMsg->pPlainText);
|
||||
pMsg->pPlainText = NULL;
|
||||
}
|
||||
|
||||
/* copy in little endian */
|
||||
evt.pValue = pMsg->pCiphertext;
|
||||
|
||||
/* find GATT database handle */
|
||||
dbhCharHandle = attsFindUuidInRange(ATT_HANDLE_START, ATT_HANDLE_MAX, ATT_16_UUID_LEN,
|
||||
(uint8_t *) attGattDbhChUuid, &pAttr, &pGroup);
|
||||
|
||||
if (dbhCharHandle != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* Set hash in service. */
|
||||
AttsSetAttr(dbhCharHandle, SEC_CMAC_HASH_LEN, evt.pValue);
|
||||
}
|
||||
|
||||
/* set hash update complete */
|
||||
attsCsfSetHashUpdateStatus(FALSE);
|
||||
|
||||
attCb.cback(&evt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if any clients are pending on a new database hash value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsCheckPendDbHashReadRsp(void)
|
||||
{
|
||||
for (uint8_t i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
attCcb_t *pCcb = &attCb.ccb[i];
|
||||
|
||||
if (pCcb->pPendDbHashRsp)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_TYPE_RSP);
|
||||
|
||||
/* set length parameter in response message */
|
||||
UINT8_TO_BSTREAM(p, ATT_DATABASE_HASH_LEN + sizeof(uint16_t));
|
||||
|
||||
/* copy result to response message */
|
||||
UINT16_TO_BSTREAM(p, pCcb->pPendDbHashRsp->handle);
|
||||
|
||||
if ((pAttr = attsFindByHandle(pCcb->pPendDbHashRsp->handle, &pGroup)) != NULL)
|
||||
{
|
||||
memcpy(p, pAttr->pValue, *pAttr->pLen);
|
||||
p += *pAttr->pLen;
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, p - (pBuf + L2C_PAYLOAD_START), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->connId, ATT_PDU_READ_TYPE_REQ, pCcb->pPendDbHashRsp->startHandle, ATT_ERR_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->connId, ATT_PDU_READ_TYPE_REQ, pCcb->pPendDbHashRsp->startHandle, ATT_ERR_RESOURCES);
|
||||
}
|
||||
|
||||
/* Free pending state information. */
|
||||
WsfBufFree(pCcb->pPendDbHashRsp);
|
||||
pCcb->pPendDbHashRsp = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Determine if attribute is hashable and return length of hashable data.
|
||||
*
|
||||
* \param pAttr Attribute to check for inclusion in hash.
|
||||
*
|
||||
* \return 0 if not hashable, else length of hashable data in bytes.
|
||||
*
|
||||
* \note Hashable attributes include Primary Service, Secondary Service, Included Service,
|
||||
* Characteristic Declaration and Characteristic Extended Properties which contribute their
|
||||
* ATT handle, ATT type and ATT value. Hashable attributes also include Characteristic User
|
||||
* Description, Client Characteristic Configuration, Server Characteristic Configuration,
|
||||
* Characteristic Format, and Characteristic Aggreate Format which contribute their ATT
|
||||
* handle and ATT type.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t attsIsHashableAttr(attsAttr_t *pAttr)
|
||||
{
|
||||
/* Initialize length to 2 for ATT Handle length */
|
||||
uint16_t length = 2;
|
||||
uint16_t uuid;
|
||||
static bool_t isAttrCharVal = FALSE;
|
||||
|
||||
/* Characteristic values are skipped */
|
||||
if (isAttrCharVal)
|
||||
{
|
||||
isAttrCharVal = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
BYTES_TO_UINT16(uuid, pAttr->pUuid);
|
||||
switch (uuid)
|
||||
{
|
||||
/* Top cases include Attribute Value length */
|
||||
case ATT_UUID_CHARACTERISTIC:
|
||||
/* Set the next characteristic in database to be skipped */
|
||||
isAttrCharVal = TRUE;
|
||||
/* Fallthrough */
|
||||
case ATT_UUID_PRIMARY_SERVICE:
|
||||
case ATT_UUID_SECONDARY_SERVICE:
|
||||
case ATT_UUID_INCLUDE:
|
||||
case ATT_UUID_CHARACTERISTIC_EXT:
|
||||
length += *pAttr->pLen;
|
||||
/* Fallthrough */
|
||||
|
||||
/* All values fall through to include Attribute Type length */
|
||||
case ATT_UUID_CHAR_USER_DESC:
|
||||
case ATT_UUID_CLIENT_CHAR_CONFIG:
|
||||
case ATT_UUID_SERVER_CHAR_CONFIG:
|
||||
case ATT_UUID_AGGREGATE_FORMAT:
|
||||
if (pAttr->settings & ATTS_SET_UUID_128)
|
||||
{
|
||||
length += 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
length += 2;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
length = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATT server.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsInit(void)
|
||||
{
|
||||
/* Initialize control block */
|
||||
WSF_QUEUE_INIT(&attsCb.groupQueue);
|
||||
attsCb.pInd = &attFcnDefault;
|
||||
attsCb.signMsgCback = (attMsgHandler_t) attEmptyHandler;
|
||||
|
||||
/* set up callback interfaces */
|
||||
attCb.pServer = &attsFcnIf;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register an authorization callback with the attribute server.
|
||||
*
|
||||
* \param cback Client callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsAuthorRegister(attsAuthorCback_t cback)
|
||||
{
|
||||
attsCb.authorCback = cback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Create hash from the database string.
|
||||
*
|
||||
* \param pKey Key for hashing.
|
||||
* \param pMsg Plaintext to hash.
|
||||
* \param msgLen Length of Plaintext data.
|
||||
*
|
||||
* \return \ref TRUE if successful, \ref FALSE if not.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AttsHashDatabaseString(uint8_t *pKey, uint8_t *pMsg, uint16_t msgLen)
|
||||
{
|
||||
return SecCmac(pKey, pMsg, msgLen, attCb.handlerId, 0, ATTS_MSG_DBH_CMAC_CMPL);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate database hash from the GATT database.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsCalculateDbHash(void)
|
||||
{
|
||||
uint16_t msgLen = 0;
|
||||
uint8_t *pMsg;
|
||||
attsGroup_t *pGroup = (attsGroup_t *) attsCb.groupQueue.pHead;
|
||||
|
||||
/* Determine length of message. */
|
||||
while (pGroup != NULL)
|
||||
{
|
||||
uint8_t numAttrs = (pGroup->endHandle - pGroup->startHandle) + 1;
|
||||
|
||||
for (attsAttr_t *pAttr = pGroup->pAttr; numAttrs != 0; numAttrs--, pAttr++)
|
||||
{
|
||||
msgLen += attsIsHashableAttr(pAttr);
|
||||
}
|
||||
|
||||
pGroup = pGroup->pNext;
|
||||
}
|
||||
|
||||
/* Allocate buffer for message. */
|
||||
if ((pMsg = WsfBufAlloc(msgLen)) != NULL)
|
||||
{
|
||||
pGroup = (attsGroup_t *)attsCb.groupQueue.pHead;
|
||||
uint8_t hashingKey[16] = { 0, };
|
||||
uint8_t *p = pMsg;
|
||||
|
||||
/* For each service in services */
|
||||
while (pGroup)
|
||||
{
|
||||
uint16_t attHandle = pGroup->startHandle;
|
||||
|
||||
/* For each attribute in the service */
|
||||
for (attsAttr_t *pAttr = pGroup->pAttr; attHandle <= pGroup->endHandle; attHandle++, pAttr++)
|
||||
{
|
||||
uint16_t valLen;
|
||||
uint8_t uuidLen = 2;
|
||||
|
||||
valLen = attsIsHashableAttr(pAttr);
|
||||
if (valLen)
|
||||
{
|
||||
/* Add handle */
|
||||
UINT16_TO_BSTREAM(p, attHandle);
|
||||
|
||||
/* Add attribute type*/
|
||||
if (pAttr->settings & ATTS_SET_UUID_128)
|
||||
{
|
||||
memcpy(p, pAttr->pUuid, 16);
|
||||
p += 16;
|
||||
uuidLen = 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t uuid;
|
||||
BYTES_TO_UINT16(uuid, pAttr->pUuid);
|
||||
UINT16_TO_BSTREAM(p,uuid);
|
||||
}
|
||||
|
||||
/* Add Attribute value if required */
|
||||
if (valLen - (uuidLen + 2))
|
||||
{
|
||||
memcpy(p, pAttr->pValue, *pAttr->pLen);
|
||||
p += *pAttr->pLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pGroup = pGroup->pNext;
|
||||
}
|
||||
|
||||
/* Send to CMAC */
|
||||
if (AttsHashDatabaseString(hashingKey, pMsg, msgLen))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Assert on failure to initiate database hash generation. */
|
||||
WSF_ASSERT(FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add an attribute group to the attribute server.
|
||||
*
|
||||
* \param pGroup Pointer to an attribute group structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsAddGroup(attsGroup_t *pGroup)
|
||||
{
|
||||
attsGroup_t *pElem;
|
||||
attsGroup_t *pPrev = NULL;
|
||||
|
||||
/* task schedule lock */
|
||||
WsfTaskLock();
|
||||
|
||||
pElem = (attsGroup_t *) attsCb.groupQueue.pHead;
|
||||
|
||||
/* iterate over queue sorted by increasing handle value */
|
||||
while (pElem != NULL)
|
||||
{
|
||||
if (pGroup->startHandle < pElem->startHandle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pPrev = pElem;
|
||||
pElem = pElem->pNext;
|
||||
}
|
||||
|
||||
/* insert new group */
|
||||
WsfQueueInsert(&attsCb.groupQueue, pGroup, pPrev);
|
||||
|
||||
/* set database hash update status to true until a new hash is generated */
|
||||
attsCsfSetHashUpdateStatus(TRUE);
|
||||
|
||||
/* task schedule unlock */
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Remove an attribute group from the attribute server.
|
||||
*
|
||||
* \param startHandle Start handle of attribute group to be removed.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsRemoveGroup(uint16_t startHandle)
|
||||
{
|
||||
attsGroup_t *pElem;
|
||||
attsGroup_t *pPrev = NULL;
|
||||
|
||||
/* task schedule lock */
|
||||
WsfTaskLock();
|
||||
|
||||
pElem = (attsGroup_t *) attsCb.groupQueue.pHead;
|
||||
|
||||
/* find group in queue */
|
||||
while (pElem != NULL)
|
||||
{
|
||||
if (pElem->startHandle == startHandle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pPrev = pElem;
|
||||
pElem = pElem->pNext;
|
||||
}
|
||||
|
||||
/* if group found remove from queue */
|
||||
if (pElem != NULL)
|
||||
{
|
||||
WsfQueueRemove(&attsCb.groupQueue, pElem, pPrev);
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN1("AttsRemoveGroup start handle not found: 0x%04x", startHandle);
|
||||
}
|
||||
|
||||
/* set database hash update status to true until a new hash is generated */
|
||||
attsCsfSetHashUpdateStatus(TRUE);
|
||||
|
||||
/* task schedule unlock */
|
||||
WsfTaskUnlock();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set an attribute value in the attribute server.
|
||||
*
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Attribute length.
|
||||
* \param pValue Attribute value.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttsSetAttr(uint16_t handle, uint16_t valueLen, uint8_t *pValue)
|
||||
{
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
WsfTaskLock();
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify write length */
|
||||
if (valueLen > pAttr->maxLen)
|
||||
{
|
||||
err = ATT_ERR_LENGTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set attribute value */
|
||||
memcpy(pAttr->pValue, pValue, valueLen);
|
||||
|
||||
/* set the length if variable length attribute */
|
||||
if ((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0)
|
||||
{
|
||||
*(pAttr->pLen) = valueLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* else attribute not found */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
WsfTaskUnlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get an attribute value in the attribute server. Tasks should be locked before
|
||||
* calling this function and remain locked until pLen and pValue are no longer used.
|
||||
*
|
||||
* \param handle Attribute handle.
|
||||
* \param pLen Returned attribute length pointer.
|
||||
* \param pValue Returned attribute value pointer.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful or other error code if failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AttsGetAttr(uint16_t handle, uint16_t *pLen, uint8_t **pValue)
|
||||
{
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* set length and value pointers */
|
||||
*pLen = *(pAttr->pLen);
|
||||
*pValue = pAttr->pValue;
|
||||
}
|
||||
/* else attribute not found */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief For testing purposes only.
|
||||
*
|
||||
* \param status ATT status
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsErrorTest(uint8_t status)
|
||||
{
|
||||
attCb.errTest = status;
|
||||
}
|
||||
Vendored
+174
@@ -0,0 +1,174 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server main module.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef ATTS_MAIN_H
|
||||
#define ATTS_MAIN_H
|
||||
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "att_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* ATTS event handler messages */
|
||||
enum
|
||||
{
|
||||
ATTS_MSG_IDLE_TIMEOUT = ATTS_MSG_START,
|
||||
ATTS_MSG_API_VALUE_IND_NTF,
|
||||
ATTS_MSG_IND_TIMEOUT,
|
||||
ATTS_MSG_SIGN_CMAC_CMPL,
|
||||
ATTS_MSG_DBH_CMAC_CMPL
|
||||
};
|
||||
|
||||
/*!
|
||||
* Data buffer format for API request messages:
|
||||
*
|
||||
* | attsPktParam_t | ATT request data |
|
||||
* | bytes 0 to 7 | bytes 8 - |
|
||||
*/
|
||||
|
||||
/* API parameters */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t len;
|
||||
uint16_t handle;
|
||||
} attsPktParam_t;
|
||||
|
||||
/* verify attsPktParam_t will work in data buffer format described above */
|
||||
WSF_CT_ASSERT(sizeof(attsPktParam_t) <= L2C_PAYLOAD_START);
|
||||
|
||||
/* API message structure */
|
||||
typedef struct
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
attsPktParam_t *pPkt;
|
||||
} attsApiMsg_t;
|
||||
|
||||
/* Connection control block for indications/notifications */
|
||||
typedef struct
|
||||
{
|
||||
wsfTimer_t outIndTimer; /* Outstanding indication timer */
|
||||
attCcb_t *pMainCcb; /* Pointer to ATT main CCB */
|
||||
uint16_t outIndHandle; /* Waiting for confirm from peer for this indication handle */
|
||||
uint16_t pendIndHandle; /* Callback to application pending for this indication handle */
|
||||
uint16_t pendNtfHandle[ATT_NUM_SIMUL_NTF]; /* Callback to application pending for this notification handle */
|
||||
} attsIndCcb_t;
|
||||
|
||||
/* Control block for indications/notifications */
|
||||
typedef struct
|
||||
{
|
||||
attsIndCcb_t ccb[DM_CONN_MAX];
|
||||
} attsIndCb_t;
|
||||
|
||||
/* Client characteristic configuration descriptor callback type
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param method Read or write.
|
||||
* \param handle Attribute handle of the descriptor.
|
||||
* \param pValue Pointer to the attribute value of the descriptor.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful otherwise error.
|
||||
*/
|
||||
typedef uint8_t (*attsCccFcn_t)(dmConnId_t connId, uint8_t method, uint16_t handle, uint8_t *pValue);
|
||||
|
||||
/* Main control block of the ATTS subsystem */
|
||||
typedef struct
|
||||
{
|
||||
wsfQueue_t groupQueue; /* Queue of attribute groups */
|
||||
attFcnIf_t const *pInd; /* Indication callback interface */
|
||||
attMsgHandler_t signMsgCback; /* Signed data callback interface */
|
||||
attsAuthorCback_t authorCback; /* Authorization callback */
|
||||
attsCccFcn_t cccCback; /* CCC callback */
|
||||
} attsCb_t;
|
||||
|
||||
/* PDU processing function type */
|
||||
typedef void (*attsProcFcn_t)(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
|
||||
/* CSF Control block */
|
||||
typedef struct
|
||||
{
|
||||
attsCsfRec_t attsCsfTable[DM_CONN_MAX]; /* connected clients' supported features record table. */
|
||||
attsCsfWriteCback_t writeCback; /* Write callback. */
|
||||
uint8_t isHashUpdating; /* Database hash update status. */
|
||||
} attsCsfCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* PDU processing function lookup table, indexed by method */
|
||||
extern attsProcFcn_t attsProcFcnTbl[ATT_METHOD_SIGNED_WRITE_CMD+1];
|
||||
|
||||
/* Control block for indications/notifications */
|
||||
extern attsIndCb_t attsIndCb;
|
||||
|
||||
/* Control block */
|
||||
extern attsCb_t attsCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
void attsErrRsp(uint16_t handle, uint8_t opcode, uint16_t attHandle, uint8_t reason);
|
||||
void attsClearPrepWrites(attCcb_t *pCcb);
|
||||
bool_t attsUuidCmp(attsAttr_t *pAttr, uint8_t uuidLen, uint8_t *pUuid);
|
||||
bool_t attsUuid16Cmp(uint8_t *pUuid16, uint8_t uuidLen, uint8_t *pUuid);
|
||||
attsAttr_t *attsFindByHandle(uint16_t handle, attsGroup_t **pAttrGroup);
|
||||
uint16_t attsFindInRange(uint16_t startHandle, uint16_t endHandle, attsAttr_t **pAttr);
|
||||
uint16_t attsFindUuidInRange(uint16_t startHandle, uint16_t endHandle, uint8_t uuidLen,
|
||||
uint8_t *pUuid, attsAttr_t **pAttr, attsGroup_t **pAttrGroup);
|
||||
uint8_t attsPermissions(dmConnId_t connId, uint8_t permit, uint16_t handle, uint8_t permissions);
|
||||
void attsDiscBusy(attCcb_t *pCcb);
|
||||
void attsCheckPendDbHashReadRsp(void);
|
||||
void attsProcessDatabaseHashUpdate(secCmacMsg_t *pMsg);
|
||||
uint16_t attsIsHashableAttr(attsAttr_t *pAttr);
|
||||
|
||||
void attsProcMtuReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcFindInfoReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcFindTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcReadTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcReadReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcReadBlobReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcReadMultReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcReadGroupTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcWrite(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcPrepWriteReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcExecWriteReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
void attsProcValueCnf(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket);
|
||||
|
||||
uint8_t attsCsfActClientState(uint16_t handle, uint8_t opcode, uint8_t *pPacket);
|
||||
uint8_t attsCsfIsClientChangeAware(dmConnId_t connId, uint16_t handle);
|
||||
void attsCsfSetHashUpdateStatus(bool_t isUpdating);
|
||||
uint8_t attsCsfGetHashUpdateStatus(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* ATTS_MAIN_H */
|
||||
Vendored
+469
@@ -0,0 +1,469 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server mandatory PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_math.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Compare the given attribute's UUID to the given UUID.
|
||||
*
|
||||
* \param pAttr Pointer to attribute.
|
||||
* \param uuidLen UUID length, either 2 or 16.
|
||||
* \param pUuid Pointer to UUID.
|
||||
*
|
||||
* \return TRUE of UUIDs match, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t attsUuidCmp(attsAttr_t *pAttr, uint8_t uuidLen, uint8_t *pUuid)
|
||||
{
|
||||
/* if both uuids are the same length */
|
||||
if ((((pAttr->settings & ATTS_SET_UUID_128) == 0) && (uuidLen == ATT_16_UUID_LEN)) ||
|
||||
(((pAttr->settings & ATTS_SET_UUID_128) != 0) && (uuidLen == ATT_128_UUID_LEN)))
|
||||
{
|
||||
/* simply compare the data */
|
||||
return (memcmp(pAttr->pUuid, pUuid, uuidLen) == 0);
|
||||
}
|
||||
/* else we need to convert one of the uuids */
|
||||
else if (((pAttr->settings & ATTS_SET_UUID_128) == 0) && (uuidLen == ATT_128_UUID_LEN))
|
||||
{
|
||||
return attUuidCmp16to128(pAttr->pUuid, pUuid);
|
||||
}
|
||||
else
|
||||
{
|
||||
return attUuidCmp16to128(pUuid, pAttr->pUuid);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Compare the given 16 bit UUID to the given UUID.
|
||||
*
|
||||
* \param pUuid16 Pointer to the 16 bit UUID.
|
||||
* \param uuidLen UUID length, either 2 or 16.
|
||||
* \param pUuid Pointer to UUID.
|
||||
*
|
||||
* \return TRUE of UUIDs match, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t attsUuid16Cmp(uint8_t *pUuid16, uint8_t uuidLen, uint8_t *pUuid)
|
||||
{
|
||||
if (uuidLen == ATT_16_UUID_LEN)
|
||||
{
|
||||
return ((pUuid16[0] == pUuid[0]) && (pUuid16[1] == pUuid[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
return attUuidCmp16to128(pUuid16, pUuid);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find an attribute with the given handle.
|
||||
*
|
||||
* \param handle Attribute handle.
|
||||
* \param pAttrGroup Return value pointer to found attribute's group.
|
||||
*
|
||||
* \return Pointer to attribute if found, othewise NULL.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
attsAttr_t *attsFindByHandle(uint16_t handle, attsGroup_t **pAttrGroup)
|
||||
{
|
||||
attsGroup_t *pGroup;
|
||||
|
||||
/* iterate over attribute group list */
|
||||
for (pGroup = attsCb.groupQueue.pHead; pGroup != NULL; pGroup = pGroup->pNext)
|
||||
{
|
||||
/* if start handle within handle range of group */
|
||||
if ((handle >= pGroup->startHandle) && (handle <= pGroup->endHandle))
|
||||
{
|
||||
/* index by handle into attribute array to return attribute */
|
||||
*pAttrGroup = pGroup;
|
||||
return &pGroup->pAttr[handle - pGroup->startHandle];
|
||||
}
|
||||
}
|
||||
|
||||
/* handle not found */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find the first attribute within the given handle range.
|
||||
*
|
||||
* \param startHandle Starting attribute handle.
|
||||
* \param endHandle Ending attribute handle.
|
||||
* \param pAttr Return value pointer to found attribute.
|
||||
*
|
||||
* \return Attribute handle or ATT_HANDLE_NONE if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t attsFindInRange(uint16_t startHandle, uint16_t endHandle, attsAttr_t **pAttr)
|
||||
{
|
||||
attsGroup_t *pGroup;
|
||||
|
||||
/* iterate over attribute group list */
|
||||
for (pGroup = attsCb.groupQueue.pHead; pGroup != NULL; pGroup = pGroup->pNext)
|
||||
{
|
||||
/* if start handle is less than group start handle but handle range is within group */
|
||||
if ((startHandle < pGroup->startHandle) && (endHandle >= pGroup->startHandle))
|
||||
{
|
||||
/* set start handle to first handle in group */
|
||||
startHandle = pGroup->startHandle;
|
||||
}
|
||||
|
||||
/* if start handle within handle range of group */
|
||||
if ((startHandle >= pGroup->startHandle) && (startHandle <= pGroup->endHandle))
|
||||
{
|
||||
/* index by handle into attribute array to return attribute */
|
||||
*pAttr = &pGroup->pAttr[startHandle - pGroup->startHandle];
|
||||
return startHandle;
|
||||
}
|
||||
}
|
||||
|
||||
/* handle within range not found */
|
||||
return ATT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform required permission and security checks when reading or writing an attribute.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
* \param permit Either ATTS_PERMIT_READ or ATTS_PERMIT_WRITE.
|
||||
* \param handle Attribute handle.
|
||||
* \param permissions Attribute permissions.
|
||||
*
|
||||
* \return ATT_SUCCESS if successful or error code on failure.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t attsPermissions(dmConnId_t connId, uint8_t permit, uint16_t handle, uint8_t permissions)
|
||||
{
|
||||
uint8_t secLevel;
|
||||
|
||||
/* verify read or write permissions */
|
||||
if (!(permissions & permit))
|
||||
{
|
||||
return (permit == ATTS_PERMIT_READ) ? ATT_ERR_READ : ATT_ERR_WRITE;
|
||||
}
|
||||
|
||||
/* convert write permissions to read permissions for easier masking */
|
||||
if (permit == ATTS_PERMIT_WRITE)
|
||||
{
|
||||
permissions >>= 4;
|
||||
}
|
||||
|
||||
/* if no security requirements return quickly */
|
||||
if ((permissions & (ATTS_PERMIT_READ_AUTH | ATTS_PERMIT_READ_AUTHORIZ | ATTS_PERMIT_READ_ENC)) == 0)
|
||||
{
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/* get security level for this connection */
|
||||
secLevel = DmConnSecLevel(connId);
|
||||
|
||||
/* check if encryption required */
|
||||
if ((permissions & ATTS_PERMIT_READ_ENC) && (secLevel == DM_SEC_LEVEL_NONE))
|
||||
{
|
||||
return ATT_ERR_AUTH;
|
||||
}
|
||||
|
||||
/* check if encryption required with authenticated key */
|
||||
if (((permissions & (ATTS_PERMIT_READ_AUTH | ATTS_PERMIT_READ_ENC)) ==
|
||||
(ATTS_PERMIT_READ_AUTH | ATTS_PERMIT_READ_ENC)) && (secLevel < DM_SEC_LEVEL_ENC_AUTH))
|
||||
{
|
||||
return ATT_ERR_AUTH;
|
||||
}
|
||||
|
||||
/* authorization check */
|
||||
if (permissions & ATTS_PERMIT_READ_AUTHORIZ)
|
||||
{
|
||||
if (attsCb.authorCback == NULL)
|
||||
{
|
||||
return ATT_ERR_AUTHOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (*attsCb.authorCback)(connId, permit, handle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process an MTU request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcMtuReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *p;
|
||||
uint16_t mtu;
|
||||
uint16_t localMtu;
|
||||
uint8_t *pRsp;
|
||||
|
||||
p = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
|
||||
/* parse mtu */
|
||||
BYTES_TO_UINT16(mtu, p);
|
||||
|
||||
/* verify */
|
||||
if (mtu < ATT_DEFAULT_MTU)
|
||||
{
|
||||
mtu = ATT_DEFAULT_MTU;
|
||||
}
|
||||
|
||||
/* get desired MTU */
|
||||
localMtu = WSF_MIN(pAttCfg->mtu, (HciGetMaxRxAclLen() - L2C_HDR_LEN));
|
||||
|
||||
/* send response */
|
||||
if ((pRsp = attMsgAlloc(L2C_PAYLOAD_START + ATT_MTU_RSP_LEN)) != NULL)
|
||||
{
|
||||
p = pRsp + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_MTU_RSP);
|
||||
UINT16_TO_BSTREAM(p, localMtu);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, ATT_MTU_RSP_LEN, pRsp);
|
||||
}
|
||||
|
||||
/* set mtu for the connection */
|
||||
attSetMtu(pCcb, mtu, localMtu);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a find information request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcFindInfoReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
uint16_t handle;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handles */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(startHandle, pPacket);
|
||||
BSTREAM_TO_UINT16(endHandle, pPacket);
|
||||
|
||||
/* verify handles */
|
||||
if ((startHandle == 0) || (startHandle > endHandle))
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
if (!err)
|
||||
{
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_FIND_INFO_RSP);
|
||||
|
||||
/* set result format */
|
||||
UINT8_TO_BSTREAM(p, ATT_FIND_HANDLE_16_UUID);
|
||||
|
||||
/* find attributes within handle range */
|
||||
handle = startHandle;
|
||||
while ((handle = attsFindInRange(handle, endHandle, &pAttr)) != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* copy handle and UUID into response buffer */
|
||||
|
||||
/* if 128 bit UUID */
|
||||
if (pAttr->settings & ATTS_SET_UUID_128)
|
||||
{
|
||||
/* if this is the first result */
|
||||
if (p == (pBuf + L2C_PAYLOAD_START + 2))
|
||||
{
|
||||
p--;
|
||||
UINT8_TO_BSTREAM(p, ATT_FIND_HANDLE_128_UUID);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pAttr->pUuid, ATT_128_UUID_LEN);
|
||||
p += ATT_128_UUID_LEN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* else 16 bit UUID */
|
||||
else
|
||||
{
|
||||
/* check if result fits */
|
||||
if ((p + ATT_16_UUID_LEN + sizeof(uint16_t)) <=
|
||||
(pBuf + pCcb->mtu + L2C_PAYLOAD_START))
|
||||
{
|
||||
/* copy result */
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
UINT8_TO_BSTREAM(p, pAttr->pUuid[0]);
|
||||
UINT8_TO_BSTREAM(p, pAttr->pUuid[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* response buffer full, we're done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* special case of handle at max range */
|
||||
if (handle == ATT_HANDLE_MAX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* try next handle */
|
||||
if (++handle > endHandle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if no results found set error, free buffer */
|
||||
if (p == (pBuf + L2C_PAYLOAD_START + 2))
|
||||
{
|
||||
WsfMsgFree(pBuf);
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer allocation failed */
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
/* set channel as busy for service discovery */
|
||||
attsDiscBusy(pCcb);
|
||||
|
||||
/* if no error send response, else send error */
|
||||
if (!err)
|
||||
{
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (p - (pBuf + L2C_PAYLOAD_START)), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_FIND_INFO_REQ, startHandle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a read request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcReadReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t handle;
|
||||
uint16_t readLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handle */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(handle, pPacket);
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify permissions */
|
||||
if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions)) == ATT_SUCCESS)
|
||||
{
|
||||
/* call read callback if desired */
|
||||
if ((pAttr->settings & ATTS_SET_READ_CBACK) &&
|
||||
(pGroup->readCback != NULL))
|
||||
{
|
||||
err = (*pGroup->readCback)(pCcb->connId, handle, ATT_PDU_READ_REQ, 0, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_READ, handle, pAttr->pValue);
|
||||
}
|
||||
|
||||
if (err == ATT_SUCCESS)
|
||||
{
|
||||
/* determine length of data to read */
|
||||
readLen = (*pAttr->pLen < (pCcb->mtu - ATT_READ_RSP_LEN)) ?
|
||||
*pAttr->pLen : (pCcb->mtu - ATT_READ_RSP_LEN);
|
||||
|
||||
/* Allocate response buffer */
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_READ_RSP_LEN + readLen)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_RSP);
|
||||
memcpy(p, pAttr->pValue, readLen);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (ATT_READ_RSP_LEN + readLen), pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* else invalid handle */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_REQ, handle, err);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+833
@@ -0,0 +1,833 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server optional read PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find the first attribute within the given handle range with a matching UUID.
|
||||
*
|
||||
* \param startHandle Starting attribute handle.
|
||||
* \param endHandle Ending attribute handle.
|
||||
* \param uuidLen UUID length, either 2 or 16.
|
||||
* \param pUUID Pointer to UUID.
|
||||
* \param pAttr Return value pointer to found attribute.
|
||||
* \param pAttrGroup Return value pointer to found attribute's group.
|
||||
*
|
||||
* \return Attribute handle or ATT_HANDLE_NONE if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t attsFindUuidInRange(uint16_t startHandle, uint16_t endHandle, uint8_t uuidLen,
|
||||
uint8_t *pUuid, attsAttr_t **pAttr, attsGroup_t **pAttrGroup)
|
||||
{
|
||||
attsGroup_t *pGroup;
|
||||
|
||||
/* iterate over attribute group list */
|
||||
for (pGroup = attsCb.groupQueue.pHead; pGroup != NULL; pGroup = pGroup->pNext)
|
||||
{
|
||||
/* if start handle is less than group start handle but handle range is within group */
|
||||
if ((startHandle < pGroup->startHandle) && (endHandle >= pGroup->startHandle))
|
||||
{
|
||||
/* set start handle to first handle in group */
|
||||
startHandle = pGroup->startHandle;
|
||||
}
|
||||
|
||||
/* if start handle within handle range of group */
|
||||
if ((startHandle >= pGroup->startHandle) && (startHandle <= pGroup->endHandle))
|
||||
{
|
||||
/* compare uuid with each attribute in group */
|
||||
*pAttr = &pGroup->pAttr[startHandle - pGroup->startHandle];
|
||||
while ((startHandle <= pGroup->endHandle) && (startHandle <= endHandle))
|
||||
{
|
||||
/* compare uuid in attribute */
|
||||
if (attsUuidCmp(*pAttr, uuidLen, pUuid))
|
||||
{
|
||||
*pAttrGroup = pGroup;
|
||||
return startHandle;
|
||||
}
|
||||
|
||||
/* special case of max handle value */
|
||||
if (startHandle == ATT_HANDLE_MAX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
startHandle++;
|
||||
(*pAttr)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* no match found */
|
||||
return ATT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find the handle of the last attribute in a service group.
|
||||
*
|
||||
* \param startHandle Starting attribute handle of service.
|
||||
*
|
||||
* \return Service group end handle.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t attsFindServiceGroupEnd(uint16_t startHandle)
|
||||
{
|
||||
attsGroup_t *pGroup;
|
||||
attsAttr_t *pAttr;
|
||||
uint16_t prevHandle;
|
||||
uint8_t primSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PRIMARY_SERVICE)};
|
||||
uint8_t secSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_SECONDARY_SERVICE)};
|
||||
|
||||
/* special case for max handle */
|
||||
if (startHandle == ATT_HANDLE_MAX)
|
||||
{
|
||||
return ATT_HANDLE_MAX;
|
||||
}
|
||||
|
||||
prevHandle = startHandle;
|
||||
startHandle++;
|
||||
|
||||
/* iterate over attribute group list */
|
||||
for (pGroup = attsCb.groupQueue.pHead; pGroup != NULL; pGroup = pGroup->pNext)
|
||||
{
|
||||
/* if start handle is less than group start handle */
|
||||
if (startHandle < pGroup->startHandle)
|
||||
{
|
||||
/* set start handle to first handle in group */
|
||||
startHandle = pGroup->startHandle;
|
||||
}
|
||||
|
||||
/* if start handle within handle range of group */
|
||||
if (startHandle <= pGroup->endHandle)
|
||||
{
|
||||
/* compare uuid with each attribute in group */
|
||||
pAttr = &pGroup->pAttr[startHandle - pGroup->startHandle];
|
||||
while (startHandle <= pGroup->endHandle)
|
||||
{
|
||||
/* compare uuid in attribute to service uuids */
|
||||
if (attsUuidCmp(pAttr, ATT_16_UUID_LEN, primSvcUuid) ||
|
||||
attsUuidCmp(pAttr, ATT_16_UUID_LEN, secSvcUuid))
|
||||
{
|
||||
/* found next service; return handle of previous attribute */
|
||||
return prevHandle;
|
||||
}
|
||||
|
||||
/* special case of max handle value */
|
||||
if (startHandle == ATT_HANDLE_MAX)
|
||||
{
|
||||
return ATT_HANDLE_MAX;
|
||||
}
|
||||
|
||||
prevHandle = startHandle;
|
||||
startHandle++;
|
||||
pAttr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* next service not found; return 0xFFFF as the last handle in the database */
|
||||
return ATT_HANDLE_MAX;
|
||||
}
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a read blob request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcReadBlobReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t handle;
|
||||
uint16_t offset;
|
||||
uint16_t readLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handle and offset */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(handle, pPacket);
|
||||
BSTREAM_TO_UINT16(offset, pPacket);
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify permissions */
|
||||
if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
/* err has been set; fail */
|
||||
}
|
||||
/* verify offset */
|
||||
else if (offset > *pAttr->pLen)
|
||||
{
|
||||
err = ATT_ERR_OFFSET;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* call read callback if desired */
|
||||
if ((pAttr->settings & ATTS_SET_READ_CBACK) &&
|
||||
(pGroup->readCback != NULL))
|
||||
{
|
||||
err = (*pGroup->readCback)(pCcb->connId, handle, ATT_PDU_READ_BLOB_REQ, offset, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_READ, handle, pAttr->pValue);
|
||||
}
|
||||
|
||||
if (err == ATT_SUCCESS)
|
||||
{
|
||||
/* determine length of data to read */
|
||||
readLen = ((*pAttr->pLen - offset) < (pCcb->mtu - ATT_READ_BLOB_RSP_LEN)) ?
|
||||
(*pAttr->pLen - offset) : (pCcb->mtu - ATT_READ_BLOB_RSP_LEN);
|
||||
|
||||
/* Allocate response buffer */
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_READ_BLOB_RSP_LEN + readLen)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_BLOB_RSP);
|
||||
memcpy(p, (pAttr->pValue + offset), readLen);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (ATT_READ_BLOB_RSP_LEN + readLen), pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* else invalid handle */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_BLOB_REQ, handle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a find by type value request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcFindTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
uint8_t *pUuid;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
uint16_t handle;
|
||||
uint16_t nextHandle;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handles and uuid; pPacket then points to the value in the request */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(startHandle, pPacket);
|
||||
BSTREAM_TO_UINT16(endHandle, pPacket);
|
||||
pUuid = pPacket;
|
||||
pPacket += ATT_16_UUID_LEN;
|
||||
|
||||
/* set len to the value length */
|
||||
len -= ATT_FIND_TYPE_REQ_LEN;
|
||||
|
||||
/* verify handles */
|
||||
if ((startHandle == 0) || (startHandle > endHandle))
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
if (!err)
|
||||
{
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_FIND_TYPE_RSP);
|
||||
|
||||
/* find attributes with matching uuid within handle range */
|
||||
handle = startHandle;
|
||||
while ((handle = attsFindUuidInRange(handle, endHandle, ATT_16_UUID_LEN,
|
||||
pUuid, &pAttr, &pGroup)) != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* if value and length matches */
|
||||
if ((pAttr->permissions & ATTS_PERMIT_READ) &&
|
||||
((len == 0) ||
|
||||
((len == *pAttr->pLen) && (memcmp(pPacket, pAttr->pValue, len) == 0))))
|
||||
{
|
||||
/* if uuid in request is for primary service */
|
||||
if (pUuid[0] == UINT16_TO_BYTE0(ATT_UUID_PRIMARY_SERVICE) &&
|
||||
pUuid[1] == UINT16_TO_BYTE1(ATT_UUID_PRIMARY_SERVICE))
|
||||
{
|
||||
/* next handle is service group end handle */
|
||||
nextHandle = attsFindServiceGroupEnd(handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* for any other uuid next handle is same as found handle */
|
||||
nextHandle = handle;
|
||||
}
|
||||
|
||||
/* copy result into response buffer; first check if it fits */
|
||||
if ((p + (sizeof(uint16_t) * 2)) <= (pBuf + pCcb->mtu + L2C_PAYLOAD_START))
|
||||
{
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
UINT16_TO_BSTREAM(p, nextHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer full, we're done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* value doesn't match; still need to set next handle */
|
||||
else
|
||||
{
|
||||
nextHandle = handle;
|
||||
}
|
||||
|
||||
/* check if handle has reached end */
|
||||
if ((nextHandle >= endHandle) || (nextHandle == ATT_HANDLE_MAX))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* try next handle */
|
||||
handle = nextHandle + 1;
|
||||
}
|
||||
|
||||
/* if no results found set error, free buffer */
|
||||
if (p == (pBuf + L2C_PAYLOAD_START + 1))
|
||||
{
|
||||
WsfMsgFree(pBuf);
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer allocation failed */
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
/* set channel as busy for service discovery */
|
||||
attsDiscBusy(pCcb);
|
||||
|
||||
/* if no error send response, else send error */
|
||||
if (!err)
|
||||
{
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (p - (pBuf + L2C_PAYLOAD_START)), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_FIND_TYPE_REQ, startHandle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a read by type request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcReadTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
uint16_t handle;
|
||||
uint8_t uuidLen;
|
||||
uint8_t attLen;
|
||||
uint8_t cbackErr = ATT_SUCCESS;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handles; pPacket then points to the uuid */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(startHandle, pPacket);
|
||||
BSTREAM_TO_UINT16(endHandle, pPacket);
|
||||
|
||||
/* get and verify uuid length */
|
||||
uuidLen = len - ATT_READ_TYPE_REQ_LEN;
|
||||
if (!((uuidLen == ATT_16_UUID_LEN) || (uuidLen == ATT_128_UUID_LEN)))
|
||||
{
|
||||
err = ATT_ERR_INVALID_PDU;
|
||||
}
|
||||
/* verify handles */
|
||||
else if ((startHandle == 0) || (startHandle > endHandle))
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
if (!err)
|
||||
{
|
||||
/* find first attribute with matching uuid within handle range */
|
||||
handle = attsFindUuidInRange(startHandle, endHandle, uuidLen, pPacket, &pAttr, &pGroup);
|
||||
startHandle = handle;
|
||||
|
||||
if (handle == ATT_HANDLE_NONE)
|
||||
{
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
/* check permissions */
|
||||
else if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
/* err is set above */
|
||||
}
|
||||
/* check if read callback should be called */
|
||||
else if ((pAttr->settings & ATTS_SET_READ_CBACK) &&
|
||||
(pGroup->readCback != NULL))
|
||||
{
|
||||
err = (*pGroup->readCback)(pCcb->connId, handle, ATT_PDU_READ_TYPE_REQ, 0, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_READ, handle, pAttr->pValue);
|
||||
}
|
||||
|
||||
if (err == ATT_SUCCESS)
|
||||
{
|
||||
/* Check if UUID is the Database Hash Characteristic Value and the value is being
|
||||
* re-calculated
|
||||
*/
|
||||
if ((memcmp(pPacket, attGattDbhChUuid, ATT_16_UUID_LEN) == 0) && attsCsfGetHashUpdateStatus())
|
||||
{
|
||||
/* Store info and return */
|
||||
pCcb->pPendDbHashRsp = WsfBufAlloc(sizeof(attPendDbHashRsp_t));
|
||||
if (pCcb->pPendDbHashRsp)
|
||||
{
|
||||
pCcb->pPendDbHashRsp->startHandle = startHandle;
|
||||
pCcb->pPendDbHashRsp->handle = handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_TYPE_REQ, startHandle, ATT_ERR_RESOURCES);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_TYPE_RSP);
|
||||
|
||||
/* get length of this first attribute */
|
||||
attLen = (*pAttr->pLen < (pCcb->mtu - ATT_READ_TYPE_RSP_LEN - sizeof(uint16_t))) ?
|
||||
*pAttr->pLen : (pCcb->mtu - ATT_READ_TYPE_RSP_LEN - sizeof(uint16_t));
|
||||
|
||||
/* set length parameter in response message */
|
||||
UINT8_TO_BSTREAM(p, attLen + sizeof(uint16_t));
|
||||
|
||||
/* copy result to response message */
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pAttr->pValue, attLen);
|
||||
p += attLen;
|
||||
|
||||
/* look for additional attributes */
|
||||
handle++;
|
||||
while ((handle = attsFindUuidInRange(handle, endHandle, uuidLen,
|
||||
pPacket, &pAttr, &pGroup)) != ATT_HANDLE_NONE)
|
||||
{
|
||||
/* call read callback if desired */
|
||||
if ((pAttr->settings & ATTS_SET_READ_CBACK) &&
|
||||
(pGroup->readCback != NULL))
|
||||
{
|
||||
cbackErr = (*pGroup->readCback)(pCcb->connId, handle, ATT_PDU_READ_TYPE_REQ, 0, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
cbackErr = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_READ, handle, pAttr->pValue);
|
||||
}
|
||||
|
||||
/* verify no error from read callback
|
||||
* verify length is same as first found attribute
|
||||
* verify attribute permissions
|
||||
*/
|
||||
if ((cbackErr == ATT_SUCCESS) &&
|
||||
(*pAttr->pLen == attLen) &&
|
||||
(attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions) == ATT_SUCCESS))
|
||||
{
|
||||
/* copy result into response buffer; first check if it fits */
|
||||
if ((p + attLen + sizeof(uint16_t)) <= (pBuf + pCcb->mtu + L2C_PAYLOAD_START))
|
||||
{
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pAttr->pValue, attLen);
|
||||
p += attLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer full, we're done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* problem with read callback, length, or permissions; send what we've got so far */
|
||||
break;
|
||||
}
|
||||
|
||||
/* special case of handle at max range */
|
||||
if (handle == ATT_HANDLE_MAX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* try next handle */
|
||||
if (++handle > endHandle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer allocation failed */
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if no error send response, else send error */
|
||||
if (!err)
|
||||
{
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (p - (pBuf + L2C_PAYLOAD_START)), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_TYPE_REQ, startHandle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a read multiple request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcReadMultReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
uint8_t *pEnd;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t handle = ATT_HANDLE_NONE;
|
||||
uint16_t readLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* points to end of payload */
|
||||
pEnd = pPacket + L2C_PAYLOAD_START + len;
|
||||
|
||||
/* points to first handle */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_MULT_RSP);
|
||||
|
||||
/* while there are handles remaining and there is space in response buffer */
|
||||
while (pPacket < pEnd)
|
||||
{
|
||||
/* parse handle */
|
||||
BSTREAM_TO_UINT16(handle, pPacket);
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) == NULL)
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* verify permissions */
|
||||
if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* call read callback if desired */
|
||||
if ((pAttr->settings & ATTS_SET_READ_CBACK) &&
|
||||
(pGroup->readCback != NULL))
|
||||
{
|
||||
err = (*pGroup->readCback)(pCcb->connId, handle, ATT_PDU_READ_MULT_REQ, 0, pAttr);
|
||||
if (err != ATT_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_READ, handle, pAttr->pValue);
|
||||
if (err != ATT_SUCCESS)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p < (pBuf + pCcb->mtu + L2C_PAYLOAD_START))
|
||||
{
|
||||
/* calculate remaining space in response buffer */
|
||||
readLen = (pBuf + pCcb->mtu + L2C_PAYLOAD_START) - p;
|
||||
|
||||
/* actual length is minimum of remaining space and attribute length */
|
||||
readLen = (*pAttr->pLen < readLen) ? *pAttr->pLen : readLen;
|
||||
|
||||
/* copy attribute to response buffer */
|
||||
memcpy(p, pAttr->pValue, readLen);
|
||||
p += readLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer allocation failed */
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
|
||||
/* if no error send response, else send error */
|
||||
if (!err)
|
||||
{
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (p - (pBuf + L2C_PAYLOAD_START)), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* discard response buffer */
|
||||
if (pBuf != NULL)
|
||||
{
|
||||
WsfMsgFree(pBuf);
|
||||
}
|
||||
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_MULT_REQ, handle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a read group by type request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcReadGroupTypeReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf = NULL;
|
||||
uint8_t *p = NULL;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint16_t startHandle;
|
||||
uint16_t endHandle;
|
||||
uint16_t handle;
|
||||
uint8_t uuidLen;
|
||||
uint8_t attLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
uint8_t primSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PRIMARY_SERVICE)};
|
||||
|
||||
/* parse handles; pPacket then points to the uuid */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(startHandle, pPacket);
|
||||
BSTREAM_TO_UINT16(endHandle, pPacket);
|
||||
|
||||
/* get and verify uuid length */
|
||||
uuidLen = len - ATT_READ_GROUP_TYPE_REQ_LEN;
|
||||
if (!((uuidLen == ATT_16_UUID_LEN) || (uuidLen == ATT_128_UUID_LEN)))
|
||||
{
|
||||
err = ATT_ERR_INVALID_PDU;
|
||||
}
|
||||
/* verify handles */
|
||||
else if ((startHandle == 0) || (startHandle > endHandle))
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
/* verify uuid is primary service group */
|
||||
else if (!attsUuid16Cmp(primSvcUuid, uuidLen, pPacket))
|
||||
{
|
||||
err = ATT_ERR_GROUP_TYPE;
|
||||
}
|
||||
|
||||
if (!err)
|
||||
{
|
||||
/* find first attribute with matching uuid within handle range */
|
||||
handle = attsFindUuidInRange(startHandle, endHandle, uuidLen, pPacket, &pAttr, &pGroup);
|
||||
|
||||
if (handle == ATT_HANDLE_NONE)
|
||||
{
|
||||
err = ATT_ERR_NOT_FOUND;
|
||||
}
|
||||
/* check permissions */
|
||||
else if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
startHandle = handle; /* this handle is returned in error response */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* allocate max size buffer for response */
|
||||
if ((pBuf = attMsgAlloc(pCcb->mtu + L2C_PAYLOAD_START)) != NULL)
|
||||
{
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_READ_GROUP_TYPE_RSP);
|
||||
|
||||
/* get length of this first attribute */
|
||||
attLen = (*pAttr->pLen < (pCcb->mtu - ATT_READ_GROUP_TYPE_RSP_LEN - (2 * sizeof(uint16_t)))) ?
|
||||
*pAttr->pLen : (pCcb->mtu - ATT_READ_GROUP_TYPE_RSP_LEN - (2 * sizeof(uint16_t)));
|
||||
|
||||
/* set length parameter in response message */
|
||||
UINT8_TO_BSTREAM(p, attLen + (2 * sizeof(uint16_t)));
|
||||
|
||||
/* copy handle to response message */
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
/* get end group handle and copy it to response message */
|
||||
handle = attsFindServiceGroupEnd(handle);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
|
||||
/* copy the attribute value to response message */
|
||||
memcpy(p, pAttr->pValue, attLen);
|
||||
p += attLen;
|
||||
|
||||
/* look for additional attributes */
|
||||
while (TRUE)
|
||||
{
|
||||
/* special case of handle at max range */
|
||||
if (handle == ATT_HANDLE_MAX)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* increment to next handle */
|
||||
if (++handle > endHandle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* find next matching handle */
|
||||
if ((handle = attsFindUuidInRange(handle, endHandle, uuidLen,
|
||||
pPacket, &pAttr, &pGroup)) == ATT_HANDLE_NONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/* verify length is same as first found attribute
|
||||
* verify attribute permissions
|
||||
*/
|
||||
if ((*pAttr->pLen == attLen) &&
|
||||
(attsPermissions(pCcb->connId, ATTS_PERMIT_READ,
|
||||
handle, pAttr->permissions) == ATT_SUCCESS))
|
||||
{
|
||||
/* copy result into response buffer; first check if it fits */
|
||||
if ((p + attLen + (2 * sizeof(uint16_t))) <=
|
||||
(pBuf + pCcb->mtu + L2C_PAYLOAD_START))
|
||||
{
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
handle = attsFindServiceGroupEnd(handle);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
memcpy(p, pAttr->pValue, attLen);
|
||||
p += attLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer full, we're done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* problem with either length or permissions; send what we've got so far */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* buffer allocation failed */
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* set channel as busy for service discovery */
|
||||
attsDiscBusy(pCcb);
|
||||
|
||||
/* if no error send response, else send error */
|
||||
if (!err)
|
||||
{
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (p - (pBuf + L2C_PAYLOAD_START)), pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_READ_GROUP_TYPE_REQ, startHandle, err);
|
||||
}
|
||||
}
|
||||
Vendored
+402
@@ -0,0 +1,402 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server signed PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_queue.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "sec_api.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/bstream.h"
|
||||
#include "util/calc128.h"
|
||||
#include "util/wstr.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
#include "att_sign.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Signed data buffer structure */
|
||||
typedef struct attsSignBuf_tag
|
||||
{
|
||||
struct attsSignBuf_tag *pNext; /* pointer to next in queue */
|
||||
attCcb_t *pCcb; /* ATT CCB associated with the packet */
|
||||
uint16_t handle; /* ATT handle */
|
||||
uint16_t writeLen; /* length of data to write */
|
||||
dmConnId_t connId; /* Connection ID associated with the packet */
|
||||
uint8_t packet[1]; /* packet */
|
||||
} attsSignBuf_t;
|
||||
|
||||
/* ATTS signed PDU connection control block */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t signCounter; /* sign counter for this connection */
|
||||
uint8_t *pCsrk; /* signing key for this connection */
|
||||
attsSignBuf_t *pBuf; /* current data being processed */
|
||||
bool_t authenticated; /* Indicate if the CSRK is authenticated or not */
|
||||
} attsSignCcb_t;
|
||||
|
||||
/* ATTS signed PDU control block */
|
||||
typedef struct
|
||||
{
|
||||
attsSignCcb_t ccb[DM_CONN_MAX];
|
||||
wsfQueue_t msgQueue;
|
||||
} attsSignCb_t;
|
||||
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
static attsSignCb_t attsSignCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the signing connection control block for the connection ID.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
*
|
||||
* \return Pointer to control block.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static attsSignCcb_t *attsSignCcbByConnId(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return &attsSignCb.ccb[connId - 1];
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start processing of data for signed write.
|
||||
*
|
||||
* \param pCcb Signed data CCB.
|
||||
* \param pBuf Signed data buffer structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsSignedWriteStart(attsSignCcb_t *pCcb, attsSignBuf_t *pBuf)
|
||||
{
|
||||
if (pCcb->pCsrk != NULL)
|
||||
{
|
||||
uint16_t cmacTxtLen = pBuf->writeLen + ATT_WRITE_CMD_LEN + sizeof(uint32_t);
|
||||
uint8_t *pCmacTxt = WsfBufAlloc(cmacTxtLen);
|
||||
|
||||
pCcb->pBuf = pBuf;
|
||||
|
||||
if (pCmacTxt)
|
||||
{
|
||||
uint8_t revPeerCsrk[SEC_CMAC_KEY_LEN] = {0};
|
||||
|
||||
WStrReverseCpy(revPeerCsrk, pCcb->pCsrk, SEC_CMAC_KEY_LEN);
|
||||
WStrReverseCpy(pCmacTxt, pBuf->packet, cmacTxtLen);
|
||||
|
||||
if (SecCmac(revPeerCsrk, pCmacTxt, cmacTxtLen, attCb.handlerId,
|
||||
pBuf->connId, ATTS_MSG_SIGN_CMAC_CMPL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WsfBufFree(pCmacTxt);
|
||||
}
|
||||
}
|
||||
|
||||
/* no CSRK-- free buffer */
|
||||
WsfBufFree(pBuf);
|
||||
ATT_TRACE_WARN0("ATTS CSRK not set");
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a signed write command PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsProcSignedWrite(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
attsSignCcb_t *pSignCcb;
|
||||
attsSignBuf_t *pBuf;
|
||||
uint16_t handle;
|
||||
uint16_t writeLen;
|
||||
|
||||
/* parse handle, calculate write length */
|
||||
p = pPacket + L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(handle, p);
|
||||
writeLen = len - ATT_SIGNED_WRITE_CMD_LEN;
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify signed write is permitted */
|
||||
if ((pAttr->settings & ATTS_SET_ALLOW_SIGNED) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify that csrk is present */
|
||||
if (attsSignCcbByConnId(pCcb->connId)->pCsrk == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify basic permissions */
|
||||
if ((pAttr->permissions & (ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC)) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify authentication */
|
||||
if ((pAttr->permissions & ATTS_PERMIT_WRITE_AUTH) &&
|
||||
(attsSignCcbByConnId(pCcb->connId)->authenticated == 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Note: authorization not verified at this stage as it is reserved for lesc
|
||||
writes; authorization occurs latter when the write cb is called */
|
||||
|
||||
/* verify write length, fixed length */
|
||||
if (((pAttr->settings & ATTS_SET_VARIABLE_LEN) == 0) &&
|
||||
(writeLen != pAttr->maxLen))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify write length, variable length */
|
||||
if (((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0) &&
|
||||
(writeLen > pAttr->maxLen))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* allocate buffer to store packet and parameters */
|
||||
if ((pBuf = WsfBufAlloc(sizeof(attsSignBuf_t) - 1 + len)) != NULL)
|
||||
{
|
||||
/* initialize buffer */
|
||||
pBuf->pCcb = pCcb;
|
||||
pBuf->handle = handle;
|
||||
pBuf->writeLen = writeLen;
|
||||
pBuf->connId = pCcb->connId;
|
||||
memcpy(pBuf->packet, (pPacket + L2C_PAYLOAD_START), len);
|
||||
|
||||
/* check if a signed write is already in progress */
|
||||
pSignCcb = attsSignCcbByConnId(pCcb->connId);
|
||||
|
||||
if (pSignCcb->pBuf != NULL)
|
||||
{
|
||||
/* signed write in progress; queue packet */
|
||||
WsfQueueEnq(&attsSignCb.msgQueue, pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* start signed data processing */
|
||||
attsSignedWriteStart(pSignCcb, pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Message handler callback for ATTS signed PDU processing.
|
||||
*
|
||||
* \param pMsg ATTS message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void attsSignMsgCback(secCmacMsg_t *pMsg)
|
||||
{
|
||||
attsSignCcb_t *pCcb;
|
||||
attsSignBuf_t *pBuf;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint32_t signCounter;
|
||||
|
||||
if (pMsg->hdr.event == ATTS_MSG_SIGN_CMAC_CMPL)
|
||||
{
|
||||
uint8_t signature[ATT_CMAC_RESULT_LEN] = {0};
|
||||
|
||||
pCcb = attsSignCcbByConnId((dmConnId_t) pMsg->hdr.param);
|
||||
pBuf = pCcb->pBuf;
|
||||
|
||||
WStrReverseCpy(signature, pMsg->pCiphertext, ATT_CMAC_RESULT_LEN);
|
||||
|
||||
/* if signature ok */
|
||||
if (memcmp(signature, pBuf->packet + pBuf->writeLen + ATT_WRITE_CMD_LEN + 4, ATT_CMAC_RESULT_LEN) == 0)
|
||||
{
|
||||
/* check sign counter */
|
||||
BYTES_TO_UINT32(signCounter, (pBuf->packet + pBuf->writeLen + ATT_WRITE_CMD_LEN));
|
||||
|
||||
if (signCounter >= pCcb->signCounter)
|
||||
{
|
||||
/* update sign counter */
|
||||
pCcb->signCounter = signCounter + 1;
|
||||
|
||||
/* perform write: */
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(pBuf->handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* if write callback is desired */
|
||||
if ((pAttr->settings & ATTS_SET_WRITE_CBACK) &&
|
||||
(pGroup->writeCback != NULL))
|
||||
{
|
||||
/* write value via write callback */
|
||||
(*pGroup->writeCback)(pBuf->connId, pBuf->handle, ATT_PDU_SIGNED_WRITE_CMD,
|
||||
0, pBuf->writeLen, (pBuf->packet + ATT_WRITE_CMD_LEN), pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
(*attsCb.cccCback)(pBuf->connId, ATT_METHOD_WRITE, pBuf->handle,
|
||||
(pBuf->packet + ATT_WRITE_CMD_LEN));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* write attribute value */
|
||||
memcpy(pAttr->pValue, (pBuf->packet + ATT_WRITE_CMD_LEN), pBuf->writeLen);
|
||||
|
||||
/* write the length if variable length attribute */
|
||||
if ((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0)
|
||||
{
|
||||
*(pAttr->pLen) = pBuf->writeLen;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN0("Signed write counter failed");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ATT_TRACE_WARN0("Signed write sig failed");
|
||||
}
|
||||
|
||||
/* we're done-- free parameter buffer */
|
||||
WsfBufFree(pBuf);
|
||||
|
||||
WsfBufFree(pMsg->pPlainText);
|
||||
|
||||
/* process next signed write in queue, if any */
|
||||
if ((pBuf = WsfQueueDeq(&attsSignCb.msgQueue)) != NULL)
|
||||
{
|
||||
pCcb = attsSignCcbByConnId(pBuf->connId);
|
||||
attsSignedWriteStart(pCcb, pBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
pCcb->pBuf = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize ATT server for data signing.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsSignInit(void)
|
||||
{
|
||||
/* initialize control block */
|
||||
WSF_QUEUE_INIT(&attsSignCb.msgQueue);
|
||||
|
||||
/* set up callback interface */
|
||||
attsCb.signMsgCback = (attMsgHandler_t) attsSignMsgCback;
|
||||
attsProcFcnTbl[ATT_METHOD_SIGNED_WRITE_CMD] = attsProcSignedWrite;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the peer's data signing key on this connection. This function
|
||||
* is typically called from the ATT connection callback when the connection is
|
||||
* established. The caller is responsible for maintaining the memory that
|
||||
* contains the key.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param pCsrk Pointer to data signing key (CSRK).
|
||||
* \param authenticated True if CSRK is authenticated and false otherwise.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsSetCsrk(dmConnId_t connId, uint8_t *pCsrk, bool_t authenticated)
|
||||
{
|
||||
attsSignCcbByConnId(connId)->pCsrk = pCsrk;
|
||||
attsSignCcbByConnId(connId)->authenticated = authenticated;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the peer's sign counter on this connection. This function
|
||||
* is typically called from the ATT connection callback when the connection is
|
||||
* established. ATT maintains the value of the sign counter internally and
|
||||
* sets the value when a signed packet is successfully received.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
* \param signCounter Sign counter.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsSetSignCounter(dmConnId_t connId, uint32_t signCounter)
|
||||
{
|
||||
attsSignCcbByConnId(connId)->signCounter = signCounter;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the current value peer's sign counter on this connection. This function
|
||||
* is typically called from the ATT connection callback when the connection is
|
||||
* closed so the application can store the sign counter for use on future
|
||||
* connections.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return Sign counter.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint32_t AttsGetSignCounter(dmConnId_t connId)
|
||||
{
|
||||
return attsSignCcbByConnId(connId)->signCounter;
|
||||
}
|
||||
Vendored
+463
@@ -0,0 +1,463 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief ATT server write PDU processing functions.
|
||||
*
|
||||
* Copyright (c) 2009-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "util/bstream.h"
|
||||
#include "att_api.h"
|
||||
#include "att_main.h"
|
||||
#include "atts_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Queued prepare write structure */
|
||||
typedef struct attsPrepWrite_tag
|
||||
{
|
||||
struct attsPrepWrite_tag *pNext;
|
||||
uint16_t writeLen;
|
||||
uint16_t handle;
|
||||
uint16_t offset;
|
||||
uint8_t packet[1];
|
||||
} attsPrepWrite_t;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Execute a queued prepared write operation.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param pPrep Pointer to buffer containing prepared write.
|
||||
*
|
||||
* \return ATT_SUCCESS or failure status.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t attsExecPrepWrite(attCcb_t *pCcb, attsPrepWrite_t *pPrep)
|
||||
{
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
p = pPrep->packet;
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(pPrep->handle, &pGroup)) == NULL)
|
||||
{
|
||||
/* handle not found; only possible unless handle was removed */
|
||||
err = ATT_ERR_UNLIKELY;
|
||||
}
|
||||
/* verify write permissions (in theory could have changed) */
|
||||
else if (!(pAttr->permissions & ATTS_PERMIT_WRITE))
|
||||
{
|
||||
err = ATT_ERR_WRITE;
|
||||
}
|
||||
/* if write callback is desired */
|
||||
else if ((pAttr->settings & ATTS_SET_WRITE_CBACK) &&
|
||||
(pGroup->writeCback != NULL))
|
||||
{
|
||||
|
||||
/* write callback performs the write */
|
||||
err = (*pGroup->writeCback)(pCcb->connId, pPrep->handle, ATT_PDU_EXEC_WRITE_REQ,
|
||||
pPrep->offset, pPrep->writeLen, p, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_WRITE, pPrep->handle, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* perform write; parameters have already been vetted by previous procedures */
|
||||
|
||||
/* write attribute value */
|
||||
memcpy((pAttr->pValue + pPrep->offset), p, pPrep->writeLen);
|
||||
|
||||
/* write the length if variable length attribute */
|
||||
if ((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0)
|
||||
{
|
||||
*(pAttr->pLen) = pPrep->writeLen + pPrep->offset;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a write request or write command PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcWrite(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint8_t opcode;
|
||||
uint16_t handle;
|
||||
uint16_t writeLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse opcode handle, calculate write length */
|
||||
pPacket += L2C_PAYLOAD_START;
|
||||
BSTREAM_TO_UINT8(opcode, pPacket);
|
||||
BSTREAM_TO_UINT16(handle, pPacket);
|
||||
writeLen = len - ATT_WRITE_REQ_LEN;
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify permissions */
|
||||
if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_WRITE,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
/* err has been set; fail */
|
||||
}
|
||||
/* verify write length, fixed length */
|
||||
else if (((pAttr->settings & ATTS_SET_VARIABLE_LEN) == 0) &&
|
||||
(writeLen != pAttr->maxLen))
|
||||
{
|
||||
err = ATT_ERR_LENGTH;
|
||||
}
|
||||
/* verify write length, variable length */
|
||||
else if (((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0) &&
|
||||
(writeLen > pAttr->maxLen))
|
||||
{
|
||||
err = ATT_ERR_LENGTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if write callback is desired */
|
||||
if ((pAttr->settings & ATTS_SET_WRITE_CBACK) &&
|
||||
(pGroup->writeCback != NULL))
|
||||
{
|
||||
err = (*pGroup->writeCback)(pCcb->connId, handle, opcode, 0, writeLen,
|
||||
pPacket, pAttr);
|
||||
}
|
||||
/* else check if CCC */
|
||||
else if ((pAttr->settings & ATTS_SET_CCC) && (attsCb.cccCback != NULL))
|
||||
{
|
||||
err = (*attsCb.cccCback)(pCcb->connId, ATT_METHOD_WRITE, handle, pPacket);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* write attribute value */
|
||||
memcpy(pAttr->pValue, pPacket, writeLen);
|
||||
|
||||
/* write the length if variable length attribute */
|
||||
if ((pAttr->settings & ATTS_SET_VARIABLE_LEN) != 0)
|
||||
{
|
||||
*(pAttr->pLen) = writeLen;
|
||||
}
|
||||
}
|
||||
|
||||
/* if success and write req allocate response buffer */
|
||||
if (err == ATT_SUCCESS && opcode == ATT_PDU_WRITE_REQ)
|
||||
{
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_WRITE_RSP_LEN)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_WRITE_RSP);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, ATT_WRITE_RSP_LEN, pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* else attribute not found */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
|
||||
/* send error response for write req only */
|
||||
if (err && (opcode == ATT_PDU_WRITE_REQ))
|
||||
{
|
||||
if (err == ATT_RSP_PENDING)
|
||||
{
|
||||
/* set response pending */
|
||||
pCcb->control |= ATT_CCB_STATUS_RSP_PENDING;
|
||||
}
|
||||
else
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_WRITE_REQ, handle, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a prepare write request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcPrepWriteReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
attsPrepWrite_t *pPrep;
|
||||
uint16_t handle;
|
||||
uint16_t offset;
|
||||
uint16_t writeLen;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
/* parse handle and offset, calculate write length */
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
BSTREAM_TO_UINT16(handle, pPacket);
|
||||
BSTREAM_TO_UINT16(offset, pPacket);
|
||||
writeLen = len - ATT_PREP_WRITE_REQ_LEN; /* length of value being written */
|
||||
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(handle, &pGroup)) == NULL)
|
||||
{
|
||||
/* attribute not found */
|
||||
err = ATT_ERR_HANDLE;
|
||||
}
|
||||
/* verify permissions */
|
||||
else if ((err = attsPermissions(pCcb->connId, ATTS_PERMIT_WRITE,
|
||||
handle, pAttr->permissions)) != ATT_SUCCESS)
|
||||
{
|
||||
/* err has been set; fail */
|
||||
}
|
||||
/* verify offset is allowed */
|
||||
else if ((offset != 0) && ((pAttr->settings & ATTS_SET_ALLOW_OFFSET) == 0))
|
||||
{
|
||||
err = ATT_ERR_NOT_LONG;
|
||||
}
|
||||
/* verify write length, fixed length */
|
||||
else if (((pAttr->settings & ATTS_SET_VARIABLE_LEN) == 0) &&
|
||||
(writeLen != pAttr->maxLen))
|
||||
{
|
||||
err = ATT_ERR_LENGTH;
|
||||
}
|
||||
/* verify prepare write queue limit not reached */
|
||||
else if (WsfQueueCount(&pCcb->prepWriteQueue) >= pAttCfg->numPrepWrites)
|
||||
{
|
||||
err = ATT_ERR_QUEUE_FULL;
|
||||
}
|
||||
/* allocate new buffer to hold prepared write */
|
||||
else if ((pPrep = WsfBufAlloc(sizeof(attsPrepWrite_t) - 1 + writeLen)) == NULL)
|
||||
{
|
||||
err = ATT_ERR_RESOURCES;
|
||||
}
|
||||
else if ((pAttr->settings & ATTS_SET_WRITE_CBACK) &&
|
||||
(pGroup->writeCback != NULL))
|
||||
{
|
||||
err = (*pGroup->writeCback)(pCcb->connId, handle, ATT_PDU_PREP_WRITE_REQ, 0, writeLen,
|
||||
pPacket, pAttr);
|
||||
}
|
||||
|
||||
if (err == ATT_SUCCESS)
|
||||
{
|
||||
/* copy data to new buffer and queue it */
|
||||
pPrep->writeLen = writeLen;
|
||||
pPrep->handle = handle;
|
||||
pPrep->offset = offset;
|
||||
memcpy(pPrep->packet, pPacket, writeLen);
|
||||
WsfQueueEnq(&pCcb->prepWriteQueue, pPrep);
|
||||
|
||||
/* allocate response buffer */
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_PREP_WRITE_RSP_LEN + writeLen)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_PREP_WRITE_RSP);
|
||||
UINT16_TO_BSTREAM(p, handle);
|
||||
UINT16_TO_BSTREAM(p, offset);
|
||||
memcpy(p, pPacket, writeLen);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, (ATT_PREP_WRITE_RSP_LEN + writeLen), pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_PREP_WRITE_REQ, handle, err);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process an execute write request PDU.
|
||||
*
|
||||
* \param pCcb Connection control block.
|
||||
* \param len The length of the L2CAP payload data in pPacket.
|
||||
* \param pPacket A buffer containing the packet.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void attsProcExecWriteReq(attCcb_t *pCcb, uint16_t len, uint8_t *pPacket)
|
||||
{
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
attsPrepWrite_t *pPrep;
|
||||
attsAttr_t *pAttr;
|
||||
attsGroup_t *pGroup;
|
||||
uint8_t err = ATT_SUCCESS;
|
||||
|
||||
pPacket += L2C_PAYLOAD_START + ATT_HDR_LEN;
|
||||
|
||||
/* if cancelling all prepared writes */
|
||||
if (*pPacket == ATT_EXEC_WRITE_CANCEL)
|
||||
{
|
||||
/* free all queued buffers */
|
||||
attsClearPrepWrites(pCcb);
|
||||
}
|
||||
/* else writing all prepared writes */
|
||||
else if (*pPacket == ATT_EXEC_WRITE_ALL)
|
||||
{
|
||||
/* iterate over prepare write queue and verify offset and length */
|
||||
for (pPrep = pCcb->prepWriteQueue.pHead; pPrep != NULL; pPrep = pPrep->pNext)
|
||||
{
|
||||
/* find attribute */
|
||||
if ((pAttr = attsFindByHandle(pPrep->handle, &pGroup)) != NULL)
|
||||
{
|
||||
/* verify offset */
|
||||
if (pPrep->offset > pAttr->maxLen)
|
||||
{
|
||||
err = ATT_ERR_OFFSET;
|
||||
}
|
||||
/* verify write length with offset */
|
||||
else if ((pPrep->writeLen + pPrep->offset) > pAttr->maxLen)
|
||||
{
|
||||
err = ATT_ERR_LENGTH;
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
/* verification failed; discard all prepared writes */
|
||||
attsClearPrepWrites(pCcb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if length and offset checks ok then write all buffers in queue */
|
||||
if (err == ATT_SUCCESS)
|
||||
{
|
||||
/* for each buffer */
|
||||
while ((pPrep = WsfQueueDeq(&pCcb->prepWriteQueue)) != NULL)
|
||||
{
|
||||
/* write buffer */
|
||||
if ((err = attsExecPrepWrite(pCcb, pPrep)) != ATT_SUCCESS)
|
||||
{
|
||||
/* write failed; discard remaining prepared writes */
|
||||
attsClearPrepWrites(pCcb);
|
||||
}
|
||||
|
||||
/* free buffer */
|
||||
WsfBufFree(pPrep);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* else unknown operation */
|
||||
else
|
||||
{
|
||||
err = ATT_ERR_INVALID_PDU;
|
||||
}
|
||||
|
||||
/* send response or error response */
|
||||
if (err)
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_EXEC_WRITE_REQ, 0, err);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_EXEC_WRITE_RSP_LEN)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_EXEC_WRITE_RSP);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, ATT_EXEC_WRITE_RSP_LEN, pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a response to a pending write request. For use with ATT_RSP_PENDING.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
* \param handle Attribute handle.
|
||||
* \param status Status of the write request.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note When a higher layer returns ATT_RSP_PENDING to an ATT write callback indicating the
|
||||
* response status is pending, the higher layer must subsequently call this function
|
||||
* with the status of the write request.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AttsContinueWriteReq(dmConnId_t connId, uint16_t handle, uint8_t status)
|
||||
{
|
||||
attCcb_t *pCcb;
|
||||
uint8_t *pBuf;
|
||||
uint8_t *p;
|
||||
|
||||
/* get connection cb for this handle */
|
||||
if ((pCcb = attCcbByConnId(connId)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* clear response pending */
|
||||
pCcb->control &= ~ATT_CCB_STATUS_RSP_PENDING;
|
||||
|
||||
if (status)
|
||||
{
|
||||
attsErrRsp(pCcb->handle, ATT_PDU_WRITE_REQ, handle, status);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((pBuf = attMsgAlloc(L2C_PAYLOAD_START + ATT_WRITE_RSP_LEN)) != NULL)
|
||||
{
|
||||
/* build and send PDU */
|
||||
p = pBuf + L2C_PAYLOAD_START;
|
||||
UINT8_TO_BSTREAM(p, ATT_PDU_WRITE_RSP);
|
||||
|
||||
L2cDataReq(L2C_CID_ATT, pCcb->handle, ATT_WRITE_RSP_LEN, pBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user