/******************************************************************************
 * Filename:       Device_Information.c
 *
 * Description:    This file contains the implementation of the service.
 *
 *                 Generated by:
 *                 BDS version: 1.1.3139.0
 *                 Plugin:      Texas Instruments BLE SDK GATT Server plugin 1.0.9
 *                 Time:        Thu Jan 11 2018 13:32:51 GMT-07:00
 *

 * Copyright (c) 2015-2017, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  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.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 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 OWNER 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.
 *
 **********************************************************************************/


/*********************************************************************
 * INCLUDES
 */
#include <string.h>

//#define xdc_runtime_Log_DISABLE_ALL 1  // Add to disable logs from this file
#include <xdc/runtime/Log.h>
#include <xdc/runtime/Diags.h>
#ifdef UARTLOG_ENABLE
#  include "UartLog.h"
#endif

#include <icall.h>
#include "util.h"
/* This Header file contains all BLE API and icall structure definition */
#include "icall_ble_api.h"

#include "device_information.h"

/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * CONSTANTS
 */

/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
* GLOBAL VARIABLES
*/

// Device_Information Service UUID
CONST uint8_t DeviceInformationUUID[ATT_BT_UUID_SIZE] =
{
  LO_UINT16(DEVICE_INFORMATION_SERV_UUID), HI_UINT16(DEVICE_INFORMATION_SERV_UUID)
};

// Serial_Number_String UUID
CONST uint8_t di_Serial_Number_StringUUID[ATT_BT_UUID_SIZE] =
{
  LO_UINT16(DI_SERIAL_NUMBER_STRING_UUID), HI_UINT16(DI_SERIAL_NUMBER_STRING_UUID)
};

// Firmware_Revision_String UUID
CONST uint8_t di_Firmware_Revision_StringUUID[ATT_BT_UUID_SIZE] =
{
  LO_UINT16(DI_FIRMWARE_REVISION_STRING_UUID), HI_UINT16(DI_FIRMWARE_REVISION_STRING_UUID)
};

// RTC UUID
CONST uint8_t di_RTCUUID[ATT_BT_UUID_SIZE] =
{
  LO_UINT16(DI_RTC_UUID), HI_UINT16(DI_RTC_UUID)
};

// WaveLength UUID
CONST uint8_t di_WaveLengthUUID[ATT_BT_UUID_SIZE] =
{
  LO_UINT16(DI_WAVELENGTH_UUID), HI_UINT16(DI_WAVELENGTH_UUID)
};

// Range UUID
CONST uint8_t di_RangeUUID[ATT_BT_UUID_SIZE] =
{
  LO_UINT16(DI_RANGE_UUID), HI_UINT16(DI_RANGE_UUID)
};

// Laser_Power_Levels UUID
CONST uint8_t di_Laser_Power_LevelsUUID[ATT_BT_UUID_SIZE] =
{
  LO_UINT16(DI_LASER_POWER_LEVELS_UUID), HI_UINT16(DI_LASER_POWER_LEVELS_UUID)
};

// XCalCoefficients UUID
CONST uint8_t di_XCalCoefficientsUUID[ATT_BT_UUID_SIZE] =
{
  LO_UINT16(DI_XCALCOEFFICIENTS_UUID), HI_UINT16(DI_XCALCOEFFICIENTS_UUID)
};


/*********************************************************************
 * LOCAL VARIABLES
 */

static DeviceInformationCBs_t *pAppCBs = NULL;

/*********************************************************************
* Profile Attributes - variables
*/

// Service declaration
static CONST gattAttrType_t DeviceInformationDecl = { ATT_BT_UUID_SIZE, DeviceInformationUUID };

// Characteristic "Serial Number String" Properties (for declaration)
static uint8_t di_Serial_Number_StringProps = GATT_PROP_READ | GATT_PROP_WRITE;

// Characteristic "Serial Number String" Value variable
static uint8_t di_Serial_Number_StringVal[DI_SERIAL_NUMBER_STRING_LEN] = {0};

