// ****************************************************************************
//
//  amvos_main.c
//! @file
//!
//! @brief Ambiq Micro's demonstration of AMVOS service.
//!
//! @{
//
// ****************************************************************************

//*****************************************************************************
//
// Copyright (c) 2024, Ambiq Micro, Inc.
// 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 ambiqvos_r4.5-fdfa8cf6a4 of the AmbiqSuite Development Package.
//
//*****************************************************************************

#include "am_vos_sys_config.h"
#include "am_vos_board_setup.h"

#include <stdbool.h>
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

#include "wsf_types.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "hci_api.h"
#include "bstream.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 "gatt_api.h"

#include "hci_drv_apollo.h"
#include "hci_core.h"

#include "am_vos_utils.h"
#include "am_vos_init.h"
#include "am_vos_audio.h"
#include "am_vos_codec.h"
#include "am_vos_ble.h"

#include "amvos_api.h"
#include "svc_amvos.h"

#ifdef USE_BLE_OTA
#include "amotas_api.h"
#include "svc_amotas.h"
#endif // USE_BLE_OTA

#if configUSE_AMVOS_AMA
#include "am_vos_ama.h"
#endif // configUSE_AMVOS_AMA

#if configUSE_AMVOS_ATVV
#include "am_vos_atvv.h"
#endif

#include "svc_hid.h"
#include "hid/hid_api.h"
#include "svc_batt.h"
#include "bas/bas_api.h"

#include "am_util.h"
#include "crc32.h"

extern hciCoreCb_t hciCoreCb;

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

/*! WSF message event starting value */
#define AMVOS_MSG_START               0xA0

/*! WSF message event enumeration */
enum
{
#ifdef USE_BLE_OTA
  AMOTA_RESET_TIMER_IND = AMVOS_MSG_START,      // AMOTA reset timer expired
  AMOTA_DISCONNECT_TIMER_IND,                   // AMOTA disconnect timer expired
  AMVOS_TX_TIMEOUT_TIMER_IND,                   // ble controller tx event timeout timer
#else // USE_BLE_OTA
  AMVOS_TX_TIMEOUT_TIMER_IND = AMVOS_MSG_START, // ble controller tx event timeout timer
#endif // USE_BLE_OTA
#if configUSE_AMVOS_BATT
  AMVOS_BATT_TIMER_IND,
#endif // configUSE_AMVOS_BATT
  AMVOS_RSP_TIMER_START_IND,                    // event to trigger a wsfTimer from other context 
  AMVOS_RSP_TIMEOUT_TIMER_IND,                  // app response event timeout timer
  AMVOS_TX_CCC_TIMEOUT_TIMER_IND,               // app setup event timeout timer
  AMVOS_TEST_TIMEOUT_TIMER_IND,                 // test timeout timer
#if configUSE_BLE_Measure_Throughput
  AMVOS_MEAS_TP_TIMER_IND,
#endif // configUSE_BLE_Measure_Throughput
};

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

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

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

/*! configurable parameters for advertising */
static const appAdvCfg_t amvosAdvCfg =
{
  {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 amvosSlaveCfg =
{
  AMVOS_CONN_MAX,                           /*! Maximum connections */
};

/*! configurable parameters for security */
static const appSecCfg_t amvosSecCfg =
{
#if configUSE_BLE_SECURE_CONNECTION
  DM_AUTH_BOND_FLAG | DM_AUTH_SC_FLAG,    /*! Authentication and bonding flags */
#else // configUSE_BLE_SECURE_CONNECTION
  DM_AUTH_BOND_FLAG,
#endif // configUSE_BLE_SECURE_CONNECTION
  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 appUpdateCfg_t amvosUpdateCfg =
{
  1000,//3000,                                   /*! Connection idle period in ms before attempting
                                              //connection parameter update; set to zero to disable */
  6,    //6,                                      /*! 7.5ms */
  12,   //15,                                     /*! 15ms */
  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 amvosSmpCfg =
{
  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 */
};

#ifdef USE_BLE_OTA
/*! AMOTAS configuration */
static const AmotasCfg_t vosAmotaCfg =
{
    0
};
#endif // USE_BLE_OTA

#if configUSE_AMVOS_BATT
/*! battery measurement configuration */
static const basCfg_t amvosBasCfg =
{
  30,       /*! Battery measurement timer expiration period in seconds */
  1,        /*! Perform battery measurement after this many timer periods */
  100       /*! Send battery level notification to peer when below this level. */
};
#endif // configUSE_AMVOS_BATT
/**************************************************************************************************
  Advertising Data
**************************************************************************************************/

// RAM buffers to be used
uint8_t amvosAdvDataDisc[31];
uint8_t amvosScanDataDisc[31];

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

#if configUSE_AMVOS_HID
  5,                                      /*! length */
  DM_ADV_TYPE_16_UUID,                    /*! AD type */
  UINT16_TO_BYTES(ATT_UUID_HID_SERVICE),        // For HID standalone operation. (It should be before AMA UUID.)
  UINT16_TO_BYTES(0xFE03),

  /*! service UUID list */
  0x15,                                    /*! length */
  DM_ADV_TYPE_SERVICE_DATA,                /*! AD type */
  UINT16_TO_BYTES(0xFE03),
  UINT16_TO_BYTES(AMA_VID),                /* Vendor ID assigned by BT */
  UINT16_TO_BYTES(0x0001),                 /* Alexa Built-in Headphone */
  ACCESSORY_COLOR_INDEX,                   /* Accessory color index */
  0,
  0,
  0,
  0,
  0, 0, 0
#else
  3,                                      /*! length */
  DM_ADV_TYPE_16_UUID,                    /*! AD type */
  UINT16_TO_BYTES(0xFE03),

  /*! service UUID list */
  0x17,                                    /*! length */
  DM_ADV_TYPE_SERVICE_DATA,                /*! AD type */
  UINT16_TO_BYTES(0xFE03),
  UINT16_TO_BYTES(AMA_VID),                /* Vendor ID assigned by BT */
  UINT16_TO_BYTES(0x0001),                 /* Alexa Built-in Headphone */
  ACCESSORY_COLOR_INDEX,                   /* Accessory color index */
  0,
  0,
  0,
  0,
  0, 0, 0, 0, 0
#endif
};
#elif configUSE_AMVOS_ATVV
/*! advertising data, discoverable mode */
static const uint8_t amvosAdvDataDiscDefault[] =
{
  /*! flags */
  2,                                      /*! length */
  DM_ADV_TYPE_FLAGS,                      /*! AD type */
  DM_FLAG_LE_GENERAL_DISC |               /*! flags */
  DM_FLAG_LE_BREDR_NOT_SUP,

  3,
  DM_ADV_TYPE_APPEARANCE,                 // used to identify remote controller
  0x80,
  0x01,

  /*! service UUID list */
  5,                                      /*! length */
  DM_ADV_TYPE_16_UUID,                    /*! AD type */
  UINT16_TO_BYTES(ATT_UUID_HID_SERVICE),
  UINT16_TO_BYTES(ATT_UUID_BATTERY_SERVICE),
};
#else // configUSE_AMVOS_AMA
/*! advertising data, discoverable mode */
static const uint8_t amvosAdvDataDiscDefault[] =
{
  /*! 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 */

  3,
  DM_ADV_TYPE_APPEARANCE,                 // used to identify kwd_ble
  0x55,
  0xAA,

  /*! service UUID list */
  3,                                      /*! length */
  DM_ADV_TYPE_16_UUID,                    /*! AD type */
  UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE),

  17,                                      /*! length */
  DM_ADV_TYPE_128_UUID,                    /*! AD type */
  ATT_UUID_AMVOS_SERVICE
};
#endif // configUSE_AMVOS_AMA

/*! scan data, discoverable mode */
static const uint8_t amvosScanDataDiscDefault[] =
{
#if configUSE_AMVOS_ATVV
  3,
  DM_ADV_TYPE_APPEARANCE,
  0x80,
  0x01,
#endif

  /*! device name */
  11,                                      /*! length */
  DM_ADV_TYPE_LOCAL_NAME,                 /*! AD type */
  'V',
  'o',
  'S',
#if configUSE_AMVOS_AMA
  '-',
  'A',
  'M',
  'A',
#elif configUSE_AMVOS_ATVV
  '-',
  'A',
  'T',
  'V',
  'V',
#endif
  '-',
  'L',
  'E'
};

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

/*! enumeration of client characteristic configuration descriptors */
enum
{
  AMVOS_GATT_SC_CCC_IDX,                  /*! GATT service, service changed characteristic */
  AMVOS_TX_CCC_IDX,                       /*! AMVOS service, tx characteristic */
#if configUSE_AMVOS_ATVV
  ATVV_CTRL_CCC_IDX,                       /*! AMVOS service, tx characteristic */
#endif
#if configUSE_AMVOS_HID
  AMVOS_HID_IN_REMOTE_CCC_HDL,             /*! HID Input Report characteristic for remote inputs */
#endif
#ifdef USE_BLE_OTA
  AMVOS_AMOTAS_TX_CCC_IDX,                /*! AMOTA service, tx characteristic */
#endif // USE_BLE_OTA
#if configUSE_AMVOS_BATT
  AMVOS_BATT_LVL_CCC_IDX,
#endif // configUSE_AMVOS_BATT
  AMVOS_NUM_CCC_IDX
};

/*! client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t amvosCccSet[AMVOS_NUM_CCC_IDX] =
{
  /* cccd handle                        value range               security level */
  {GATT_SC_CH_CCC_HDL,                  ATT_CLIENT_CFG_INDICATE,  DM_SEC_LEVEL_NONE},   /* AMVOS_GATT_SC_CCC_IDX */
  {AMVOS_TX_CH_CCC_HDL,                 ATT_CLIENT_CFG_NOTIFY,    DM_SEC_LEVEL_NONE},   /* AMVOS_TX_CCC_IDX */

#if configUSE_AMVOS_ATVV
  {ATVV_CTL_CH_CCC_HDL,                 ATT_CLIENT_CFG_NOTIFY,    DM_SEC_LEVEL_NONE},   /* AMVOS_TX_CCC_IDX */
#endif
#if configUSE_AMVOS_HID
  {HID_INPUT_REPORT_1_CH_CCC_HDL,       ATT_CLIENT_CFG_NOTIFY,    DM_SEC_LEVEL_NONE},   /* AMVOS_HID_IN_REMOTE_CCC_HDL */
#endif
#ifdef USE_BLE_OTA
  {AMOTAS_TX_CH_CCC_HDL,                ATT_CLIENT_CFG_NOTIFY,    DM_SEC_LEVEL_NONE},   /* AMOTA_AMOTAS_TX_CCC_IDX */
#endif // USE_BLE_OTA
#if configUSE_AMVOS_BATT
  {BATT_LVL_CH_CCC_HDL,                 ATT_CLIENT_CFG_NOTIFY,    DM_SEC_LEVEL_NONE},   /* AMVOS_BATT_LVL_CCC_IDX */
#endif // configUSE_AMVOS_BATT
};

#if configUSE_AMVOS_HID
/*! HID Report Type/ID and attribute handle map */
static const hidReportIdMap_t hidAppReportIdSet[] =
{
  /* type                       ID                            handle */
  {HID_REPORT_TYPE_INPUT,       HIDAPP_REMOTE_REPORT_ID,      HID_INPUT_REPORT_1_HDL},     /* Remote Input Report */
};

/*! HidApp Report Map (Descriptor) */
const uint8_t hidReportMap[] =
{
  0x05, 0x0c,                    /*	Usage Page (Consumer Devices) */
  0x09, 0x01,                    /*	Usage (Consumer Control) */
  0xa1, 0x01,                    /*	Collection (Application) */
  0x85, HIDAPP_REMOTE_REPORT_ID, /*   report ID (HIDAPP_REMOTE_REPORT_ID) */
  0x15, 0x00,       // Logical minimum (0)
  0x25, 0x01,       // Logical maximum (1)
  0x75, 0x01,       // Report Size (1)
  0x95, 0x01,       // Report Count (1)

  0x09, 0xCD,       // Usage (Play/Pause)
  0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)
  0x0A, 0x83, 0x01, // Usage (AL Consumer Control Configuration)
  0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)
  0x09, 0xB5,       // Usage (Scan Next Track)
  0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)
  0x09, 0xB6,       // Usage (Scan Previous Track)
  0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)

  0x09, 0xEA,       // Usage (Volume Down)
  0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)
  0x09, 0xE9,       // Usage (Volume Up)
  0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)
  0x0A, 0x21, 0x02, // Usage (AC Search)
  0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)
