Updated

Fork of BLE_API by Bluetooth Low Energy

Revision:
942:93f74c23991f
Parent:
940:ae3eaccfac3a
Child:
948:1bb402105289
diff -r 3ee6a753313d -r 93f74c23991f ble/GapAdvertisingData.h
--- a/ble/GapAdvertisingData.h	Thu Nov 26 12:52:07 2015 +0000
+++ b/ble/GapAdvertisingData.h	Thu Nov 26 12:52:07 2015 +0000
@@ -89,22 +89,23 @@
     */
     /**********************************************************************/
     enum DataType_t {
-        FLAGS                              = 0x01, /**< \ref *Flags. */
-        INCOMPLETE_LIST_16BIT_SERVICE_IDS  = 0x02, /**< Incomplete list of 16-bit service IDs. */
-        COMPLETE_LIST_16BIT_SERVICE_IDS    = 0x03, /**< Complete list of 16-bit service IDs. */
-        INCOMPLETE_LIST_32BIT_SERVICE_IDS  = 0x04, /**< Incomplete list of 32-bit service IDs (not relevant for Bluetooth 4.0). */
-        COMPLETE_LIST_32BIT_SERVICE_IDS    = 0x05, /**< Complete list of 32-bit service IDs (not relevant for Bluetooth 4.0). */
-        INCOMPLETE_LIST_128BIT_SERVICE_IDS = 0x06, /**< Incomplete list of 128-bit service IDs. */
-        COMPLETE_LIST_128BIT_SERVICE_IDS   = 0x07, /**< Complete list of 128-bit service IDs. */
-        SHORTENED_LOCAL_NAME               = 0x08, /**< Shortened local name. */
-        COMPLETE_LOCAL_NAME                = 0x09, /**< Complete local name. */
-        TX_POWER_LEVEL                     = 0x0A, /**< TX power level (in dBm). */
-        DEVICE_ID                          = 0x10, /**< Device ID. */
-        SLAVE_CONNECTION_INTERVAL_RANGE    = 0x12, /**< Slave connection interval range. */
-        SERVICE_DATA                       = 0x16, /**< Service data. */
-        APPEARANCE                         = 0x19, /**< \ref Appearance. */
-        ADVERTISING_INTERVAL               = 0x1A, /**< Advertising interval. */
-        MANUFACTURER_SPECIFIC_DATA         = 0xFF  /**< Manufacturer specific data. */
+        FLAGS                              = 0x01, /**< \ref *Flags */
+        INCOMPLETE_LIST_16BIT_SERVICE_IDS  = 0x02, /**< Incomplete list of 16-bit Service IDs */
+        COMPLETE_LIST_16BIT_SERVICE_IDS    = 0x03, /**< Complete list of 16-bit Service IDs */
+        INCOMPLETE_LIST_32BIT_SERVICE_IDS  = 0x04, /**< Incomplete list of 32-bit Service IDs (not relevant for Bluetooth 4.0) */
+        COMPLETE_LIST_32BIT_SERVICE_IDS    = 0x05, /**< Complete list of 32-bit Service IDs (not relevant for Bluetooth 4.0) */
+        INCOMPLETE_LIST_128BIT_SERVICE_IDS = 0x06, /**< Incomplete list of 128-bit Service IDs */
+        COMPLETE_LIST_128BIT_SERVICE_IDS   = 0x07, /**< Complete list of 128-bit Service IDs */
+        SHORTENED_LOCAL_NAME               = 0x08, /**< Shortened Local Name */
+        COMPLETE_LOCAL_NAME                = 0x09, /**< Complete Local Name */
+        TX_POWER_LEVEL                     = 0x0A, /**< TX Power Level (in dBm) */
+        DEVICE_ID                          = 0x10, /**< Device ID */
+        SLAVE_CONNECTION_INTERVAL_RANGE    = 0x12, /**< Slave Connection Interval Range */
+        LIST_128BIT_SOLICITATION_IDS       = 0x15, /**< List of 128 bit service UUIDs the device is looking for */
+        SERVICE_DATA                       = 0x16, /**< Service Data */
+        APPEARANCE                         = 0x19, /**< \ref Appearance */
+        ADVERTISING_INTERVAL               = 0x1A, /**< Advertising Interval */
+        MANUFACTURER_SPECIFIC_DATA         = 0xFF  /**< Manufacturer Specific Data */
     };
     typedef enum DataType_t DataType; /* Deprecated type alias. This may be dropped in a future release. */
 
