// ****************************************************************************
//
//  vole_main.c
//! @file
//!
//! @brief Ambiq Micro's demonstration of Voice Over LE service.
//!
//! @{
//
// ****************************************************************************

//*****************************************************************************
//
// Copyright (c) 2018, Ambiq Micro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision 1.2.12 of the AmbiqSuite Development Package.
//
//*****************************************************************************

#include "usr_include.h"

#include <string.h>
#include "wsf_types.h"
#include "bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "hci_api.h"
#include "dm_api.h"
#include "att_api.h"
#include "smp_api.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "app_hw.h"
#include "svc_ch.h"
#include "svc_core.h"
#include "svc_dis.h"

#include "vole_api.h"
#include "svc_amvole.h"
#include "vole_common.h"
#include "vole_board_config.h"

#include "am_util.h"
#include "crc32.h"
#include "am_app_KWD_AMA.h"
#include "FreeRTOS.h"
#include "task.h"
#include "voles_api.h"


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

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

/*! Application message type */
typedef union
{
  wsfMsgHdr_t     hdr;
  dmEvt_t         dm;
  attsCccEvt_t    ccc;
  attEvt_t        att;
} amvoleMsg_t;


/**************************************************************************************************
  Configurable Parameters
**************************************************************************************************/

/*! configurable parameters for advertising */
static const appAdvCfg_t amvoleAdvCfg =
{
  {60000,     0,     0},                  /*! Advertising durations in ms */
  {  800,   800,     0}                   /*! Advertising intervals in 0.625 ms units */
};

/*! configurable parameters for slave */
static const appSlaveCfg_t amvoleSlaveCfg =
{
  VOLE_CONN_MAX,                           /*! Maximum connections */
};

/*! configurable parameters for security */
static const appSecCfg_t amvoleSecCfg =
{
  DM_AUTH_BOND_FLAG | DM_AUTH_SC_FLAG,    /*! Authentication and bonding flags */
  0,                                      /*! Initiator key distribution flags */
  DM_KEY_DIST_LTK,                        /*! Responder key distribution flags */
  FALSE,                                  /*! TRUE if Out-of-band pairing data is present */
  FALSE                                   /*! TRUE to initiate security upon connection */
};


static const appUpdateCfg_t amvoleUpdateCfg =
{
  1000,//3000,                                   /*! Connection idle period in ms before attempting
                                              //connection parameter update; set to zero to disable */
  6,    //6,                                      /*! 7.5ms */
  12,   //15,                                     /*! 30ms */
  0,                                      /*! Connection latency */
  400, //2000, //600,                                    /*! Supervision timeout in 10ms units */
  5                                       /*! Number of update attempts before giving up */
};

/*! SMP security parameter configuration */
static const smpCfg_t amvoleSmpCfg =
{
  3000,                                   /*! 'Repeated attempts' timeout in msec */
  SMP_IO_NO_IN_NO_OUT,                    /*! I/O Capability */
  7,                                      /*! Minimum encryption key length */
  16,                                     /*! Maximum encryption key length */
  3,                                      /*! Attempts to trigger 'repeated attempts' timeout */
  0,                                      /*! Device authentication requirements */
};

/**************************************************************************************************
  Advertising Data
**************************************************************************************************/

// RAM buffers to be used
uint8_t amvoleAdvDataDisc[31];
uint8_t amvoleScanDataDisc[31];

/*! advertising data, discoverable mode */
static const uint8_t amvoleAdvDataDiscDefault[] =
{
  /*! flags */
  2,                                      /*! length */
  DM_ADV_TYPE_FLAGS,                      /*! AD type */
  DM_FLAG_LE_GENERAL_DISC |               /*! flags */
  DM_FLAG_LE_BREDR_NOT_SUP,

  /*! tx power */
  2,                                      /*! length */
  DM_ADV_TYPE_TX_POWER,                   /*! AD type */
  0,                                      /*! tx power */

  /*! service UUID list */
  17,                                      /*! length */
  DM_ADV_TYPE_128_UUID,                    /*! AD type */
  ATT_UUID_VOLES_SERVICE
};

