Esempio n. 1
0
void smpSendPairingFailed(smpCcb_t *pCcb, uint8_t reason)
{
  uint8_t *pPacket;
  uint8_t *p;

  if ((pPacket = smpMsgAlloc(L2C_PAYLOAD_START + SMP_PAIR_FAIL_LEN)) != NULL)
  {
    p = pPacket + L2C_PAYLOAD_START;
    UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_FAIL);
    UINT8_TO_BSTREAM(p, reason);

    smpSendPkt(pCcb, pPacket);
  }
}
Esempio n. 2
0
void smprActSendPairRandom(smpCcb_t *pCcb, smpMsg_t *pMsg)
{
  uint8_t   *pPkt;
  uint8_t   *p;
  uint8_t   encKeyLen;

  /* get max STK length */
  encKeyLen = (pCcb->pairReq[SMP_MAXKEY_POS] < pCcb->pairRsp[SMP_MAXKEY_POS]) ?
               pCcb->pairReq[SMP_MAXKEY_POS] : pCcb->pairRsp[SMP_MAXKEY_POS];

  /* store STK and adjust based on max key length */
  memcpy(pCcb->pScr->buf.b3, pMsg->aes.pCiphertext, encKeyLen);
  memset((pCcb->pScr->buf.b3 + encKeyLen), 0, (SMP_KEY_LEN - encKeyLen));

  /* start smp response timer */
  smpStartRspTimer(pCcb);

  /* allocate packet buffer and send pairing random packet */
  if ((pPkt = smpMsgAlloc(SMP_PAIR_RAND_LEN + L2C_PAYLOAD_START)) != NULL)
  {
    /* build packet */
    p = pPkt + L2C_PAYLOAD_START;
    UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_RAND);
    memcpy(p, pCcb->pScr->buf.b4, SMP_RAND_LEN);

    /* send packet */
    smpSendPkt(pCcb, pPkt);
  }
}
Esempio n. 3
0
void HciLeSetConnCteTxParamsCmd(uint16_t connHandle, uint8_t cteTypeBits, uint8_t switchPatternLen,
                                uint8_t *pAntennaIDs)
{
  uint8_t *pBuf;
  uint8_t *p;

  if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_SET_CONN_CTE_TX_PARAMS,
                          HCI_LEN_LE_SET_CONN_CTE_TX_PARAMS(switchPatternLen))) != NULL)
  {
    p = pBuf + HCI_CMD_HDR_LEN;
    UINT16_TO_BSTREAM(p, connHandle);
    UINT8_TO_BSTREAM(p, cteTypeBits);
    UINT8_TO_BSTREAM(p, switchPatternLen);
    memcpy(p, pAntennaIDs, switchPatternLen);
    hciCmdSend(pBuf);
  }
}
Esempio n. 4
0
void HciLeConnCteReqEnableCmd(uint16_t connHandle, uint8_t enable, uint16_t cteReqInt,
                              uint8_t reqCteLen, uint8_t reqCteType)
{
  uint8_t *pBuf;
  uint8_t *p;

  if ((pBuf = hciCmdAlloc(HCI_OPCODE_LE_CONN_CTE_REQ_ENABLE,
                          HCI_LEN_LE_CONN_CTE_REQ_ENABLE)) != NULL)
  {
    p = pBuf + HCI_CMD_HDR_LEN;
    UINT16_TO_BSTREAM(p, connHandle);
    UINT8_TO_BSTREAM(p, enable);
    UINT16_TO_BSTREAM(p, cteReqInt);
    UINT8_TO_BSTREAM(p, reqCteLen);
    UINT8_TO_BSTREAM(p, reqCteType);
    hciCmdSend(pBuf);
  }
}
Esempio n. 5
0
void L2cDmConnUpdateRsp(uint8_t identifier, uint16_t handle, uint16_t result)
{
  uint8_t *pPacket;
  uint8_t *p;

  /* allocate msg buffer */
  if ((pPacket = l2cMsgAlloc(L2C_SIG_PKT_BASE_LEN + L2C_SIG_CONN_UPDATE_RSP_LEN)) != NULL)
  {
    /* build message */
    p = pPacket + L2C_PAYLOAD_START;
    UINT8_TO_BSTREAM(p, L2C_SIG_CONN_UPDATE_RSP);       /* command code */
    UINT8_TO_BSTREAM(p, identifier);                    /* identifier */
    UINT16_TO_BSTREAM(p, L2C_SIG_CONN_UPDATE_RSP_LEN);  /* parameter length */
    UINT16_TO_BSTREAM(p, result);                       /* result */

    /* send packet */
    L2cDataReq(L2C_CID_LE_SIGNALING, handle, (L2C_SIG_HDR_LEN + L2C_SIG_CONN_UPDATE_RSP_LEN), pPacket);
  }
}
Esempio n. 6
0
static uint8_t lhciVsPackScanReportEvt(uint8_t *pBuf, const LlScanReportInd_t *pEvt)
{
  const uint8_t len = LHCI_LEN_VS_SUBEVT_SCAN_REPORT;

  UINT8_TO_BSTREAM(pBuf, pEvt->peerAddrType);
  BDA64_TO_BSTREAM(pBuf, pEvt->peerAddr);
  BDA64_TO_BSTREAM(pBuf, pEvt->peerRpa);

  return len;
}
Esempio n. 7
0
void smprActSendSecurityReq(smpCcb_t *pCcb, smpMsg_t *pMsg)
{
  uint8_t   *pPkt;
  uint8_t   *p;

  /* start smp response timer */
  smpStartRspTimer(pCcb);

  /* allocate packet buffer */
  if ((pPkt = smpMsgAlloc(SMP_SECURITY_REQ_LEN + L2C_PAYLOAD_START)) != NULL)
  {
    /* build packet */
    p = pPkt + L2C_PAYLOAD_START;
    UINT8_TO_BSTREAM(p, SMP_CMD_SECURITY_REQ);
    UINT8_TO_BSTREAM(p, pMsg->dm.securityReq.auth);

    /* send packet */
    smpSendPkt(pCcb, pPkt);
  }
}
Esempio n. 8
0
void smprActSendPairRsp(smpCcb_t *pCcb, smpMsg_t *pMsg)
{
  uint8_t   *pPkt;
  uint8_t   *p;
  uint8_t   oob;
  uint8_t   display;

  /* build packet to pairing response buffer in ccb */
  p = pCcb->pairRsp;
  UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_RSP);
  UINT8_TO_BSTREAM(p, pSmpCfg->ioCap);
  UINT8_TO_BSTREAM(p, pMsg->dm.pair.oob);
  UINT8_TO_BSTREAM(p, pMsg->dm.pair.auth);
  UINT8_TO_BSTREAM(p, pSmpCfg->maxKeyLen);
  UINT8_TO_BSTREAM(p, pMsg->dm.pair.iKeyDist);
  UINT8_TO_BSTREAM(p, pMsg->dm.pair.rKeyDist);

  /* process pairing request and response data */
  if (smpCb.procPairing(pCcb, &oob, &display))
  {
    /* set next expected packet */
    if ((pCcb->pairReq[SMP_AUTHREQ_POS] & pMsg->dm.pair.auth & SMP_AUTH_SC_FLAG) == SMP_AUTH_SC_FLAG)
    {
      pCcb->nextCmdCode = SMP_CMD_PUBLIC_KEY;
    }
    else
    {
      pCcb->nextCmdCode = SMP_CMD_PAIR_CNF;
    }

    /* start smp response timer */
    smpStartRspTimer(pCcb);

    /* send pairing response; allocate packet buffer */
    if ((pPkt = smpMsgAlloc(SMP_PAIR_RSP_LEN + L2C_PAYLOAD_START)) != NULL)
    {
      /* build packet from pairing response buffer */
      memcpy(pPkt + L2C_PAYLOAD_START, pCcb->pairRsp, SMP_PAIR_RSP_LEN);

      /* send packet */
      smpSendPkt(pCcb, pPkt);
    }

    /* request authentication data */
    smpCb.procAuthReq(pCcb, oob, display);
  }
}
Esempio n. 9
0
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);
  }
}
Esempio n. 10
0
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);
  }
}
Esempio n. 11
0
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);
  }
}
Esempio n. 12
0
void smpActSendPairCnf(smpCcb_t *pCcb, smpMsg_t *pMsg)
{
  uint8_t   *pPkt;
  uint8_t   *p;

  /* set next expected packet */
  pCcb->nextCmdCode = (pCcb->initiator) ? SMP_CMD_PAIR_CNF : SMP_CMD_PAIR_RAND;

  /* start smp response timer */
  smpStartRspTimer(pCcb);

  /* allocate packet buffer */
  if ((pPkt = smpMsgAlloc(SMP_PAIR_CNF_LEN + L2C_PAYLOAD_START)) != NULL)
  {
    /* build packet */
    p = pPkt + L2C_PAYLOAD_START;
    UINT8_TO_BSTREAM(p, SMP_CMD_PAIR_CNF);
    memcpy(p, pMsg->aes.pCiphertext, SMP_CONFIRM_LEN);

    /* send packet */
    smpSendPkt(pCcb, pPkt);
  }
}
Esempio n. 13
0
bool_t smpSendKey(smpCcb_t *pCcb, uint8_t keyDist)
{
  uint8_t     *pPkt;
  uint8_t     *p;
  wsfMsgHdr_t *pHdr;

  if (smpCb.lescSupported && pCcb->pScCcb->lescEnabled && pCcb->lastSentKey == 0)
  {
    dmSecKeyIndEvt_t keyInd;

    /* pass LTK to app via DM */
    if (DmConnRole(pCcb->connId) == DM_ROLE_MASTER)
    {
      keyInd.type = DM_KEY_PEER_LTK;
    }
    else
    {
      keyInd.type = DM_KEY_LOCAL_LTK;
    }

    keyInd.hdr.event = DM_SEC_KEY_IND;
    keyInd.hdr.param = pCcb->connId;
    keyInd.secLevel = smpGetScSecLevel(pCcb);
    keyInd.keyData.ltk.ediv = 0;
    memset(keyInd.keyData.ltk.rand, 0, SMP_RAND8_LEN);
    Calc128Cpy(keyInd.keyData.ltk.key, pCcb->pScCcb->pLtk->ltk_t);
    DmSmpCbackExec((dmEvt_t *)&keyInd);

    pCcb->lastSentKey = SMP_CMD_MASTER_ID;
  }

  /* check if we're done sending keys */
  if ((keyDist == 0) ||
      (keyDist == SMP_KEY_DIST_ENC && pCcb->lastSentKey == SMP_CMD_MASTER_ID) ||
      (keyDist <= (SMP_KEY_DIST_ENC | SMP_KEY_DIST_ID) && pCcb->lastSentKey == SMP_CMD_ID_ADDR_INFO) ||
      (pCcb->lastSentKey == SMP_CMD_SIGN_INFO))
  {
      return TRUE;
  }

  /* if flow disabled return */
  if (pCcb->flowDisabled)
  {
    return FALSE;
  }

  /* allocate packet buffer for largest packet size */
  if ((pPkt = smpMsgAlloc(SMP_ENC_INFO_LEN + L2C_PAYLOAD_START)) != NULL)
  {
    p = pPkt + L2C_PAYLOAD_START;

    /* determine next key to send */
    if (pCcb->lastSentKey == 0 && (keyDist & SMP_KEY_DIST_ENC))
    {
      /* generate LTK, EDIV, and RAND */
      smpGenerateLtk(pCcb);

      /* send first part of LTK */
      UINT8_TO_BSTREAM(p, SMP_CMD_ENC_INFO);
      Calc128Cpy(p, pCcb->pScr->keyInd.keyData.ltk.key);
    }
    else if (pCcb->lastSentKey == SMP_CMD_ENC_INFO)
    {
      /* send second part of LTK */
      UINT8_TO_BSTREAM(p, SMP_CMD_MASTER_ID);
      UINT16_TO_BSTREAM(p, pCcb->pScr->keyInd.keyData.ltk.ediv);
      memcpy(p, pCcb->pScr->keyInd.keyData.ltk.rand, SMP_RAND8_LEN);
    }
    else if ((keyDist & SMP_KEY_DIST_ID) &&
             (pCcb->lastSentKey == 0 || pCcb->lastSentKey == SMP_CMD_MASTER_ID))
    {
      /* send first part of IRK */
      UINT8_TO_BSTREAM(p, SMP_CMD_ID_INFO);
      Calc128Cpy(p, DmSecGetLocalIrk());
    }
    else if (pCcb->lastSentKey == SMP_CMD_ID_INFO)
    {
      /* send second part of IRK */
      UINT8_TO_BSTREAM(p, SMP_CMD_ID_ADDR_INFO);
      UINT8_TO_BSTREAM(p, DM_ADDR_PUBLIC);
      BDA_TO_BSTREAM(p, HciGetBdAddr());

    }
    else if ((keyDist & SMP_KEY_DIST_SIGN) &&
             (pCcb->lastSentKey == 0 || pCcb->lastSentKey == SMP_CMD_ID_ADDR_INFO ||
              pCcb->lastSentKey == SMP_CMD_MASTER_ID))
    {
      /* send SRK */
      UINT8_TO_BSTREAM(p, SMP_CMD_SIGN_INFO);
      Calc128Cpy(p, DmSecGetLocalCsrk());
    }
    else
    {
      /* should never get here */
      WsfMsgFree(pPkt);
      SMP_TRACE_WARN2("smpSendKey unexpected state keyDist:%d lastSentKey:%d", keyDist, pCcb->lastSentKey);
      return TRUE;
    }

    /* set last sent key to command code */
    pCcb->lastSentKey = pPkt[L2C_PAYLOAD_START];

    /* send command packet */
    smpSendPkt(pCcb, pPkt);

    /* if flow not disabled set up to send next key */
    if (!pCcb->flowDisabled)
    {
      if ((pHdr = WsfMsgAlloc(sizeof(wsfMsgHdr_t))) != NULL)
      {
        pHdr->event = SMP_MSG_INT_SEND_NEXT_KEY;
        pHdr->param = pCcb->connId;
        WsfMsgSend(smpCb.handlerId, pHdr);
      }
    }
  }

  return FALSE;
}
Esempio n. 14
0
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);
    }
  }
}
Esempio n. 15
0
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);
  }
}
Esempio n. 16
0
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))
  {
    attsErrRsp(pCcb->handle, ATT_PDU_WRITE_REQ, handle, err);
  }
}