//  0x0A, 0x25, 0x02, // Usage (AC Forward)
//  0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)
  0x0A, 0x24, 0x02, // Usage (AC Back)
  0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)
  0xC0,              // End Collection
};

const uint16_t hidReportMapLen = sizeof(hidReportMap);

/*! HID Callback prototypes */
static void hidAppOutputCback(dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport);
static void hidAppFeatureCback(dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport);
static void hidAppInfoCback(dmConnId_t connId, uint8_t type, uint8_t value);

/*! HID Profile Configuration */
static const hidConfig_t hidAppHidConfig =
{
  (hidReportIdMap_t*) hidAppReportIdSet,                  /* Report ID to Attribute Handle map */
  sizeof(hidAppReportIdSet)/sizeof(hidReportIdMap_t),     /* Size of Report ID to Attribute Handle map */
  &hidAppOutputCback,                                     /* Output Report Callback */
  &hidAppFeatureCback,                                    /* Feature Report Callback */
  &hidAppInfoCback                                        /* Info Callback */
};

/**************************************************************************************************
  Local Variables
**************************************************************************************************/

/*! application control block */
struct
{
  wsfHandlerId_t handlerId;             /* task handle */

  /* Mouse pending event data */
//  uint8_t buttonMask;                   /* pending button mask */
//  uint16_t xDisplacement;                /* pending X Displacement */
//  uint16_t yDisplacement;                /* pending Y Displacement */
//  uint8_t devSpecific;                  /* pending Device Specific */

  /* Keyboard pending event data */
//  uint8_t modifier;                     /* pending key modifiers */
//  uint8_t keys[KEYBOARD_IR_MAX_KEYS];   /* pending keys */

  /* Remote pending event data */
  uint16_t btnData;                      /* pending remote button data */

  uint8_t testBtnState;                 /* identifier of test button action */
  uint8_t reportId;                     /* pending reportId button data */
  uint8_t txFlags;                      /* transmit flags */
  uint8_t protocolMode;                 /* current protocol mode */
  uint8_t hostSuspended;                /* TRUE if host suspended */
} hidAppCb;
#else
// HidApp Report Map (Descriptor) : dummy value for preventing build error at Keil.
//uint8_t hidReportMap[] = {0,};
//const uint16_t hidReportMapLen = 1;
#endif

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

VosBleInfo g_sVosBle =
{
    .ui8AmVosConnId = DM_CONN_ID_NONE,

#if configUSE_AMVOS_AMA || configUSE_AMVOS_ATVV
    .bComSecured = false,
    .bSlaveSecReq = false,
    .bCccSetupFlag = false,
    .ui32TxPktSize = 0,
#endif // configUSE_AMVOS_AMA

#if configUSE_BLE_Measure_Throughput
    .ui32AmaDataSentLength = 0,
    .ui32AmaDataSentCnt = 0,
    .ui32AmaDataCnfCnt = 0,
#endif // configUSE_BLE_Measure_Throughput
};

#if configUSE_BLE_WATCHDOG
#define AMVOS_TX_TIMER_START()                                                  \
    do                                                                          \
    {                                                                           \
      WsfTimerStartMs(&(g_sVosBle.sAmvosTxTimer), (10000));                                \
      WsfTaskSetReady(0,0);                                                     \
    } while (0)

#define AMVOS_TX_TIMER_STOP()                                                   \
    do                                                                          \
    {                                                                           \
      WsfTimerStop(&(g_sVosBle.sAmvosTxTimer));                                            \
      WsfTaskSetReady(0,0);                                                     \
    } while (0)

#define AMVOS_TX_TIMER_RESTART()                                                \
    do                                                                          \
    {                                                                           \
        WsfTimerStop(&(g_sVosBle.sAmvosTxTimer));                                          \
        WsfTimerStartMs(&(g_sVosBle.sAmvosTxTimer), (10000));                              \
        WsfTaskSetReady(0,0);                                                   \
    } while (0)

#define AMVOS_TX_CCC_TIMER_START()                                              \
    do                                                                          \
    {                                                                           \
      WsfTimerStartMs(&(g_sVosBle.sAmvosTxCccTimer), (20000));                             \
      WsfTaskSetReady(0,0);                                                     \
    } while (0)

void AMVOS_TX_CCC_TIMER_STOP(void)
{                                                                        
      WsfTimerStop(&(g_sVosBle.sAmvosTxCccTimer));
      AM_VOS_LOG_INFO("[AMA] AMVOS_TX_CCC_TIMER_STOP();\n");
      WsfTaskSetReady(0,0);
}

#define AMVOS_TX_CCC_TIMER_RESTART()                                            \
    do                                                                          \
    {                                                                           \
        WsfTimerStop(&(g_sVosBle.sAmvosTxCccTimer));                                       \
        WsfTimerStartMs(&(g_sVosBle.sAmvosTxCccTimer), (20000));                           \
        WsfTaskSetReady(0,0);                                                   \
    } while (0)

        
void AMVOS_RSP_TIMER_START(void)
{
//    WsfTimerStartSec(&g_AmvosRspTimer, (10));
    amvosMsg_t *pMsg;
    uint16_t len;
    len = sizeof(wsfMsgHdr_t);
    if ((pMsg = WsfMsgAlloc(len)) != NULL)
    {
        pMsg->hdr.event = AMVOS_RSP_TIMER_START_IND;
#if configUSE_AMVOS_HID
        WsfMsgSend(hidAppCb.handlerId, pMsg);
#else
        WsfMsgSend(g_sVosBle.ui8AmVosHandlerId, pMsg);
#endif
    }
    WsfTaskSetReady(0,0); 
    //AM_VOS_LOG_DEBUG("---- AMVOS_RSP_TIMER_START(), tick = %d\n", xTaskGetTickCount());
}
      