@@ -210,27 +211,98 @@
      */
     ble_error_t addData(DataType advDataType, const uint8_t *payload, uint8_t len)
     {
-        /* To Do: Check if an AD type already exists and if the existing */
-        /*       value is exclusive or not (flags and so on). */
+        ble_error_t result = BLE_ERROR_BUFFER_OVERFLOW;
+
+        // find field
+        uint8_t* field = findField(advDataType);
+
+        // Field type already exist, either add to field or replace
+        if (field) {
+            switch(advDataType) {
+                //  These fields will be overwritten with the new value
+                case FLAGS:
+                case SHORTENED_LOCAL_NAME:
+                case COMPLETE_LOCAL_NAME:
+                case TX_POWER_LEVEL:
+                case DEVICE_ID:
+                case SLAVE_CONNECTION_INTERVAL_RANGE:
+                case SERVICE_DATA:
+                case APPEARANCE:
+                case ADVERTISING_INTERVAL:
+                case MANUFACTURER_SPECIFIC_DATA: {
+                    // current field length, with the type subtracted
+                    uint8_t dataLength = field[0] - 1;
+
+                    // new data has same length, do in-order replacement
+                    if (len == dataLength) {
+                        for (uint8_t idx = 0; idx < dataLength; idx++) {
+                            field[2 + idx] = payload[idx];
+                        }
+                    } else {
+                        // check if data fits
+                        if ((_payloadLen - dataLength + len) <= GAP_ADVERTISING_DATA_MAX_PAYLOAD) {
+
+                            // remove old field
+                            while ((field + dataLength + 2) < &_payload[_payloadLen]) {
+                                *field = field[dataLength + 2];
+                                field++;
+                            }
+
+                            // reduce length
+                            _payloadLen -= dataLength + 2;
+
+                            // add new field
+                            result = appendField(advDataType, payload, len);
+                        }
+                    }
 
-        /* Make sure we don't exceed the 31 byte payload limit. */
-        if (_payloadLen + len + 2 > GAP_ADVERTISING_DATA_MAX_PAYLOAD) {
-            return BLE_ERROR_BUFFER_OVERFLOW;
+                    break;
+                }
+                // These fields will have the new data appended if there is sufficient space
+                case INCOMPLETE_LIST_16BIT_SERVICE_IDS:
+                case COMPLETE_LIST_16BIT_SERVICE_IDS:
+                case INCOMPLETE_LIST_32BIT_SERVICE_IDS:
+                case COMPLETE_LIST_32BIT_SERVICE_IDS:
+                case INCOMPLETE_LIST_128BIT_SERVICE_IDS:
+                case COMPLETE_LIST_128BIT_SERVICE_IDS:
+                case LIST_128BIT_SOLICITATION_IDS: {
+                    // check if data fits
+                    if ((_payloadLen + len) <= GAP_ADVERTISING_DATA_MAX_PAYLOAD) {
+                        // make room for new field by moving the remainder of the
+                        // advertisement payload "to the right" starting after the
+                        // TYPE field.
+                        uint8_t* end = &_payload[_payloadLen];
+
+                        while (&field[1] < end) {
+                            end[len] = *end;
+                            end--;
+                        }
+
+                        // insert new data
+                        for (uint8_t idx = 0; idx < len; idx++) {
+                            field[2 + idx] = payload[idx];
+                        }
+
+                        // increment lengths
+                        field[0] += len;
+                        _payloadLen += len;
+
+                        result = BLE_ERROR_NONE;
+                    }
+
+                    break;
+                }
+                // Field exists but updating it is not supported. Abort operation.
+                default:
+                    result = BLE_ERROR_NOT_IMPLEMENTED;
+                    break;
+            }
+        } else {
+            // field doesn't exists, insert new
+            result = appendField(advDataType, payload, len);
         }
 
-        /* Field length. */
-        memset(&_payload[_payloadLen], len + 1, 1);
-        _payloadLen++;
-
-        /* Field ID. */
-        memset(&_payload[_payloadLen], (uint8_t)advDataType, 1);
-        _payloadLen++;
-
-        /* Payload. */
-        memcpy(&_payload[_payloadLen], payload, len);
-        _payloadLen += len;
-
-        return BLE_ERROR_NONE;
+        return result;
     }
 
     /**
@@ -345,7 +417,63 @@
         return (uint16_t)_appearance;
     }
 
+    /**
+     * Search advertisement data for field.
+     * Returns pointer to the first element in the field if found, NULL otherwise.
+     * Where the first element is the length of the field.
+     */
+    const uint8_t* findField(DataType_t type) const {
+        return findField(type);
+    }
+
 private:
+    /**
+     * Append advertising data based on the specified AD type (see DataType)
+     */
+    ble_error_t appendField(DataType advDataType, const uint8_t *payload, uint8_t len)
+    {
+        /* Make sure we don't exceed the 31 byte payload limit */
+        if (_payloadLen + len + 2 > GAP_ADVERTISING_DATA_MAX_PAYLOAD) {
+            return BLE_ERROR_BUFFER_OVERFLOW;
+        }
+
+        /* Field length. */
+        memset(&_payload[_payloadLen], len + 1, 1);
+        _payloadLen++;
+
+        /* Field ID. */
+        memset(&_payload[_payloadLen], (uint8_t)advDataType, 1);
+        _payloadLen++;
+
+        /* Payload. */
+        memcpy(&_payload[_payloadLen], payload, len);
+        _payloadLen += len;
+
+        return BLE_ERROR_NONE;
+    }
+
+    /**
+     * Search advertisement data for field.
+     * Returns pointer to the first element in the field if found, NULL otherwise.
+     * Where the first element is the length of the field.
+     */
+    uint8_t* findField(DataType_t type) {
+        // scan through advertisement data
+        for (uint8_t idx = 0; idx < _payloadLen; ) {
+            uint8_t fieldType = _payload[idx + 1];
+
+            if (fieldType == type) {
+                return &_payload[idx];
+            }
+
+            // advance to next field
+            idx += _payload[idx] + 1;
+        }
+
+        // field not found
+        return NULL;
+    }
+
     uint8_t  _payload[GAP_ADVERTISING_DATA_MAX_PAYLOAD];
     uint8_t  _payloadLen;
     uint16_t _appearance;