/******************************************************************************* * Function Name: ScpsCallBack() ******************************************************************************** * * Summary: * This is an event callback function to receive service specific events from * SCPS Service. * * Parameters: * event - the event code * *eventParam - the event parameters * * Return: * None. * ********************************************************************************/ void ScpsCallBack (uint32 event, void *eventParam) { DBG_PRINTF("SCPS event: %lx, ", event); switch(event) { case CYBLE_EVT_SCPSS_NOTIFICATION_ENABLED: DBG_PRINTF("CYBLE_EVT_SCPSS_NOTIFICATION_ENABLED \r\n"); requestScanRefresh = ENABLED; break; case CYBLE_EVT_SCPSS_NOTIFICATION_DISABLED: DBG_PRINTF("CYBLE_EVT_SCPSS_NOTIFICATION_DISABLED \r\n"); requestScanRefresh = DISABLED; break; case CYBLE_EVT_SCPSS_SCAN_INT_WIN_CHAR_WRITE: scanInterval = CyBle_Get16ByPtr(((CYBLE_SCPS_CHAR_VALUE_T *)eventParam)->value->val); scanWindow = CyBle_Get16ByPtr(((CYBLE_SCPS_CHAR_VALUE_T *)eventParam)->value->val + sizeof(scanInterval)); DBG_PRINTF("CYBLE_EVT_SCPSS_SCAN_INT_WIN_CHAR_WRITE scanInterval: %x, scanWindow: %x \r\n", scanInterval, scanWindow); break; case CYBLE_EVT_SCPSC_NOTIFICATION: break; case CYBLE_EVT_SCPSC_READ_DESCR_RESPONSE: break; case CYBLE_EVT_SCPSC_WRITE_DESCR_RESPONSE: break; default: DBG_PRINTF("Not supported event\r\n"); break; } }
/******************************************************************************* * Function Name: HrssGetHrmDescriptor ******************************************************************************** * * Summary: * Gets the Heart Rate Measurement characteristic * Configuration Descriptor value * * Parameters: * None. * * Return: * uint16 value of the Heart Rate Measurement characteristic * configuration Descriptor. * *******************************************************************************/ uint16 HrssGetHrmDescriptor(void) { uint8 val[CYBLE_CCCD_LEN]; uint16 retVal; if(CYBLE_ERROR_OK == CyBle_HrssGetCharacteristicDescriptor(CYBLE_HRS_HRM, CYBLE_HRS_HRM_CCCD, CYBLE_CCCD_LEN, val)) { /* Using the length as a temporary variable with suitable type to return */ retVal = CyBle_Get16ByPtr(val); } else { retVal = 0u; } return (retVal); }
/******************************************************************************* * Function Name: Ancs_EventHandler() ******************************************************************************** * Summary: * Event handler for the Apple Notification Center Service. * * Parameters: * uint32 eventCode: The event to be processed * void * eventParam: Pointer to hold the additional information associated * with an event * * Return: * None * * Theory: * This function is responsible for the discovery of the ANCS service, its * characteristics, and the characteristic descriptors. * Three characteristics are identified - * 1. Notification source * 2. Control Point * 3. Data source * All other characteristics (if present) are ignored. * The CCCD of Notification Source and Data Source characteristics are identified. * All other descriptors (if present) are ignored. * * Once the discovery procedure is complete, this function writes to the CCCDs * of the Notification Source and Data Source characteristics to enable GATT * notifications on those characteristics. * It also subscribes to the Service Changed indication on the GATT service for * any future service change. * *******************************************************************************/ void Ancs_EventHandler(uint32 eventCode, void * eventParam) { uint16 counter; uint16 dataLength; uint16 attributeLength; SERVICE_DATA_STRUCT * serviceData; CHAR_DATA_STRUCT characteristicData; DESC_DATA_STRUCT * descriptorData; CYBLE_GATTC_READ_BY_GRP_RSP_PARAM_T * readByGroupResponse; CYBLE_GATTC_READ_BY_TYPE_RSP_PARAM_T * readByTypeResponse; CYBLE_GATTC_FIND_INFO_RSP_PARAM_T * findInfoResponse; CYBLE_GATTC_ERR_RSP_PARAM_T * errorResponse; switch(eventCode) { /* Case to discover services */ case CYBLE_EVT_GATTC_READ_BY_GROUP_TYPE_RSP: /* Typecast the event paramter for Read by Group Response data */ readByGroupResponse = (CYBLE_GATTC_READ_BY_GRP_RSP_PARAM_T *)eventParam; dataLength = readByGroupResponse->attrData.length; attributeLength = readByGroupResponse->attrData.attrLen; /* Search for ANCS service within Services with 128-bit UUID only */ if((dataLength == CYBLE_DISC_SRVC_INFO_128_LEN) && (ancsDiscoveryStatus == ANCS_DISC_NONE_DISCOVERED)) { for(counter = 0; counter < attributeLength; counter += dataLength) { /* Read by Group response data consists of rows of * {Service Handle range, Service UUID}. * So typecast the relevant data into that structure. */ serviceData = (SERVICE_DATA_STRUCT *)(readByGroupResponse->attrData.attrValue + counter); /* Check whether the Service UUID matches that of ANCS */ if(memcmp(serviceData->uuid.value, ancsServiceUuid, CYBLE_GATT_128_BIT_UUID_SIZE) == 0) { /* Match! ANCS Service found. */ ancsServiceRange.startHandle = serviceData->range.startHandle; ancsServiceRange.endHandle = serviceData->range.endHandle; ancsDiscoveryStatus = ANCS_DISC_SERVICE_DISCOVERED; break; } } } break; /* Case to discover characteristics */ case CYBLE_EVT_GATTC_READ_BY_TYPE_RSP: if(ANCS_DISC_SERVICE_DISCOVERED == ancsDiscoveryStatus) { /* Typecast the event paramter for Read by Type Response data */ readByTypeResponse = (CYBLE_GATTC_READ_BY_TYPE_RSP_PARAM_T *)eventParam; dataLength = readByTypeResponse->attrData.length; attributeLength = readByTypeResponse->attrData.attrLen; /* Search for ANCS characteristics (128-bit UUID) in the data */ if(dataLength == CYBLE_DISC_CHAR_INFO_128_LEN) { for(counter = 0; counter < attributeLength; counter += dataLength) { /* Cache the characteristic UUID in a buffer */ characteristicData.valueHandle = CyBle_Get16ByPtr(readByTypeResponse->attrData.attrValue + counter + 3); memcpy(characteristicData.uuid.value, (uint8 *)(readByTypeResponse->attrData.attrValue + counter + 5), CYBLE_GATT_128_BIT_UUID_SIZE); /* Compare the UUID with known UUIDs for ANCS */ if(memcmp(characteristicData.uuid.value, ancsNotifSourceCharUuid, CYBLE_GATT_128_BIT_UUID_SIZE) == 0) { /* Match! ANCS Notification Source characteristic */ ancsNotifSourceCharHandle = characteristicData.valueHandle; } else if(memcmp(characteristicData.uuid.value, ancsControlPointCharUuid, CYBLE_GATT_128_BIT_UUID_SIZE) == 0) { /* Match! ANCS Control Point characteristic */ ancsControlPointCharHandle = characteristicData.valueHandle; } else if(memcmp(characteristicData.uuid.value, ancsDataSourceCharUuid, CYBLE_GATT_128_BIT_UUID_SIZE) == 0) { /* Match! ANCS Data Source characteristic */ ancsDataSourceCharHandle = characteristicData.valueHandle; } else { /* Unknown characteristic */ } } } } break; /* Case to discover characteristic descriptors */ case CYBLE_EVT_GATTC_FIND_INFO_RSP: if(ANCS_DISC_CHAR_DISCOVERED == ancsDiscoveryStatus) { /* Typecast the event paramter for Find Info Response data */ findInfoResponse = (CYBLE_GATTC_FIND_INFO_RSP_PARAM_T *)eventParam; dataLength = CYBLE_DB_ATTR_HANDLE_LEN + CYBLE_GATT_16_BIT_UUID_SIZE; attributeLength = findInfoResponse->handleValueList.byteCount; /* Search within the descriptors with 16-bit UUID */ if(findInfoResponse->uuidFormat == CYBLE_GATT_16_BIT_UUID_FORMAT) { for(counter = 0; counter < attributeLength; counter += dataLength) { /* Find Info Response data consists of rows of * {Handle, UUID} of descriptors. So typecast the * relevant data into that structure. */ descriptorData = (DESC_DATA_STRUCT *)(findInfoResponse->handleValueList.list + counter); /* Search for CCCD descriptors in the data */ if(descriptorData->uuid == CCCD_UUID_16BIT) { /* Found the CCCD descriptor; now check which * characteristic it belongs to. */ if((descriptorData->descHandle < ancsNotifSourceCharHandle) && (descriptorData->descHandle < ancsDataSourceCharHandle)) { /* Unknown CCCD; ignore */ } else if((descriptorData->descHandle > ancsNotifSourceCharHandle) && (descriptorData->descHandle < ancsDataSourceCharHandle)) { /* This belongs to the Notification source * characteristic. */ ancsNotifSourceCccdHandle = descriptorData->descHandle; } else if((descriptorData->descHandle < ancsNotifSourceCharHandle) && (descriptorData->descHandle > ancsDataSourceCharHandle)) { /* This belongs to the Data source * characteristic. */ ancsDataSourceCccdHandle = descriptorData->descHandle; } else if((descriptorData->descHandle > ancsNotifSourceCharHandle) && (descriptorData->descHandle > ancsDataSourceCharHandle)) { /* Check if the CCCD is within range of the * ANCS service. */ if(descriptorData->descHandle <= ancsServiceRange.endHandle) { /* Need to know whether Notification source * char handle is more or Data source char. */ if(ancsNotifSourceCharHandle < ancsDataSourceCharHandle) { /* Data source CCCD */ ancsDataSourceCccdHandle = descriptorData->descHandle; } else if(ancsNotifSourceCharHandle > ancsDataSourceCharHandle) { /* Notification source CCCD */ ancsNotifSourceCccdHandle = descriptorData->descHandle; } } else { /* Unknown CCCD; ignore */ } } else { /* Invalid data; ignore */ } } if(descriptorData->descHandle == descriptorHandleRange.endHandle) { /* Descriptor discovery is complete */ ancsDiscoveryStatus = ANCS_DISC_DESC_DISCOVERED; /* Service discovery procedure complete; subscribe to the GATT * Service changed indication by writing 0x02 to its CCCD. */ if((serviceChangedCccdWriteStatus == SERVICE_CHANGED_CCCD_WRITE_REQ_NOT_SENT) && (cyBle_gattc.serviceChanged.valueHandle != CYBLE_GATT_INVALID_ATTR_HANDLE_VALUE)) { serviceChangedCccdPacket.value = cccdIndFlagSetStruct; serviceChangedCccdPacket.attrHandle = cyBle_gattc.cccdHandle; CyBle_GattcWriteCharacteristicDescriptors(cyBle_connHandle, &serviceChangedCccdPacket); } serviceChangedCccdWriteStatus = SERVICE_CHANGED_CCCD_WRITE_REQ_SENT; break; } } } } break; case CYBLE_EVT_GATTC_WRITE_RSP: /* Service changed CCCD set to 0x02 to enable indications */ if(serviceChangedCccdWriteStatus == SERVICE_CHANGED_CCCD_WRITE_REQ_SENT) { serviceChangedCccdWriteStatus = SERVICE_CHANGED_CCCD_WRITE_RESP_RECEIVED; /* Enable Notification source characteristic notifications */ if(ancsDiscoveryStatus == ANCS_DISC_DESC_DISCOVERED) { ancsNotificationSourceCccdPacket.value = cccdNotifFlagSetStruct; ancsNotificationSourceCccdPacket.attrHandle = ancsNotifSourceCccdHandle; CyBle_GattcWriteCharacteristicDescriptors(cyBle_connHandle, &ancsNotificationSourceCccdPacket); ancsDiscoveryStatus = ANCS_DISC_NOTIF_SOURCE_CCCD_SET; } } /* ANCS Notification source characteristic CCCD set to 0x01 * to enable notifications */ else if((ancsDiscoveryStatus == ANCS_DISC_NOTIF_SOURCE_CCCD_SET) && (ancsDataSourceCccdHandle != 0)) { ancsDataSourceCccdPacket.value = cccdNotifFlagSetStruct; ancsDataSourceCccdPacket.attrHandle = ancsDataSourceCccdHandle; CyBle_GattcWriteCharacteristicDescriptors(cyBle_connHandle, &ancsDataSourceCccdPacket); ancsDiscoveryStatus = ANCS_DISC_DATA_SOURCE_CCCD_SET; } /* ANCS Data source characteristic CCCD set to 0x01 to enable * notifications. */ else if(ancsDiscoveryStatus == ANCS_DISC_DATA_SOURCE_CCCD_SET) { ancsDiscoveryStatus = ANCS_DISC_WRITE_COMPLETE; } break; case CYBLE_EVT_GATTC_ERROR_RSP: errorResponse = (CYBLE_GATTC_ERR_RSP_PARAM_T *)eventParam; /* Check if the ANCS characteristic discovery is complete. * Initiate descriptor discovery if it is done. */ if(ancsDiscoveryStatus == ANCS_DISC_SERVICE_DISCOVERED) { if((errorResponse->opCode == CYBLE_GATT_READ_BY_TYPE_REQ) && (errorResponse->errorCode == CYBLE_GATT_ERR_ATTRIBUTE_NOT_FOUND) && (errorResponse->attrHandle == ancsServiceRange.endHandle) && (cyBle_clientState != CYBLE_CLIENT_STATE_CHAR_DISCOVERING)) { ancsDiscoveryStatus = ANCS_DISC_CHAR_DISCOVERED; descriptorHandleRange.startHandle = (ancsNotifSourceCharHandle < ancsDataSourceCharHandle) ? (ancsNotifSourceCharHandle + 1) : (ancsDataSourceCharHandle + 1); descriptorHandleRange.endHandle = ancsServiceRange.endHandle; CyBle_GattcDiscoverAllCharacteristicDescriptors(cyBle_connHandle, &descriptorHandleRange); } } break; default: break; } }
/******************************************************************************* * Function Name: ProcessWriteReq ******************************************************************************** * * Summary: * Process all GATT level write requests and responds with appropriate status * * Parameters: * CYBLE_GATTS_WRITE_CMD_REQ_PARAM_T: GATT write command request prameter * * Return: * None * *******************************************************************************/ void ProcessWriteReq(CYBLE_GATTS_WRITE_CMD_REQ_PARAM_T writeCmdReq) { bool value_val; uint8 status = LOCKED, key_buf[LOCK_CODE_LENGTH]; uint16 beaconPeriod = 0; CYBLE_GATT_HANDLE_VALUE_PAIR_T valuePairT; /* Reset error send flag */ errorSent = false; /* Retrieve the LOCK status from the GATT DB */ valuePairT.attrHandle = CYBLE_EDDYSTONE_CONFIGURATION_LOCK_STATE_CHAR_HANDLE; valuePairT.value.val = (uint8 *)&value_val; valuePairT.value.len = sizeof(bool); CyBle_GattsReadAttributeValue( &valuePairT, &cyBle_connHandle, CYBLE_GATT_DB_LOCALLY_INITIATED ); /* Check the LOCK status */ if(valuePairT.value.val[0] == UNLOCKED) { /*URL Data*/ if(writeCmdReq.handleValPair.attrHandle == CYBLE_EDDYSTONE_CONFIGURATION_URI_DATA_CHAR_HANDLE) { /* First byte should be scheme prefix and length should be less than * or equal to MAX_URL_LENGTH */ if( (writeCmdReq.handleValPair.value.len <= MAX_URL_LENGTH) && (writeCmdReq.handleValPair.value.val[0] < URL_PREFIX_MAX) ) { uint8 TempURL[MAX_URL_LENGTH]; memset(TempURL, 0, MAX_URL_LENGTH); memcpy( TempURL, writeCmdReq.handleValPair.value.val, writeCmdReq.handleValPair.value.len ); if( CYBLE_GATT_ERR_NONE == WriteAttributeValue ( CYBLE_EDDYSTONE_CONFIGURATION_URI_DATA_CHAR_HANDLE, writeCmdReq.handleValPair.value.len, writeCmdReq.handleValPair.value.val, CYBLE_GATT_DB_PEER_INITIATED ) ) { /* Update the length as per the new URL data */ URLLength = writeCmdReq.handleValPair.value.len; cyBle_attValuesLen[16].actualLength = URLLength; /* Update the URL data */ memcpy(CurrentURL, TempURL, MAX_URL_LENGTH); } } else if (writeCmdReq.handleValPair.value.len > MAX_URL_LENGTH) { /* Invalid length. Send error response */ SendErrorResponse ( CYBLE_EDDYSTONE_CONFIGURATION_URI_DATA_CHAR_HANDLE, CYBLE_GATT_ERR_INVALID_ATTRIBUTE_LEN ); } } /* Lock Characteristic */ else if(writeCmdReq.handleValPair.attrHandle == CYBLE_EDDYSTONE_CONFIGURATION_LOCK_CHAR_HANDLE) { if(writeCmdReq.handleValPair.value.len == LOCK_CODE_LENGTH) { WriteAttributeValue ( CYBLE_EDDYSTONE_CONFIGURATION_LOCK_CHAR_HANDLE, writeCmdReq.handleValPair.value.len, writeCmdReq.handleValPair.value.val, CYBLE_GATT_DB_PEER_INITIATED ); /* Update the LOCK characteristic */ status = LOCKED; WriteAttributeValue ( CYBLE_EDDYSTONE_CONFIGURATION_LOCK_STATE_CHAR_HANDLE, sizeof(bool), &status, CYBLE_GATT_DB_LOCALLY_INITIATED ); } else { /* Invalid length. Send error response */ SendErrorResponse(CYBLE_EDDYSTONE_CONFIGURATION_LOCK_CHAR_HANDLE, CYBLE_GATT_ERR_INVALID_ATTRIBUTE_LEN); } } /* Advertised Tx power level */ else if(writeCmdReq.handleValPair.attrHandle == CYBLE_EDDYSTONE_CONFIGURATION_ADVERTISED_TX_POWER_LEVELS_CHAR_HANDLE) { if (writeCmdReq.handleValPair.value.len == MAX_NUM_PWR_LVL) { WriteAttributeValue ( CYBLE_EDDYSTONE_CONFIGURATION_ADVERTISED_TX_POWER_LEVELS_CHAR_HANDLE, writeCmdReq.handleValPair.value.len, writeCmdReq.handleValPair.value.val, CYBLE_GATT_DB_PEER_INITIATED ); if (writeCmdReq.handleValPair.value.len == MAX_NUM_PWR_LVL) { /* Update Tx Power levels */ memcpy ( currentTxPowerLevels, writeCmdReq.handleValPair.value.val, MAX_NUM_PWR_LVL ); } else { SendErrorResponse ( CYBLE_EDDYSTONE_CONFIGURATION_ADVERTISED_TX_POWER_LEVELS_CHAR_HANDLE, CYBLE_GATT_ERR_INVALID_ATTRIBUTE_LEN ); } } else { /* Invalid length. Send error response */ SendErrorResponse ( CYBLE_EDDYSTONE_CONFIGURATION_ADVERTISED_TX_POWER_LEVELS_CHAR_HANDLE, CYBLE_GATT_ERR_INVALID_ATTRIBUTE_LEN ); } } /* Tx Power Mode */ else if(writeCmdReq.handleValPair.attrHandle == CYBLE_EDDYSTONE_CONFIGURATION_TX_POWER_MODE_CHAR_HANDLE) { if(writeCmdReq.handleValPair.value.val[0] <= 0x03) { WriteAttributeValue ( CYBLE_EDDYSTONE_CONFIGURATION_TX_POWER_MODE_CHAR_HANDLE, writeCmdReq.handleValPair.value.len, writeCmdReq.handleValPair.value.val, CYBLE_GATT_DB_PEER_INITIATED ); currentTxmode = writeCmdReq.handleValPair.value.val[0]; UpdateTxPower(currentTxmode); } else /* Invalid value. Write not permitted. */ { SendErrorResponse ( CYBLE_EDDYSTONE_CONFIGURATION_TX_POWER_MODE_CHAR_HANDLE, CYBLE_GATT_ERR_WRITE_NOT_PERMITTED ); } } /* Beacon Period */ else if(writeCmdReq.handleValPair.attrHandle == CYBLE_EDDYSTONE_CONFIGURATION_BEACON_PERIOD_CHAR_HANDLE) { beaconPeriod = CyBle_Get16ByPtr(writeCmdReq.handleValPair.value.val); /* Disable URL FRAMES */ if(beaconPeriod == 0) { eddystoneImplenmentation = EDDYSTONE_UID; WriteAttributeValue ( CYBLE_EDDYSTONE_CONFIGURATION_BEACON_PERIOD_CHAR_HANDLE, writeCmdReq.handleValPair.value.len, writeCmdReq.handleValPair.value.val, CYBLE_GATT_DB_PEER_INITIATED ); } /* Values in valid range */ else if((beaconPeriod >= MIN_BEACON_PERIOD) && (beaconPeriod <= MAX_BEACON_PERIOD)) { WriteAttributeValue ( CYBLE_EDDYSTONE_CONFIGURATION_BEACON_PERIOD_CHAR_HANDLE, writeCmdReq.handleValPair.value.len, writeCmdReq.handleValPair.value.val, CYBLE_GATT_DB_PEER_INITIATED ); CurrentAdvPeriod = beaconPeriod / 0.625; eddystoneImplenmentation = EDDYSTONE_URL; } else { uint16 temp = MIN_BEACON_PERIOD; /* Values not supportes. Write default values */ WriteAttributeValue( CYBLE_EDDYSTONE_CONFIGURATION_BEACON_PERIOD_CHAR_HANDLE, sizeof(temp), (uint8 *)&temp, CYBLE_GATT_DB_PEER_INITIATED); CurrentAdvPeriod = CYBLE_GAP_ADV_ADVERT_INTERVAL_NONCON_MIN; eddystoneImplenmentation = EDDYSTONE_URL; } } /* Reset the Configurations to default */ else if((writeCmdReq.handleValPair.attrHandle == CYBLE_EDDYSTONE_CONFIGURATION_RESET_CHAR_HANDLE) && (writeCmdReq.handleValPair.value.val[0] != 0)) { ResetGattDb(); } } else if(valuePairT.value.val[0] == LOCKED) { if(writeCmdReq.handleValPair.attrHandle == CYBLE_EDDYSTONE_CONFIGURATION_LOCK_CHAR_HANDLE) { /* Accesing the lock in LOCKED state */ SendErrorResponse(writeCmdReq.handleValPair.attrHandle, CYBLE_GATT_ERR_INSUFFICIENT_AUTHORIZATION); } } if(writeCmdReq.handleValPair.attrHandle == CYBLE_EDDYSTONE_CONFIGURATION_UNLOCK_CHAR_HANDLE) { if(writeCmdReq.handleValPair.value.len == LOCK_CODE_LENGTH) { if(valuePairT.value.val[0] == LOCKED) { int compareResult; valuePairT.attrHandle = CYBLE_EDDYSTONE_CONFIGURATION_LOCK_CHAR_HANDLE; valuePairT.value.val = key_buf; valuePairT.value.len = sizeof(LOCK); CyBle_GattsReadAttributeValue(&valuePairT, &cyBle_connHandle, CYBLE_GATT_DB_LOCALLY_INITIATED); compareResult = memcmp ( valuePairT.value.val, writeCmdReq.handleValPair.value.val, LOCK_CODE_LENGTH ); if(compareResult == 0) { status = UNLOCKED; /* Update the LOCK STATE */ WriteAttributeValue ( CYBLE_EDDYSTONE_CONFIGURATION_LOCK_STATE_CHAR_HANDLE, sizeof(bool), &status, CYBLE_GATT_DB_LOCALLY_INITIATED ); /* Reset the LOCK */ WriteAttributeValue ( CYBLE_EDDYSTONE_CONFIGURATION_LOCK_CHAR_HANDLE, sizeof(LOCK), (uint8 *)LOCK, CYBLE_GATT_DB_LOCALLY_INITIATED ); } else /* LOCK not matched */ { SendErrorResponse(writeCmdReq.handleValPair.attrHandle, CYBLE_GATT_ERR_INSUFFICIENT_AUTHORIZATION); } } } else /* Invalid length */ { SendErrorResponse(writeCmdReq.handleValPair.attrHandle, CYBLE_GATT_ERR_INVALID_ATTRIBUTE_LEN); } } if (errorSent == false) { CyBle_GattsWriteRsp(cyBle_connHandle); } }
/****************************************************************************** ##Function Name: CyBle_HtssWriteEventHandler ******************************************************************************* Summary: Handles Write Request Event for HTS service. Parameters: void *eventParam: The pointer to the data structure specified by the event. Return: Return value is of type CYBLE_GATT_ERR_CODE_T. * CYBLE_GATT_ERR_NONE - Write is successful ******************************************************************************/ CYBLE_GATT_ERR_CODE_T CyBle_HtssWriteEventHandler(CYBLE_GATTS_WRITE_REQ_PARAM_T *eventParam) { CYBLE_HTS_CHAR_INDEX_T locCharIndex; CYBLE_HTS_CHAR_VALUE_T locCharValue; CYBLE_GATT_ERR_CODE_T gattErr = CYBLE_GATT_ERR_NONE; uint8 locReqHandle = 0u; if(NULL != CyBle_HtsApplCallback) { for(locCharIndex = CYBLE_HTS_TEMP_MEASURE; (locCharIndex < CYBLE_HTS_CHAR_COUNT) && (locReqHandle == 0u); locCharIndex++) { if((eventParam->handleValPair.attrHandle == cyBle_htss.charInfo[locCharIndex].descrHandle[CYBLE_HTS_CCCD]) || (eventParam->handleValPair.attrHandle == cyBle_htss.charInfo[locCharIndex].charHandle)) { locCharValue.connHandle = eventParam->connHandle; locCharValue.charIndex = locCharIndex; locCharValue.value = &eventParam->handleValPair.value; /* Characteristic value write request */ if(eventParam->handleValPair.attrHandle == cyBle_htss.charInfo[locCharIndex].charHandle) { /* Validate Measure Interval value */ if(locCharIndex == CYBLE_HTS_MEASURE_INTERVAL) { uint8 locAttrValue[CYBLE_HTS_VRD_LEN]; uint16 requestValue; requestValue = CyBle_Get16ByPtr(eventParam->handleValPair.value.val); if(requestValue != 0u) /* 0 is valid interval value for no periodic measurement */ { /* Check Valid range for Measure Interval characteristic value */ if(CYBLE_ERROR_OK == CyBle_HtssGetCharacteristicDescriptor(locCharIndex, CYBLE_HTS_VRD, CYBLE_HTS_VRD_LEN, locAttrValue)) { uint16 lowerValue; uint16 upperValue; lowerValue = CyBle_Get16ByPtr(locAttrValue); upperValue = CyBle_Get16ByPtr(locAttrValue + sizeof(lowerValue)); requestValue = CyBle_Get16ByPtr(eventParam->handleValPair.value.val); if((requestValue != 0u) && ((requestValue < lowerValue) || (requestValue > upperValue))) { gattErr = CYBLE_GATT_ERR_HTS_OUT_OF_RANGE; } } } } if(CYBLE_GATT_ERR_NONE == gattErr) { /* Store value to database */ gattErr = CyBle_GattsWriteAttributeValue(&eventParam->handleValPair, 0u, &eventParam->connHandle, CYBLE_GATT_DB_PEER_INITIATED); if(CYBLE_GATT_ERR_NONE == gattErr) { CyBle_HtsApplCallback((uint32)CYBLE_EVT_HTSS_CHAR_WRITE, &locCharValue); } } } else /* Client Characteristic Configuration descriptor write request */ { /* Store value to database */ gattErr = CyBle_GattsWriteAttributeValue(&eventParam->handleValPair, 0u, &eventParam->connHandle, CYBLE_GATT_DB_PEER_INITIATED); if(CYBLE_GATT_ERR_NONE == gattErr) { /* Check characteristic properties for Notification */ if(CYBLE_IS_NOTIFICATION_SUPPORTED(cyBle_htss.charInfo[locCharIndex].charHandle)) { if(CYBLE_IS_NOTIFICATION_ENABLED_IN_PTR(eventParam->handleValPair.value.val)) { CyBle_HtsApplCallback((uint32)CYBLE_EVT_HTSS_NOTIFICATION_ENABLED, &locCharValue); } else { CyBle_HtsApplCallback((uint32)CYBLE_EVT_HTSS_NOTIFICATION_DISABLED, &locCharValue); } } /* Check characteristic properties for Indication */ if(CYBLE_IS_INDICATION_SUPPORTED(cyBle_htss.charInfo[locCharIndex].charHandle)) { if(CYBLE_IS_INDICATION_ENABLED_IN_PTR(eventParam->handleValPair.value.val)) { CyBle_HtsApplCallback((uint32)CYBLE_EVT_HTSS_INDICATION_ENABLED, &locCharValue); } else { CyBle_HtsApplCallback((uint32)CYBLE_EVT_HTSS_INDICATION_DISABLED, &locCharValue); } } #if((CYBLE_GAP_ROLE_PERIPHERAL || CYBLE_GAP_ROLE_CENTRAL) && \ (CYBLE_BONDING_REQUIREMENT == CYBLE_BONDING_YES)) /* Set flag to store bonding data to flash */ cyBle_pendingFlashWrite |= CYBLE_PENDING_CCCD_FLASH_WRITE_BIT; #endif /* (CYBLE_BONDING_REQUIREMENT == CYBLE_BONDING_YES) */ } } cyBle_eventHandlerFlag &= (uint8)~CYBLE_CALLBACK; locReqHandle = 1u; } } } return (gattErr); }