#define AMVOS_RSP_TIMER_STOP()                                                  \
    do                                                                          \
    {                                                                           \
      if(g_sVosBle.sAmvosRspTimer.isStarted == true)                                     \
      {                                                                         \
        WsfTimerStop(&(g_sVosBle.sAmvosRspTimer));                                         \
        AM_VOS_LOG_DEBUG("---- AMVOS_RSP_TIMER_STOP(), tick = %d\n", xTaskGetTickCount());                      \
        WsfTaskSetReady(0,0);                                                   \
      }                                                                         \
    } while (0)

void AMVOS_RSP_TIMER_RESTART(void)
{
    if(g_sVosBle.sAmvosRspTimer.isStarted == true)
    {
        WsfTimerStop(&(g_sVosBle.sAmvosRspTimer));
    }
    WsfTimerStartSec(&(g_sVosBle.sAmvosRspTimer), (3));
    WsfTaskSetReady(0,0); 
    AM_VOS_LOG_DEBUG("---- AMVOS_RSP_TIMER_RESTART(), tick = %d\n", xTaskGetTickCount());
}

#define AMVOS_TEST_TIMER_START()                                                \
    do { WsfTimerStartMs(&(g_sVosBle.sAmvosTestTimer), (10000));                           \
        AM_VOS_LOG_DEBUG("---- test timer start, tick = %d\n", xTaskGetTickCount()); \
    } while (0)

#define AMVOS_TEST_TIMER_STOP()                                                 \
    do { WsfTimerStop(&(g_sVosBle.sAmvosTestTimer)); } while (0)

void AMVOS_TEST_TIMER_RESTART(void)
{
    WsfTimerStop(&(g_sVosBle.sAmvosTestTimer));
    WsfTimerStartMs(&(g_sVosBle.sAmvosTestTimer), (10000));
}
#else // configUSE_BLE_WATCHDOG
void AMVOS_TX_CCC_TIMER_STOP(void)
{
    return;
}

void AMVOS_RSP_TIMER_START(void)
{
    return;
}
#endif // configUSE_BLE_WATCHDOG

// index is the starting point of the local name, local name only in advData
static bool amvosSetLocalName(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 amvosKwdSetDemoName(void)
{
    uint8_t test_bdaddress[6];
    uint8_t ble_device_name[20] = "VoS-";    //local name = device name
    uint8_t * pBda;
    uint8_t index = 4;

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

    pBda = (uint8_t*)Bda2Str(test_bdaddress);
    AM_VOS_LOG_INFO("[AM-VoS] Local Device BD Address: ");
    AM_VOS_LOG_INFO(Bda2Str(test_bdaddress));
    AM_VOS_LOG_INFO("\n");

    // build demo name here
    // 1st letter is board variant
    ble_device_name[index++] = 'E';     // 'E' means EVB.

    // 3rd letter is wake-on-sound variant
#if configUSE_AAD
    ble_device_name[index++] = 'W';   // Acoustic Activity Detection (Wake on Sound enabled)
#else // configUSE_AAD
    ble_device_name[index++] = 'A';   // A for always listening...
#endif // configUSE_AAD

#ifdef USE_BLE_OTA
    ble_device_name[index++] = 'O';
#endif // USE_BLE_OTA

#if configUSE_AMVOS_AMA
    ble_device_name[index++] = '-';

    ble_device_name[index++] = 'A';
    ble_device_name[index++] = 'M';
    ble_device_name[index++] = 'A';
#endif // configUSE_AMVOS_AMA
    // 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];

#if configUSE_AMVOS_AMA
    AmaDevInfo devinfo =
    {
      .eAudioProfile = AudioProfile_NEAR_FIELD, // AudioProfile_CLOSE_TALK, AudioProfile_NEAR_FIELD, AudioProfile_FAR_FIELD
      .ui8AudioFormat = AM_TX_AUDIO_FORMAT,
      .device_type = AM_AMA_DEVICE_TYPE_STR,
      .ui8TransportType = 0,                    // 0: BLE, 1: RFCOMM, 2: iAP
    };

    strcpy(devinfo.serial_number, (char const *)pBda);
    if(index < 17)
        strncpy(devinfo.name, (char const *)ble_device_name, index);
    else
        strncpy(devinfo.name, (char const *)ble_device_name, 16);

    am_vos_ama_devinfo_set(&devinfo);

    // set local name here:
    amvosSetLocalName(amvosScanDataDisc, ble_device_name, index, 0);

#elif configUSE_AMVOS_ATVV
    AtvvDevInfo devinfo =
    {
      .ui8AudioFormat = AM_TX_AUDIO_FORMAT,
      .ui8TransportType = 0,                    // 0: BLE, 1: RFCOMM, 2: iAP
    };

    am_vos_atvv_devinfo_set(&devinfo);

    // set local name here:
    amvosSetLocalName(amvosScanDataDisc, ble_device_name, index, 4);

#else
    // set local name here:
    amvosSetLocalName(amvosScanDataDisc, ble_device_name, index, 0);
#endif // configUSE_AMVOS_AMA, configUSE_AMVOS_ATVV
    
    //SvcCoreSetDevName(ble_device_name, index);
}

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

  len = DmSizeOfEvt(pDmEvt);

  if ((pMsg = WsfMsgAlloc(len)) != NULL)
  {
    memcpy(pMsg, pDmEvt, len);
#if configUSE_AMVOS_HID
    WsfMsgSend(hidAppCb.handlerId, pMsg);
#else
    WsfMsgSend(g_sVosBle.ui8AmVosHandlerId, pMsg);
#endif
  }
}

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

void am_vos_cmd_packet_tx(uint8_t * buf, uint32_t len)
{
    if(amvosTxChannelIsAvailable())
    {
        // simply tries to send notification
#if configUSE_AMVOS_ATVV
        //AM_VOS_LOG_INFO("[AM-VoS] am_vos_packet_tx cid %d len %d\n", g_sVosBle.ui8AmVosConnId, len);
        AttsHandleValueNtf(g_sVosBle.ui8AmVosConnId, ATVV_CTL_HDL, len, buf);   // connId always group 0 since support only 1 connection.
#else
        AttsHandleValueNtf(g_sVosBle.ui8AmVosConnId, AMVOS_TX_HDL, len, buf);   // connId always group 0 since support only 1 connection.
#endif
        g_sVosBle.ui8VosTxBusy = 1;

#if configUSE_BLE_Measure_Throughput
        g_sVosBle.ui32AmaDataSentLength += len;
#endif // configUSE_BLE_Measure_Throughput

#if configUSE_BLE_WATCHDOG
        AMVOS_TX_TIMER_RESTART();
#endif // configUSE_BLE_WATCHDOG
    }
}

void am_vos_audio_packet_tx(uint8_t * buf, uint32_t len)
{
    if(amvosTxChannelIsAvailable())
    {
        // simply tries to send notification
        AttsHandleValueNtf(g_sVosBle.ui8AmVosConnId, AMVOS_TX_HDL, len, buf);   // connId always group 0 since support only 1 connection.

        g_sVosBle.ui8VosTxBusy = 1;

#if configUSE_BLE_Measure_Throughput
        g_sVosBle.ui32AmaDataSentLength += len;
#endif // configUSE_BLE_Measure_Throughput
    }
}

uint8_t
am_vos_packet_rx(dmConnId_t connId, uint16_t handle, uint8_t operation,
                       uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr)
{
    if (handle == AMVOS_RX_HDL)
    {
#if configUSE_AMVOS_AMA
#if configUSE_BLE_WATCHDOG
        AMVOS_RSP_TIMER_STOP(); // stop app response timer
#endif // configUSE_BLE_WATCHDOG
        am_vos_ama_rx_handler(pValue, len);
#elif configUSE_AMVOS_ATVV
#if configUSE_BLE_WATCHDOG
        AMVOS_RSP_TIMER_STOP(); // stop app response timer
#endif // configUSE_BLE_WATCHDOG
			am_vos_atvv_rx_handler(pValue, len);
#else // configUSE_AMVOS_AMA
        am_vos_ble_rx_handler(pValue, len);
#endif // configUSE_AMVOS_AMA
    }
    return ATT_SUCCESS;
}

/*************************************************************************************************/
/*!
 *  \fn     amvosAttCback
 *
 *  \brief  Application ATT callback.
 *
 *  \param  pEvt    ATT callback event
 *
 *  \return None.
 */
/*************************************************************************************************/
static void amvosAttCback(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);

#if configUSE_AMVOS_HID
    WsfMsgSend(hidAppCb.handlerId, pMsg);
#else
    WsfMsgSend(g_sVosBle.ui8AmVosHandlerId, pMsg);
#endif
  }
}

/*************************************************************************************************/
/*!
 *  \fn     amvosCccCback
 *
 *  \brief  Application ATTS client characteristic configuration callback.
 *
 *  \param  pDmEvt  DM callback event
 *
 *  \return None.
 */
/*************************************************************************************************/
static void amvosCccCback(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));

#if configUSE_AMVOS_HID
    WsfMsgSend(hidAppCb.handlerId, pMsg);
#else
    WsfMsgSend(g_sVosBle.ui8AmVosHandlerId, pMsg);
#endif
  }
}