// Length of data in characteristic "Serial Number String" Value variable, initialized to minimal size.
static uint16_t di_Serial_Number_StringValLen = DI_SERIAL_NUMBER_STRING_LEN_MIN;



// Characteristic "Firmware Revision String" Properties (for declaration)
static uint8_t di_Firmware_Revision_StringProps = GATT_PROP_READ | GATT_PROP_WRITE;

// Characteristic "Firmware Revision String" Value variable
static uint8_t di_Firmware_Revision_StringVal[DI_FIRMWARE_REVISION_STRING_LEN] = {0};

// Length of data in characteristic "Firmware Revision String" Value variable, initialized to minimal size.
static uint16_t di_Firmware_Revision_StringValLen = DI_FIRMWARE_REVISION_STRING_LEN_MIN;



// Characteristic "RTC" Properties (for declaration)
static uint8_t di_RTCProps = GATT_PROP_READ | GATT_PROP_WRITE;

// Characteristic "RTC" Value variable
static uint8_t di_RTCVal[DI_RTC_LEN] = {0};

// Length of data in characteristic "RTC" Value variable, initialized to minimal size.
static uint16_t di_RTCValLen = DI_RTC_LEN_MIN;



// Characteristic "WaveLength" Properties (for declaration)
static uint8_t di_WaveLengthProps = GATT_PROP_READ | GATT_PROP_WRITE;

// Characteristic "WaveLength" Value variable
static uint8_t di_WaveLengthVal[DI_WAVELENGTH_LEN] = {0};

// Length of data in characteristic "WaveLength" Value variable, initialized to minimal size.
static uint16_t di_WaveLengthValLen = DI_WAVELENGTH_LEN_MIN;



// Characteristic "Range" Properties (for declaration)
static uint8_t di_RangeProps = GATT_PROP_READ | GATT_PROP_WRITE;

// Characteristic "Range" Value variable
static uint8_t di_RangeVal[DI_RANGE_LEN] = {0};

// Length of data in characteristic "Range" Value variable, initialized to minimal size.
static uint16_t di_RangeValLen = DI_RANGE_LEN_MIN;



// Characteristic "Laser Power Levels" Properties (for declaration)
static uint8_t di_Laser_Power_LevelsProps = GATT_PROP_READ | GATT_PROP_WRITE;

// Characteristic "Laser Power Levels" Value variable
static uint8_t di_Laser_Power_LevelsVal[DI_LASER_POWER_LEVELS_LEN] = {0};

// Length of data in characteristic "Laser Power Levels" Value variable, initialized to minimal size.
static uint16_t di_Laser_Power_LevelsValLen = DI_LASER_POWER_LEVELS_LEN_MIN;



// Characteristic "XCalCoefficients" Properties (for declaration)
static uint8_t di_XCalCoefficientsProps = GATT_PROP_READ | GATT_PROP_WRITE;

// Characteristic "XCalCoefficients" Value variable
static uint8_t di_XCalCoefficientsVal[DI_XCALCOEFFICIENTS_LEN] = {0};

// Length of data in characteristic "XCalCoefficients" Value variable, initialized to minimal size.
static uint16_t di_XCalCoefficientsValLen = DI_XCALCOEFFICIENTS_LEN_MIN;



/*********************************************************************
* Profile Attributes - Table
*/