/*! scan data, discoverable mode */
static const uint8_t amvoleScanDataDiscDefault[] =
{
  /*! device name */
  5,                                      /*! length */
  DM_ADV_TYPE_LOCAL_NAME,                 /*! AD type */
  'V',
  'o',
  'L',
  'E'
};

dmConnId_t g_AmaConnId = DM_CONN_ID_NONE;

bool_t g_start_voice_send = FALSE;

extern bool am_app_KWD_AMA_tx_ver_exchange_send(void);

// index is the starting point of the local name, local name only in advData
static bool amvoleSetLocalName(uint8_t* pAdvData, uint8_t* pLocalName, uint8_t len, uint8_t index)
{
    if(index+len+1 > 31)
    {
        // max adv data is 31 byte long
        return false;
    }

    // set parameter length
    pAdvData[index] = len + 1;
    // set parameter type
    pAdvData[index+1] = DM_ADV_TYPE_LOCAL_NAME;

    // set local name
    for(uint8_t i = 0; i < len; i++)
    {
        pAdvData[i+2+index] = pLocalName[i];
    }

    return true;
}

void amvoleKwdSetDemoName(void)
{
    uint8_t test_bdaddress[6];
    uint8_t ble_device_name[20] = "VOLES-";    //local name = device name
    uint8_t * pBda;
	uint8_t index = 6;

    //fixme: read bd address and print out
    pBda = HciGetBdAddr();
    BdaCpy(test_bdaddress, pBda);

    pBda = (uint8_t*)Bda2Str(test_bdaddress);
    APP_TRACE_INFO0("Local Device BD Address: ");
    APP_TRACE_INFO0(Bda2Str(test_bdaddress));
    APP_TRACE_INFO0("\n");

    // build demo name here
    // 1st letter is board variant
#if USE_MAYA
    ble_device_name[index++] = 'M';
#elif USE_APOLLO2_QT
    ble_device_name[index++] = 'Q';
#else
    ble_device_name[index++] = 'E';
#endif


    // 3rd letter is wake-on-sound variant
#if USE_WAKE_ON_SOUND
    ble_device_name[index++] = 'W';
#else
    ble_device_name[index++] = 'A';   // A for always listening...
#endif


    ble_device_name[index++] = '-';

    ble_device_name[index++] = 'A';
    ble_device_name[index++] = 'M';
    ble_device_name[index++] = 'A';

    // a hyphen...
    ble_device_name[index++] = '-';

    // take the last 4 hex digit
    ble_device_name[index++] = pBda[8];
    ble_device_name[index++] = pBda[9];
    ble_device_name[index++] = pBda[10];
    ble_device_name[index++] = pBda[11];

    // set local name here:
    amvoleSetLocalName(amvoleScanDataDisc, ble_device_name, index, 0);
    //SvcCoreSetDevName(ble_device_name, index);
}

/**************************************************************************************************
  Client Characteristic Configuration Descriptors
**************************************************************************************************/

/*! enumeration of client characteristic configuration descriptors */
enum
{
  VOLES_GATT_SC_CCC_IDX,                  /*! GATT service, service changed characteristic */
  VOLES_TX_CCC_IDX,                /*! AMDTP service, tx characteristic */
  VOLES_NUM_CCC_IDX
};

/*! client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t amvoleCccSet[VOLES_NUM_CCC_IDX] =
{
  /* cccd handle                value range               security level */
  {GATT_SC_CH_CCC_HDL,          ATT_CLIENT_CFG_INDICATE,  DM_SEC_LEVEL_NONE},   /* VOLES_GATT_SC_CCC_IDX */
  {VOLES_TX_CH_CCC_HDL,        ATT_CLIENT_CFG_NOTIFY,    DM_SEC_LEVEL_NONE},   /* VOLES_TX_CCC_IDX */
};

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

/*! WSF handler ID */
wsfHandlerId_t amvoleHandlerId;