/*************************************************************************************************/
/*!
 *  \fn     amvosProcCccState
 *
 *  \brief  Process CCC state change.
 *
 *  \param  pMsg    Pointer to message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void amvosProcCccState(amvosMsg_t *pMsg)
{
    AM_VOS_LOG_INFO("[AM-VoS] ccc state ind value:%d handle:0x%X idx:%d\n", pMsg->ccc.value, pMsg->ccc.handle, pMsg->ccc.idx);

#if configUSE_AMVOS_AMA
    if(pMsg->ccc.value == ATT_CLIENT_CFG_NOTIFY && pMsg->ccc.handle == AMVOS_TX_CH_CCC_HDL)     // Lewis : Need to check
    {
        g_sVosBle.bCccSetupFlag = true;
        AM_VOS_LOG_INFO("[AM-VoS] AMA TX CCC enabled.\n");

        if(g_sVosBle.bComSecured == true)
        {
#if configUSE_BLE_WATCHDOG
            if(g_sVosBle.bConnected == false)
            {
                AM_VOS_LOG_INFO("[AMA] Version Exchange Send.\n");
                am_vos_ama_tx_ver_exchange_send();
            }
#else // configUSE_BLE_WATCHDOG
            AM_VOS_LOG_INFO("[AMA] Version Exchange Send.\n");
            am_vos_ama_tx_ver_exchange_send();
#endif // configUSE_BLE_WATCHDOG
        }
        else
        {
            // channel not secured
            // probably iOS APP
            /* request security */
            AM_VOS_LOG_INFO("[AM-VoS] Request for security from slave.\n");
            g_sVosBle.bSlaveSecReq = true;
            AppSlaveSecurityReq(AppConnIsOpen());
        }
    }
#elif configUSE_AMVOS_ATVV
    if(pMsg->ccc.value == ATT_CLIENT_CFG_NOTIFY && pMsg->ccc.handle == ATVV_CTL_CH_CCC_HDL)     // Lewis : Need to check
    {
        g_sVosBle.bCccSetupFlag = true;
        AM_VOS_LOG_INFO("[AM-VoS] ATVV TX CCC enabled.\n");
    }
#endif

#if configUSE_BLE_WATCHDOG && (configUSE_AMVOS_AMA || configUSE_AMVOS_ATVV)
    if(g_sVosBle.bConnected == true)
    {
        am_vos_ama_status_ready();
        am_vos_voice_start();
    }
    else if(!am_vos_is_connected())
    {
        AM_VOS_LOG_INFO("[AMA] AMVOS_TX_CCC_TIMER_RESTART();\n");
        AMVOS_TX_CCC_TIMER_RESTART();
    }
#endif // configUSE_BLE_WATCHDOG && configUSE_AMVOS_AMA

  /* AMVOS TX CCC */
#if configUSE_AMVOS_ATVV
    if (pMsg->ccc.idx == ATVV_CTRL_CCC_IDX)
#else
    if (pMsg->ccc.idx == AMVOS_TX_CCC_IDX)
#endif
    {
        if (pMsg->ccc.value == ATT_CLIENT_CFG_NOTIFY)
        {
            // notify enabled
            g_sVosBle.ui8AmVosConnId = (dmConnId_t) pMsg->ccc.hdr.param;
            AM_VOS_LOG_INFO("[AM-VoS] connId : %d\r\n", pMsg->ccc.hdr.param);
            AM_VOS_LOG_INFO("[AM-VoS] AttGetMtu: %d\n", AttGetMtu(g_sVosBle.ui8AmVosConnId));

#if configUSE_AMVOS_ATVV
            if(AttGetMtu(g_sVosBle.ui8AmVosConnId) - 3 >= BLE_DATA_BUFFER_SIZE)
            {
                g_sVosBle.ui32TxPktSize = BLE_DATA_BUFFER_SIZE;
            }
            else
            {
                g_sVosBle.ui32TxPktSize = 20;
            }
#elif (configUSE_AMVOS_AMA == 0)
            am_vos_mic_enable();        // PDM enabled at GATT app test mode. 
#endif
        }
        else
        {
            g_sVosBle.ui8AmVosConnId = DM_CONN_ID_NONE;
        }
        return;
    }

#ifdef USE_BLE_OTA
  /* AMOTAS TX CCC */
    if (pMsg->ccc.idx == AMVOS_AMOTAS_TX_CCC_IDX)
    {
        if (pMsg->ccc.value == ATT_CLIENT_CFG_NOTIFY)
        {
            // notify enabled
            amotas_start((dmConnId_t) pMsg->ccc.hdr.param, 
            AMOTA_RESET_TIMER_IND, AMOTA_DISCONNECT_TIMER_IND,
            AMVOS_AMOTAS_TX_CCC_IDX);

            AM_VOS_LOG_INFO("[AM-VoS] MIC (PDM/ADC) disable\n");

            am_vos_mic_disable();    // PDM deinit to solving performance issue with VoS Framework.
            AMVOS_TX_CCC_TIMER_STOP();
        }
        else
        {
            // notify disabled
            amotas_stop((dmConnId_t) pMsg->ccc.hdr.param);
        }
        return;
    }
#endif // USE_BLE_OTA
#if configUSE_AMVOS_BATT
  /* handle battery level CCC */
    if (pMsg->ccc.idx == AMVOS_BATT_LVL_CCC_IDX)
    {
      if (pMsg->ccc.value == ATT_CLIENT_CFG_NOTIFY)
      {
        BasMeasBattStart((dmConnId_t) pMsg->ccc.hdr.param, AMVOS_BATT_TIMER_IND, AMVOS_BATT_LVL_CCC_IDX);
      }
      else
      {
        BasMeasBattStop((dmConnId_t) pMsg->ccc.hdr.param);
      }
      return;
    }
#endif // configUSE_AMVOS_BATT
}

/*************************************************************************************************/
/*!
 *  \fn     amvosClose
 *
 *  \brief  Perform UI actions on connection close.
 *
 *  \param  pMsg    Pointer to message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void amvosClose(amvosMsg_t *pMsg)
{
#ifdef USE_BLE_OTA
    /* stop amotas */
    amotas_conn_close((dmConnId_t) pMsg->hdr.param);
#endif // USE_BLE_OTA
}

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

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

  /* start advertising; automatically set connectable/discoverable mode and bondable mode */
#if configUSE_AMVOS_ATVV
  AppAdvStart(APP_MODE_AUTO_INIT);
#else
  AppAdvStart(APP_MODE_AUTO_INIT);
#endif
}

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

  /* button actions when connected */
  if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE)
  {
    switch (btn)
    {
      case APP_UI_BTN_1_SHORT:
        break;

      case APP_UI_BTN_1_MED:
        break;

      case APP_UI_BTN_1_LONG:
        AppConnClose(connId);
        break;

      case APP_UI_BTN_2_SHORT:
        break;

      default:
        break;
    }
  }
  /* button actions when not connected */
  else
  {
    switch (btn)
    {
      case APP_UI_BTN_1_SHORT:
        /* start or restart advertising */
        AppAdvStart(APP_MODE_AUTO_INIT);
        break;

      case APP_UI_BTN_1_MED:
        /* enter discoverable and bondable mode mode */
        AppSetBondable(TRUE);
        AppAdvStart(APP_MODE_DISCOVERABLE);
        break;

      case APP_UI_BTN_1_LONG:
        /* clear bonded device info and restart advertising */
        AppDbDeleteAllRecords();
        AppAdvStart(APP_MODE_AUTO_INIT);
        break;

      default:
        break;
    }
  }
}

void amvosConnParameterReqSend(void)
{
	  
    hciConnSpec_t connSpec;
    connSpec.connIntervalMin = amvosUpdateCfg.connIntervalMin;//(15/1.25);//(30/1.25);
    connSpec.connIntervalMax = amvosUpdateCfg.connIntervalMax;//(15/1.25);//(30/1.25);
    connSpec.connLatency = amvosUpdateCfg.connLatency;//0;
    connSpec.supTimeout = amvosUpdateCfg.supTimeout;
    connSpec.minCeLen = 0;
    connSpec.maxCeLen = 0xffff; //fixme
    DmConnUpdate(1, &connSpec);
}

#if configUSE_AMVOS_AMA
void amvosConnIntervalUpdate(uint8_t platform)
{
    if(platform == 1)
    {
        pAppUpdateCfg->connIntervalMax = 12;
        amvosUpdateCfg.connIntervalMax = 12;
        AM_VOS_LOG_INFO("[AMA] iOS connection parameter setup\n");
    }
    else if(platform == 2)
    {
        pAppUpdateCfg->connIntervalMax = 99;
        amvosUpdateCfg.connIntervalMax = 99;
        AM_VOS_LOG_INFO("[AMA] Android connection parameter setup\n");
    }
}
#endif // configUSE_AMVOS_AMA

//*****************************************************************************
//
// Connection Open event
//
//*****************************************************************************
static void
amvosOpen(dmEvt_t *pMsg)
{
    hciLeConnCmplEvt_t *evt = (hciLeConnCmplEvt_t*) pMsg;

    AM_VOS_LOG_INFO("[AM-VoS] Connection opened\n");
    AM_VOS_LOG_INFO("handle = 0x%x\t", evt->handle);
    AM_VOS_LOG_INFO("role = 0x%x\n", evt->role);
    AM_VOS_LOG_INFO("addrMSB = %02x%02x%02x%02x%02x%02x\t", evt->peerAddr[0], evt->peerAddr[1], evt->peerAddr[2]);
    AM_VOS_LOG_INFO("addrLSB = %02x%02x%02x%02x%02x%02x\n", evt->peerAddr[3], evt->peerAddr[4], evt->peerAddr[5]);
    AM_VOS_LOG_INFO("connInterval = %d x 1.25 ms\n", evt->connInterval);
    AM_VOS_LOG_INFO("connLatency = %d\t", evt->connLatency);
    AM_VOS_LOG_INFO("supTimeout = %d ms\n\n", evt->supTimeout * 10);
    
//    if(evt->connInterval > amvosUpdateCfg.connIntervalMax)
//        amvosConnParameterReqSend();
}