static gattAttribute_t Device_InformationAttrTbl[] =
{
  // Device_Information Service Declaration
  {
    { ATT_BT_UUID_SIZE, primaryServiceUUID },
    GATT_PERMIT_READ,
    0,
    (uint8_t *)&DeviceInformationDecl
  },
    // Serial Number String Characteristic Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &di_Serial_Number_StringProps
    },
      // Serial Number String Characteristic Value
      {
        { ATT_BT_UUID_SIZE, di_Serial_Number_StringUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        di_Serial_Number_StringVal
      },
    // Firmware Revision String Characteristic Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &di_Firmware_Revision_StringProps
    },
      // Firmware Revision String Characteristic Value
      {
        { ATT_BT_UUID_SIZE, di_Firmware_Revision_StringUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        di_Firmware_Revision_StringVal
      },
    // RTC Characteristic Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &di_RTCProps
    },
      // RTC Characteristic Value
      {
        { ATT_BT_UUID_SIZE, di_RTCUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        di_RTCVal
      },
    // WaveLength Characteristic Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &di_WaveLengthProps
    },
      // WaveLength Characteristic Value
      {
        { ATT_BT_UUID_SIZE, di_WaveLengthUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        di_WaveLengthVal
      },
    // Range Characteristic Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &di_RangeProps
    },
      // Range Characteristic Value
      {
        { ATT_BT_UUID_SIZE, di_RangeUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        di_RangeVal
      },
    // Laser Power Levels Characteristic Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &di_Laser_Power_LevelsProps
    },
      // Laser Power Levels Characteristic Value
      {
        { ATT_BT_UUID_SIZE, di_Laser_Power_LevelsUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        di_Laser_Power_LevelsVal
      },
    // XCalCoefficients Characteristic Declaration
    {
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ,
      0,
      &di_XCalCoefficientsProps
    },
      // XCalCoefficients Characteristic Value
      {
        { ATT_BT_UUID_SIZE, di_XCalCoefficientsUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,
        0,
        di_XCalCoefficientsVal
      },
};

/*********************************************************************
 * LOCAL FUNCTIONS
 */
static bStatus_t Device_Information_ReadAttrCB( uint16_t connHandle, gattAttribute_t *pAttr,
                                           uint8_t *pValue, uint16_t *pLen, uint16_t offset,
                                           uint16_t maxLen, uint8_t method );
static bStatus_t Device_Information_WriteAttrCB( uint16_t connHandle, gattAttribute_t *pAttr,
                                            uint8_t *pValue, uint16_t len, uint16_t offset,
                                            uint8_t method );

/*********************************************************************
 * PROFILE CALLBACKS
 */
// Simple Profile Service Callbacks
CONST gattServiceCBs_t Device_InformationCBs =
{
  Device_Information_ReadAttrCB,  // Read callback function pointer
  Device_Information_WriteAttrCB, // Write callback function pointer
  NULL                       // Authorization callback function pointer
};

/*********************************************************************
* PUBLIC FUNCTIONS
*/

/*
 * DeviceInformation_AddService- Initializes the DeviceInformation service by registering
 *          GATT attributes with the GATT server.
 *
 *    rspTaskId - The ICall Task Id that should receive responses for Indications.
 */
extern bStatus_t DeviceInformation_AddService( uint8_t rspTaskId )
{
  uint8_t status;

  // Register GATT attribute list and CBs with GATT Server App
  status = GATTServApp_RegisterService( Device_InformationAttrTbl,
                                        GATT_NUM_ATTRS( Device_InformationAttrTbl ),
                                        GATT_MAX_ENCRYPT_KEY_SIZE,
                                        &Device_InformationCBs );
  Log_info1("Registered service, %d attributes", (IArg)GATT_NUM_ATTRS( Device_InformationAttrTbl ));
  return ( status );
}

/*
 * DeviceInformation_RegisterAppCBs - Registers the application callback function.
 *                    Only call this function once.
 *
 *    appCallbacks - pointer to application callbacks.
 */
bStatus_t DeviceInformation_RegisterAppCBs( DeviceInformationCBs_t *appCallbacks )
{
  if ( appCallbacks )
  {
    pAppCBs = appCallbacks;
    Log_info1("Registered callbacks to application. Struct %p", (IArg)appCallbacks);
    return ( SUCCESS );
  }
  else
  {
    Log_warning0("Null pointer given for app callbacks.");
    return ( FAILURE );
  }
}

/*
 * DeviceInformation_SetParameter - Set a DeviceInformation parameter.
 *
 *    param - Profile parameter ID
 *    len   - length of data to write
 *    value - pointer to data to write.  This is dependent on
 *            the parameter ID and may be cast to the appropriate
 *            data type (example: data type of uint16_t will be cast to
 *            uint16_t pointer).
 */
bStatus_t DeviceInformation_SetParameter( uint8_t param, uint16_t len, void *value )
{
  bStatus_t ret = SUCCESS;
  uint8_t  *pAttrVal;
  uint16_t *pValLen;
  uint16_t valMinLen;
  uint16_t valMaxLen;

  switch ( param )
  {
    case DI_SERIAL_NUMBER_STRING_ID:
      pAttrVal  =  di_Serial_Number_StringVal;
      pValLen   = &di_Serial_Number_StringValLen;
      valMinLen =  DI_SERIAL_NUMBER_STRING_LEN_MIN;
      valMaxLen =  DI_SERIAL_NUMBER_STRING_LEN;
      Log_info2("SetParameter : %s len: %d", (IArg)"Serial_Number_String", (IArg)len);
      break;

    case DI_FIRMWARE_REVISION_STRING_ID:
      pAttrVal  =  di_Firmware_Revision_StringVal;
      pValLen   = &di_Firmware_Revision_StringValLen;
      valMinLen =  DI_FIRMWARE_REVISION_STRING_LEN_MIN;
      valMaxLen =  DI_FIRMWARE_REVISION_STRING_LEN;
      Log_info2("SetParameter : %s len: %d", (IArg)"Firmware_Revision_String", (IArg)len);
      break;

    case DI_RTC_ID:
      pAttrVal  =  di_RTCVal;
      pValLen   = &di_RTCValLen;
      valMinLen =  DI_RTC_LEN_MIN;
      valMaxLen =  DI_RTC_LEN;
      Log_info2("SetParameter : %s len: %d", (IArg)"RTC", (IArg)len);
      break;

    case DI_WAVELENGTH_ID:
      pAttrVal  =  di_WaveLengthVal;
      pValLen   = &di_WaveLengthValLen;
      valMinLen =  DI_WAVELENGTH_LEN_MIN;
      valMaxLen =  DI_WAVELENGTH_LEN;
      Log_info2("SetParameter : %s len: %d", (IArg)"WaveLength", (IArg)len);
      break;

    case DI_RANGE_ID:
      pAttrVal  =  di_RangeVal;
      pValLen   = &di_RangeValLen;
      valMinLen =  DI_RANGE_LEN_MIN;
      valMaxLen =  DI_RANGE_LEN;
      Log_info2("SetParameter : %s len: %d", (IArg)"Range", (IArg)len);
      break;

    case DI_LASER_POWER_LEVELS_ID:
      pAttrVal  =  di_Laser_Power_LevelsVal;
      pValLen   = &di_Laser_Power_LevelsValLen;
      valMinLen =  DI_LASER_POWER_LEVELS_LEN_MIN;
      valMaxLen =  DI_LASER_POWER_LEVELS_LEN;
      Log_info2("SetParameter : %s len: %d", (IArg)"Laser_Power_Levels", (IArg)len);
      break;

    case DI_XCALCOEFFICIENTS_ID:
      pAttrVal  =  di_XCalCoefficientsVal;
      pValLen   = &di_XCalCoefficientsValLen;
      valMinLen =  DI_XCALCOEFFICIENTS_LEN_MIN;
      valMaxLen =  DI_XCALCOEFFICIENTS_LEN;
      Log_info2("SetParameter : %s len: %d", (IArg)"XCalCoefficients", (IArg)len);
      break;

    default:
      Log_error1("SetParameter: Parameter #%d not valid.", (IArg)param);
      return INVALIDPARAMETER;
  }

  // Check bounds, update value and send notification or indication if possible.
  if ( len <= valMaxLen && len >= valMinLen )
  {
    memcpy(pAttrVal, value, len);
    *pValLen = len; // Update length for read and get.
  }
  else
  {
    Log_error3("Length outside bounds: Len: %d MinLen: %d MaxLen: %d.", (IArg)len, (IArg)valMinLen, (IArg)valMaxLen);
    ret = bleInvalidRange;
  }

  return ret;
}


/*
 * DeviceInformation_GetParameter - Get a DeviceInformation parameter.
 *
 *    param - Profile parameter ID
 *    len   - pointer to a variable that contains the maximum length that can be written to *value.
              After the call, this value will contain the actual returned length.
 *    value - pointer to data to write.  This is dependent on
 *            the parameter ID and may be cast to the appropriate
 *            data type (example: data type of uint16_t will be cast to
 *            uint16_t pointer).
 */
bStatus_t DeviceInformation_GetParameter( uint8_t param, uint16_t *len, void *value )
{
  bStatus_t ret = SUCCESS;
  switch ( param )
  {
    case DI_SERIAL_NUMBER_STRING_ID:
      *len = MIN(*len, di_Serial_Number_StringValLen);
      memcpy(value, di_Serial_Number_StringVal, *len);
      Log_info2("GetParameter : %s returning %d bytes", (IArg)"Serial_Number_String", (IArg)*len);
      break;

    case DI_FIRMWARE_REVISION_STRING_ID:
      *len = MIN(*len, di_Firmware_Revision_StringValLen);
      memcpy(value, di_Firmware_Revision_StringVal, *len);
      Log_info2("GetParameter : %s returning %d bytes", (IArg)"Firmware_Revision_String", (IArg)*len);
      break;

    case DI_RTC_ID:
      *len = MIN(*len, di_RTCValLen);
      memcpy(value, di_RTCVal, *len);
      Log_info2("GetParameter : %s returning %d bytes", (IArg)"RTC", (IArg)*len);
      break;

    case DI_WAVELENGTH_ID:
      *len = MIN(*len, di_WaveLengthValLen);
      memcpy(value, di_WaveLengthVal, *len);
      Log_info2("GetParameter : %s returning %d bytes", (IArg)"WaveLength", (IArg)*len);
      break;

    case DI_RANGE_ID:
      *len = MIN(*len, di_RangeValLen);
      memcpy(value, di_RangeVal, *len);
      Log_info2("GetParameter : %s returning %d bytes", (IArg)"Range", (IArg)*len);
      break;

    case DI_LASER_POWER_LEVELS_ID:
      *len = MIN(*len, di_Laser_Power_LevelsValLen);
      memcpy(value, di_Laser_Power_LevelsVal, *len);
      Log_info2("GetParameter : %s returning %d bytes", (IArg)"Laser_Power_Levels", (IArg)*len);
      break;

    case DI_XCALCOEFFICIENTS_ID:
      *len = MIN(*len, di_XCalCoefficientsValLen);
      memcpy(value, di_XCalCoefficientsVal, *len);
      Log_info2("GetParameter : %s returning %d bytes", (IArg)"XCalCoefficients", (IArg)*len);
      break;

    default:
      Log_error1("GetParameter: Parameter #%d not valid.", (IArg)param);
      ret = INVALIDPARAMETER;
      break;
  }
  return ret;
}

/*********************************************************************
 * @internal
 * @fn          Device_Information_findCharParamId
 *
 * @brief       Find the logical param id of an attribute in the service's attr table.
 *
 *              Works only for Characteristic Value attributes and
 *              Client Characteristic Configuration Descriptor attributes.
 *
 * @param       pAttr - pointer to attribute
 *
 * @return      uint8_t paramID (ref Device_Information.h) or 0xFF if not found.
 */
static uint8_t Device_Information_findCharParamId(gattAttribute_t *pAttr)
{
  // Is this a Client Characteristic Configuration Descriptor?
  if (ATT_BT_UUID_SIZE == pAttr->type.len && GATT_CLIENT_CHAR_CFG_UUID == *(uint16_t *)pAttr->type.uuid)
    return Device_Information_findCharParamId(pAttr - 1); // Assume the value attribute precedes CCCD and recurse

  // Is this attribute in "Serial Number String"?
  else if ( ATT_BT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, di_Serial_Number_StringUUID, pAttr->type.len))
    return DI_SERIAL_NUMBER_STRING_ID;

  // Is this attribute in "Firmware Revision String"?
  else if ( ATT_BT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, di_Firmware_Revision_StringUUID, pAttr->type.len))
    return DI_FIRMWARE_REVISION_STRING_ID;

  // Is this attribute in "RTC"?
  else if ( ATT_BT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, di_RTCUUID, pAttr->type.len))
    return DI_RTC_ID;

  // Is this attribute in "WaveLength"?
  else if ( ATT_BT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, di_WaveLengthUUID, pAttr->type.len))
    return DI_WAVELENGTH_ID;

  // Is this attribute in "Range"?
  else if ( ATT_BT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, di_RangeUUID, pAttr->type.len))
    return DI_RANGE_ID;

  // Is this attribute in "Laser Power Levels"?
  else if ( ATT_BT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, di_Laser_Power_LevelsUUID, pAttr->type.len))
    return DI_LASER_POWER_LEVELS_ID;

  // Is this attribute in "XCalCoefficients"?
  else if ( ATT_BT_UUID_SIZE == pAttr->type.len && !memcmp(pAttr->type.uuid, di_XCalCoefficientsUUID, pAttr->type.len))
    return DI_XCALCOEFFICIENTS_ID;

  else
    return 0xFF; // Not found. Return invalid.
}

