Maxim Integrated / MaximBLE

Dependents:   BLE_Thermometer MAXWSNENV_demo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MaximGattServer.cpp Source File

MaximGattServer.cpp

00001 /*******************************************************************************
00002  * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a
00005  * copy of this software and associated documentation files (the "Software"),
00006  * to deal in the Software without restriction, including without limitation
00007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00008  * and/or sell copies of the Software, and to permit persons to whom the
00009  * Software is furnished to do so, subject to the following conditions:
00010  *
00011  * The above copyright notice and this permission notice shall be included
00012  * in all copies or substantial portions of the Software.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
00015  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00016  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
00017  * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
00018  * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00020  * OTHER DEALINGS IN THE SOFTWARE.
00021  *
00022  * Except as contained in this notice, the name of Maxim Integrated
00023  * Products, Inc. shall not be used except as stated in the Maxim Integrated
00024  * Products, Inc. Branding Policy.
00025  *
00026  * The mere transfer of this software does not imply any licenses
00027  * of trade secrets, proprietary technology, copyrights, patents,
00028  * trademarks, maskwork rights, or any other form of intellectual
00029  * property whatsoever. Maxim Integrated Products, Inc. retains all
00030  * ownership rights.
00031  *******************************************************************************
00032  */
00033 
00034 #include "MaximGattServer.h"
00035 #include "mbed.h"
00036 #include "MaximGap.h"
00037 #include "wsf_types.h"
00038 #include "att_api.h"
00039 
00040 typedef struct mxmChar_s {
00041     uint16_t descLen;
00042     mxmChar_s() {}
00043 } mxmChar_t;
00044 
00045 typedef struct mxmService_s mxmService_t;
00046 struct mxmService_s {
00047     uint16_t uuidLen;
00048     mxmChar_t *chars;
00049     attsGroup_t  *attGroup;
00050     mxmService_t *next;
00051     mxmService_s() {}
00052 };
00053 
00054 static uint16_t currentHandle = 0x20;
00055 
00056 static UUID cccUUID(BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG);
00057 static const uint16_t cccSize = sizeof(uint16_t);
00058 
00059 MaximGattServer &MaximGattServer::getInstance() {
00060     static MaximGattServer m_instance;
00061     return m_instance;
00062 }
00063 
00064 ble_error_t MaximGattServer::addService(GattService &service)
00065 {
00066     currentHandle = (currentHandle + 0xF) & ~0xF;
00067     uint16_t startHandle = currentHandle;
00068 
00069     mxmService_t *mxmSvc = new mxmService_t;
00070 
00071     // Create WiCentric attribute group
00072     mxmSvc->attGroup = new attsGroup_t ;
00073 
00074     // Determine the attribute list length
00075     unsigned int attListLen = 1;
00076     for (int i = 0; i < service.getCharacteristicCount(); i++) {
00077         attListLen += 2;
00078         GattCharacteristic *p_char = service.getCharacteristic(i);
00079 
00080         if (p_char->getProperties() & (GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE)) {
00081             // add a CCCD
00082             attListLen++;
00083         }
00084     }
00085 
00086     // Create WiCentric attribute list
00087     mxmSvc->attGroup->pAttr  = (attsAttr_t *)malloc(attListLen * sizeof(attsAttr_t ));;
00088     if (mxmSvc->attGroup->pAttr == NULL) {
00089         return BLE_ERROR_BUFFER_OVERFLOW;
00090     }
00091 
00092     // Create characteristics
00093     mxmSvc->chars = new mxmChar_t [service.getCharacteristicCount()];
00094 
00095     attsAttr_t  *currAtt = mxmSvc->attGroup->pAttr;
00096 
00097     /* Service */
00098     currAtt->pUuid = attPrimSvcUuid ;
00099     if (service.getUUID().shortOrLong() == UUID::UUID_TYPE_LONG) {
00100         mxmSvc->uuidLen = UUID::LENGTH_OF_LONG_UUID;
00101     } else {
00102         mxmSvc->uuidLen = sizeof(UUID::ShortUUIDBytes_t);
00103     }
00104     currAtt->pValue  = (uint8_t*)malloc(mxmSvc->uuidLen);
00105     memcpy(currAtt->pValue , service.getUUID().getBaseUUID(), mxmSvc->uuidLen);
00106     currAtt->maxLen  = mxmSvc->uuidLen;
00107     currAtt->pLen  = &mxmSvc->uuidLen;
00108     currAtt->settings  = 0;
00109     currAtt->permissions  = ATTS_PERMIT_READ;
00110 
00111     currAtt++;
00112 
00113     /* Add characteristics to the service */
00114     for (int i = 0; i < service.getCharacteristicCount(); i++) {
00115         GattCharacteristic *p_char = service.getCharacteristic(i);
00116 
00117         /* Skip any incompletely defined, read-only characteristics. */
00118         if ((p_char->getValueAttribute().getValuePtr() == NULL) &&
00119             (p_char->getValueAttribute().getLength() == 0) &&
00120             (p_char->getProperties() == GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ)) {
00121             continue;
00122         }
00123 
00124         // Create Characteristic Attribute
00125         currentHandle += 2;
00126         currAtt->pUuid = attChUuid ;
00127 
00128         p_char->getValueAttribute().setHandle(currentHandle);
00129         mxmSvc->chars[i].descLen = 1 + sizeof(currentHandle) + p_char->getValueAttribute().getUUID().getLen();
00130         currAtt->pValue  = (uint8_t*)malloc(mxmSvc->chars[i].descLen);
00131         uint8_t *pValue = currAtt->pValue ;
00132         *pValue++ = p_char->getProperties();
00133         memcpy(pValue, &currentHandle, sizeof(currentHandle));
00134         pValue += sizeof(currentHandle);
00135         memcpy(pValue, p_char->getValueAttribute().getUUID().getBaseUUID(), p_char->getValueAttribute().getUUID().getLen());
00136 
00137         currAtt->pLen  = &mxmSvc->chars[i].descLen;
00138         currAtt->maxLen  = mxmSvc->chars[i].descLen;
00139         currAtt->settings  = 0;
00140         currAtt->permissions  = ATTS_PERMIT_READ;
00141         currAtt++;
00142 
00143         // Create Value Attribute
00144         currAtt->pUuid = p_char->getValueAttribute().getUUID().getBaseUUID();
00145         currAtt->pValue  = p_char->getValueAttribute().getValuePtr();
00146         currAtt->pLen  = p_char->getValueAttribute().getLengthPtr();
00147         currAtt->maxLen  = p_char->getValueAttribute().getMaxLength();
00148         currAtt->settings  = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK;
00149         if (p_char->getValueAttribute().getUUID().shortOrLong() == UUID::UUID_TYPE_LONG) {
00150             currAtt->settings  |= ATTS_SET_UUID_128;
00151         }
00152         currAtt->permissions  = 0;
00153         if (p_char->getProperties() & GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ)  { currAtt->permissions  |= ATTS_PERMIT_READ; }
00154         if (p_char->getProperties() & GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE) { currAtt->permissions  |= ATTS_PERMIT_WRITE; }
00155         currAtt++;
00156 
00157         bool cccCreated = false;
00158 
00159         for (int i = 0; i < p_char->getDescriptorCount(); i++) {
00160             GattAttribute *p_att = p_char->getDescriptor(i);
00161 
00162             currentHandle++;
00163 
00164             currAtt->pUuid = p_att->getUUID().getBaseUUID();
00165             currAtt->pValue  = p_att->getValuePtr();
00166             currAtt->pLen  = p_att->getLengthPtr();
00167             currAtt->maxLen  = p_att->getMaxLength();
00168             currAtt->settings  = 0;
00169             currAtt->permissions  = 0;
00170             if (p_att->getUUID().shortOrLong() == UUID::UUID_TYPE_LONG) {
00171                 currAtt->settings  |= ATTS_SET_UUID_128;
00172             }
00173             if (p_att->getUUID() == UUID(BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG)) {
00174                 cccCreated = true;
00175                 currAtt->settings  |= ATTS_SET_CCC;
00176                 currAtt->permissions  |= ATTS_PERMIT_READ;
00177                 currAtt->permissions  |= ATTS_PERMIT_WRITE;
00178 
00179                 if (cccCnt < MAX_CCC_CNT) {
00180                     cccSet[cccCnt].handle = currentHandle;
00181                     cccSet[cccCnt].valueRange = 0;
00182                     if (p_char->getProperties() & GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY) {
00183                         cccSet[cccCnt].valueRange |= ATT_CLIENT_CFG_NOTIFY;
00184                     }
00185                     if (p_char->getProperties() & GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE) {
00186                         cccSet[cccCnt].valueRange |= ATT_CLIENT_CFG_INDICATE;
00187                     }
00188                     cccSet[cccCnt].secLevel = DM_SEC_LEVEL_NONE;
00189                     cccHandles[cccCnt] = p_char->getValueAttribute().getHandle();
00190                     cccCnt++;
00191                 } else {
00192                     return BLE_ERROR_PARAM_OUT_OF_RANGE;
00193                 }
00194             }
00195             currAtt++;
00196         }
00197 
00198         if (!cccCreated && (p_char->getProperties() & (GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE))) {
00199             /* There was not a CCCD included in the descriptors, but this
00200              * characteristic is notifiable and/or indicatable. A CCCD is
00201              * required so create one now.
00202              */
00203             if (cccCnt >= MAX_CCC_CNT) {
00204                 return BLE_ERROR_PARAM_OUT_OF_RANGE;
00205             }
00206 
00207             currentHandle++;
00208 
00209             currAtt->pUuid = cccUUID.getBaseUUID();
00210             currAtt->pValue  = (uint8_t*)&cccValues[cccCnt];
00211             currAtt->pLen  = (uint16_t*)&cccSize;
00212             currAtt->maxLen  = sizeof(uint16_t);
00213             currAtt->settings  = ATTS_SET_CCC;
00214             currAtt->permissions  = (ATTS_PERMIT_READ | ATTS_PERMIT_WRITE);
00215 
00216             cccSet[cccCnt].handle = currentHandle;
00217             cccSet[cccCnt].valueRange = 0;
00218             if (p_char->getProperties() & GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY) {
00219                 cccSet[cccCnt].valueRange |= ATT_CLIENT_CFG_NOTIFY;
00220             }
00221             if (p_char->getProperties() & GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE) {
00222                 cccSet[cccCnt].valueRange |= ATT_CLIENT_CFG_INDICATE;
00223             }
00224             cccSet[cccCnt].secLevel = DM_SEC_LEVEL_NONE;
00225             cccHandles[cccCnt] = p_char->getValueAttribute().getHandle();
00226 
00227             cccCnt++;
00228             currAtt++;
00229         }
00230     }
00231 
00232     mxmSvc->attGroup->pNext = NULL;
00233     mxmSvc->attGroup->readCback = attsReadCback;
00234     mxmSvc->attGroup->writeCback = attsWriteCback;
00235     mxmSvc->attGroup->startHandle = startHandle;
00236     mxmSvc->attGroup->endHandle = currentHandle;
00237     AttsAddGroup(mxmSvc->attGroup);
00238 
00239     AttRegister(attCback);
00240     AttsCccRegister(cccCnt, (attsCccSet_t *)cccSet, cccCback);
00241 
00242     return BLE_ERROR_NONE;
00243 }
00244 
00245 ble_error_t MaximGattServer::read(GattAttribute::Handle_t attributeHandle, uint8_t buffer[], uint16_t *const lengthP)
00246 {
00247     if (AttsGetAttr(attributeHandle, lengthP, &buffer) != ATT_SUCCESS) {
00248         return BLE_ERROR_PARAM_OUT_OF_RANGE;
00249     }
00250 
00251     return BLE_ERROR_NONE;
00252 }
00253 
00254 ble_error_t MaximGattServer::read(Gap::Handle_t connectionHandle, GattAttribute::Handle_t attributeHandle, uint8_t buffer[], uint16_t *lengthP)
00255 {
00256     // Check to see if this is a CCCD
00257     uint8_t idx;
00258     for (idx = 0; idx < cccCnt; idx++) {
00259         if (attributeHandle == cccSet[idx].handle) {
00260             if (connectionHandle == DM_CONN_ID_NONE) { // CCCDs are always 16 bits
00261                 return BLE_ERROR_PARAM_OUT_OF_RANGE;
00262             }
00263             *((uint16_t*)buffer) = AttsCccGet(connectionHandle, idx);
00264             *lengthP = 2;   // CCCDs are always 16 bits
00265             return BLE_ERROR_NONE;
00266         }
00267     }
00268 
00269     // This is not a CCCD. Use the non-connection specific update method.
00270     return read(attributeHandle, buffer, lengthP);
00271 }
00272 
00273 ble_error_t MaximGattServer::write(GattAttribute::Handle_t attributeHandle, const uint8_t buffer[], uint16_t len, bool localOnly)
00274 {
00275     uint16_t connectionHandle = MaximGap::getInstance().getConnectionHandle();
00276 
00277     if (AttsSetAttr(attributeHandle, len, (uint8_t*)buffer) != ATT_SUCCESS) {
00278         return BLE_ERROR_PARAM_OUT_OF_RANGE;
00279     }
00280 
00281     if (!localOnly) {
00282         if (connectionHandle != DM_CONN_ID_NONE) {
00283 
00284             // Check to see if this characteristic has a CCCD attribute
00285             uint8_t idx;
00286             for (idx = 0; idx < cccCnt; idx++) {
00287                 if (attributeHandle == cccHandles[idx]) {
00288                     break;
00289                 }
00290             }
00291             if (idx < cccCnt) {
00292                 // This characteristic has a CCCD attribute. Handle notifications and indications.
00293                 uint16_t cccEnabled = AttsCccEnabled(connectionHandle, idx);
00294                 if (cccEnabled & ATT_CLIENT_CFG_NOTIFY) {
00295                     AttsHandleValueNtf(connectionHandle, attributeHandle, len, (uint8_t*)buffer);
00296                 }
00297                 if (cccEnabled & ATT_CLIENT_CFG_INDICATE) {
00298                     AttsHandleValueInd(connectionHandle, attributeHandle, len, (uint8_t*)buffer);
00299                 }
00300             }
00301         }
00302     }
00303 
00304     return BLE_ERROR_NONE;
00305 }
00306 
00307 ble_error_t MaximGattServer::write(Gap::Handle_t connectionHandle, GattAttribute::Handle_t attributeHandle, const uint8_t buffer[], uint16_t len, bool localOnly)
00308 {
00309     // Check to see if this is a CCCD
00310     uint8_t idx;
00311     for (idx = 0; idx < cccCnt; idx++) {
00312         if (attributeHandle == cccSet[idx].handle) {
00313             if ((connectionHandle == DM_CONN_ID_NONE) || (len != 2)) { // CCCDs are always 16 bits
00314                 return BLE_ERROR_PARAM_OUT_OF_RANGE;
00315             }
00316             AttsCccSet(connectionHandle, idx, *((uint16_t*)buffer));
00317             return BLE_ERROR_NONE;
00318         }
00319     }
00320 
00321     // This is not a CCCD. Use the non-connection specific update method.
00322     return write(attributeHandle, buffer, len, localOnly);
00323 }
00324 
00325 ble_error_t MaximGattServer::areUpdatesEnabled(const GattCharacteristic &characteristic, bool *enabledP)
00326 {
00327     uint16_t connectionHandle = MaximGap::getInstance().getConnectionHandle();
00328 
00329     if (connectionHandle != DM_CONN_ID_NONE) {
00330         uint8_t idx;
00331         for (idx = 0; idx < cccCnt; idx++) {
00332             if (characteristic.getValueHandle() == cccHandles[idx]) {
00333                 uint16_t cccValue = AttsCccGet(connectionHandle, idx);
00334                 if (cccValue & ATT_CLIENT_CFG_NOTIFY) {
00335                     *enabledP = true;
00336                 } else {
00337                     *enabledP = false;
00338                 }
00339                 return BLE_ERROR_NONE;
00340             }
00341         }
00342     }
00343 
00344     return BLE_ERROR_PARAM_OUT_OF_RANGE;
00345 }
00346 
00347 ble_error_t MaximGattServer::areUpdatesEnabled(Gap::Handle_t connectionHandle, const GattCharacteristic &characteristic, bool *enabledP)
00348 {
00349     if (connectionHandle != DM_CONN_ID_NONE) {
00350         uint8_t idx;
00351         for (idx = 0; idx < cccCnt; idx++) {
00352             if (characteristic.getValueHandle() == cccHandles[idx]) {
00353                 uint16_t cccValue = AttsCccGet(connectionHandle, idx);
00354                 if (cccValue & ATT_CLIENT_CFG_NOTIFY) {
00355                     *enabledP = true;
00356                 } else {
00357                     *enabledP = false;
00358                 }
00359                 return BLE_ERROR_NONE;
00360             }
00361         }
00362     }
00363 
00364     return BLE_ERROR_PARAM_OUT_OF_RANGE;
00365 }
00366 
00367 void MaximGattServer::cccCback(attsCccEvt_t  *pEvt)
00368 {
00369     if (pEvt->value  & (ATT_CLIENT_CFG_NOTIFY | ATT_CLIENT_CFG_INDICATE)) {
00370         getInstance().handleEvent(GattServerEvents::GATT_EVENT_UPDATES_ENABLED, pEvt->handle );
00371     } else {
00372         getInstance().handleEvent(GattServerEvents::GATT_EVENT_UPDATES_DISABLED, pEvt->handle );
00373     }
00374 }
00375 
00376 void MaximGattServer::attCback(attEvt_t  *pEvt)
00377 {
00378     if (pEvt->hdr.status  == ATT_SUCCESS) {
00379         getInstance().handleEvent(GattServerEvents::GATT_EVENT_DATA_SENT, pEvt->handle );
00380     }
00381 }
00382 
00383 uint8_t MaximGattServer::attsReadCback(dmConnId_t  connId, uint16_t handle, uint8_t operation, uint16_t offset, attsAttr_t  *pAttr)
00384 {
00385     GattReadCallbackParams cbParams = {
00386         .connHandle = connId,
00387         .handle     = handle,
00388         .offset     = offset,
00389         .len        = *pAttr->pLen ,
00390         .data       = pAttr->pValue 
00391     };
00392     getInstance().handleDataReadEvent(&cbParams);
00393 
00394     return ATT_SUCCESS;
00395 }
00396 
00397 uint8_t MaximGattServer::attsWriteCback(dmConnId_t  connId, uint16_t handle, uint8_t operation, uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t  *pAttr)
00398 {
00399     uint8_t err;
00400 
00401     /* TODO: offset is not handled properly */
00402     if ((err = AttsSetAttr(handle, len, pValue)) != ATT_SUCCESS) {
00403         return err;
00404     }
00405 
00406     GattWriteCallbackParams::WriteOp_t writeOp;
00407     switch (operation) {
00408         case ATT_PDU_WRITE_REQ:
00409             writeOp = GattWriteCallbackParams::OP_WRITE_REQ;
00410             break;
00411         case ATT_PDU_WRITE_CMD:
00412             writeOp = GattWriteCallbackParams::OP_WRITE_CMD;
00413             break;
00414         case ATT_PDU_SIGNED_WRITE_CMD:
00415             writeOp = GattWriteCallbackParams::OP_SIGN_WRITE_CMD;
00416             break;
00417         case ATT_PDU_PREP_WRITE_REQ:
00418             writeOp = GattWriteCallbackParams::OP_PREP_WRITE_REQ;
00419             break;
00420         case ATT_PDU_EXEC_WRITE_REQ:
00421             writeOp = GattWriteCallbackParams::OP_EXEC_WRITE_REQ_NOW;
00422             break;
00423         default:
00424             writeOp = GattWriteCallbackParams::OP_INVALID;
00425             break;
00426     }
00427 
00428     GattWriteCallbackParams cbParams = {
00429         .connHandle = connId,
00430         .handle     = handle,
00431         .writeOp    = writeOp,
00432         .offset     = offset,
00433         .len        = len,
00434         .data       = pValue
00435     };
00436     getInstance().handleDataWrittenEvent(&cbParams);
00437 
00438     return ATT_SUCCESS;
00439 }