/*************************************************************************************************/
/*!
 *  \fn     amvoleDmCback
 *
 *  \brief  Application DM callback.
 *
 *  \param  pDmEvt  DM callback event
 *
 *  \return None.
 */
/*************************************************************************************************/
static void amvoleDmCback(dmEvt_t *pDmEvt)
{
  dmEvt_t *pMsg;
  uint16_t len;

  len = DmSizeOfEvt(pDmEvt);

  if ((pMsg = WsfMsgAlloc(len)) != NULL)
  {
    memcpy(pMsg, pDmEvt, len);
    WsfMsgSend(amvoleHandlerId, pMsg);
  }
}

bool amvoleTxChannelIsAvailable(void)
{
  return(DM_CONN_ID_NONE != AppConnIsOpen());
}

void VoleBleSend(uint8_t * buf, uint32_t len)
{
    if(amvoleTxChannelIsAvailable())
    {
        // simply tries to send notification
        AttsHandleValueNtf(g_AmaConnId, VOLES_TX_HDL, len, buf);   // connId always group 0 since support only 1 connection.
    }
}

/*************************************************************************************************/
/*!
 *  \fn     amvoleAttCback
 *
 *  \brief  Application ATT callback.
 *
 *  \param  pEvt    ATT callback event
 *
 *  \return None.
 */
/*************************************************************************************************/
static void amvoleAttCback(attEvt_t *pEvt)
{
  attEvt_t *pMsg;

  if ((pMsg = WsfMsgAlloc(sizeof(attEvt_t) + pEvt->valueLen)) != NULL)
  {
    memcpy(pMsg, pEvt, sizeof(attEvt_t));
    pMsg->pValue = (uint8_t *) (pMsg + 1);
    memcpy(pMsg->pValue, pEvt->pValue, pEvt->valueLen);
    WsfMsgSend(amvoleHandlerId, pMsg);
  }
}

/*************************************************************************************************/
/*!
 *  \fn     amvoleCccCback
 *
 *  \brief  Application ATTS client characteristic configuration callback.
 *
 *  \param  pDmEvt  DM callback event
 *
 *  \return None.
 */
/*************************************************************************************************/
static void amvoleCccCback(attsCccEvt_t *pEvt)
{
  attsCccEvt_t  *pMsg;
  appDbHdl_t    dbHdl;

  /* if CCC not set from initialization and there's a device record */
  if ((pEvt->handle != ATT_HANDLE_NONE) &&
      ((dbHdl = AppDbGetHdl((dmConnId_t) pEvt->hdr.param)) != APP_DB_HDL_NONE))
  {
    /* store value in device database */
    AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value);
  }

  if ((pMsg = WsfMsgAlloc(sizeof(attsCccEvt_t))) != NULL)
  {
    memcpy(pMsg, pEvt, sizeof(attsCccEvt_t));
    WsfMsgSend(amvoleHandlerId, pMsg);
  }
}

/*************************************************************************************************/
/*!
 *  \fn     amvoleProcCccState
 *
 *  \brief  Process CCC state change.
 *
 *  \param  pMsg    Pointer to message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void amvoleProcCccState(amvoleMsg_t *pMsg)
{
  APP_TRACE_INFO3("ccc state ind value:%d handle:%d idx:%d\n", pMsg->ccc.value, pMsg->ccc.handle, pMsg->ccc.idx);

  /* VOLES TX CCC */
  if (pMsg->ccc.idx == VOLES_TX_CCC_IDX)
  {
    if (pMsg->ccc.value == ATT_CLIENT_CFG_NOTIFY)
    {
      // notify enabled
      g_AmaConnId = (dmConnId_t) pMsg->ccc.hdr.param;
      APP_TRACE_INFO1("connId : %d\r\n", pMsg->ccc.hdr.param);
    }
    else
    {
      g_AmaConnId = DM_CONN_ID_NONE;
    }
    return;
  }
}