/*********************************************************************
 * @fn          Device_Information_ReadAttrCB
 *
 * @brief       Read an attribute.
 *
 * @param       connHandle - connection message was received on
 * @param       pAttr - pointer to attribute
 * @param       pValue - pointer to data to be read
 * @param       pLen - length of data to be read
 * @param       offset - offset of the first octet to be read
 * @param       maxLen - maximum length of data to be read
 * @param       method - type of read message
 *
 * @return      SUCCESS, blePending or Failure
 */
static bStatus_t Device_Information_ReadAttrCB( uint16_t connHandle, gattAttribute_t *pAttr,
                                       uint8_t *pValue, uint16_t *pLen, uint16_t offset,
                                       uint16_t maxLen, uint8_t method )
{
  bStatus_t status = SUCCESS;
  uint16_t valueLen;
  uint8_t paramID = 0xFF;

  // Find settings for the characteristic to be read.
  paramID = Device_Information_findCharParamId( pAttr );
  switch ( paramID )
  {
    case DI_SERIAL_NUMBER_STRING_ID:
      valueLen = di_Serial_Number_StringValLen;

      Log_info4("ReadAttrCB : %s connHandle: %d offset: %d method: 0x%02x",
                 (IArg)"Serial_Number_String",
                 (IArg)connHandle,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for Serial Number String can be inserted here */
      break;

    case DI_FIRMWARE_REVISION_STRING_ID:
      valueLen = di_Firmware_Revision_StringValLen;

      Log_info4("ReadAttrCB : %s connHandle: %d offset: %d method: 0x%02x",
                 (IArg)"Firmware_Revision_String",
                 (IArg)connHandle,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for Firmware Revision String can be inserted here */
      break;

    case DI_RTC_ID:
      valueLen = di_RTCValLen;

      Log_info4("ReadAttrCB : %s connHandle: %d offset: %d method: 0x%02x",
                 (IArg)"RTC",
                 (IArg)connHandle,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for RTC can be inserted here */
      break;

    case DI_WAVELENGTH_ID:
      valueLen = di_WaveLengthValLen;

      Log_info4("ReadAttrCB : %s connHandle: %d offset: %d method: 0x%02x",
                 (IArg)"WaveLength",
                 (IArg)connHandle,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for WaveLength can be inserted here */
      break;

    case DI_RANGE_ID:
      valueLen = di_RangeValLen;

      Log_info4("ReadAttrCB : %s connHandle: %d offset: %d method: 0x%02x",
                 (IArg)"Range",
                 (IArg)connHandle,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for Range can be inserted here */
      break;

    case DI_LASER_POWER_LEVELS_ID:
      valueLen = di_Laser_Power_LevelsValLen;

      Log_info4("ReadAttrCB : %s connHandle: %d offset: %d method: 0x%02x",
                 (IArg)"Laser_Power_Levels",
                 (IArg)connHandle,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for Laser Power Levels can be inserted here */
      break;

    case DI_XCALCOEFFICIENTS_ID:
      valueLen = di_XCalCoefficientsValLen;

      Log_info4("ReadAttrCB : %s connHandle: %d offset: %d method: 0x%02x",
                 (IArg)"XCalCoefficients",
                 (IArg)connHandle,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for XCalCoefficients can be inserted here */
      break;

    default:
      Log_error0("Attribute was not found.");
      return ATT_ERR_ATTR_NOT_FOUND;
  }
  // Check bounds and return the value
  if ( offset > valueLen )  // Prevent malicious ATT ReadBlob offsets.
  {
    Log_error0("An invalid offset was requested.");
    status = ATT_ERR_INVALID_OFFSET;
  }
  else
  {
    *pLen = MIN(maxLen, valueLen - offset);  // Transmit as much as possible
    memcpy(pValue, pAttr->pValue + offset, *pLen);
  }

  return status;
}

/*********************************************************************
 * @fn      Device_Information_WriteAttrCB
 *
 * @brief   Validate attribute data prior to a write operation
 *
 * @param   connHandle - connection message was received on
 * @param   pAttr - pointer to attribute
 * @param   pValue - pointer to data to be written
 * @param   len - length of data
 * @param   offset - offset of the first octet to be written
 * @param   method - type of write message
 *
 * @return  SUCCESS, blePending or Failure
 */
static bStatus_t Device_Information_WriteAttrCB( uint16_t connHandle, gattAttribute_t *pAttr,
                                        uint8_t *pValue, uint16_t len, uint16_t offset,
                                        uint8_t method )
{
  bStatus_t status  = SUCCESS;
  uint8_t   paramID = 0xFF;
  uint8_t   changeParamID = 0xFF;
  uint16_t writeLenMin;
  uint16_t writeLenMax;
  uint16_t *pValueLenVar;

  // Find settings for the characteristic to be written.
  paramID = Device_Information_findCharParamId( pAttr );
  switch ( paramID )
  {
    case DI_SERIAL_NUMBER_STRING_ID:
      writeLenMin  = DI_SERIAL_NUMBER_STRING_LEN_MIN;
      writeLenMax  = DI_SERIAL_NUMBER_STRING_LEN;
      pValueLenVar = &di_Serial_Number_StringValLen;

      Log_info5("WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",
                 (IArg)"Serial_Number_String",
                 (IArg)connHandle,
                 (IArg)len,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for Serial Number String can be inserted here */
      break;

    case DI_FIRMWARE_REVISION_STRING_ID:
      writeLenMin  = DI_FIRMWARE_REVISION_STRING_LEN_MIN;
      writeLenMax  = DI_FIRMWARE_REVISION_STRING_LEN;
      pValueLenVar = &di_Firmware_Revision_StringValLen;

      Log_info5("WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",
                 (IArg)"Firmware_Revision_String",
                 (IArg)connHandle,
                 (IArg)len,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for Firmware Revision String can be inserted here */
      break;

    case DI_RTC_ID:
      writeLenMin  = DI_RTC_LEN_MIN;
      writeLenMax  = DI_RTC_LEN;
      pValueLenVar = &di_RTCValLen;

      Log_info5("WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",
                 (IArg)"RTC",
                 (IArg)connHandle,
                 (IArg)len,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for RTC can be inserted here */
      break;

    case DI_WAVELENGTH_ID:
      writeLenMin  = DI_WAVELENGTH_LEN_MIN;
      writeLenMax  = DI_WAVELENGTH_LEN;
      pValueLenVar = &di_WaveLengthValLen;

      Log_info5("WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",
                 (IArg)"WaveLength",
                 (IArg)connHandle,
                 (IArg)len,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for WaveLength can be inserted here */
      break;

    case DI_RANGE_ID:
      writeLenMin  = DI_RANGE_LEN_MIN;
      writeLenMax  = DI_RANGE_LEN;
      pValueLenVar = &di_RangeValLen;

      Log_info5("WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",
                 (IArg)"Range",
                 (IArg)connHandle,
                 (IArg)len,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for Range can be inserted here */
      break;

    case DI_LASER_POWER_LEVELS_ID:
      writeLenMin  = DI_LASER_POWER_LEVELS_LEN_MIN;
      writeLenMax  = DI_LASER_POWER_LEVELS_LEN;
      pValueLenVar = &di_Laser_Power_LevelsValLen;

      Log_info5("WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",
                 (IArg)"Laser_Power_Levels",
                 (IArg)connHandle,
                 (IArg)len,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for Laser Power Levels can be inserted here */
      break;

    case DI_XCALCOEFFICIENTS_ID:
      writeLenMin  = DI_XCALCOEFFICIENTS_LEN_MIN;
      writeLenMax  = DI_XCALCOEFFICIENTS_LEN;
      pValueLenVar = &di_XCalCoefficientsValLen;

      Log_info5("WriteAttrCB : %s connHandle(%d) len(%d) offset(%d) method(0x%02x)",
                 (IArg)"XCalCoefficients",
                 (IArg)connHandle,
                 (IArg)len,
                 (IArg)offset,
                 (IArg)method);
      /* Other considerations for XCalCoefficients can be inserted here */
      break;

    default:
      Log_error0("Attribute was not found.");
      return ATT_ERR_ATTR_NOT_FOUND;
  }
  // Check whether the length is within bounds.
  if ( offset >= writeLenMax )
  {
    Log_error0("An invalid offset was requested.");
    status = ATT_ERR_INVALID_OFFSET;
  }
  else if ( offset + len > writeLenMax )
  {
    Log_error0("Invalid value length was received.");
    status = ATT_ERR_INVALID_VALUE_SIZE;
  }
  else if ( offset + len < writeLenMin && ( method == ATT_EXECUTE_WRITE_REQ || method == ATT_WRITE_REQ ) )
  {
    // Refuse writes that are lower than minimum.
    // Note: Cannot determine if a Reliable Write (to several chars) is finished, so those will
    //       only be refused if this attribute is the last in the queue (method is execute).
    //       Otherwise, reliable writes are accepted and parsed piecemeal.
    Log_error0("Invalid value length was received.");
    status = ATT_ERR_INVALID_VALUE_SIZE;
  }
  else
  {
    // Copy pValue into the variable we point to from the attribute table.
    memcpy(pAttr->pValue + offset, pValue, len);

    // Only notify application and update length if enough data is written.
    //
    // Note: If reliable writes are used (meaning several attributes are written to using ATT PrepareWrite),
    //       the application will get a callback for every write with an offset + len larger than _LEN_MIN.
    // Note: For Long Writes (ATT Prepare + Execute towards only one attribute) only one callback will be issued,
    //       because the write fragments are concatenated before being sent here.
    if ( offset + len >= writeLenMin )
    {
      changeParamID = paramID;
      *pValueLenVar = offset + len; // Update data length.
    }
  }

  // Let the application know something changed (if it did) by using the
  // callback it registered earlier (if it did).
  if (changeParamID != 0xFF)
    if ( pAppCBs && pAppCBs->pfnChangeCb )
      pAppCBs->pfnChangeCb( connHandle, DEVICE_INFORMATION_SERV_UUID, paramID, pValue, len+offset ); // Call app function from stack task context.

  return status;
}
