/*************************************************************************************************/
/*!
 *  \file
 *
 *  \brief  DM connection management module.
 *
 *  Copyright (c) 2016-2018 Arm Ltd. All Rights Reserved.
 *
 *  Copyright (c) 2019 Packetcraft, Inc.
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/*************************************************************************************************/
#ifndef DM_CONN_H
#define DM_CONN_H

#include "dm_api.h"

#ifdef __cplusplus
extern "C" {
#endif

/**************************************************************************************************
  Macros
**************************************************************************************************/

/* Uninitialized HCI handle */
#define DM_CONN_HCI_HANDLE_NONE             0xFFFF

/* Action set initializer */
#define DM_CONN_ACT_SET_INIT(n)             ((n) << 4)

/* Get action set ID from action */
#define DM_CONN_ACT_SET_ID(action)          ((action) >> 4)

/* Get action ID from action */
#define DM_CONN_ACT_ID(action)              ((action) & 0x0F)

/**************************************************************************************************
  Data Types
**************************************************************************************************/

/*! DM conn event handler messages for state machine */
enum
{
  /* messages from API */
  DM_CONN_MSG_API_OPEN = DM_MSG_START(DM_ID_CONN), /*!< Open Connection */
  DM_CONN_MSG_API_CLOSE,                           /*!< Close Connection */
  DM_CONN_MSG_API_ACCEPT,                          /*!< Accept Connection */

  /* messages from HCI */
  DM_CONN_MSG_HCI_LE_CONN_CMPL_FAIL,               /*!< HCI LE Connection Complete Failure Event */
  DM_CONN_MSG_HCI_LE_CONN_CMPL,                    /*!< HCI LE Connection Compelte Event */
  DM_CONN_MSG_HCI_DISCONNECT_CMPL,                 /*!< HCI Disconnection Complete Event */
  DM_CONN_MSG_HCI_LE_CONN_UPDATE_CMPL,             /*!< HCI LE Connection Update Complete Event */
  DM_CONN_MSG_HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL,  /*!< HCI LE Create Connection Cancel Command Complet Event */
};

/* Number of messages */
#define DM_CONN_NUM_MSGS    (DM_CONN_MSG_HCI_LE_CREATE_CONN_CANCEL_CMD_CMPL - DM_CONN_MSG_API_OPEN + 1)

/* DM conn event handler messages, non-state machine */
enum
{
  DM_CONN_MSG_API_FEAT  = DM_MSG_START(DM_ID_CONN_2),
  DM_CONN_MSG_API_READ_RSSI,
  DM_CONN_MSG_API_REM_CONN_PARAM_REQ_REPLY,
  DM_CONN_MSG_API_REM_CONN_PARAM_REQ_NEG_REPLY,
  DM_CONN_MSG_API_SET_DATA_LEN,
  DM_CONN_MSG_API_WRITE_AUTH_TO,
  DM_CONN_MSG_API_REQ_PEER_SCA
};

/*! DM conn event handler messages for connection update */
enum
{
  /* messages from API */
  DM_CONN_MSG_API_UPDATE_MASTER = DM_MSG_START(DM_ID_CONN_UPD), /*!< Master Connection Parameter Update */
  DM_CONN_MSG_API_UPDATE_SLAVE,                                 /*!< Slave Connecteion Parameter Update */

  /* messages from L2C */
  DM_CONN_MSG_L2C_UPDATE_IND,                                   /*!< L2CAP Parameter update indication */
  DM_CONN_MSG_L2C_UPDATE_CNF,                                   /*!< L2CAP Parameter update confirmation */
};

/* Number of messages */
#define DM_CONN_UPD_NUM_MSGS    (DM_CONN_MSG_L2C_UPDATE_CNF - DM_CONN_MSG_API_UPDATE_MASTER + 1)

/* State machine action function sets */
enum
{
  DM_CONN_ACT_SET_MAIN,
  DM_CONN_ACT_SET_MASTER,
  DM_CONN_ACT_SET_SLAVE,
  DM_CONN_NUM_ACT_SETS
};

/*! State machine actions */
enum
{
  DM_CONN_SM_ACT_NONE = DM_CONN_ACT_SET_INIT(DM_CONN_ACT_SET_MAIN),    /*!< No Action */
  DM_CONN_SM_ACT_CLOSE,                                                /*!< Process Connection Close */
  DM_CONN_SM_ACT_CONN_OPENED,                                          /*!< Procoess Connection Opened */
  DM_CONN_SM_ACT_CONN_FAILED,                                          /*!< Process Connection Failed */
  DM_CONN_SM_ACT_CONN_CLOSED,                                          /*!< Process Connection Closed */
  DM_CONN_SM_ACT_HCI_UPDATED,                                          /*!< Process HCI Connection Update */

  DM_CONN_SM_ACT_OPEN = DM_CONN_ACT_SET_INIT(DM_CONN_ACT_SET_MASTER),  /*!< Process Master Connection Open */
  DM_CONN_SM_ACT_CANCEL_OPEN,                                          /*!< Process Master Cancel Connection Open */

  DM_CONN_SM_ACT_ACCEPT = DM_CONN_ACT_SET_INIT(DM_CONN_ACT_SET_SLAVE), /*!< Process Slave Connection Accept */
  DM_CONN_SM_ACT_CANCEL_ACCEPT,                                        /*!< Process Slave Cancel Connection Accept */
  DM_CONN_SM_ACT_CONN_ACCEPTED,                                        /*!< Process Slave Connection Accepted */
  DM_CONN_SM_ACT_ACCEPT_FAILED                                         /*!< Process Slave Connection Accept Failure */
};

/*! Connection update actions */
enum
{
  DM_CONN_UPD_ACT_NONE = DM_CONN_ACT_SET_INIT(DM_CONN_ACT_SET_MAIN),            /*!< No Action */

  DM_CONN_UPD_ACT_UPDATE_MASTER = DM_CONN_ACT_SET_INIT(DM_CONN_ACT_SET_MASTER), /*!< Process Master Connection Parameter Update */
  DM_CONN_UPD_ACT_L2C_UPDATE_IND,                                               /*!< Process Master L2CAP Connection Parameter Update Indication */

  DM_CONN_UPD_ACT_UPDATE_SLAVE = DM_CONN_ACT_SET_INIT(DM_CONN_ACT_SET_SLAVE),   /*!< Process Slave Connection Update */
  DM_CONN_UPD_ACT_L2C_UPDATE_CNF                                                /*!< Process Slave L2CAP Connection Parameter Update Confirmation */
};

/*! State machine states */
enum
{
  DM_CONN_SM_ST_IDLE,          /*!< Idle State */
  DM_CONN_SM_ST_CONNECTING,    /*!< Connecting State */
  DM_CONN_SM_ST_ACCEPTING,     /*!< Accepting State */
  DM_CONN_SM_ST_CONNECTED,     /*!< Connected State */
  DM_CONN_SM_ST_DISCONNECTING, /*!< Disconnecting State */
  DM_CONN_SM_NUM_STATES
};

/* Data structure for DM_CONN_MSG_API_OPEN and DM_CONN_MSG_API_ACCEPT */
typedef struct
{
  wsfMsgHdr_t             hdr;
  uint8_t                 initPhys;
  uint8_t                 advHandle;
  uint8_t                 advType;
  uint16_t                duration;
  uint8_t                 maxEaEvents;
  bdAddr_t                peerAddr;
  uint8_t                 addrType;
  uint8_t                 clientId;
} dmConnApiOpen_t;

/*! Data structure for DM_CONN_MSG_API_CLOSE */
typedef struct
{
  wsfMsgHdr_t              hdr;
  uint8_t                  reason;
  uint8_t                  clientId;
} dmConnApiClose_t;

/*! Data structure for DM_CONN_MSG_API_UPDATE_MASTER and SLAVE */
typedef struct
{
  wsfMsgHdr_t             hdr;
  hciConnSpec_t           connSpec;
} dmConnApiUpdate_t;

/*! Data structure for DM_CONN_MSG_L2C_UPDATE_IND */
typedef struct
{
  wsfMsgHdr_t             hdr;
  hciConnSpec_t           *pConnSpec;
  uint8_t                 identifier;
} dmConnL2cUpdateInd_t;

/*! Data structure for DM_CONN_MSG_L2C_UPDATE_CNF */
typedef struct
{
  wsfMsgHdr_t             hdr;
  uint16_t                result;
} dmConnL2cUpdateCnf_t;

/*! Union of all DM Conn state machine messages */
typedef union
{
  wsfMsgHdr_t               hdr;
  dmConnApiOpen_t           apiOpen;
  dmConnApiClose_t          apiClose;
  dmConnApiUpdate_t         apiUpdate;
  dmConnL2cUpdateInd_t      l2cUpdateInd;
  dmConnL2cUpdateCnf_t      l2cUpdateCnf;
  hciLeConnCmplEvt_t        hciLeConnCmpl;
  hciDisconnectCmplEvt_t    hciDisconnectCmpl;
  hciLeConnUpdateCmplEvt_t  hciLeConnUpdateCmpl;
} dmConnMsg_t;

/*! Data structure for DM_CONN_MSG_API_FEAT */
typedef struct
{
  wsfMsgHdr_t             hdr;
  uint16_t                handle;
  uint8_t                 type;
  uint8_t                 feature;
} dmConnApiFeat_t;

/*! Data structure for DM_CONN_MSG_API_READ_RSSI */
typedef struct
{
  wsfMsgHdr_t             hdr;
} dmConnApiReadRssi_t;

/*! Data structure for DM_CONN_MSG_API_REM_CONN_PARAM_REQ_REPLY */
typedef struct
{
  wsfMsgHdr_t             hdr;
  hciConnSpec_t           connSpec;
} dmConnApiRemConnParamReqReply_t;

/*! Data structure for DM_CONN_MSG_API_REM_CONN_PARAM_REQ_NEG_REPLY */
typedef struct
{
  wsfMsgHdr_t             hdr;
  uint8_t                 reason;
} dmConnApiRemConnParamReqNegReply_t;

/*! Data structure for DM_CONN_MSG_API_SET_DATA_LEN */
typedef struct
{
  wsfMsgHdr_t             hdr;
  uint16_t                txOctets;
  uint16_t                txTime;
} dmConnApiSetDataLen_t;

/*! Data structure for DM_CONN_MSG_API_WRITE_AUTH_TO */
typedef struct
{
  wsfMsgHdr_t             hdr;
  uint16_t                timeout;
} dmConnApiWriteAuthPayloadTo_t;

/*! Data structure for DM_CONN_MSG_API_REQ_PEER_SCA */
typedef struct
{
  wsfMsgHdr_t             hdr;
} dmConnApiReqPeerSca_t;

/*! Union of all DM Conn 2 messages */
typedef union
{
  wsfMsgHdr_t                         hdr;
  dmConnApiFeat_t                     apiFeat;
  dmConnApiReadRssi_t                 apiReadRssi;
  dmConnApiRemConnParamReqReply_t     apiRemConnParamReqReply;
  dmConnApiRemConnParamReqNegReply_t  apiRemConnParamReqNegReply;
  dmConnApiSetDataLen_t               apiSetDataLen;
  dmConnApiWriteAuthPayloadTo_t       apiWriteAuthPayloadTo;
  dmConnApiReqPeerSca_t               apiReqPeerSca;
} dmConn2Msg_t;

/*! Connection control block */
typedef struct
{
  bdAddr_t        peerAddr;
  bdAddr_t        localAddr;
  uint16_t        handle;
  uint16_t        idleMask;
  dmConnId_t      connId;
  bool_t          updating;
  bool_t          usingLtk;
  uint8_t         peerAddrType;
  uint8_t         localAddrType;
  uint8_t         state;
  uint8_t         inUse;
  uint8_t         secLevel;
  uint8_t         tmpSecLevel;
  uint8_t         role;

  /* enhanced fields */
  bdAddr_t        localRpa;
  bdAddr_t        peerRpa;

  uint32_t        features;
  bool_t          featuresPresent;
} dmConnCcb_t;

/*! Action function */
typedef void (*dmConnAct_t)(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);

/*! Control block of the DM conn module */
typedef struct
{
  dmConnCcb_t     ccb[DM_CONN_MAX];
  dmCback_t       connCback[DM_CLIENT_ID_MAX];
  hciConnSpec_t   connSpec[DM_NUM_PHYS];
  uint16_t        scanInterval[DM_NUM_PHYS];
  uint16_t        scanWindow[DM_NUM_PHYS];
} dmConnCb_t;

/**************************************************************************************************
  Global Variables
**************************************************************************************************/

/*! State machine action sets */
extern dmConnAct_t *dmConnActSet[DM_CONN_NUM_ACT_SETS];

/*! Connection update action sets */
extern dmConnAct_t *dmConnUpdActSet[DM_CONN_NUM_ACT_SETS];

/* Action set for master connection update */
extern const dmConnAct_t dmConnUpdActSetMaster[];

/* Action set for slave connection update */
extern const dmConnAct_t dmConnUpdActSetSlave[];

/*! Control block */
extern dmConnCb_t dmConnCb;

/**************************************************************************************************
  Function Declarations
**************************************************************************************************/

/* utility functions */
dmConnCcb_t *dmConnCcbAlloc(uint8_t *pAddr);
void dmConnCcbDealloc(dmConnCcb_t *pCcb);
dmConnCcb_t *dmConnCcbByHandle(uint16_t handle);
dmConnCcb_t *dmConnCcbByBdAddr(uint8_t *pAddr);
dmConnCcb_t *dmConnCcbById(dmConnId_t connId);
uint8_t dmConnNum(void);
dmConnId_t dmConnOpenAccept(uint8_t clientId, uint8_t initPhys, uint8_t advHandle, uint8_t advType,
                            uint16_t duration, uint8_t maxEaEvents, uint8_t addrType, uint8_t *pAddr,
                            uint8_t role);
void dmConnExecCback(dmConnMsg_t *pMsg);

/* component inteface */
void dmConnReset(void);
void dmConnMsgHandler(wsfMsgHdr_t *pMsg);
void dmConnHciHandler(hciEvt_t *pEvent);

/* component 2 inteface */
void dmConn2MsgHandler(wsfMsgHdr_t *pMsg);
void dmConn2HciHandler(hciEvt_t *pEvent);

/* connection update inteface */
void dmConnUpdMsgHandler(wsfMsgHdr_t *pMsg);

/* state machine */
void dmConnSmExecute(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);

/* connection update action execute */
void dmConnUpdExecute(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);

/* main action functions */
void dmConnSmActNone(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
void dmConnSmActClose(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
void dmConnSmActConnOpened(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
void dmConnSmActConnFailed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
void dmConnSmActConnClosed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
void dmConnSmActHciUpdated(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);

/* connection update main action function */
void dmConnUpdActNone(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);

/* common master action functions */
void dmConnSmActCancelOpen(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);

/* connection update common master action functions */
void dmConnUpdActUpdateMaster(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
void dmConnUpdActL2cUpdateInd(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);

/* legacy master action functions */
void dmConnSmActOpen(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);

/* extended master action functions */
void dmExtConnSmActOpen(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);

/* connection update common slave action functions */
void dmConnUpdActUpdateSlave(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
void dmConnUpdActL2cUpdateCnf(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);

/* legacy slave action functions */
void dmConnSmActAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
void dmConnSmActCancelAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
void dmConnSmActConnAccepted(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
void dmConnSmActAcceptFailed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);

/* extended slave action functions */
void dmExtConnSmActAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
void dmExtConnSmActCancelAccept(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
void dmExtConnSmActConnAccepted(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);
void dmExtConnSmActAcceptFailed(dmConnCcb_t *pCcb, dmConnMsg_t *pMsg);

#ifdef __cplusplus
};
#endif

#endif /* DM_CONN_H */