//*****************************************************************************
//
// Connection Update event
//
//*****************************************************************************
static void
amvosConnUpdate(dmEvt_t *pMsg)
{
#if configUSE_STDIO_PRINTF
    volatile hciLeConnUpdateCmplEvt_t *evt = (hciLeConnUpdateCmplEvt_t*) pMsg;

    AM_VOS_LOG_INFO("[AM-VoS] Connection update status = 0x%x\n", evt->status);
    AM_VOS_LOG_INFO("handle = 0x%x\t", evt->handle);
    AM_VOS_LOG_INFO("connInterval = %d x 1.25 ms\n", evt->connInterval);
    AM_VOS_LOG_INFO("connLatency = %d\t", evt->connLatency);
    AM_VOS_LOG_INFO("supTimeout = %d ms\n\n", evt->supTimeout * 10);
#endif // configUSE_STDIO_PRINTF
}

#if configUSE_AMVOS_HID
/*************************************************************************************************/
/*!
 *  \brief  Send example data.
 *
 *  \param  connId    Connection identifier.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void hidAppRemoteSendData(dmConnId_t connId)
{
  //am_vos_stdio_printf(1, "connId %d, reportId %d, CCC en %d, hidAppCb.txFlags 0x%x\n", connId, hidAppCb.reportId, AttsCccEnabled(connId, AMVOS_HID_IN_REMOTE_CCC_HDL), hidAppCb.txFlags);
  if (AttsCccEnabled(connId, AMVOS_HID_IN_REMOTE_CCC_HDL))
  {
    if (hidAppCb.txFlags & HIDAPP_TX_FLAGS_PENDING)
    {
      if (hidAppCb.txFlags & HIDAPP_TX_FLAGS_READY)
      {
#if 0
        uint8_t buffer[HIDAPP_REMOTE_INPUT_REPORT_LEN];
         /* bitmask of remote buttons */
        buffer[0] = hidAppCb.btnData & 0xFF;
        buffer[1] = (hidAppCb.btnData >> 8) & 0xFF;

        hidAppCb.txFlags &= ~(HIDAPP_TX_FLAGS_READY | HIDAPP_TX_FLAGS_PENDING);

        /* Send the message */
        HidSendInputReport(connId, hidAppCb.reportId, HIDAPP_REMOTE_INPUT_REPORT_LEN, buffer);
#else
        uint8_t buffer;
        /* bitmask of remote buttons */
        buffer = hidAppCb.btnData;

        hidAppCb.txFlags &= ~(HIDAPP_TX_FLAGS_READY | HIDAPP_TX_FLAGS_PENDING);

        /* Send the message */
        HidSendInputReport(connId, hidAppCb.reportId, HIDAPP_REMOTE_INPUT_REPORT_LEN, &buffer);
#endif
      }
    }
  }
  else
  {
    hidAppCb.txFlags &= ~HIDAPP_TX_FLAGS_PENDING;
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Send example data.
 *
 *  \param  connId    Connection identifier.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void hidAppSendData(dmConnId_t connId)
{
  /* Send the report based on report ID */
  switch(hidAppCb.reportId)
  {
  case HIDAPP_REMOTE_REPORT_ID:
    hidAppRemoteSendData(connId);
    break;
//  case HIDAPP_MOUSE_REPORT_ID:
//    hidAppMouseSendData(connId);
//    break;
  default:
    break;
  }
}

#if 0
/*************************************************************************************************/
/*!
 *  \brief  Report or queue a mouse event to the host.
 *
 *  \param  buttonMask        Mouse button mask.
 *  \param  xDisplacement     Mouse displacement in the x direction.
 *  \param  yDisplacement     Mouse displacement in the y direction.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void hidAppMouseReportEvent(uint8_t buttonMask, uint16_t xDisplacement, uint16_t yDisplacement)
{
  dmConnId_t connId;

  if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE)
  {
    /* record mouse data */
    hidAppCb.buttonMask = buttonMask;
    hidAppCb.xDisplacement = xDisplacement;
    hidAppCb.yDisplacement = yDisplacement;
    hidAppCb.devSpecific = 0;
    hidAppCb.reportId = HIDAPP_MOUSE_REPORT_ID;

    /* Indicate new data is pending */
    hidAppCb.txFlags |= HIDAPP_TX_FLAGS_PENDING;

    /* send the data */
    hidAppSendData(connId);
  }
}
#endif

/*************************************************************************************************/
/*!
 *  \brief  Send or queue a remote event to the host
 *
 *  \param  button        Remote control depressed button
 *
 *  \return None.
 */
/*************************************************************************************************/
void hidAppRemoteReportEvent(uint16_t button)
{
  dmConnId_t connId;

  if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE)
  {
    /* record key data */
    hidAppCb.btnData = button;
    hidAppCb.reportId = HIDAPP_REMOTE_REPORT_ID;

    /* Indicate new data is pending */
    hidAppCb.txFlags |= HIDAPP_TX_FLAGS_PENDING;

    /* send the data */
    hidAppSendData(connId);
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Callback to handle an output report from the host.
 *
 *  \param  connId    The connection identifier.
 *  \param  id        The ID of the report.
 *  \param  len       The length of the report data in pReport.
 *  \param  pReport   A buffer containing the report.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void hidAppOutputCback(dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport)
{
  /* TODO: process output reports */
}

/*************************************************************************************************/
/*!
 *  \brief  Callback to handle a feature report from the host.
 *
 *  \param  connId    The connection identifier.
 *  \param  id        The ID of the report.
 *  \param  len       The length of the report data in pReport.
 *  \param  pReport   A buffer containing the report.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void hidAppFeatureCback(dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport)
{
  /* TODO: process feature reports */
}

/*************************************************************************************************/
/*!
 *  \brief  Callback to handle a change in protocol mode or control point from the host.
 *
 *  \param  connId    The connection identifier.
 *  \param  mode      The type of information (HID_INFO_CONTROL_POINT or HID_INFO_PROTOCOL_MODE)
 *  \param  value     The value of the information
 *
 *  \return None.
 */