/*************************************************************************************************/
/*!
 *  \fn     amvoleClose
 *
 *  \brief  Perform UI actions on connection close.
 *
 *  \param  pMsg    Pointer to message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void amvoleClose(amvoleMsg_t *pMsg)
{
}

/*************************************************************************************************/
/*!
 *  \fn     amvoleSetup
 *
 *  \brief  Set up advertising and other procedures that need to be performed after
 *          device reset.
 *
 *  \param  pMsg    Pointer to message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void amvoleSetup(amvoleMsg_t *pMsg)
{
  /* set advertising and scan response data for discoverable mode */
  AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(amvoleAdvDataDisc), (uint8_t *) amvoleAdvDataDisc);
  AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(amvoleScanDataDisc), (uint8_t *) amvoleScanDataDisc);

  /* set advertising and scan response data for connectable mode */
  AppAdvSetData(APP_ADV_DATA_CONNECTABLE, sizeof(amvoleAdvDataDisc), (uint8_t *) amvoleAdvDataDisc);
  AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, sizeof(amvoleScanDataDisc), (uint8_t *) amvoleScanDataDisc);

  AppSetBondable(TRUE);
  /* start advertising; automatically set connectable/discoverable mode and bondable mode */
  AppAdvStart(APP_MODE_AUTO_INIT);
}


//*****************************************************************************
//
// LED task to indicate external events, such as heart beat and key word detected.
//
//*****************************************************************************

void am_app_led_on(void)
{
#if defined (AM_PART_APOLLO2)
        am_hal_gpio_out_bit_toggle(LED_D6);
        am_hal_gpio_out_bit_toggle(LED_D7);
        am_hal_gpio_out_bit_toggle(LED_D8);
#endif // #if defined (AM_PART_APOLLO2)

#if defined (AM_PART_APOLLO3)
#if USE_APOLLO3_BLUE_EVB
        am_hal_gpio_state_write(LED_D5, AM_HAL_GPIO_OUTPUT_TOGGLE);
        am_hal_gpio_state_write(LED_D6, AM_HAL_GPIO_OUTPUT_TOGGLE);
        am_hal_gpio_state_write(LED_D7, AM_HAL_GPIO_OUTPUT_TOGGLE);
        am_hal_gpio_state_write(LED_D8, AM_HAL_GPIO_OUTPUT_TOGGLE);

#endif // #if USE_APOLLO3_BLUE_EVB
#endif //#if defined (AM_PART_APOLLO3)

}

void am_app_led_off(void)
{
#if defined (AM_PART_APOLLO2)
    am_hal_gpio_out_bit_clear(LED_D6);
    am_hal_gpio_out_bit_clear(LED_D7);
    am_hal_gpio_out_bit_clear(LED_D8);
#endif

#if defined (AM_PART_APOLLO3)
#if USE_APOLLO3_BLUE_EVB
    am_hal_gpio_state_write(LED_D5, AM_HAL_GPIO_OUTPUT_CLEAR);
    am_hal_gpio_state_write(LED_D6, AM_HAL_GPIO_OUTPUT_CLEAR);
    am_hal_gpio_state_write(LED_D7, AM_HAL_GPIO_OUTPUT_CLEAR);
    am_hal_gpio_state_write(LED_D8, AM_HAL_GPIO_OUTPUT_CLEAR);
#endif
#endif
}

/*************************************************************************************************/
/*!
 *  \fn     amvoleBtnCback
 *
 *  \brief  Button press callback.
 *
 *  \param  btn    Button press.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void amvoleBtnCback(uint8_t btn)
{
  dmConnId_t      connId = AppConnIsOpen();

  APP_TRACE_INFO2("button %d pressed, connection open:%d", btn, connId);
   
  /* button actions when connected */
  if (connId != DM_CONN_ID_NONE)
  {
    APP_TRACE_INFO1("btn:%d", btn);
    switch (btn)
    {
      // transmit voice data using mSBC encode
      case APP_UI_BTN_1_SHORT:
        APP_TRACE_INFO0("start speech data send...");
        voles_set_codec_type(MSBC_CODEC_IN_USE);
        voles_init(amvoleHandlerId, MSBC_CODEC_IN_USE);
        am_app_KWD_AMA_start_speech_send();
        break;

      case APP_UI_BTN_2_SHORT:
       voles_set_codec_type(OPUS_CODEC_IN_USE);
        voles_init(amvoleHandlerId, OPUS_CODEC_IN_USE);
       am_app_KWD_AMA_start_speech_send();
        break;

      default:
        break;
    }
  }
}


