/************************************************************************************************** Phyplus Microelectronics Limited confidential and proprietary. All rights reserved. IMPORTANT: All rights of this software belong to Phyplus Microelectronics Limited ("Phyplus"). Your use of this Software is limited to those specific rights granted under the terms of the business contract, the confidential agreement, the non-disclosure agreement and any other forms of agreements as a customer or a partner of Phyplus. You may not use this Software unless you agree to abide by the terms of these agreements. You acknowledge that the Software may not be modified, copied, distributed or disclosed unless embedded on a Phyplus Bluetooth Low Energy (BLE) integrated circuit, either as a product or is integrated into your products. Other than for the aforementioned purposes, you may not use, reproduce, copy, prepare derivative works of, modify, distribute, perform, display or sell this Software and/or its documentation for any purposes. YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL PHYPLUS OR ITS SUBSIDIARIES BE LIABLE OR OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS. **************************************************************************************************/ /************************************************************************************************* Filename: gattservapp.c Revised: Revision: Description: This file contains the GATT Server Application. **************************************************************************************************/ #if ( HOST_CONFIG & ( CENTRAL_CFG | PERIPHERAL_CFG ) ) /******************************************************************************* * INCLUDES */ #include "bcomdef.h" #include "linkdb.h" #include "gatt.h" #include "gatt_uuid.h" #include "gattservapp.h" /********************************************************************* * MACROS */ /********************************************************************* * CONSTANTS */ /********************************************************************* * TYPEDEFS */ // Structure to keep Prepare Write Requests for each Client typedef struct { uint16 connHandle; // connection message was received on attPrepareWriteReq_t *pPrepareWriteQ; // Prepare Write Request queue } prepareWrites_t; // GATT Structure to keep CBs information for each service being registered typedef struct { uint16 handle; // Service handle - assigned internally by GATT Server CONST gattServiceCBs_t *pCBs; // Service callback function pointers } gattServiceCBsInfo_t; // Service callbacks list item typedef struct _serviceCBsList { struct _serviceCBsList *next; // pointer to next service callbacks record gattServiceCBsInfo_t serviceInfo; // service handle/callbacks } serviceCBsList_t; /********************************************************************* * GLOBAL VARIABLES */ /********************************************************************* * EXTERNAL VARIABLES */ /********************************************************************* * EXTERNAL FUNCTIONS */ extern l2capSegmentBuff_t l2capSegmentPkt; /********************************************************************* * LOCAL VARIABLES */ uint8 GATTServApp_TaskID; // Task ID for internal task/event processing uint8 appTaskID = INVALID_TASK_ID; // The task ID of an app/profile that // wants GATT Server event messages // Server Prepare Write table (one entry per each physical link) static prepareWrites_t prepareWritesTbl[MAX_NUM_LL_CONN]; // Maximum number of attributes that Server can prepare for writing per Client static uint8 maxNumPrepareWrites = 0; #ifdef PREPARE_QUEUE_STATIC static attPrepareWriteReq_t prepareQueue[MAX_NUM_LL_CONN*GATT_MAX_NUM_PREPARE_WRITES]; #endif // Callbacks for services static serviceCBsList_t *serviceCBsList = NULL; // Globals to be used for processing an incoming request static uint8 attrLen; static uint8 attrValue[ATT_MTU_SIZE-1]; static attMsg_t rsp; /*** Defined GATT Attributes ***/ // GATT Service attribute static CONST gattAttrType_t gattService = { ATT_BT_UUID_SIZE, gattServiceUUID }; #ifndef HID_VOICE_SPEC // Service Changed Characteristic Properties static uint8 serviceChangedCharProps = GATT_PROP_INDICATE; #endif // Service Changed attribute (hidden). Set the affected Attribute Handle range // to 0x0001 to 0xFFFF to indicate to the client to rediscover the entire set // of Attribute Handles on the server. // Client Characteristic configuration. Each client has its own instantiation // of the Client Characteristic Configuration. Reads of the Client Characteristic // Configuration only shows the configuration for that client and writes only // affect the configuration of that client. static gattCharCfg_t indCharCfg[GATT_MAX_NUM_CONN]; #if defined ( TESTMODES ) static uint16 paramValue = 0; #endif /********************************************************************* * Profile Attributes - Table */ // GATT Attribute Table static gattAttribute_t gattAttrTbl[] = { // Generic Attribute Profile { { ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */ GATT_PERMIT_READ, /* permissions */ 0, /* handle */ (uint8 *)&gattService /* pValue */ }, #ifndef HID_VOICE_SPEC // Characteristic Declaration { { ATT_BT_UUID_SIZE, characterUUID }, GATT_PERMIT_READ, 0, &serviceChangedCharProps }, // Attribute Service Changed { { ATT_BT_UUID_SIZE, serviceChangedUUID }, 0, 0, NULL }, // Client Characteristic configuration { { ATT_BT_UUID_SIZE, clientCharCfgUUID }, GATT_PERMIT_READ | GATT_PERMIT_WRITE, 0, (uint8 *)indCharCfg } #endif }; /********************************************************************* * LOCAL FUNCTIONS */ static void gattServApp_ProcessMsg( gattMsgEvent_t *pMsg ); static bStatus_t gattServApp_ProcessExchangeMTUReq( gattMsgEvent_t *pMsg ); static bStatus_t gattServApp_ProcessFindByTypeValueReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ); static bStatus_t gattServApp_ProcessReadByTypeReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ); static bStatus_t gattServApp_ProcessReadReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ); static bStatus_t gattServApp_ProcessReadBlobReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ); static bStatus_t gattServApp_ProcessReadMultiReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ); static bStatus_t gattServApp_ProcessReadByGrpTypeReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ); static bStatus_t gattServApp_ProcessWriteReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ); static bStatus_t gattServApp_ProcessPrepareWriteReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ); static bStatus_t gattServApp_ProcessExecuteWriteReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ); static bStatus_t gattServApp_RegisterServiceCBs( uint16 handle, CONST gattServiceCBs_t *pServiceCBs ); static bStatus_t gattServApp_DeregisterServiceCBs( uint16 handle ); static bStatus_t gattServApp_SetNumPrepareWrites( uint8 numPrepareWrites ); static uint8 gattServApp_PrepareWriteQInUse( void ); static CONST gattServiceCBs_t *gattServApp_FindServiceCBs( uint16 service ); static bStatus_t gattServApp_EnqueuePrepareWriteReq( uint16 connHandle, attPrepareWriteReq_t *pReq ); static prepareWrites_t *gattServApp_FindPrepareWriteQ( uint16 connHandle ); static gattCharCfg_t *gattServApp_FindCharCfgItem( uint16 connHandle, gattCharCfg_t *charCfgTbl ); static pfnGATTReadAttrCB_t gattServApp_FindReadAttrCB( uint16 handle ); static pfnGATTWriteAttrCB_t gattServApp_FindWriteAttrCB( uint16 handle ); static pfnGATTAuthorizeAttrCB_t gattServApp_FindAuthorizeAttrCB( uint16 handle ); /********************************************************************* * API FUNCTIONS */ // GATT App Callback functions static void gattServApp_HandleConnStatusCB( uint16 connHandle, uint8 changeType ); static bStatus_t gattServApp_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint8 len, uint16 offset ); /********************************************************************* * PROFILE CALLBACKS */ // GATT Service Callbacks CONST gattServiceCBs_t gattServiceCBs = { NULL, // Read callback function pointer gattServApp_WriteAttrCB, // Write callback function pointer NULL // Authorization callback function pointer }; static gattServMsgCB_t s_GATTServCB = NULL; /********************************************************************* * @fn GATTServApp_RegisterForMsgs * * @brief Register your task ID to receive event messages from * the GATT Server Application. * * @param taskId - Default task ID to send events * * @return none */ void GATTServApp_RegisterForMsg( uint8 taskID ) { appTaskID = taskID; } /********************************************************************* * @fn GATTServApp_Init * * @brief Initialize the GATT Server Application. * * @param taskId - Task identifier for the desired task * * @return none */ void GATTServApp_Init( uint8 taskId ) { GATTServApp_TaskID = taskId; // Initialize Client Characteristic Configuration attributes GATTServApp_InitCharCfg( INVALID_CONNHANDLE, indCharCfg ); // Initialize Prepare Write Table for ( uint8 i = 0; i < MAX_NUM_LL_CONN; i++ ) { // Initialize connection handle prepareWritesTbl[i].connHandle = INVALID_CONNHANDLE; // Initialize the prepare write queue prepareWritesTbl[i].pPrepareWriteQ = NULL; } // Set up the initial prepare write queues gattServApp_SetNumPrepareWrites( GATT_MAX_NUM_PREPARE_WRITES ); // Register to receive incoming ATT Requests GATT_RegisterForReq( GATTServApp_TaskID ); // Register with Link DB to receive link status change callback VOID linkDB_Register( gattServApp_HandleConnStatusCB ); } /********************************************************************* * @fn GATTServApp_ProcessEvent * * @brief GATT Server Application Task event processor. This function * is called to process all events for the task. Events include * timers, messages and any other user defined events. * * @param task_id - The OSAL assigned task ID. * @param events - events to process. This is a bit map and can * contain more than one event. * * @return none */ uint16 GATTServApp_ProcessEvent( uint8 task_id, uint16 events ) { if ( events & SYS_EVENT_MSG ) { osal_event_hdr_t *pMsg; if ( (pMsg = ( osal_event_hdr_t *)osal_msg_receive( GATTServApp_TaskID )) != NULL ) { // Process incoming messages switch ( pMsg->event ) { // Incoming GATT message case GATT_MSG_EVENT: gattServApp_ProcessMsg( (gattMsgEvent_t *)pMsg ); break; default: // Unsupported message break; } // Release the OSAL message VOID osal_msg_deallocate( (uint8 *)pMsg ); } // return unprocessed events return (events ^ SYS_EVENT_MSG); } // Discard unknown events return 0; } /****************************************************************************** * @fn GATTServApp_RegisterService * * @brief Register a service's attribute list and callback functions with * the GATT Server Application. * * @param pAttrs - Array of attribute records to be registered * @param numAttrs - Number of attributes in array * @param pServiceCBs - Service callback function pointers * * @return SUCCESS: Service registered successfully. * INVALIDPARAMETER: Invalid service field. * FAILURE: Not enough attribute handles available. * bleMemAllocError: Memory allocation error occurred. */ bStatus_t GATTServApp_RegisterService( gattAttribute_t *pAttrs, uint16 numAttrs, CONST gattServiceCBs_t *pServiceCBs ) { uint8 status; // First register the service attribute list with GATT Server if ( pAttrs != NULL ) { gattService_t service; service.attrs = pAttrs; service.numAttrs = numAttrs; status = GATT_RegisterService( &service ); if ( ( status == SUCCESS ) && ( pServiceCBs != NULL ) ) { // Register the service CBs with GATT Server Application status = gattServApp_RegisterServiceCBs( GATT_SERVICE_HANDLE( pAttrs ), pServiceCBs ); } } else { status = INVALIDPARAMETER; } return ( status ); } /****************************************************************************** * @fn GATTServApp_DeregisterService * * @brief Deregister a service's attribute list and callback functions from * the GATT Server Application. * * NOTE: It's the caller's responsibility to free the service attribute * list returned from this API. * * @param handle - handle of service to be deregistered * @param p2pAttrs - pointer to array of attribute records (to be returned) * * @return SUCCESS: Service deregistered successfully. * FAILURE: Service not found. */ bStatus_t GATTServApp_DeregisterService( uint16 handle, gattAttribute_t **p2pAttrs ) { uint8 status; // First deregister the service CBs with GATT Server Application status = gattServApp_DeregisterServiceCBs( handle ); if ( status == SUCCESS ) { gattService_t service; // Deregister the service attribute list with GATT Server status = GATT_DeregisterService( handle, &service ); if ( status == SUCCESS ) { if ( p2pAttrs != NULL ) { *p2pAttrs = service.attrs; } } } return ( status ); } /********************************************************************* * @fn GATTServApp_SetParameter * * @brief Set a GATT Server parameter. * * @param param - Profile parameter ID * @param len - length of data to right * @param pValue - pointer to data to write. This is dependent on the * the parameter ID and WILL be cast to the appropriate * data type (example: data type of uint16 will be cast * to uint16 pointer). * * @return SUCCESS: Parameter set successful * FAILURE: Parameter in use * INVALIDPARAMETER: Invalid parameter * bleInvalidRange: Invalid value * bleMemAllocError: Memory allocation failed */ bStatus_t GATTServApp_SetParameter( uint8 param, uint8 len, void *pValue ) { bStatus_t status = SUCCESS; switch ( param ) { case GATT_PARAM_NUM_PREPARE_WRITES: if ( len == sizeof ( uint8 ) ) { if ( !gattServApp_PrepareWriteQInUse() ) { // Set the new nunber of prepare writes status = gattServApp_SetNumPrepareWrites( *((uint8*)pValue) ); } else { status = FAILURE; } } else { status = bleInvalidRange; } break; default: status = INVALIDPARAMETER; break; } return ( status ); } /********************************************************************* * @fn GATTServApp_GetParameter * * @brief Get a GATT Server parameter. * * @param param - Profile parameter ID * @param pValue - pointer to data to put. This is dependent on the * parameter ID and WILL be cast to the appropriate * data type (example: data type of uint16 will be * cast to uint16 pointer). * * @return SUCCESS: Parameter get successful * INVALIDPARAMETER: Invalid parameter */ bStatus_t GATTServApp_GetParameter( uint8 param, void *pValue ) { bStatus_t status = SUCCESS; switch ( param ) { case GATT_PARAM_NUM_PREPARE_WRITES: *((uint8*)pValue) = maxNumPrepareWrites; break; default: status = INVALIDPARAMETER; break; } return ( status ); } /********************************************************************* * @fn gattServApp_SetNumPrepareWrites * * @brief Set the maximum number of the prepare writes. * * @param numPrepareWrites - number of prepare writes * * @return SUCCESS: New number set successfully. * bleMemAllocError: Memory allocation failed. */ static bStatus_t gattServApp_SetNumPrepareWrites( uint8 numPrepareWrites ) { attPrepareWriteReq_t *pQueue; uint16 queueSize = ( MAX_NUM_LL_CONN * numPrepareWrites * sizeof( attPrepareWriteReq_t ) ); // First make sure no one can get access to the Prepare Write Table maxNumPrepareWrites = 0; // Free the existing prepare write queues if ( prepareWritesTbl[0].pPrepareWriteQ != NULL ) { #ifndef PREPARE_QUEUE_STATIC osal_mem_free( prepareWritesTbl[0].pPrepareWriteQ ); #endif // Null out the prepare writes queues for ( uint8 i = 0; i < MAX_NUM_LL_CONN; i++ ) { prepareWritesTbl[i].pPrepareWriteQ = NULL; } } // Allocate the prepare write queues #ifdef PREPARE_QUEUE_STATIC pQueue = prepareQueue; #else pQueue = osal_mem_alloc( queueSize ); #endif if ( pQueue != NULL ) { // Initialize the prepare write queues VOID osal_memset( pQueue, 0, queueSize ); // Set up the prepare write queue for each client (i.e., connection) for ( uint8 i = 0; i < MAX_NUM_LL_CONN; i++ ) { uint8 nextQ = i * numPrepareWrites; // Index of next available queue prepareWritesTbl[i].pPrepareWriteQ = &(pQueue[nextQ]); // Mark the prepare write request items as empty for ( uint8 j = 0; j < numPrepareWrites; j++ ) { prepareWritesTbl[i].pPrepareWriteQ[j].handle = GATT_INVALID_HANDLE; } } // Set the new number of prepare writes maxNumPrepareWrites = numPrepareWrites; return ( SUCCESS ); } return ( bleMemAllocError ); } /********************************************************************* * @fn GATTServApp_FindAttr * * @brief Find the attribute record within a service attribute * table for a given attribute value pointer. * * @param pAttrTbl - pointer to attribute table * @param numAttrs - number of attributes in attribute table * @param pValue - pointer to attribute value * * @return Pointer to attribute record. NULL, if not found. */ gattAttribute_t *GATTServApp_FindAttr( gattAttribute_t *pAttrTbl, uint16 numAttrs, uint8 *pValue ) { for ( uint16 i = 0; i < numAttrs; i++ ) { if ( pAttrTbl[i].pValue == pValue ) { // Attribute record found return ( &(pAttrTbl[i]) ); } } return ( (gattAttribute_t *)NULL ); } /****************************************************************************** * @fn GATTServApp_AddService * * @brief Add function for the GATT Service. * * @param services - services to add. This is a bit map and can * contain more than one service. * * @return SUCCESS: Service added successfully. * INVALIDPARAMETER: Invalid service field. * FAILURE: Not enough attribute handles available. * bleMemAllocError: Memory allocation error occurred. */ bStatus_t GATTServApp_AddService( uint32 services ) { uint8 status = SUCCESS; if ( services & GATT_SERVICE ) { // Register GATT attribute list and CBs with GATT Server Application status = GATTServApp_RegisterService( gattAttrTbl, GATT_NUM_ATTRS( gattAttrTbl ), &gattServiceCBs ); } return ( status ); } /****************************************************************************** * @fn GATTServApp_DelService * * @brief Delete function for the GATT Service. * * @param services - services to delete. This is a bit map and can * contain more than one service. * * @return SUCCESS: Service deleted successfully. * FAILURE: Service not found. */ bStatus_t GATTServApp_DelService( uint32 services ) { uint8 status = SUCCESS; if ( services & GATT_SERVICE ) { // Deregister GATT attribute list and CBs from GATT Server Application status = GATTServApp_DeregisterService( GATT_SERVICE_HANDLE( gattAttrTbl ), NULL ); } return ( status ); } /****************************************************************************** * @fn gattServApp_RegisterServiceCBs * * @brief Register callback functions for a service. * * @param handle - handle of service being registered * @param pServiceCBs - pointer to service CBs to be registered * * @return SUCCESS: Service CBs were registered successfully. * INVALIDPARAMETER: Invalid service CB field. * bleMemAllocError: Memory allocation error occurred. */ static bStatus_t gattServApp_RegisterServiceCBs( uint16 handle, CONST gattServiceCBs_t *pServiceCBs ) { serviceCBsList_t *pNewItem; // Make sure the service handle is specified if ( handle == GATT_INVALID_HANDLE ) { return ( INVALIDPARAMETER ); } // Fill in the new service list pNewItem = (serviceCBsList_t *)osal_mem_alloc( sizeof( serviceCBsList_t ) ); if ( pNewItem == NULL ) { // Not enough memory return ( bleMemAllocError ); } // Set up new service CBs item pNewItem->next = NULL; pNewItem->serviceInfo.handle = handle; pNewItem->serviceInfo.pCBs = pServiceCBs; // Find spot in list if ( serviceCBsList == NULL ) { // First item in list serviceCBsList = pNewItem; } else { serviceCBsList_t *pLoop = serviceCBsList; // Look for end of list while ( pLoop->next != NULL ) { pLoop = pLoop->next; } // Put new item at end of list pLoop->next = pNewItem; } return ( SUCCESS ); } /****************************************************************************** * @fn gattServApp_DeregisterServiceCBs * * @brief Deregister callback functions for a service. * * @param handle - handle of service CBs to be deregistered * * @return SUCCESS: Service CBs were deregistered successfully. * FAILURE: Service CBs were not found. */ static bStatus_t gattServApp_DeregisterServiceCBs( uint16 handle ) { serviceCBsList_t *pLoop = serviceCBsList; serviceCBsList_t *pPrev = NULL; // Look for service while ( pLoop != NULL ) { if ( pLoop->serviceInfo.handle == handle ) { // Service CBs found; unlink it if ( pPrev == NULL ) { // First item in list serviceCBsList = pLoop->next; } else { pPrev->next = pLoop->next; } // Free the service CB record osal_mem_free( pLoop ); return ( SUCCESS ); } pPrev = pLoop; pLoop = pLoop->next; } // Service CBs not found return ( FAILURE ); } /********************************************************************* * @fn gattServApp_FindServiceCBs * * @brief Find service's callback record. * * @param handle - owner of service * * @return Pointer to service record. NULL, otherwise. */ static CONST gattServiceCBs_t *gattServApp_FindServiceCBs( uint16 handle ) { serviceCBsList_t *pLoop = serviceCBsList; while ( pLoop != NULL ) { if ( pLoop->serviceInfo.handle == handle ) { return ( pLoop->serviceInfo.pCBs ); } // Try next service pLoop = pLoop->next; } return ( (gattServiceCBs_t *)NULL ); } /********************************************************************* * @fn gattServApp_ProcessMsg * * @brief GATT Server App message processing function. * * @param pMsg - pointer to received message * * @return Success or Failure */ static void gattServApp_ProcessMsg( gattMsgEvent_t *pMsg ) { uint16 errHandle = GATT_INVALID_HANDLE; uint8 status; #if defined ( TESTMODES ) if ( paramValue == GATT_TESTMODE_NO_RSP ) { // Notify GATT that a message has been processed // Note: This call is optional if flow control is not used. GATT_AppCompletedMsg( pMsg ); // Just ignore the incoming request messages return; } #endif // Process the GATT server message switch ( pMsg->method ) { case ATT_EXCHANGE_MTU_REQ: status = gattServApp_ProcessExchangeMTUReq( pMsg ); break; case ATT_FIND_BY_TYPE_VALUE_REQ: status = gattServApp_ProcessFindByTypeValueReq( pMsg, &errHandle ); break; case ATT_READ_BY_TYPE_REQ: status = gattServApp_ProcessReadByTypeReq( pMsg, &errHandle ); break; case ATT_READ_REQ: status = gattServApp_ProcessReadReq( pMsg, &errHandle ); break; case ATT_READ_BLOB_REQ: status = gattServApp_ProcessReadBlobReq( pMsg, &errHandle ); break; case ATT_READ_MULTI_REQ: status = gattServApp_ProcessReadMultiReq( pMsg, &errHandle ); break; case ATT_READ_BY_GRP_TYPE_REQ: status = gattServApp_ProcessReadByGrpTypeReq( pMsg, &errHandle ); break; case ATT_WRITE_REQ: status = gattServApp_ProcessWriteReq( pMsg, &errHandle ); break; case ATT_PREPARE_WRITE_REQ: status = gattServApp_ProcessPrepareWriteReq( pMsg, &errHandle ); break; case ATT_EXECUTE_WRITE_REQ: status = gattServApp_ProcessExecuteWriteReq( pMsg, &errHandle ); break; default: // Unknown request - ignore it! status = SUCCESS; break; } // See if we need to send an error response back if ( status != SUCCESS ) { // Make sure the request was not sent locally if ( pMsg->hdr.status != bleNotConnected ) { attErrorRsp_t *pRsp = &rsp.errorRsp; pRsp->reqOpcode = pMsg->method; pRsp->handle = errHandle; pRsp->errCode = status; VOID ATT_ErrorRsp( pMsg->connHandle, pRsp ); } } // Notify GATT that a message has been processed // Note: This call is optional if flow control is not used. GATT_AppCompletedMsg( pMsg ); // if app task ask the gatt message, copy and send to app task if(s_GATTServCB) s_GATTServCB(pMsg); } /********************************************************************* * @fn gattServApp_ProcessExchangeMTUReq * * @brief Process Exchange MTU Request. * * @param pMsg - pointer to received message * * @return Success */ static bStatus_t gattServApp_ProcessExchangeMTUReq( gattMsgEvent_t *pMsg ) { attExchangeMTURsp_t *pRsp = &rsp.exchangeMTURsp; // ATT_MTU shall be set to the minimum of the Client Rx MTU and Server Rx MTU values // Set the Server Rx MTU parameter to the maximum MTU that this server can receive #if defined ( TESTMODES ) if ( paramValue == GATT_TESTMODE_MAX_MTU_SIZE ) { pRsp->serverRxMTU = ATT_MAX_MTU_SIZE; } else #endif pRsp->serverRxMTU = g_ATT_MTU_SIZE_MAX;//ATT_MTU_SIZE; // Send response back VOID ATT_ExchangeMTURsp( pMsg->connHandle, pRsp ); return ( SUCCESS ); } /********************************************************************* * @fn gattServApp_ProcessFindByTypeValueReq * * @brief Process Find By Type Value Request. * * @param pMsg - pointer to received message * @param pErrHandle - attribute handle that generates an error * * @return Success or Failure */ static bStatus_t gattServApp_ProcessFindByTypeValueReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ) { attFindByTypeValueReq_t *pReq = &pMsg->msg.findByTypeValueReq; attFindByTypeValueRsp_t *pRsp = &rsp.findByTypeValueRsp; gattAttribute_t *pAttr; uint16 service; // Initialize the response VOID osal_memset( pRsp, 0, sizeof( attFindByTypeValueRsp_t ) ); // Only attributes with attribute handles between and including the Starting // Handle parameter and the Ending Handle parameter that match the requested // attribute type and the attribute value will be returned. // All attribute types are effectively compared as 128-bit UUIDs, // even if a 16-bit UUID is provided in this request or defined // for an attribute. pAttr = GATT_FindHandleUUID( pReq->startHandle, pReq->endHandle, pReq->type.uuid, pReq->type.len, &service ); while ( ( pAttr != NULL ) && ( pRsp->numInfo < g_ATT_MAX_NUM_HANDLES_INFO ) ) { uint16 grpEndHandle; // It is not possible to use this request on an attribute that has a value // that is longer than (ATT_MTU - 7). if ( GATTServApp_ReadAttr( pMsg->connHandle, pAttr, service, attrValue, &attrLen, 0, (g_ATT_MTU_SIZE-7) ) == SUCCESS ) { // Attribute values should be compared in terms of length and binary representation. if ( ( pReq->len == attrLen ) && osal_memcmp( pReq->value, attrValue, attrLen) ) { // New attribute found // Set the Found Handle to the attribute that has the exact attribute // type and attribute value from the request. pRsp->handlesInfo[pRsp->numInfo].handle = pAttr->handle; } } // Try to find the next attribute pAttr = GATT_FindNextAttr( pAttr, pReq->endHandle, service, &grpEndHandle ); // Set Group End Handle if ( pRsp->handlesInfo[pRsp->numInfo].handle != 0 ) { // If the attribute type is a grouping attribute, the Group End Handle // shall be defined by that higher layer specification. If the attribute // type is not a grouping attribute, the Group End Handle shall be equal // to the Found Attribute Handle. if ( pAttr != NULL ) { pRsp->handlesInfo[pRsp->numInfo++].grpEndHandle = grpEndHandle; } else { // If no other attributes with the same attribute type exist after the // Found Attribute Handle, the Group End Handle shall be set to 0xFFFF. pRsp->handlesInfo[pRsp->numInfo++].grpEndHandle = GATT_MAX_HANDLE; } } } // while if ( pRsp->numInfo > 0 ) { // Send a response back VOID ATT_FindByTypeValueRsp( pMsg->connHandle, pRsp ); return ( SUCCESS ); } *pErrHandle = pReq->startHandle; return ( ATT_ERR_ATTR_NOT_FOUND ); } /********************************************************************* * @fn gattServApp_ProcessReadByTypeReq * * @brief Process Read By Type Request. * * @param pMsg - pointer to received message * @param pErrHandle - attribute handle that generates an error * * @return Success or Failure */ static bStatus_t gattServApp_ProcessReadByTypeReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ) { attReadByTypeReq_t *pReq = &pMsg->msg.readByTypeReq; attReadByTypeRsp_t *pRsp = &rsp.readByTypeRsp; uint16 startHandle = pReq->startHandle; uint8 dataLen = 0; uint8 status = SUCCESS; // Only the attributes with attribute handles between and including the // Starting Handle and the Ending Handle with the attribute type that is // the same as the Attribute Type given will be returned. // Make sure there's enough room at least for an attribute handle (no value) while ( dataLen <= (g_ATT_MTU_SIZE-4) ) { uint16 service; gattAttribute_t *pAttr; // All attribute types are effectively compared as 128-bit UUIDs, even if // a 16-bit UUID is provided in this request or defined for an attribute. pAttr = GATT_FindHandleUUID( startHandle, pReq->endHandle, pReq->type.uuid, pReq->type.len, &service ); if ( pAttr == NULL ) { break; // No more attribute found } // Update start handle so it has the right value if we break from the loop startHandle = pAttr->handle; // Make sure the attribute has sufficient permissions to allow reading status = GATT_VerifyReadPermissions( pMsg->connHandle, pAttr->permissions ); if ( status != SUCCESS ) { break; } // Read the attribute value. If the attribute value is longer than // (ATT_MTU - 4) or 253 octets, whichever is smaller, then the first // (ATT_MTU - 4) or 253 octets shall be included in this response. status = GATTServApp_ReadAttr( pMsg->connHandle, pAttr, service, attrValue, &attrLen, 0, (g_ATT_MTU_SIZE-4) ); if ( status != SUCCESS ) { break; // Cannot read the attribute value } // See if this is the first attribute found if ( dataLen == 0 ) { // Use the length of the first attribute value for the length field pRsp->len = 2 + attrLen; } else { // If the attributes have attribute values that have the same length // then these attributes can all be read in a single request. if ( pRsp->len != 2 + attrLen ) { break; } } // Make sure there's enough room for this attribute handle and value if ( dataLen + attrLen > (g_ATT_MTU_SIZE-4) ) { break; } // Add the handle value pair to the response pRsp->dataList[dataLen++] = LO_UINT16( pAttr->handle ); pRsp->dataList[dataLen++] = HI_UINT16( pAttr->handle ); VOID osal_memcpy( &(pRsp->dataList[dataLen]), attrValue, attrLen ); dataLen += attrLen; if ( startHandle == GATT_MAX_HANDLE ) { break; // We're done } // Update start handle and search again startHandle++; } // while // See what to respond if ( dataLen > 0 ) { // Set the number of attribute handle-value pairs found pRsp->numPairs = dataLen / pRsp->len; // Send a response back VOID ATT_ReadByTypeRsp( pMsg->connHandle, pRsp ); return ( SUCCESS ); } if ( status == SUCCESS ) { // Attribute not found -- dataLen must be 0 status = ATT_ERR_ATTR_NOT_FOUND; } *pErrHandle = startHandle; return ( status ); } /********************************************************************* * @fn gattServApp_ProcessReadReq * * @brief Process Read Request. * * @param pMsg - pointer to received message * @param pErrHandle - attribute handle that generates an error * * @return Success or Failure */ static bStatus_t gattServApp_ProcessReadReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ) { attReadReq_t *pReq = &pMsg->msg.readReq; gattAttribute_t *pAttr; uint16 service; uint8 status; pAttr = GATT_FindHandle( pReq->handle, &service ); if ( pAttr != NULL ) { attReadRsp_t *pRsp = &rsp.readRsp; // Build and send a response back. If the attribute value is longer // than (ATT_MTU - 1) then (ATT_MTU - 1) octets shall be included // in this response. status = GATTServApp_ReadAttr( pMsg->connHandle, pAttr, service, pRsp->value, &pRsp->len, 0, (g_ATT_MTU_SIZE-1) ); if ( status == SUCCESS ) { // Send a response back VOID ATT_ReadRsp( pMsg->connHandle, pRsp ); } } else { status = ATT_ERR_INVALID_HANDLE; } if ( status != SUCCESS ) { *pErrHandle = pReq->handle; } return ( status ); } /********************************************************************* * @fn gattServApp_ProcessReadBlobReq * * @brief Process Read Blob Request. * * @param pMsg - pointer to received message * @param pErrHandle - attribute handle that generates an error * * @return Success or Failure */ static bStatus_t gattServApp_ProcessReadBlobReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ) { attReadBlobReq_t *pReq = &pMsg->msg.readBlobReq; gattAttribute_t *pAttr; uint16 service; uint8 status; pAttr = GATT_FindHandle( pReq->handle, &service ); if ( pAttr != NULL ) { attReadBlobRsp_t *pRsp = &rsp.readBlobRsp; // Read part attribute value. If the attribute value is longer than // (Value Offset + ATT_MTU - 1) then (ATT_MTU - 1) octets from Value // Offset shall be included in this response. status = GATTServApp_ReadAttr( pMsg->connHandle, pAttr, service, pRsp->value, &pRsp->len, pReq->offset, (g_ATT_MTU_SIZE-1) ); if ( status == SUCCESS ) { // Send a response back VOID ATT_ReadBlobRsp( pMsg->connHandle, pRsp ); } } else { status = ATT_ERR_INVALID_HANDLE; } if ( status != SUCCESS ) { *pErrHandle = pReq->handle; } return ( status ); } /********************************************************************* * @fn gattServApp_ProcessReadMultiReq * * @brief Process Read Multiple Request. * * @param pMsg - pointer to received message * @param pErrHandle - attribute handle that generates an error * * @return Success or Failure */ static bStatus_t gattServApp_ProcessReadMultiReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ) { attReadMultiReq_t *pReq = &pMsg->msg.readMultiReq; attReadMultiRsp_t *pRsp = &rsp.readMultiRsp; uint8 status = SUCCESS; // Set the response length pRsp->len = 0; for ( uint8 i = 0; ( i < pReq->numHandles ) && ( pRsp->len < (g_ATT_MTU_SIZE-1) ); i++ ) { gattAttribute_t *pAttr; uint16 service; pAttr = GATT_FindHandle( pReq->handle[i], &service ); if ( pAttr == NULL ) { // Should never get here! status = ATT_ERR_INVALID_HANDLE; // The handle of the first attribute causing the error *pErrHandle = pReq->handle[i]; break; } // If the Set Of Values parameter is longer than (ATT_MTU - 1) then only // the first (ATT_MTU - 1) octets shall be included in this response. status = GATTServApp_ReadAttr( pMsg->connHandle, pAttr, service, attrValue, &attrLen, 0, (g_ATT_MTU_SIZE-1) ); if ( status != SUCCESS ) { // The handle of the first attribute causing the error *pErrHandle = pReq->handle[i]; break; } // Make sure there's enough room in the response for this attribute value if ( pRsp->len + attrLen > (g_ATT_MTU_SIZE-1) ) { attrLen = (g_ATT_MTU_SIZE-1) - pRsp->len; } // Append this value to the end of the response VOID osal_memcpy( &(pRsp->values[pRsp->len]), attrValue, attrLen ); pRsp->len += attrLen; } if ( status == SUCCESS ) { // Send a response back VOID ATT_ReadMultiRsp( pMsg->connHandle, pRsp ); } return ( status ); } /********************************************************************* * @fn gattServApp_ProcessReadByGrpTypeReq * * @brief Process Read By Group Type Request. * * @param pMsg - pointer to received message * @param pErrHandle - attribute handle that generates an error * * @return Success or Failure */ static bStatus_t gattServApp_ProcessReadByGrpTypeReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ) { attReadByGrpTypeReq_t *pReq = &pMsg->msg.readByGrpTypeReq; attReadByGrpTypeRsp_t *pRsp = &rsp.readByGrpTypeRsp; uint16 service; gattAttribute_t *pAttr; uint8 dataLen = 0; uint8 status = SUCCESS; // Only the attributes with attribute handles between and including the // Starting Handle and the Ending Handle with the attribute type that is // the same as the Attribute Type given will be returned. // All attribute types are effectively compared as 128-bit UUIDs, // even if a 16-bit UUID is provided in this request or defined // for an attribute. pAttr = GATT_FindHandleUUID( pReq->startHandle, pReq->endHandle, pReq->type.uuid, pReq->type.len, &service ); while ( pAttr != NULL ) { uint16 endGrpHandle; // The service, include and characteristic declarations are readable and // require no authentication or authorization, therefore insufficient // authentication or read not permitted errors shall not occur. status = GATT_VerifyReadPermissions( pMsg->connHandle, pAttr->permissions ); if ( status != SUCCESS ) { *pErrHandle = pAttr->handle; break; } // Read the attribute value. If the attribute value is longer than // (ATT_MTU - 6) or 251 octets, whichever is smaller, then the first // (ATT_MTU - 6) or 251 octets shall be included in this response. status = GATTServApp_ReadAttr( pMsg->connHandle, pAttr, service, attrValue, &attrLen, 0, (g_ATT_MTU_SIZE-6) ); if ( status != SUCCESS ) { // Cannot read the attribute value *pErrHandle = pAttr->handle; break; } // See if this is the first attribute found if ( dataLen == 0 ) { // Use the length of the first attribute value for the length field pRsp->len = 2 + 2 + attrLen; } else { // If the attributes have attribute values that have the same length // then these attributes can all be read in a single request. if ( pRsp->len != 2 + 2 + attrLen ) { break; // We're done here } // Make sure there's enough room for this attribute handle, end group handle and value if ( dataLen + attrLen > (g_ATT_MTU_SIZE-6) ) { break; // We're done here } } // Add Attribute Handle to the response pRsp->dataList[dataLen++] = LO_UINT16( pAttr->handle ); pRsp->dataList[dataLen++] = HI_UINT16( pAttr->handle ); // Try to find the next attribute pAttr = GATT_FindNextAttr( pAttr, pReq->endHandle, service, &endGrpHandle ); // Add End Group Handle to the response if ( pAttr != NULL ) { // The End Group Handle is the handle of the last attribute within the // service definition pRsp->dataList[dataLen++] = LO_UINT16( endGrpHandle ); pRsp->dataList[dataLen++] = HI_UINT16( endGrpHandle ); } else { // The ending handle of the last service can be 0xFFFF pRsp->dataList[dataLen++] = LO_UINT16( GATT_MAX_HANDLE ); pRsp->dataList[dataLen++] = HI_UINT16( GATT_MAX_HANDLE ); } // Add Attribute Value to the response VOID osal_memcpy( &(pRsp->dataList[dataLen]), attrValue, attrLen ); dataLen += attrLen; } // while // See what to respond if ( dataLen > 0 ) { // Set the number of attribute handle, end group handle and value sets found pRsp->numGrps = dataLen / pRsp->len; // Send a response back VOID ATT_ReadByGrpTypeRsp( pMsg->connHandle, pRsp ); return ( SUCCESS ); } if ( status == SUCCESS ) { // No grouping attribute found -- dataLen must be 0 status = ATT_ERR_ATTR_NOT_FOUND; } *pErrHandle = pReq->startHandle; return ( status ); } /********************************************************************* * @fn gattServApp_ProcessWriteReq * * @brief Process Write Request or Command. * * @param pMsg - pointer to received message * @param pErrHandle - attribute handle that generates an error * * @return Success or Failure */ static bStatus_t gattServApp_ProcessWriteReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ) { attWriteReq_t *pReq = &(pMsg->msg.writeReq); gattAttribute_t *pAttr; uint16 service; uint8 status = SUCCESS; // No Error Response or Write Response shall be sent in response to Write // Command. If the server cannot write this attribute for any reason the // command shall be ignored. pAttr = GATT_FindHandle( pReq->handle, &service ); if ( pAttr != NULL ) { // Authorization is handled by the application/profile if ( gattPermitAuthorWrite( pAttr->permissions ) ) { // Use Service's authorization callback to authorize the request pfnGATTAuthorizeAttrCB_t pfnCB = gattServApp_FindAuthorizeAttrCB( service ); if ( pfnCB != NULL ) { status = (*pfnCB)( pMsg->connHandle, pAttr, ATT_WRITE_REQ ); } else { status = ATT_ERR_UNLIKELY; } } // If everything is fine then try to write the new value if ( status == SUCCESS ) { // Use Service's write callback to write the request status = GATTServApp_WriteAttr( pMsg->connHandle, pReq->handle, pReq->value, pReq->len, 0 ); if ( ( status == SUCCESS ) && ( pReq->cmd == FALSE ) ) { // Send a response back //VOID ATT_WriteRsp( pMsg->connHandle ); uint8 st=ATT_WriteRsp( pMsg->connHandle ); // if(st) // { // AT_LOG("[ATT_RSP ERR] %x %x\n",st,l2capSegmentPkt.fragment); // } } } } else { status = ATT_ERR_INVALID_HANDLE; } if ( status != SUCCESS ) { *pErrHandle = pReq->handle; } return ( pReq->cmd ? SUCCESS : status ); } /********************************************************************* * @fn gattServApp_ProcessPrepareWriteReq * * @brief Process Prepare Write Request. * * @param pMsg - pointer to received message * @param pErrHandle - attribute handle that generates an error * * @return Success or Failure */ static bStatus_t gattServApp_ProcessPrepareWriteReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ) { attPrepareWriteReq_t *pReq = &pMsg->msg.prepareWriteReq; gattAttribute_t *pAttr; uint16 service; uint8 status = SUCCESS; pAttr = GATT_FindHandle( pReq->handle, &service ); if ( pAttr != NULL ) { // Authorization is handled by the application/profile if ( gattPermitAuthorWrite( pAttr->permissions ) ) { // Use Service's authorization callback to authorize the request pfnGATTAuthorizeAttrCB_t pfnCB = gattServApp_FindAuthorizeAttrCB( service ); if ( pfnCB != NULL ) { status = (*pfnCB)( pMsg->connHandle, pAttr, ATT_WRITE_REQ ); } else { status = ATT_ERR_UNLIKELY; } } if ( status == SUCCESS ) { #if defined ( TESTMODES ) if ( paramValue == GATT_TESTMODE_CORRUPT_PW_DATA ) { pReq->value[0] = ~(pReq->value[0]); } #endif // Enqueue the request for now status = gattServApp_EnqueuePrepareWriteReq( pMsg->connHandle, pReq ); if ( status == SUCCESS ) { //LOG("pre off[%d] len[%d]\n", pReq->offset, pReq->len); // Send a response back VOID ATT_PrepareWriteRsp( pMsg->connHandle, (attPrepareWriteRsp_t *)pReq ); } } } else { status = ATT_ERR_INVALID_HANDLE; } if ( status != SUCCESS ) { *pErrHandle = pReq->handle; } return ( status ); } /********************************************************************* * @fn gattServApp_ProcessExecuteWriteReq * * @brief Process Execute Write Request. * * @param pMsg - pointer to received message * @param pErrHandle - attribute handle that generates an error * * @return Success or Failure */ static bStatus_t gattServApp_ProcessExecuteWriteReq( gattMsgEvent_t *pMsg, uint16 *pErrHandle ) { attExecuteWriteReq_t *pReq = &pMsg->msg.executeWriteReq; prepareWrites_t *pQueue; uint8 status = SUCCESS; // See if this client has a prepare write queue pQueue = gattServApp_FindPrepareWriteQ( pMsg->connHandle ); if ( pQueue != NULL ) { for ( uint8 i = 0; i < maxNumPrepareWrites; i++ ) { attPrepareWriteReq_t *pWriteReq = &(pQueue->pPrepareWriteQ[i]); // See if there're any prepared write requests in the queue if ( pWriteReq->handle == GATT_INVALID_HANDLE ) { break; // We're done } // Execute the request if ( pReq->flags == ATT_WRITE_PREPARED_VALUES ) { status = GATTServApp_WriteAttr( pMsg->connHandle, pWriteReq->handle, pWriteReq->value, pWriteReq->len, pWriteReq->offset ); //LOG("exe off[%d]len[%d]", pWriteReq->offset, pWriteReq->len); // If the prepare write requests can not be written, the queue shall // be cleared and then an Error Response shall be sent with a high // layer defined error code. if ( status != SUCCESS ) { // Cancel the remaining prepared writes pReq->flags = ATT_CANCEL_PREPARED_WRITES; // The Attribute Handle in Error shall be set to the attribute handle // of the attribute from the prepare write queue that caused this // application error *pErrHandle = pWriteReq->handle; } } else // ATT_CANCEL_PREPARED_WRITES { // Cancel all prepared writes - just ignore the request } // Clear the queue item VOID osal_memset( pWriteReq, 0, sizeof( attPrepareWriteRsp_t ) ); // Mark this item as empty pWriteReq->handle = GATT_INVALID_HANDLE; } // for loop // Mark this queue as empty pQueue->connHandle = INVALID_CONNHANDLE; } // Send a response back if ( status == SUCCESS ) { VOID ATT_ExecuteWriteRsp( pMsg->connHandle ); } return ( status ); } /********************************************************************* * @fn gattServApp_EnqueuePrepareWriteReq * * @brief Enqueue Prepare Write Request. * * @param connHandle - connection packet was received on * @param pReq - pointer to request * * @return Success or Failure */ static bStatus_t gattServApp_EnqueuePrepareWriteReq( uint16 connHandle, attPrepareWriteReq_t *pReq ) { prepareWrites_t *pQueue; // First see if there's queue already assocaited with this client pQueue = gattServApp_FindPrepareWriteQ( connHandle ); if ( pQueue == NULL ) { // Find a queue for this client pQueue = gattServApp_FindPrepareWriteQ( INVALID_CONNHANDLE ); if ( pQueue != NULL ) { pQueue->connHandle = connHandle; } } // If a queue is found for this client then enqueue the request if ( pQueue != NULL ) { for ( uint8 i = 0; i < maxNumPrepareWrites; i++ ) { if ( pQueue->pPrepareWriteQ[i].handle == GATT_INVALID_HANDLE ) { // Store the request here VOID osal_memcpy( &(pQueue->pPrepareWriteQ[i]), pReq, sizeof ( attPrepareWriteReq_t ) ); //LOG("enq off[%d]len[%d]\n", pReq->offset, pReq->len); return ( SUCCESS ); } } } return ( ATT_ERR_PREPARE_QUEUE_FULL ); } /********************************************************************* * @fn gattServApp_FindPrepareWriteQ * * @brief Find client's queue. * * @param connHandle - connection used by client * * @return Pointer to queue. NULL, otherwise. */ static prepareWrites_t *gattServApp_FindPrepareWriteQ( uint16 connHandle ) { // First see if this client has already a queue for ( uint8 i = 0; i < MAX_NUM_LL_CONN; i++ ) { if ( prepareWritesTbl[i].connHandle == connHandle ) { // Queue found return ( &(prepareWritesTbl[i]) ); } } return ( (prepareWrites_t *)NULL ); } /********************************************************************* * @fn gattServApp_PrepareWriteQInUse * * @brief Check to see if the prepare write queue is in use. * * @param void * * @return TRUE if queue in use. FALSE, otherwise. */ static uint8 gattServApp_PrepareWriteQInUse( void ) { // See if any prepare write queue is in use for ( uint8 i = 0; i < MAX_NUM_LL_CONN; i++ ) { if ( prepareWritesTbl[i].connHandle != INVALID_CONNHANDLE ) { for ( uint8 j = 0; j < maxNumPrepareWrites; j++ ) { if ( prepareWritesTbl[i].pPrepareWriteQ[j].handle != GATT_INVALID_HANDLE ) { // Queue item is in use return ( TRUE ); } } // for } } // for return ( FALSE ); } /********************************************************************* * @fn gattServApp_FindCharCfgItem * * @brief Find the characteristic configuration for a given client. * Uses the connection handle to search the charactersitic * configuration table of a client. * * @param connHandle - connection handle (0xFFFF for empty entry) * @param charCfgTbl - characteristic configuration table. * * @return pointer to the found item. NULL, otherwise. */ static gattCharCfg_t *gattServApp_FindCharCfgItem( uint16 connHandle, gattCharCfg_t *charCfgTbl ) { for ( uint8 i = 0; i < GATT_MAX_NUM_CONN; i++ ) { if ( charCfgTbl[i].connHandle == connHandle ) { // Entry found return ( &(charCfgTbl[i]) ); } } return ( (gattCharCfg_t *)NULL ); } /********************************************************************* * @fn gattServApp_FindReadAttrCB * * @brief Find the Read Attribute CB function pointer for a given service. * * @param handle - service attribute handle * * @return pointer to the found CB. NULL, otherwise. */ static pfnGATTReadAttrCB_t gattServApp_FindReadAttrCB( uint16 handle ) { CONST gattServiceCBs_t *pCBs = gattServApp_FindServiceCBs( handle ); return ( ( pCBs == NULL ) ? NULL : pCBs->pfnReadAttrCB ); } /********************************************************************* * @fn gattServApp_FindWriteAttrCB * * @brief Find the Write CB Attribute function pointer for a given service. * * @param handle - service attribute handle * * @return pointer to the found CB. NULL, otherwise. */ static pfnGATTWriteAttrCB_t gattServApp_FindWriteAttrCB( uint16 handle ) { CONST gattServiceCBs_t *pCBs = gattServApp_FindServiceCBs( handle ); return ( ( pCBs == NULL ) ? NULL : pCBs->pfnWriteAttrCB ); } /********************************************************************* * @fn gattServApp_FindAuthorizeAttrCB * * @brief Find the Authorize Attribute CB function pointer for a given service. * * @param handle - service attribute handle * * @return pointer to the found CB. NULL, otherwise. */ static pfnGATTAuthorizeAttrCB_t gattServApp_FindAuthorizeAttrCB( uint16 handle ) { CONST gattServiceCBs_t *pCBs = gattServApp_FindServiceCBs( handle ); return ( ( pCBs == NULL ) ? NULL : pCBs->pfnAuthorizeAttrCB ); } /********************************************************************* * @fn gattServApp_ValidateWriteAttrCB * * @brief Validate and/or Write attribute data * * @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 * * @return Success or Failure */ static bStatus_t gattServApp_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint8 len, uint16 offset ) { bStatus_t status = SUCCESS; if ( pAttr->type.len == ATT_BT_UUID_SIZE ) { // 16-bit UUID uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]); switch ( uuid ) { case GATT_CLIENT_CHAR_CFG_UUID: status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len, offset, GATT_CLIENT_CFG_INDICATE ); break; default: // Should never get here! status = ATT_ERR_INVALID_HANDLE; } } else { // 128-bit UUID status = ATT_ERR_INVALID_HANDLE; } return ( status ); } /********************************************************************* * @fn GATTServApp_ReadAttr * * @brief Read an attribute. If the format of the attribute value * is unknown to GATT Server, use the callback function * provided by the Service. * * @param connHandle - connection message was received on * @param pAttr - pointer to attribute * @param service - handle of owner service * @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 * * @return Success or Failure */ uint8 GATTServApp_ReadAttr( uint16 connHandle, gattAttribute_t *pAttr, uint16 service, uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen ) { uint8 useCB = FALSE; bStatus_t status = SUCCESS; // Authorization is handled by the application/profile if ( gattPermitAuthorRead( pAttr->permissions ) ) { // Use Service's authorization callback to authorize the request pfnGATTAuthorizeAttrCB_t pfnCB = gattServApp_FindAuthorizeAttrCB( service ); if ( pfnCB != NULL ) { status = (*pfnCB)( connHandle, pAttr, ATT_READ_REQ ); } else { status = ATT_ERR_UNLIKELY; } if ( status != SUCCESS ) { // Read operation failed! return ( status ); } } // Check the UUID length if ( pAttr->type.len == ATT_BT_UUID_SIZE ) { // 16-bit UUID uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]); switch ( uuid ) { case GATT_PRIMARY_SERVICE_UUID: case GATT_SECONDARY_SERVICE_UUID: // Make sure it's not a blob operation if ( offset == 0 ) { gattAttrType_t *pType = (gattAttrType_t *)(pAttr->pValue); *pLen = pType->len; VOID osal_memcpy( pValue, pType->uuid, pType->len ); } else { status = ATT_ERR_ATTR_NOT_LONG; } break; case GATT_CHARACTER_UUID: // Make sure it's not a blob operation if ( offset == 0 ) { gattAttribute_t *pCharValue; // The Attribute Value of a Characteristic Declaration includes the // Characteristic Properties, Characteristic Value Attribute Handle // and UUID. *pLen = 1; pValue[0] = *pAttr->pValue; // Properties // The Characteristic Value Attribute exists immediately following // the Characteristic Declaration. pCharValue = GATT_FindHandle( pAttr->handle+1, NULL ); if ( pCharValue != NULL ) { // It can be a 128-bit UUID *pLen += (2 + pCharValue->type.len); // Attribute Handle pValue[1] = LO_UINT16( pCharValue->handle ); pValue[2] = HI_UINT16( pCharValue->handle ); // Attribute UUID VOID osal_memcpy( &(pValue[3]), pCharValue->type.uuid, pCharValue->type.len ); } else { // Should never get here! *pLen += (2 + ATT_BT_UUID_SIZE); // Set both Attribute Handle and UUID to 0 VOID osal_memset( &(pValue[1]), 0, (2 + ATT_BT_UUID_SIZE) ); } } else { status = ATT_ERR_ATTR_NOT_LONG; } break; case GATT_INCLUDE_UUID: // Make sure it's not a blob operation if ( offset == 0 ) { uint16 servHandle; uint16 endGrpHandle; gattAttribute_t *pIncluded; uint16 handle = *((uint16 *)(pAttr->pValue)); // The Attribute Value of an Include Declaration is set the // included service Attribute Handle, the End Group Handle, // and the service UUID. The Service UUID shall only be present // when the UUID is a 16-bit Bluetooth UUID. *pLen = 4; pValue[0] = LO_UINT16( handle ); pValue[1] = HI_UINT16( handle ); // Find the included service attribute record pIncluded = GATT_FindHandle( handle, &servHandle ); if ( pIncluded != NULL ) { gattAttrType_t *pServiceUUID = (gattAttrType_t *)pIncluded->pValue; // Find out the End Group handle if ( ( GATT_FindNextAttr( pIncluded, GATT_MAX_HANDLE, servHandle, &endGrpHandle ) == NULL ) && ( !gattSecondaryServiceType( pIncluded->type ) ) ) { // The ending handle of the last service can be 0xFFFF endGrpHandle = GATT_MAX_HANDLE; } // Include only 16-bit Service UUID if ( pServiceUUID->len == ATT_BT_UUID_SIZE ) { VOID osal_memcpy( &(pValue[4]), pServiceUUID->uuid, ATT_BT_UUID_SIZE ); *pLen += ATT_BT_UUID_SIZE; } } else { // Should never get here! endGrpHandle = handle; } // End Group Handle pValue[2] = LO_UINT16( endGrpHandle ); pValue[3] = HI_UINT16( endGrpHandle ); } else { status = ATT_ERR_ATTR_NOT_LONG; } break; case GATT_CLIENT_CHAR_CFG_UUID: // Make sure it's not a blob operation if ( offset == 0 ) { uint16 value = GATTServApp_ReadCharCfg( connHandle, (gattCharCfg_t *)(pAttr->pValue) ); *pLen = 2; pValue[0] = LO_UINT16( value ); pValue[1] = HI_UINT16( value ); } else { status = ATT_ERR_ATTR_NOT_LONG; } break; case GATT_CHAR_EXT_PROPS_UUID: case GATT_SERV_CHAR_CFG_UUID: // Make sure it's not a blob operation if ( offset == 0 ) { uint16 value = *((uint16 *)(pAttr->pValue)); *pLen = 2; pValue[0] = LO_UINT16( value ); pValue[1] = HI_UINT16( value ); } else { status = ATT_ERR_ATTR_NOT_LONG; } break; case GATT_CHAR_USER_DESC_UUID: { uint8 len = osal_strlen( (char *)(pAttr->pValue) ); // Could be a long attribute // If the value offset of the Read Blob Request is greater than the // length of the attribute value, an Error Response shall be sent with // the error code Invalid Offset. if ( offset <= len ) { // If the value offset is equal than the length of the attribute // value, then the length of the part attribute value shall be zero. if ( offset == len ) { len = 0; } else { // If the attribute value is longer than (Value Offset + maxLen) // then maxLen octets from Value Offset shall be included in // this response. if ( len > ( offset + maxLen ) ) { len = maxLen; } else { len -= offset; } } *pLen = len; VOID osal_memcpy( pValue, &(pAttr->pValue[offset]), len ); } else { status = ATT_ERR_INVALID_OFFSET; } } break; case GATT_CHAR_FORMAT_UUID: // Make sure it's not a blob operation if ( offset == 0 ) { gattCharFormat_t *pFormat = (gattCharFormat_t *)(pAttr->pValue); *pLen = 7; pValue[0] = pFormat->format; pValue[1] = pFormat->exponent; pValue[2] = LO_UINT16( pFormat->unit ); pValue[3] = HI_UINT16( pFormat->unit ); pValue[4] = pFormat->nameSpace; pValue[5] = LO_UINT16( pFormat->desc ); pValue[6] = HI_UINT16( pFormat->desc ); } else { status = ATT_ERR_ATTR_NOT_LONG; } break; default: useCB = TRUE; break; } } else { useCB = TRUE; } if ( useCB == TRUE ) { // Use Service's read callback to process the request pfnGATTReadAttrCB_t pfnCB = gattServApp_FindReadAttrCB( service ); if ( pfnCB != NULL ) { // Read the attribute value status = (*pfnCB)( connHandle, pAttr, pValue, pLen, offset, maxLen ); } else { status = ATT_ERR_UNLIKELY; } } return ( status ); } /********************************************************************* * @fn GATTServApp_WriteAttr * * @brief Write attribute data * * @param connHandle - connection message was received on * @param handle - attribute handle * @param pValue - pointer to data to be written * @param len - length of data * @param offset - offset of the first octet to be written * * @return Success or Failure */ uint8 GATTServApp_WriteAttr( uint16 connHandle, uint16 handle, uint8 *pValue, uint16 len, uint16 offset ) { uint16 service; gattAttribute_t *pAttr; bStatus_t status; // Find the owner of the attribute pAttr = GATT_FindHandle( handle, &service ); if ( pAttr != NULL ) { // Find out the owner's callback functions pfnGATTWriteAttrCB_t pfnCB = gattServApp_FindWriteAttrCB( service ); if ( pfnCB != NULL ) { // Try to write the new value status = (*pfnCB)( connHandle, pAttr, pValue, len, offset ); } else { status = ATT_ERR_UNLIKELY; } } else { status = ATT_ERR_INVALID_HANDLE; } return ( status ); } /********************************************************************* * @fn GATTServApp_SetParamValue * * @brief Set a GATT Server Application Parameter value. Use this * function to change the default GATT parameter values. * * @param value - new param value * * @return void */ void GATTServApp_SetParamValue( uint16 value ) { #if defined ( TESTMODES ) paramValue = value; #else VOID value; #endif } /********************************************************************* * @fn GATTServApp_GetParamValue * * @brief Get a GATT Server Application Parameter value. * * @param none * * @return GATT Parameter value */ uint16 GATTServApp_GetParamValue( void ) { #if defined ( TESTMODES ) return ( paramValue ); #else return ( 0 ); #endif } /********************************************************************* * @fn GATTServApp_UpdateCharCfg * * @brief Update the Client Characteristic Configuration for a given * Client. * * Note: This API should only be called from the Bond Manager. * * @param connHandle - connection handle. * @param attrHandle - attribute handle. * @param value - characteristic configuration value (from NV). * * @return Success or Failure */ bStatus_t GATTServApp_UpdateCharCfg( uint16 connHandle, uint16 attrHandle, uint16 value ) { uint8 buf[2]; buf[0] = LO_UINT16( value ); buf[1] = HI_UINT16( value ); return ( GATTServApp_WriteAttr( connHandle, attrHandle, buf, 2, 0 ) ); } /********************************************************************* * @fn GATTServApp_SendServiceChangedInd * * @brief Send out a Service Changed Indication. * * @param connHandle - connection to use * @param taskId - task to be notified of confirmation * * @return SUCCESS: Indication was sent successfully. * FAILURE: Service Changed attribute not found. * INVALIDPARAMETER: Invalid connection handle or request field. * MSG_BUFFER_NOT_AVAIL: No HCI buffer is available. * bleNotConnected: Connection is down. * blePending: A confirmation is pending with this client. */ bStatus_t GATTServApp_SendServiceChangedInd( uint16 connHandle, uint8 taskId ) { uint16 value = GATTServApp_ReadCharCfg( connHandle, indCharCfg ); if ( value & GATT_CLIENT_CFG_INDICATE ) { return ( GATT_ServiceChangedInd( connHandle, taskId ) ); } return ( FAILURE ); } /********************************************************************* * @fn GATTServApp_InitCharCfg * * @brief Initialize the client characteristic configuration table. * * Note: Each client has its own instantiation of the Client * Characteristic Configuration. Reads/Writes of the Client * Characteristic Configuration only only affect the * configuration of that client. * * @param connHandle - connection handle (0xFFFF for all connections). * @param charCfgTbl - client characteristic configuration table. * * @return none */ void GATTServApp_InitCharCfg( uint16 connHandle, gattCharCfg_t *charCfgTbl ) { // Initialize Client Characteristic Configuration attributes if ( connHandle == INVALID_CONNHANDLE ) { for ( uint8 i = 0; i < GATT_MAX_NUM_CONN; i++ ) { charCfgTbl[i].connHandle = INVALID_CONNHANDLE; charCfgTbl[i].value = GATT_CFG_NO_OPERATION; } } else { gattCharCfg_t *pItem = gattServApp_FindCharCfgItem( connHandle, charCfgTbl ); if ( pItem != NULL ) { pItem->connHandle = INVALID_CONNHANDLE; pItem->value = GATT_CFG_NO_OPERATION; } } } /********************************************************************* * @fn GATTServApp_ReadCharCfg * * @brief Read the client characteristic configuration for a given * client. * * Note: Each client has its own instantiation of the Client * Characteristic Configuration. Reads of the Client * Characteristic Configuration only shows the configuration * for that client. * * @param connHandle - connection handle. * @param charCfgTbl - client characteristic configuration table. * * @return attribute value */ uint16 GATTServApp_ReadCharCfg( uint16 connHandle, gattCharCfg_t *charCfgTbl ) { gattCharCfg_t *pItem; pItem = gattServApp_FindCharCfgItem( connHandle, charCfgTbl ); if ( pItem != NULL ) { return ( (uint16)(pItem->value) ); } return ( (uint16)GATT_CFG_NO_OPERATION ); } /********************************************************************* * @fn GATTServApp_WriteCharCfg * * @brief Write the client characteristic configuration for a given * client. * * Note: Each client has its own instantiation of the Client * Characteristic Configuration. Writes of the Client * Characteristic Configuration only only affect the * configuration of that client. * * @param connHandle - connection handle. * @param charCfgTbl - client characteristic configuration table. * @param value - attribute new value. * * @return Success or Failure */ uint8 GATTServApp_WriteCharCfg( uint16 connHandle, gattCharCfg_t *charCfgTbl, uint16 value ) { gattCharCfg_t *pItem; pItem = gattServApp_FindCharCfgItem( connHandle, charCfgTbl ); if ( pItem == NULL ) { pItem = gattServApp_FindCharCfgItem( INVALID_CONNHANDLE, charCfgTbl ); if ( pItem == NULL ) { return ( ATT_ERR_INSUFFICIENT_RESOURCES ); } pItem->connHandle = connHandle; } // Write the new value for this client pItem->value = value; return ( SUCCESS ); } /********************************************************************* * @fn GATTServApp_ProcessCCCWriteReq * * @brief Process the client characteristic configuration * write request for a given client. * * @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 validCfg - valid configuration * * @return Success or Failure */ bStatus_t GATTServApp_ProcessCCCWriteReq( uint16 connHandle, gattAttribute_t *pAttr, uint8 *pValue, uint8 len, uint16 offset, uint16 validCfg ) { bStatus_t status = SUCCESS; // Validate the value if ( offset == 0 ) { if ( len == 2 ) { uint16 value = BUILD_UINT16( pValue[0], pValue[1] ); // Validate characteristic configuration bit field if ( ( value & ~validCfg ) == 0 ) // indicate and/or notify { // Write the value if it's changed if ( GATTServApp_ReadCharCfg( connHandle, (gattCharCfg_t *)(pAttr->pValue) ) != value ) { status = GATTServApp_WriteCharCfg( connHandle, (gattCharCfg_t *)(pAttr->pValue), value ); if ( status == SUCCESS ) { // Notify the application GATTServApp_SendCCCUpdatedEvent( connHandle, pAttr->handle, value ); } } } else { status = ATT_ERR_INVALID_VALUE; } } else { status = ATT_ERR_INVALID_VALUE_SIZE; } } else { status = ATT_ERR_ATTR_NOT_LONG; } return ( status ); } /********************************************************************* * @fn GATTServApp_ProcessCharCfg * * @brief Process Client Charateristic Configuration change. * * @param charCfgTbl - characteristic configuration table. * @param pValue - pointer to attribute value. * @param authenticated - whether an authenticated link is required. * @param attrTbl - attribute table. * @param numAttrs - number of attributes in attribute table. * @param taskId - task to be notified of confirmation. * * @return Success or Failure */ bStatus_t GATTServApp_ProcessCharCfg( gattCharCfg_t *charCfgTbl, uint8 *pValue, uint8 authenticated, gattAttribute_t *attrTbl, uint16 numAttrs, uint8 taskId ) { bStatus_t status = SUCCESS; for ( uint8 i = 0; i < GATT_MAX_NUM_CONN; i++ ) { gattCharCfg_t *pItem = &(charCfgTbl[i]); if ( ( pItem->connHandle != INVALID_CONNHANDLE ) && ( pItem->value != GATT_CFG_NO_OPERATION ) ) { gattAttribute_t *pAttr; // Find the characteristic value attribute pAttr = GATTServApp_FindAttr( attrTbl, numAttrs, pValue ); if ( pAttr != NULL ) { attHandleValueNoti_t noti; // If the attribute value is longer than (ATT_MTU - 3) octets, then // only the first (ATT_MTU - 3) octets of this attributes value can // be sent in a notification. if ( GATTServApp_ReadAttr( pItem->connHandle, pAttr, GATT_SERVICE_HANDLE( attrTbl ), noti.value, ¬i.len, 0, (g_ATT_MTU_SIZE-3) ) == SUCCESS ) { noti.handle = pAttr->handle; if ( pItem->value & GATT_CLIENT_CFG_NOTIFY ) { status |= GATT_Notification( pItem->connHandle, ¬i, authenticated ); } if ( pItem->value & GATT_CLIENT_CFG_INDICATE ) { status |= GATT_Indication( pItem->connHandle, (attHandleValueInd_t *)¬i, authenticated, taskId ); } } } } } // for return ( status ); } /********************************************************************* * @fn gattServApp_HandleConnStatusCB * * @brief GATT Server Application link status change handler function. * * @param connHandle - connection handle * @param changeType - type of change * * @return none */ static void gattServApp_HandleConnStatusCB( uint16 connHandle, uint8 changeType ) { // Check to see if the connection has dropped if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED ) || ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) && ( !linkDB_Up( connHandle ) ) ) ) { prepareWrites_t *pQueue = gattServApp_FindPrepareWriteQ( connHandle ); // See if this client has a prepare write queue if ( pQueue != NULL ) { for ( uint8 i = 0; i < maxNumPrepareWrites; i++ ) { attPrepareWriteReq_t *pWriteReq = &(pQueue->pPrepareWriteQ[i]); // See if there're any prepared write requests in the queue if ( pWriteReq->handle == GATT_INVALID_HANDLE ) { break; } // Clear the queue item VOID osal_memset( pWriteReq, 0, sizeof( attPrepareWriteRsp_t ) ); } // for loop // Mark this queue as empty pQueue->connHandle = INVALID_CONNHANDLE; } // Reset Client Char Config when connection drops GATTServApp_InitCharCfg( connHandle, indCharCfg ); } } /********************************************************************* * @fn GATTServApp_SendCCCUpdatedEvent * * @brief Build and send the GATT_CLIENT_CHAR_CFG_UPDATED_EVENT to * the app. * * @param connHandle - connection handle * @param attrHandle - attribute handle * @param value - attribute new value * * @return none */ void GATTServApp_SendCCCUpdatedEvent( uint16 connHandle, uint16 attrHandle, uint16 value ) { if ( appTaskID != INVALID_TASK_ID ) { // Allocate, build and send event gattClientCharCfgUpdatedEvent_t *pEvent = (gattClientCharCfgUpdatedEvent_t *)osal_msg_allocate( (uint16)(sizeof ( gattClientCharCfgUpdatedEvent_t )) ); if ( pEvent ) { pEvent->hdr.event = GATT_SERV_MSG_EVENT; pEvent->hdr.status = SUCCESS; pEvent->method = GATT_CLIENT_CHAR_CFG_UPDATED_EVENT; pEvent->connHandle = connHandle; pEvent->attrHandle = attrHandle; pEvent->value = value; VOID osal_msg_send( appTaskID, (uint8 *)pEvent ); } } } bStatus_t gattServApp_RegisterCB(gattServMsgCB_t cb) { s_GATTServCB = cb; return SUCCESS; } #endif // ( CENTRAL_CFG | PERIPHERAL_CFG ) /**************************************************************************** ****************************************************************************/