/*************************************************************************************************/
static void hidAppInfoCback(dmConnId_t connId, uint8_t type, uint8_t value)
{
  if (type == HID_INFO_PROTOCOL_MODE)
  {
    hidAppCb.protocolMode = value;
  }
  else if (type == HID_INFO_CONTROL_POINT)
  {
    hidAppCb.hostSuspended = (value == HID_CONTROL_POINT_SUSPEND) ? TRUE : FALSE;
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Initialize the report attributes to default values for the hidApp.
 *
 *  \param  None.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void hidAppReportInit(void)
{
  //uint8_t iKeyboardBuffer[HIDAPP_KEYBOARD_INPUT_REPORT_LEN];
  //uint8_t iMouseBuffer[HIDAPP_MOUSE_INPUT_REPORT_LEN];
  uint8_t iRemoteBuffer[HIDAPP_REMOTE_INPUT_REPORT_LEN];
  //uint8_t oBuffer[HIDAPP_OUTPUT_REPORT_LEN];
  uint8_t fBuffer[HIDAPP_FEATURE_REPORT_LEN];

  /* Remote Input report */
  memset(iRemoteBuffer, 0, HIDAPP_REMOTE_INPUT_REPORT_LEN);
  AttsSetAttr(HID_INPUT_REPORT_1_HDL, HIDAPP_REMOTE_INPUT_REPORT_LEN, iRemoteBuffer);

  /* Keyboard Input report */
//  memset(iKeyboardBuffer, 0, HIDAPP_KEYBOARD_INPUT_REPORT_LEN);
//  AttsSetAttr(HID_INPUT_REPORT_2_HDL, HIDAPP_KEYBOARD_INPUT_REPORT_LEN, iKeyboardBuffer);

  /* Mouse Input report */
//  memset(iMouseBuffer, 0, HIDAPP_MOUSE_INPUT_REPORT_LEN);
//  AttsSetAttr(HID_INPUT_REPORT_3_HDL, HIDAPP_MOUSE_INPUT_REPORT_LEN, iMouseBuffer);

  /* Output report */
////  memset(oBuffer, 0, HIDAPP_OUTPUT_REPORT_LEN);
////  AttsSetAttr(HID_OUTPUT_REPORT_HDL, HIDAPP_OUTPUT_REPORT_LEN, oBuffer);

  /* Feature report */
  memset(fBuffer, 0, HIDAPP_FEATURE_REPORT_LEN);
  AttsSetAttr(HID_FEATURE_REPORT_HDL, HIDAPP_FEATURE_REPORT_LEN, fBuffer);
}
#endif
/*************************************************************************************************/
/*!
 *  \fn     amvosProcMsg
 *
 *  \brief  Process messages from the event handler.
 *
 *  \param  pMsg    Pointer to message.
 *
 *  \return None.
 */
/*************************************************************************************************/
static void amvosProcMsg(amvosMsg_t *pMsg)
{
    uint8_t uiEvent = APP_UI_NONE;
    static uint8_t retry_cnt = 0;
    
    switch(pMsg->hdr.event)
    {
#ifdef USE_BLE_OTA
        case AMOTA_RESET_TIMER_IND:
            amotas_proc_msg(&pMsg->hdr);
            break;
            
        case AMOTA_DISCONNECT_TIMER_IND:
            amotas_proc_msg(&pMsg->hdr);
            break;
#endif // USE_BLE_OTA

#if configUSE_AMVOS_BATT
        case AMVOS_BATT_TIMER_IND:
            BasProcMsg(&pMsg->hdr);
            break;
#endif // configUSE_AMVOS_BATT

#if configUSE_BLE_WATCHDOG
        case AMVOS_RSP_TIMER_START_IND:
            AMVOS_RSP_TIMER_RESTART();
            break;
            
        case AMVOS_TX_TIMEOUT_TIMER_IND:   // stuck somehow...

            AM_VOS_LOG_DEBUG("---- stuck in TX event... hciCoreCb.availBufs = %d\n", hciCoreCb.availBufs);

            // When it times out, pretty much we have to
            // reset/reboot controller and initialize HCI
            // layer and SPI transport layer again.

            // stop the app response timeout timer
            AMVOS_RSP_TIMER_STOP();

            // clear ama channel flags
            g_sVosBle.ui8AmVosConnId = DM_CONN_ID_NONE;
#if configUSE_AMVOS_AMA
            am_vos_ama_status_reset();
#elif configUSE_AMVOS_ATVV
            am_vos_atvv_status_reset();
#endif // configUSE_AMVOS_AMA or configUSE_AMVOS_ATVV

            //
            // VOS-5, reset BLE controller here instead of hciCmdTimeout
            //
#if configUSE_BLE_ERROR_SW_RESET
#if defined(AM_PART_APOLLO2)
            am_hal_reset_por();
#elif defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
            am_hal_reset_control(AM_HAL_RESET_CONTROL_SWPOR, 0);
#endif // AM_PART_APOLLO2, AM_PART_APOLLO3, AM_PART_APOLLO3P

#else // configUSE_BLE_ERROR_SW_RESET
            HciDrvRadioShutdown();
        
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
            am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_XTAL_STOP, 0);
            //vTaskDelay(pdMS_TO_TICKS(10));
            am_util_delay_ms(200);
            am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_XTAL_START, 0);
#endif // AM_PART_APOLLO3, AM_PART_APOLLO3P
        
            HciDrvRadioBoot(1);
            DmDevReset();
#endif // configUSE_BLE_ERROR_SW_RESET
            break;

        case AMVOS_RSP_TIMEOUT_TIMER_IND:
            // timeout for APP response, close the current opened connection
            AM_VOS_LOG_DEBUG("---- app not responding timeout, disconnect... tick = %d\n", xTaskGetTickCount());
            // clear ama channel flags
            g_sVosBle.ui8AmVosConnId = DM_CONN_ID_NONE;
#if configUSE_AMVOS_AMA
            am_vos_ama_status_reset();
#elif configUSE_AMVOS_ATVV
            am_vos_atvv_status_reset();
#endif // configUSE_AMVOS_AMA or configUSE_AMVOS_ATVV

            AppConnClose(AppConnIsOpen());
            break;
            
        case AMVOS_TX_CCC_TIMEOUT_TIMER_IND:
            // TX CCC timed out, disconnect...
#if configUSE_BLE_ERROR_SW_RESET
            AM_VOS_LOG_INFO("---- CCC not set timeout, reset board...\n");
#if defined(AM_PART_APOLLO2)
            am_hal_reset_por();
#elif defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
            am_hal_reset_control(AM_HAL_RESET_CONTROL_SWPOR, 0);
#endif // AM_PART_APOLLO2, AM_PART_APOLLO3, AM_PART_APOLLO3P

#else // configUSE_BLE_ERROR_SW_RESET
            AppConnClose(AppConnIsOpen());
            AM_VOS_LOG_INFO("---- CCC not set timeout, disconnect...\n");
            AMVOS_TX_CCC_TIMER_STOP();
#endif // configUSE_BLE_ERROR_SW_RESET
            break;
        
        case AMVOS_TEST_TIMEOUT_TIMER_IND:
    //        AM_VOS_LOG_DEBUG("---- test timer timeout, tick = %d\n", xTaskGetTickCount());
            AMVOS_TEST_TIMER_RESTART();
            break;
#endif // configUSE_BLE_WATCHDOG

#if configUSE_BLE_Measure_Throughput
        case AMVOS_MEAS_TP_TIMER_IND:
            //showThroughput();
            break;
#endif // configUSE_BLE_Measure_Throughput

        case ATTS_HANDLE_VALUE_CNF:
#if configUSE_BLE_Measure_Throughput
            g_sVosBle.ui32AmaDataCnfCnt++;
#endif // configUSE_BLE_Measure_Throughput
		
#if configUSE_BLE && configUSE_AUDIO_CODEC
#if configUSE_AMVOS_ATVV
            if((pMsg->att.handle == AMVOS_TX_HDL || pMsg->att.handle == ATVV_CTL_HDL) && (pMsg->hdr.status == ATT_SUCCESS))
#else
            if((pMsg->att.handle == AMVOS_TX_HDL) && (pMsg->hdr.status == ATT_SUCCESS))
#endif
            {
                uint8_t* pui8CommadBuffer = NULL;
                uint32_t ui32CommandLen = 0;
                bool bCmdFlag = false;

#if configUSE_BLE_WATCHDOG
                AMVOS_TX_TIMER_STOP();
#endif // configUSE_BLE_WATCHDOG
                if(am_vos_ble_nextdata_check(&pui8CommadBuffer, &ui32CommandLen, &bCmdFlag))
                {
                    if(bCmdFlag)
                    {
                        am_vos_cmd_packet_tx(pui8CommadBuffer, ui32CommandLen);
                    }
                    else
                    {
                        am_vos_audio_packet_tx(pui8CommadBuffer, ui32CommandLen);
                    }
                }
                else
                {     
                    uint32_t data_len = am_vos_get_ring_buffer_status(&(g_sAmUtil.sRingBuf[AM_AUDIO_BUFFER_ENCODED]));
#if configUSE_AMVOS_ATVV
                    if((data_len > g_sVosBle.ui32TxPktSize / 2) && g_sVosSys.ui8ProvideSpeechFlag)
                    {
                        uint8_t ui8AmVosTxBuf[BLE_DATA_BUFFER_SIZE];
                        uint16_t ui16TxLength = 0;
                        if(++g_sVosSys.ui8FragmentCount == 7 && g_sVosBle.ui32TxPktSize == 20)
                        {
                            ui16TxLength = 14;
                            g_sVosSys.ui8FragmentCount = 0;
                        }
                        else if(data_len >= g_sVosBle.ui32TxPktSize)
                        {
                            ui16TxLength = g_sVosBle.ui32TxPktSize;
                        }
                        else
                        {
                            ui16TxLength = data_len;
                        }

                        am_vos_ble_tx_packet_encap(ui8AmVosTxBuf, ui16TxLength);
                        am_vos_audio_packet_tx(ui8AmVosTxBuf, ui16TxLength);
                    }
#else
                    if(data_len >= BLE_DATA_BUFFER_SIZE)
                    {
                        uint8_t ui8AmVosTxBuf[BLE_DATA_BUFFER_SIZE + 3];
                        am_vos_ble_tx_packet_encap(ui8AmVosTxBuf, BLE_DATA_BUFFER_SIZE);
                        
                        am_vos_audio_packet_tx(ui8AmVosTxBuf, BLE_DATA_BUFFER_SIZE + 3);
#if configUSE_BLE_Measure_Throughput
                        //AM_VOS_LOG_INFO("%d ", g_sVosBle.ui32AmaDataSentCnt - g_sVosBle.ui32AmaDataCnfCnt);
#endif // configUSE_BLE_Measure_Throughput
                    }
#endif
                    else
                    {
                        g_sVosBle.ui8VosTxBusy = 0;
                    }
                }
            }
#if configUSE_AMVOS_HID
            if ((pMsg->att.handle == HID_INPUT_REPORT_1_HDL) && (pMsg->hdr.status == ATT_SUCCESS))
            {
                hidAppCb.txFlags |= HIDAPP_TX_FLAGS_READY;
                hidAppSendData((dmConnId_t) pMsg->hdr.param);
            }
#endif // configUSE_AMVOS_HID
#endif // configUSE_BLE && configUSE_AUDIO_CODEC
            break;

        case ATTS_CCC_STATE_IND:
            amvosProcCccState(pMsg);
            break;

        case DM_RESET_CMPL_IND:
            AM_VOS_LOG_INFO("[AM-VoS] DM_RESET_CMPL_IND\n");
            
            // clear ama channel flags
            g_sVosBle.ui8AmVosConnId = DM_CONN_ID_NONE;
#if configUSE_AMVOS_AMA
            am_vos_ama_status_reset();
#elif configUSE_AMVOS_ATVV
            am_vos_atvv_status_reset();
#endif // configUSE_AMVOS_AMA or configUSE_AMVOS_ATVV
            AttsCalculateDbHash();
            DmSecGenerateEccKeyReq();
            amvosKwdSetDemoName();
            amvosSetup(pMsg);

#if configUSE_BLE_TX_POWER_SET
#if defined(AM_PART_APOLLO3) || defined(AM_PART_APOLLO3P)
            HciVscSetRfPowerLevelEx(TX_POWER_LEVEL_PLUS_3P0_dBm);
#endif
#endif // configUSE_BLE_TX_POWER_SET

            uiEvent = APP_UI_RESET_CMPL;
            break;

        case DM_ADV_START_IND:
            AM_VOS_LOG_INFO("[AM-VoS] DM_ADV_START_IND\n");
            uiEvent = APP_UI_ADV_START;
#if configUSE_BLE_BURST_MODE
            am_vos_burst_mode_disable();
#endif
            break;

        case DM_ADV_STOP_IND:
            AM_VOS_LOG_INFO("[AM-VoS] DM_ADV_STOP_IND\n");
            uiEvent = APP_UI_ADV_STOP;
            break;

        case DM_CONN_OPEN_IND:
            AM_VOS_LOG_INFO("[AM-VoS] DM_CONN_OPEN_IND\n");
#if configUSE_BLE
            am_vos_mic_disable();
#endif
#if configUSE_BLE_BURST_MODE
            am_vos_burst_mode_enable();
#endif
            AppSetBondable(TRUE);
            amvosOpen((dmEvt_t *)&pMsg->hdr);

#ifdef USE_BLE_OTA
            amotas_proc_msg(&pMsg->hdr);
#endif // USE_BLE_OTA

#if configUSE_AMVOS_BATT
            BasProcMsg(&pMsg->hdr);
#endif // configUSE_AMVOS_BATT

            //AM_VOS_LOG_INFO("[AM-VoS] DmConnSetDataLen()\n");
            //DmConnSetDataLen(1, 251, 0x848);
            uiEvent = APP_UI_CONN_OPEN;
            retry_cnt = 0;
        
#if configUSE_BLE_WATCHDOG
            AMVOS_TEST_TIMER_RESTART();
#endif // configUSE_BLE_WATCHDOG

            break;

        case ATT_MTU_UPDATE_IND:
            AM_VOS_LOG_INFO("[AM-VoS] ATT_MTU_UPDATE_IND AttGetMtu(), return = %d pMsg->att.mtu = %d\n", AttGetMtu(1), pMsg->att.mtu);
            if(pMsg->att.mtu < (BLE_DATA_BUFFER_SIZE + 3))
            {
                if(retry_cnt < 5)
                {
                    retry_cnt++;
                    AttcMtuReq(1, BLE_DATA_BUFFER_SIZE + 3);
                    AM_VOS_LOG_INFO("[AM-VoS] AttcMtuReq retry_cnt = %d\n", retry_cnt);
                }
            }
            else
            {
                // once succeeded, clear retry count and enable next time to retry
                retry_cnt = 0;
                AM_VOS_LOG_INFO("[AM-VoS] DmConnSetDataLen() %d\n", pMsg->att.mtu);
                DmConnSetDataLen(pMsg->hdr.param, pMsg->att.mtu, 0x848);

#if configUSE_AMVOS_ATVV
                if(pMsg->att.mtu - 3 >= BLE_DATA_BUFFER_SIZE)
                {
                    g_sVosBle.ui32TxPktSize = BLE_DATA_BUFFER_SIZE;
                }
                else
                {
                    g_sVosBle.ui32TxPktSize = 20;
                }
#endif
            }
            break;

        case DM_CONN_DATA_LEN_CHANGE_IND:
            am_util_debug_printf("[AM-VoS] 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);
            if (AttGetMtu(pMsg->hdr.param) == ATT_DEFAULT_MTU)
            {  
                AttcMtuReq(pMsg->hdr.param, MTU_REQ_SIZE);
            }
            break;

        case DM_CONN_CLOSE_IND:
#if configUSE_BLE_WATCHDOG
            AMVOS_TX_TIMER_STOP();
            AMVOS_RSP_TIMER_STOP();
#endif // configUSE_BLE_WATCHDOG
            AM_VOS_LOG_INFO("[AM-VoS] DM_CONN_CLOSE_IND reason = 0x%02x\n", pMsg->dm.connClose.reason);

            amvosClose(pMsg);
            uiEvent = APP_UI_CONN_CLOSE;

            am_vos_audio_reset_flag_and_buffer();
          
#if configUSE_AMVOS_AMA || configUSE_AMVOS_ATVV
#if configUSE_BLE_WATCHDOG
            if(pMsg->dm.connClose.reason != HCI_ERR_LOCAL_TERMINATED)
            {
                g_sVosBle.bConnected = false;
            }
#endif // configUSE_BLE_WATCHDOG
            g_sVosBle.bComSecured = false;
            g_sVosBle.bSlaveSecReq = false;
            g_sVosBle.bCccSetupFlag = false;
            
            am_vos_mic_disable();
#endif // configUSE_AMVOS_AMA || configUSE_AMVOS_ATVV

#if configUSE_AMVOS_AMA
            am_vos_ama_status_reset();
#elif configUSE_AMVOS_ATVV
            am_vos_atvv_status_reset();
#endif

            g_sVosBle.ui8AmVosConnId = DM_CONN_ID_NONE;

            //AppAdvStart(APP_MODE_AUTO_INIT);
            break;

        case DM_CONN_UPDATE_IND:
            amvosConnUpdate((dmEvt_t *)&pMsg->hdr);
#if 0
            if(pMsg->dm.connUpdate.connInterval > amvosUpdateCfg.connIntervalMax)
            {
                // retry
                amvosConnParameterReqSend();
            }
#endif
            break;

        case DM_SEC_PAIR_CMPL_IND:
#if configUSE_BLE_SECURE_CONNECTION
            DmSecGenerateEccKeyReq();
#endif // configUSE_BLE_SECURE_CONNECTION
            uiEvent = APP_UI_SEC_PAIR_CMPL;
            AM_VOS_LOG_INFO("[AM-VoS] DM_SEC_PAIR_CMPL_IND.\n");
            break;

        case DM_SEC_PAIR_FAIL_IND:
#if configUSE_BLE_SECURE_CONNECTION
            DmSecGenerateEccKeyReq();
#endif // configUSE_BLE_SECURE_CONNECTION
            uiEvent = APP_UI_SEC_PAIR_FAIL;
            AM_VOS_LOG_DEBUG("[AM-VoS] DM_SEC_PAIR_FAIL_IND. auth=0x%x status=0x%x\n", pMsg->dm.pairCmpl.auth, pMsg->hdr.status);
            //AppConnClose(AppConnIsOpen());
            break;

        case DM_SEC_ENCRYPT_IND:
            uiEvent = APP_UI_SEC_ENCRYPT;
#if configUSE_AMVOS_AMA || configUSE_AMVOS_ATVV
            AM_VOS_LOG_INFO("[AM-VoS] DM_SEC_ENCRYPT_IND \n");
            g_sVosBle.bComSecured = true;   // channel encrypted
#if configUSE_AMVOS_AMA
            if((g_sVosBle.ui8AmVosConnId != DM_CONN_ID_NONE) && (AttsCccEnabled(AppConnIsOpen(), AMVOS_TX_CCC_IDX)))
#elif configUSE_AMVOS_ATVV
            if((g_sVosBle.ui8AmVosConnId != DM_CONN_ID_NONE) && (AttsCccEnabled(AppConnIsOpen(), ATVV_CTRL_CCC_IDX)))
#endif
            {
#if configUSE_BLE_WATCHDOG
                AMVOS_TX_CCC_TIMER_STOP();
#endif // configUSE_BLE_WATCHDOG
                if(g_sVosBle.bCccSetupFlag)
                {
#if configUSE_AMVOS_AMA
                    AM_VOS_LOG_INFO("[AMA] Version Exchange Send.\n");
                    am_vos_ama_tx_ver_exchange_send();
#endif
#if configUSE_AMVOS_ATVV
                    if(am_vos_atvv_isready())
                    {
                        am_vos_voice_start();
                    }
#endif
                }
            }
#if configUSE_BLE_WATCHDOG && (configUSE_AMVOS_AMA || configUSE_AMVOS_ATVV)
            else
            {
                AMVOS_TX_CCC_TIMER_START();
                AM_VOS_LOG_INFO("[AMA] AMVOS_TX_CCC_TIMER_START();\n");
            }
#endif // configUSE_BLE_WATCHDOG && (configUSE_AMVOS_AMA || configUSE_AMVOS_ATVV)
#endif // configUSE_AMVOS_AMA || configUSE_AMVOS_ATVV
            break;

        case DM_SEC_ENCRYPT_FAIL_IND:
            uiEvent = APP_UI_SEC_ENCRYPT_FAIL;
            //AppConnClose(AppConnIsOpen());
            //g_sVosBle.ui8AmVosConnId = DM_CONN_ID_NONE;
            break;

        case DM_SEC_AUTH_REQ_IND:
            AM_VOS_LOG_INFO("[AM-VoS] DM_SEC_AUTH_REQ_IND \n");
            AppHandlePasskey(&pMsg->dm.authReq);
            break;
        case DM_SEC_PAIR_IND:
            AM_VOS_LOG_INFO("[AM-VoS] DM_SEC_PAIR_IND \n");
            
            break;
        case DM_SEC_ECC_KEY_IND:
            DmSecSetEccKey(&pMsg->dm.eccMsg.data.key);
            break;

        case DM_SEC_COMPARE_IND:
            AM_VOS_LOG_INFO("[AM-VoS] DM_SEC_COMPARE_IND \n");
            AppHandleNumericComparison(&pMsg->dm.cnfInd);
            break;
      
        case DM_VENDOR_SPEC_CMD_CMPL_IND:
            {
#if defined(AM_PART_APOLLO) || defined(AM_PART_APOLLO2)

                uint8_t *param_ptr = &pMsg->dm.vendorSpecCmdCmpl.param[0];

                switch (pMsg->dm.vendorSpecCmdCmpl.opcode)
                {
                    case 0xFC20: //read at address
                    {
                        uint32_t read_value;

                        BSTREAM_TO_UINT32(read_value, param_ptr);
                        AM_VOS_LOG_INFO("[AM-VoS] VSC 0x%0x complete status %x param %x",
                          pMsg->dm.vendorSpecCmdCmpl.opcode,
                          pMsg->hdr.status,
                          read_value);
                    }

                    break;
                    default:
                          AM_VOS_LOG_INFO("[AM-VoS] VSC 0x%0x complete status %x",
                              pMsg->dm.vendorSpecCmdCmpl.opcode,
                              pMsg->hdr.status);
                    break;
                }
#endif // AM_PART_APOLLO, AM_PART_APOLLO2
            }
            break;

        default:
            break;
    }

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

/*************************************************************************************************/
/*!
 *  \fn     AmVosHandlerInit
 *
 *  \brief  Application handler init function called during system initialization.
 *
 *  \param  handlerID  WSF handler ID.
 *
 *  \return None.
 */
/*************************************************************************************************/
void AmVosHandlerInit(wsfHandlerId_t handlerId)
{
    AM_VOS_LOG_INFO("[AM-VoS] AmVosHandlerInit\n");

#if configUSE_AMVOS_HID
    /* Initialize the control block */
    memset(&hidAppCb, 0, sizeof(hidAppCb));
    hidAppCb.txFlags = HIDAPP_TX_FLAGS_READY;

    /* store handler ID */
    hidAppCb.handlerId = handlerId;
#else
    g_sVosBle.ui8AmVosHandlerId = handlerId;
#endif
    /* store handler ID */

    /* Set configuration pointers */
    pAppAdvCfg = (appAdvCfg_t *) &amvosAdvCfg;
    pAppSlaveCfg = (appSlaveCfg_t *) &amvosSlaveCfg;
    pAppSecCfg = (appSecCfg_t *) &amvosSecCfg;
    pAppUpdateCfg = (appUpdateCfg_t *) &amvosUpdateCfg;

    /* Initialize application framework */
    AppSlaveInit();
    AppServerInit();

    /* Set stack configuration pointers */
    pSmpCfg = (smpCfg_t *) &amvosSmpCfg;

#ifdef USE_BLE_OTA
    /* initialize amota service server */
    amotas_init(handlerId, (AmotasCfg_t *) &vosAmotaCfg);
#endif // USE_BLE_OTA

#if configUSE_AMVOS_BATT
    /* initialize battery service server */
    BasInit(handlerId, (basCfg_t *) &amvosBasCfg);
#endif // configUSE_AMVOS_BATT

#if configUSE_BLE_Measure_Throughput
    g_sVosBle.sMeasTpTimer.handlerId = handlerId;
    g_sVosBle.sMeasTpTimer.msg.event = AMVOS_MEAS_TP_TIMER_IND;
#endif // configUSE_BLE_Measure_Throughput
    
#if configUSE_BLE_WATCHDOG
    g_sVosBle.sAmvosTxTimer.handlerId = handlerId;
    g_sVosBle.sAmvosTxTimer.msg.event = AMVOS_TX_TIMEOUT_TIMER_IND;
    
    g_sVosBle.sAmvosRspTimer.handlerId = handlerId;
    g_sVosBle.sAmvosRspTimer.msg.event = AMVOS_RSP_TIMEOUT_TIMER_IND;

    g_sVosBle.sAmvosTxCccTimer.handlerId = handlerId;
    g_sVosBle.sAmvosTxCccTimer.msg.event = AMVOS_TX_CCC_TIMEOUT_TIMER_IND;
    
    g_sVosBle.sAmvosTestTimer.handlerId = handlerId;
    g_sVosBle.sAmvosTestTimer.msg.event = AMVOS_TEST_TIMEOUT_TIMER_IND;
#endif // configUSE_BLE_WATCHDOG
}

/*************************************************************************************************/
/*!
 *  \fn     AmVosHandler
 *
 *  \brief  WSF event handler for application.
 *
 *  \param  event   WSF event mask.
 *  \param  pMsg    WSF message.
 *
 *  \return None.
 */
/*************************************************************************************************/
void AmVosHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
{
    if (pMsg != NULL)
    {
        //AM_VOS_LOG_INFO("Amvos 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 */
        amvosProcMsg((amvosMsg_t *) pMsg);
    }
}

/*************************************************************************************************/
/*!
 *  \fn     AmVosStart
 *
 *  \brief  Start the application.
 *
 *  \return None.
 */
/*************************************************************************************************/
void AmVosStart(void)
{
    /* Register for stack callbacks */
    DmRegister(amvosDmCback);
    DmConnRegister(DM_CLIENT_ID_APP, amvosDmCback);
    AttRegister(amvosAttCback);
    AttConnRegister(AppServerConnCback);
    AttsCccRegister(AMVOS_NUM_CCC_IDX, (attsCccSet_t *) amvosCccSet, amvosCccCback);

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

    // set up adv data
    memcpy(amvosAdvDataDisc, amvosAdvDataDiscDefault, sizeof(amvosAdvDataDiscDefault));
    memcpy(amvosScanDataDisc, amvosScanDataDiscDefault, sizeof(amvosScanDataDiscDefault));

    /* Initialize attribute server database */
    SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
    SvcCoreAddGroup();
    SvcDisAddGroup();
    SvcAmvosCbackRegister(NULL, am_vos_packet_rx);
    SvcAmvosAddGroup();

    /* Set Service Changed CCCD index. */
    GattSetSvcChangedIdx(AMVOS_GATT_SC_CCC_IDX);

#ifdef USE_BLE_OTA
    SvcAmotasCbackRegister(NULL, amotas_write_cback);
    SvcAmotasAddGroup();
#endif // USE_BLE_OTA

#if configUSE_AMVOS_BATT
    /* Add the Battery service statically */
    SvcBattAddGroup();
    SvcBattCbackRegister(BasReadCback, NULL);
#endif // configUSE_AMVOS_BATT

#if configUSE_AMVOS_HID
    /* Add the HID service statically */
    SvcHidAddGroup();
    SvcHidRegister(HidAttsWriteCback, NULL);

    /* Initialize the HID profile */
    HidInit(&hidAppHidConfig);

    /* Initialize the report attributes */
    hidAppReportInit();
#endif

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

#if configUSE_PAIRING_MODE_BTN && configUSE_BLE
extern void am_vos_nvm_erase_data(void);

void
am_vos_ble_pairing_mode(void)
{
    dmConnId_t      connId;

    AM_VOS_LOG_INFO("[AM-VoS] am_vos_ble_pairing_mode\n");
    g_sVosBle.ui8AmVosConnId = DM_CONN_ID_NONE;

#if configUSE_AMVOS_AMA
    am_vos_ama_status_reset();
#elif configUSE_AMVOS_ATVV
    am_vos_atvv_status_reset();
#endif // configUSE_AMVOS_AMA

    if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE)
    {
        AppConnClose(connId);
    }

    /* clear bonded device info and restart advertising */
    AppDbDeleteAllRecords();
    am_vos_nvm_erase_data();

    //AppAdvStart(APP_MODE_CONNECTABLE);
}
#endif // configUSE_PAIRING_MODE_BTN && configUSE_BLE

void
am_vos_voice_start(void)
{
    if(am_vos_is_connected())
    {
        AM_VOS_LOG_INFO("[AM-VoS] am_vos_voice_start()\n");
#if configUSE_BLE_BURST_MODE
        am_vos_burst_mode_disable();
#endif
        am_vos_mic_enable();
#if configUSE_BLE_WATCHDOG && (configUSE_AMVOS_AMA || configUSE_AMVOS_ATVV)
        g_sVosBle.bConnected = true;
#endif // configUSE_BLE_WATCHDOG && (configUSE_AMVOS_AMA || configUSE_AMVOS_ATVV)
    }
    else
    {
#if configUSE_BLE_WATCHDOG && (configUSE_AMVOS_AMA || configUSE_AMVOS_ATVV)
        AM_VOS_LOG_INFO("[AMA] AMVOS_TX_CCC_TIMER_RESTART();\n");
        AMVOS_TX_CCC_TIMER_RESTART();
#endif // configUSE_BLE_WATCHDOG && (configUSE_AMVOS_AMA || configUSE_AMVOS_ATVV)
    }
}