uint8_t
amvole_write_cback(dmConnId_t connId, uint16_t handle, uint8_t operation,
                       uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr)
{
    if (handle == VOLES_RX_HDL)
    {
        am_app_KWD_AMA_rx_handler(pValue, len);
    }
    return ATT_SUCCESS;
}

/*************************************************************************************************/
/*!
 *  \fn     amvoleProcMsg
 *
 *  \brief  Process messages from the event handler.
 *
 *  \param  pMsg    Pointer to message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void amvoleProcMsg(amvoleMsg_t *pMsg)
{
  uint8_t uiEvent = APP_UI_NONE;

  static uint8_t retry_cnt = 0;

  switch(pMsg->hdr.event)
  {    
    case ATTS_HANDLE_VALUE_CNF:

      voles_proc_msg(&pMsg->hdr);
      
      break;

    case ATTS_CCC_STATE_IND:
      amvoleProcCccState(pMsg);
      if(pMsg->ccc.handle == VOLES_TX_CH_CCC_HDL)
          am_app_KWD_AMA_tx_ver_exchange_send();
      break;

    case DM_RESET_CMPL_IND:
      DmSecGenerateEccKeyReq();

      #if USE_BLE_TX_POWER_SET
        HciVsEM_SetRfPowerLevelEx(TX_POWER_LEVEL_PLUS_6P2_dBm);
      #endif

      uiEvent = APP_UI_RESET_CMPL;
      break;

    case DM_ADV_START_IND:
      uiEvent = APP_UI_ADV_START;
      break;

    case DM_ADV_STOP_IND:
      uiEvent = APP_UI_ADV_STOP;
      break;

    case DM_CONN_OPEN_IND:
      voles_proc_msg(&pMsg->hdr);
      DmConnSetDataLen(1, 251, 0x848);

      uiEvent = APP_UI_CONN_OPEN;

      retry_cnt = 0;

      break;

    case ATT_MTU_UPDATE_IND:
      if(AttGetMtu(1) < BLE_MSBC_DATA_BUFFER_SIZE) {
        if(retry_cnt < 5)
        {
          retry_cnt++;
          AttcMtuReq(1, 247);
        }
      }

      APP_TRACE_INFO2("ATT_MTU_UPDATE_IND AttGetMtu(), return = %d pMsg->att.mtu = %d\n", AttGetMtu(1), pMsg->att.mtu);
      break;

    case DM_CONN_DATA_LEN_CHANGE_IND:
      am_util_debug_printf("DM_CONN_DATA_LEN_CHANGE_IND: status = %d, max RX len = %d, max TX len = %d \n", pMsg->dm.dataLenChange.hdr.status, pMsg->dm.dataLenChange.maxRxOctets, pMsg->dm.dataLenChange.maxTxOctets);
      break;

    case DM_CONN_CLOSE_IND:
      APP_TRACE_INFO1("DM_CONN_CLOSE_IND reason = 0x%02x\n", pMsg->dm.connClose.reason);

      amvoleClose(pMsg);
      uiEvent = APP_UI_CONN_CLOSE;

//      AppAdvStart(APP_MODE_DISCOVERABLE);

      g_AmaConnId = DM_CONN_ID_NONE;
      //g_eAmaStatus = VOS_AMA_INIT;
      break;

    case DM_CONN_UPDATE_IND:
      voles_proc_msg(&pMsg->hdr);
      break;

    case DM_SEC_PAIR_CMPL_IND:
      uiEvent = APP_UI_SEC_PAIR_CMPL;
      break;

    case DM_SEC_PAIR_FAIL_IND:
      uiEvent = APP_UI_SEC_PAIR_FAIL;
      break;

    case DM_SEC_ENCRYPT_IND:
      uiEvent = APP_UI_SEC_ENCRYPT;
      break;

    case DM_SEC_ENCRYPT_FAIL_IND:
      uiEvent = APP_UI_SEC_ENCRYPT_FAIL;
      break;

    case DM_SEC_AUTH_REQ_IND:
      AppHandlePasskey(&pMsg->dm.authReq);
      break;

    case DM_SEC_ECC_KEY_IND:
      amvoleSetup(pMsg);
      DmSecSetEccKey(&pMsg->dm.eccMsg.data.key);
      break;

    case DM_SEC_COMPARE_IND:
      AppHandleNumericComparison(&pMsg->dm.cnfInd);
      break;

    default:
      break;
  }

  if (uiEvent != APP_UI_NONE)
  {
    AppUiAction(uiEvent);
  }
}

void amvoleStartSendVoiceData()
{
    g_start_voice_send = TRUE;
    am_app_led_on();
    voles_transmit_voice_data();
}
/*************************************************************************************************/
/*!
 *  \fn     VoleHandlerInit
 *
 *  \brief  Application handler init function called during system initialization.
 *
 *  \param  handlerID  WSF handler ID.
 *
 *  \return None.
 */
