test

Fork of nRF51822 by Nordic Semiconductor

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers nRF5xGattServer.cpp Source File

nRF5xGattServer.cpp

00001 /* mbed Microcontroller Library
00002  * Copyright (c) 2006-2013 ARM Limited
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #include "nRF5xGattServer.h"
00018 #include "mbed.h"
00019 
00020 #include "common/common.h"
00021 #include "btle/custom/custom_helper.h"
00022 
00023 #include "nRF5xGap.h"
00024 
00025 nRF5xGattServer &nRF5xGattServer::getInstance(void) {
00026     static nRF5xGattServer m_instance;
00027     return m_instance;
00028 }
00029 
00030 /**************************************************************************/
00031 /*!
00032     @brief  Adds a new service to the GATT table on the peripheral
00033 
00034     @returns    ble_error_t
00035 
00036     @retval     BLE_ERROR_NONE
00037                 Everything executed properly
00038 
00039     @section EXAMPLE
00040 
00041     @code
00042 
00043     @endcode
00044 */
00045 /**************************************************************************/
00046 ble_error_t nRF5xGattServer::addService(GattService &service)
00047 {
00048     /* ToDo: Make sure we don't overflow the array, etc. */
00049     /* ToDo: Make sure this service UUID doesn't already exist (?) */
00050     /* ToDo: Basic validation */
00051 
00052     /* Add the service to the nRF51 */
00053     ble_uuid_t nordicUUID;
00054     nordicUUID = custom_convert_to_nordic_uuid(service.getUUID());
00055 
00056     uint16_t serviceHandle;
00057     ASSERT( ERROR_NONE ==
00058             sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
00059                                      &nordicUUID,
00060                                      &serviceHandle),
00061             BLE_ERROR_PARAM_OUT_OF_RANGE );
00062     service.setHandle(serviceHandle);
00063 
00064     /* Add characteristics to the service */
00065     for (uint8_t i = 0; i < service.getCharacteristicCount(); i++) {
00066         GattCharacteristic *p_char = service.getCharacteristic(i);
00067 
00068         /* Skip any incompletely defined, read-only characteristics. */
00069         if ((p_char->getValueAttribute().getValuePtr() == NULL) &&
00070             (p_char->getValueAttribute().getInitialLength() == 0) &&
00071             (p_char->getProperties() == GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ)) {
00072             continue;
00073         }
00074 
00075         nordicUUID = custom_convert_to_nordic_uuid(p_char->getValueAttribute().getUUID());
00076 
00077         /* The user-description descriptor is a special case which needs to be
00078          * handled at the time of adding the characteristic. The following block
00079          * is meant to discover its presence. */
00080         const uint8_t *userDescriptionDescriptorValuePtr = NULL;
00081         uint16_t userDescriptionDescriptorValueLen = 0;
00082         for (uint8_t j = 0; j < p_char->getDescriptorCount(); j++) {
00083             GattAttribute *p_desc = p_char->getDescriptor(j);
00084             if (p_desc->getUUID() == BLE_UUID_DESCRIPTOR_CHAR_USER_DESC) {
00085                 userDescriptionDescriptorValuePtr = p_desc->getValuePtr();
00086                 userDescriptionDescriptorValueLen = p_desc->getLength();
00087             }
00088         }
00089 
00090         ASSERT ( ERROR_NONE ==
00091                  custom_add_in_characteristic(BLE_GATT_HANDLE_INVALID,
00092                                               &nordicUUID,
00093                                               p_char->getProperties(),
00094                                               p_char->getRequiredSecurity(),
00095                                               p_char->getValueAttribute().getValuePtr(),
00096                                               p_char->getValueAttribute().getInitialLength(),
00097                                               p_char->getValueAttribute().getMaxLength(),
00098                                               userDescriptionDescriptorValuePtr,
00099                                               userDescriptionDescriptorValueLen,
00100                                               p_char->isReadAuthorizationEnabled(),
00101                                               p_char->isWriteAuthorizationEnabled(),
00102                                               &nrfCharacteristicHandles[characteristicCount]),
00103                  BLE_ERROR_PARAM_OUT_OF_RANGE );
00104 
00105         /* Update the characteristic handle */
00106         p_characteristics[characteristicCount] = p_char;
00107         p_char->getValueAttribute().setHandle(nrfCharacteristicHandles[characteristicCount].value_handle);
00108         characteristicCount++;
00109 
00110         /* Add optional descriptors if any */
00111         /* ToDo: Make sure we don't overflow the array */
00112         for (uint8_t j = 0; j < p_char->getDescriptorCount(); j++) {
00113             GattAttribute *p_desc = p_char->getDescriptor(j);
00114             /* skip the user-description-descriptor here; this has already been handled when adding the characteristic (above). */
00115             if (p_desc->getUUID() == BLE_UUID_DESCRIPTOR_CHAR_USER_DESC) {
00116                 continue;
00117             }
00118 
00119             nordicUUID = custom_convert_to_nordic_uuid(p_desc->getUUID());
00120 
00121             ASSERT(ERROR_NONE ==
00122                    custom_add_in_descriptor(BLE_GATT_HANDLE_INVALID,
00123                                             &nordicUUID,
00124                                             p_desc->getValuePtr(),
00125                                             p_desc->getInitialLength(),
00126                                             p_desc->getMaxLength(),
00127                                             &nrfDescriptorHandles[descriptorCount]),
00128                 BLE_ERROR_PARAM_OUT_OF_RANGE);
00129 
00130             p_descriptors[descriptorCount++] = p_desc;
00131             p_desc->setHandle(nrfDescriptorHandles[descriptorCount]);
00132         }
00133     }
00134 
00135     serviceCount++;
00136 
00137     return BLE_ERROR_NONE;
00138 }
00139 
00140 /**************************************************************************/
00141 /*!
00142     @brief  Reads the value of a characteristic, based on the service
00143             and characteristic index fields
00144 
00145     @param[in]  attributeHandle
00146                 The handle of the GattCharacteristic to read from
00147     @param[in]  buffer
00148                 Buffer to hold the the characteristic's value
00149                 (raw byte array in LSB format)
00150     @param[in/out] len
00151                 input:  Length in bytes to be read.
00152                 output: Total length of attribute value upon successful return.
00153 
00154     @returns    ble_error_t
00155 
00156     @retval     BLE_ERROR_NONE
00157                 Everything executed properly
00158 */
00159 /**************************************************************************/
00160 ble_error_t nRF5xGattServer::read(GattAttribute::Handle_t attributeHandle, uint8_t buffer[], uint16_t *lengthP)
00161 {
00162     return read(BLE_CONN_HANDLE_INVALID, attributeHandle, buffer, lengthP);
00163 }
00164 
00165 ble_error_t nRF5xGattServer::read(Gap::Handle_t connectionHandle, GattAttribute::Handle_t attributeHandle, uint8_t buffer[], uint16_t *lengthP)
00166 {
00167     ble_gatts_value_t value = {
00168         .len     = *lengthP,
00169         .offset  = 0,
00170         .p_value = buffer,
00171     };
00172 
00173     ASSERT( ERROR_NONE ==
00174             sd_ble_gatts_value_get(connectionHandle, attributeHandle, &value),
00175             BLE_ERROR_PARAM_OUT_OF_RANGE);
00176     *lengthP = value.len;
00177 
00178     return BLE_ERROR_NONE;
00179 }
00180 
00181 /**************************************************************************/
00182 /*!
00183     @brief  Updates the value of a characteristic, based on the service
00184             and characteristic index fields
00185 
00186     @param[in]  charHandle
00187                 The handle of the GattCharacteristic to write to
00188     @param[in]  buffer
00189                 Data to use when updating the characteristic's value
00190                 (raw byte array in LSB format)
00191     @param[in]  len
00192                 The number of bytes in buffer
00193 
00194     @returns    ble_error_t
00195 
00196     @retval     BLE_ERROR_NONE
00197                 Everything executed properly
00198 */
00199 /**************************************************************************/
00200 ble_error_t nRF5xGattServer::write(GattAttribute::Handle_t attributeHandle, const uint8_t buffer[], uint16_t len, bool localOnly)
00201 {
00202     return write(BLE_CONN_HANDLE_INVALID, attributeHandle, buffer, len, localOnly);
00203 }
00204 
00205 ble_error_t nRF5xGattServer::write(Gap::Handle_t connectionHandle, GattAttribute::Handle_t attributeHandle, const uint8_t buffer[], uint16_t len, bool localOnly)
00206 {
00207     ble_error_t returnValue = BLE_ERROR_NONE;
00208 
00209     ble_gatts_value_t value = {
00210         .len     = len,
00211         .offset  = 0,
00212         .p_value = const_cast<uint8_t *>(buffer),
00213     };
00214 
00215     if (localOnly) {
00216         /* Only update locally regardless of notify/indicate */
00217         ASSERT_INT( ERROR_NONE,
00218                     sd_ble_gatts_value_set(connectionHandle, attributeHandle, &value),
00219                     BLE_ERROR_PARAM_OUT_OF_RANGE );
00220         return BLE_ERROR_NONE;
00221     }
00222 
00223     int characteristicIndex = resolveValueHandleToCharIndex(attributeHandle);
00224     if ((characteristicIndex != -1) &&
00225         (p_characteristics[characteristicIndex]->getProperties() & (GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY))) {
00226         /* HVX update for the characteristic value */
00227         ble_gatts_hvx_params_t hvx_params;
00228 
00229         hvx_params.handle = attributeHandle;
00230         hvx_params.type   =
00231             (p_characteristics[characteristicIndex]->getProperties() & GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY) ? BLE_GATT_HVX_NOTIFICATION : BLE_GATT_HVX_INDICATION;
00232         hvx_params.offset = 0;
00233         hvx_params.p_data = const_cast<uint8_t *>(buffer);
00234         hvx_params.p_len  = &len;
00235 
00236         if (connectionHandle == BLE_CONN_HANDLE_INVALID) { /* use the default connection handle if the caller hasn't specified a valid connectionHandle. */
00237             connectionHandle = nRF5xGap::getInstance().getConnectionHandle();
00238         }
00239         error_t error = (error_t) sd_ble_gatts_hvx(connectionHandle, &hvx_params);
00240         if (error != ERROR_NONE) {
00241             switch (error) {
00242                 case ERROR_BLE_NO_TX_BUFFERS: /*  Notifications consume application buffers. The return value can be used for resending notifications. */
00243                 case ERROR_BUSY:
00244                     returnValue = BLE_STACK_BUSY;
00245                     break;
00246 
00247                 case ERROR_INVALID_STATE:
00248                 case ERROR_BLEGATTS_SYS_ATTR_MISSING:
00249                     returnValue = BLE_ERROR_INVALID_STATE;
00250                     break;
00251 
00252                 default :
00253                     ASSERT_INT( ERROR_NONE,
00254                                 sd_ble_gatts_value_set(connectionHandle, attributeHandle, &value),
00255                                 BLE_ERROR_PARAM_OUT_OF_RANGE );
00256 
00257                     /* Notifications consume application buffers. The return value can
00258                      * be used for resending notifications. */
00259                     returnValue = BLE_STACK_BUSY;
00260                     break;
00261             }
00262         }
00263     } else {
00264         returnValue = BLE_ERROR_INVALID_STATE; // if assert is not used
00265         ASSERT_INT( ERROR_NONE,
00266                     sd_ble_gatts_value_set(connectionHandle, attributeHandle, &value),
00267                     BLE_ERROR_PARAM_OUT_OF_RANGE );
00268     }
00269 
00270     return returnValue;
00271 }
00272 
00273 ble_error_t nRF5xGattServer::areUpdatesEnabled(const GattCharacteristic &characteristic, bool *enabledP)
00274 {
00275     /* Forward the call with the default connection handle. */
00276     return areUpdatesEnabled(nRF5xGap::getInstance().getConnectionHandle(), characteristic, enabledP);
00277 }
00278 
00279 ble_error_t nRF5xGattServer::areUpdatesEnabled(Gap::Handle_t connectionHandle, const GattCharacteristic &characteristic, bool *enabledP)
00280 {
00281     int characteristicIndex = resolveValueHandleToCharIndex(characteristic.getValueHandle());
00282     if (characteristicIndex == -1) {
00283         return BLE_ERROR_INVALID_PARAM;
00284     }
00285 
00286     /* Read the cccd value from the GATT server. */
00287     GattAttribute::Handle_t cccdHandle = nrfCharacteristicHandles[characteristicIndex].cccd_handle;
00288     uint16_t cccdValue;
00289     uint16_t length = sizeof(cccdValue);
00290     ble_error_t rc = read(connectionHandle, cccdHandle, reinterpret_cast<uint8_t *>(&cccdValue), &length);
00291     if (rc != BLE_ERROR_NONE) {
00292         return rc;
00293     }
00294     if (length != sizeof(cccdValue)) {
00295         return BLE_ERROR_INVALID_STATE;
00296     }
00297 
00298     /* Check for NOTFICATION or INDICATION in CCCD. */
00299     if ((cccdValue & BLE_GATT_HVX_NOTIFICATION) || (cccdValue & BLE_GATT_HVX_INDICATION)) {
00300         *enabledP = true;
00301     }
00302 
00303     return BLE_ERROR_NONE;
00304 }
00305 
00306 /**************************************************************************/
00307 /*!
00308     @brief  Callback handler for events getting pushed up from the SD
00309 */
00310 /**************************************************************************/
00311 void nRF5xGattServer::hwCallback(ble_evt_t *p_ble_evt)
00312 {
00313     GattAttribute::Handle_t        handle_value;
00314     GattServerEvents::gattEvent_t  eventType;
00315     const ble_gatts_evt_t         *gattsEventP = &p_ble_evt->evt.gatts_evt;
00316 
00317     switch (p_ble_evt->header.evt_id) {
00318         case BLE_GATTS_EVT_WRITE: {
00319                 /* There are 2 use case here: Values being updated & CCCD (indicate/notify) enabled */
00320 
00321                 /* 1.) Handle CCCD changes */
00322                 handle_value = gattsEventP->params.write.handle;
00323                 int characteristicIndex = resolveCCCDHandleToCharIndex(handle_value);
00324                 if ((characteristicIndex != -1) &&
00325                     (p_characteristics[characteristicIndex]->getProperties() &
00326                         (GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY))) {
00327 
00328                     uint16_t cccd_value = (gattsEventP->params.write.data[1] << 8) | gattsEventP->params.write.data[0]; /* Little Endian but M0 may be mis-aligned */
00329 
00330                     if (((p_characteristics[characteristicIndex]->getProperties() & GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE) && (cccd_value & BLE_GATT_HVX_INDICATION)) ||
00331                         ((p_characteristics[characteristicIndex]->getProperties() & GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY) && (cccd_value & BLE_GATT_HVX_NOTIFICATION))) {
00332                         eventType = GattServerEvents::GATT_EVENT_UPDATES_ENABLED;
00333                     } else {
00334                         eventType = GattServerEvents::GATT_EVENT_UPDATES_DISABLED;
00335                     }
00336 
00337                     handleEvent(eventType, p_characteristics[characteristicIndex]->getValueHandle());
00338                     return;
00339                 }
00340 
00341                 /* 2.) Changes to the characteristic value will be handled with other events below */
00342                 eventType = GattServerEvents::GATT_EVENT_DATA_WRITTEN;
00343             }
00344             break;
00345 
00346         case BLE_GATTS_EVT_HVC:
00347             /* Indication confirmation received */
00348             eventType    = GattServerEvents::GATT_EVENT_CONFIRMATION_RECEIVED;
00349             handle_value = gattsEventP->params.hvc.handle;
00350             break;
00351 
00352         case BLE_EVT_TX_COMPLETE: {
00353             handleDataSentEvent(p_ble_evt->evt.common_evt.params.tx_complete.count);
00354             return;
00355         }
00356 
00357         case BLE_GATTS_EVT_SYS_ATTR_MISSING:
00358             sd_ble_gatts_sys_attr_set(gattsEventP->conn_handle, NULL, 0, 0);
00359             return;
00360 
00361         case BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST:
00362             switch (gattsEventP->params.authorize_request.type) {
00363                 case BLE_GATTS_AUTHORIZE_TYPE_READ:
00364                     eventType    = GattServerEvents::GATT_EVENT_READ_AUTHORIZATION_REQ;
00365                     handle_value = gattsEventP->params.authorize_request.request.read.handle;
00366                     break;
00367                 case BLE_GATTS_AUTHORIZE_TYPE_WRITE:
00368                     eventType    = GattServerEvents::GATT_EVENT_WRITE_AUTHORIZATION_REQ;
00369                     handle_value = gattsEventP->params.authorize_request.request.write.handle;
00370                     break;
00371                 default:
00372                     return;
00373             }
00374             break;
00375 
00376         default:
00377             return;
00378     }
00379 
00380     int characteristicIndex = resolveValueHandleToCharIndex(handle_value);
00381     if (characteristicIndex == -1) {
00382         return;
00383     }
00384 
00385     /* Find index (charHandle) in the pool */
00386     switch (eventType) {
00387         case GattServerEvents::GATT_EVENT_DATA_WRITTEN: {
00388             GattWriteCallbackParams cbParams = {
00389                 .connHandle = gattsEventP->conn_handle,
00390                 .handle     = handle_value,
00391                 .writeOp    = static_cast<GattWriteCallbackParams::WriteOp_t>(gattsEventP->params.write.op),
00392                 .offset     = gattsEventP->params.write.offset,
00393                 .len        = gattsEventP->params.write.len,
00394                 .data       = gattsEventP->params.write.data
00395             };
00396             handleDataWrittenEvent(&cbParams);
00397             break;
00398         }
00399         case GattServerEvents::GATT_EVENT_WRITE_AUTHORIZATION_REQ: {
00400             GattWriteAuthCallbackParams cbParams = {
00401                 .connHandle = gattsEventP->conn_handle,
00402                 .handle     = handle_value,
00403                 .offset     = gattsEventP->params.authorize_request.request.write.offset,
00404                 .len        = gattsEventP->params.authorize_request.request.write.len,
00405                 .data       = gattsEventP->params.authorize_request.request.write.data,
00406                 .authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS /* the callback handler must leave this member
00407                                                                    * set to AUTH_CALLBACK_REPLY_SUCCESS if the client
00408                                                                    * request is to proceed. */
00409             };
00410             ble_gatts_rw_authorize_reply_params_t reply = {
00411                 .type = BLE_GATTS_AUTHORIZE_TYPE_WRITE,
00412                 .params = {
00413                     .write = {
00414                         .gatt_status = p_characteristics[characteristicIndex]->authorizeWrite(&cbParams)
00415                     }
00416                 }
00417             };
00418             sd_ble_gatts_rw_authorize_reply(gattsEventP->conn_handle, &reply);
00419 
00420             /*
00421              * If write-authorization is enabled for a characteristic,
00422              * AUTHORIZATION_REQ event (if replied with true) is *not*
00423              * followed by another DATA_WRITTEN event; so we still need
00424              * to invoke handleDataWritten(), much the same as we would
00425              * have done if write-authorization had not been enabled.
00426              */
00427             if (reply.params.write.gatt_status == BLE_GATT_STATUS_SUCCESS) {
00428                 GattWriteCallbackParams cbParams = {
00429                     .connHandle = gattsEventP->conn_handle,
00430                     .handle     = handle_value,
00431                     .writeOp    = static_cast<GattWriteCallbackParams::WriteOp_t>(gattsEventP->params.authorize_request.request.write.op),
00432                     .offset     = gattsEventP->params.authorize_request.request.write.offset,
00433                     .len        = gattsEventP->params.authorize_request.request.write.len,
00434                     .data       = gattsEventP->params.authorize_request.request.write.data,
00435                 };
00436                 handleDataWrittenEvent(&cbParams);
00437             }
00438             break;
00439         }
00440         case GattServerEvents::GATT_EVENT_READ_AUTHORIZATION_REQ: {
00441             GattReadAuthCallbackParams cbParams = {
00442                 .connHandle         = gattsEventP->conn_handle,
00443                 .handle             = handle_value,
00444                 .offset             = gattsEventP->params.authorize_request.request.read.offset,
00445                 .len                = 0,
00446                 .data               = NULL,
00447                 .authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS /* the callback handler must leave this member
00448                                                                    * set to AUTH_CALLBACK_REPLY_SUCCESS if the client
00449                                                                    * request is to proceed. */
00450             };
00451 
00452             ble_gatts_rw_authorize_reply_params_t reply = {
00453                 .type = BLE_GATTS_AUTHORIZE_TYPE_READ,
00454                 .params = {
00455                     .read = {
00456                         .gatt_status = p_characteristics[characteristicIndex]->authorizeRead(&cbParams)
00457                     }
00458                 }
00459             };
00460 
00461             if (cbParams.authorizationReply == BLE_GATT_STATUS_SUCCESS) {
00462                 if (cbParams.data != NULL) {
00463                     reply.params.read.update = 1;
00464                     reply.params.read.offset = cbParams.offset;
00465                     reply.params.read.len    = cbParams.len;
00466                     reply.params.read.p_data = cbParams.data;
00467                 }
00468             }
00469 
00470             sd_ble_gatts_rw_authorize_reply(gattsEventP->conn_handle, &reply);
00471             break;
00472         }
00473 
00474         default:
00475             handleEvent(eventType, handle_value);
00476             break;
00477     }
00478 }