/*************************************************************************************************/
void VoleHandlerInit(wsfHandlerId_t handlerId)
{
  APP_TRACE_INFO0("VoleHandlerInit");

  /* store handler ID */
  amvoleHandlerId = handlerId;

  /* Set configuration pointers */
  pAppAdvCfg = (appAdvCfg_t *) &amvoleAdvCfg;
  pAppSlaveCfg = (appSlaveCfg_t *) &amvoleSlaveCfg;
  pAppSecCfg = (appSecCfg_t *) &amvoleSecCfg;
  pAppUpdateCfg = (appUpdateCfg_t *) &amvoleUpdateCfg;

  /* Initialize application framework */
  AppSlaveInit();

  /* Set stack configuration pointers */
  pSmpCfg = (smpCfg_t *) &amvoleSmpCfg;
  //voles_init(handlerId);
}

/*************************************************************************************************/
/*!
 *  \fn     VoleHandler
 *
 *  \brief  WSF event handler for application.
 *
 *  \param  event   WSF event mask.
 *  \param  pMsg    WSF message.
 *
 *  \return None.
 */
/*************************************************************************************************/
void VoleHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
{
  if (pMsg != NULL)
  {
    APP_TRACE_INFO1("vole got evt 0x%x", pMsg->event);

    if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
    {
      /* process advertising and connection-related messages */
      AppSlaveProcDmMsg((dmEvt_t *) pMsg);

      /* process security-related messages */
      AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
    }

    /* perform profile and user interface-related operations */
    amvoleProcMsg((amvoleMsg_t *) pMsg);
  }
}

/*************************************************************************************************/
/*!
 *  \fn     VoleStart
 *
 *  \brief  Start the application.
 *
 *  \return None.
 */
/*************************************************************************************************/
void VoleStart(void)
{
  /* Register for stack callbacks */
  DmRegister(amvoleDmCback);
  DmConnRegister(DM_CLIENT_ID_APP, amvoleDmCback);
  AttRegister(amvoleAttCback);
  AttConnRegister(AppServerConnCback);
  AttsCccRegister(VOLES_NUM_CCC_IDX, (attsCccSet_t *) amvoleCccSet, amvoleCccCback);

  /* Register for app framework callbacks */
  AppUiBtnRegister(amvoleBtnCback);

  // set up adv data
  memcpy(amvoleAdvDataDisc, amvoleAdvDataDiscDefault, sizeof(amvoleAdvDataDiscDefault));
  memcpy(amvoleScanDataDisc, amvoleScanDataDiscDefault, sizeof(amvoleScanDataDiscDefault));

  /* Initialize attribute server database */
  SvcCoreAddGroup();
  SvcDisAddGroup();
  SvcVolesCbackRegister(NULL, amvole_write_cback);
  SvcVolesAddGroup();

  /* Reset the device */
  DmDevReset();
}