BLE_API

Revision:
1096:c3a43f09f1c5
Parent:
1094:ff274a1b7a14
Child:
1097:4d22814faf46
--- a/ble/GapAdvertisingData.h	Mon Jan 11 08:51:52 2016 +0000
+++ b/ble/GapAdvertisingData.h	Mon Jan 11 08:51:52 2016 +0000
@@ -202,149 +202,63 @@
 
     /**
      * Adds advertising data based on the specified AD type (see DataType).
-     *
-     * @param  advDataType The Advertising 'DataType' to add.
-     * @param  payload     Pointer to the payload contents.
-     * @param  len         Size of the payload in bytes.
-     *
-     * @return BLE_ERROR_BUFFER_OVERFLOW if the specified data would cause the
-     * advertising buffer to overflow, else BLE_ERROR_NONE.
-     */
-    ble_error_t addData(DataType advDataType, const uint8_t *payload, uint8_t len)
-    {
-        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);
-                        }
-                    }
-
-                    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);
-        }
-
-        return result;
-    }
-
-    /**
-     * Update a particular ADV field in the advertising payload (based on
-     * matching type and length). Note: the length of the new data must be the
-     * same as the old one.
+     * If the supplied AD type is already present in the advertising
+     * payload, then the value is updated.
      *
      * @param[in] advDataType  The Advertising 'DataType' to add.
      * @param[in] payload      Pointer to the payload contents.
      * @param[in] len          Size of the payload in bytes.
      *
-     * @return BLE_ERROR_UNSPECIFIED if the specified field is not found, else
-     * BLE_ERROR_NONE.
+     * @return BLE_ERROR_BUFFER_OVERFLOW if the new value causes the
+     *         advertising buffer to overflow. BLE_ERROR_NONE is returned
+     *         on success.
+     *
+     * @note When the specified AD type is INCOMPLETE_LIST_16BIT_SERVICE_IDS,
+     *       COMPLETE_LIST_16BIT_SERVICE_IDS, INCOMPLETE_LIST_32BIT_SERVICE_IDS,
+     *       COMPLETE_LIST_32BIT_SERVICE_IDS, INCOMPLETE_LIST_128BIT_SERVICE_IDS,
+     *       COMPLETE_LIST_128BIT_SERVICE_IDS or LIST_128BIT_SOLICITATION_IDS the
+     *       supplied value is appended to the values previously added to the
+     *       payload.
+     */
+    ble_error_t addData(DataType_t advDataType, const uint8_t *payload, uint8_t len)
+    {
+        // find field
+        uint8_t* field = findField(advDataType);
+
+        if (field) {
+            // Field type already exist, either add to field or replace
+            return updateFieldPayload(advDataType, payload, len, field);
+        } else {
+            // field doesn't exists, insert new
+            return appendField(advDataType, payload, len);
+        }
+    }
+
+    /**
+     * Update a particular ADV field in the advertising payload (based on
+     * matching type).
+     *
+     * @param[in] advDataType  The Advertising 'DataType' to add.
+     * @param[in] payload      Pointer to the payload contents.
+     * @param[in] len          Size of the payload in bytes.
+     *
+     * @return BLE_ERROR_UNSPECIFIED if the specified field is not found,
+     *         BLE_ERROR_BUFFER_OVERFLOW if the new value causes the
+     *         advertising buffer to overflow. BLE_ERROR_NONE is returned
+     *         on success.
      */
     ble_error_t updateData(DataType_t advDataType, const uint8_t *payload, uint8_t len)
     {
-        if ((payload == NULL) || (len == 0)) {
-            return BLE_ERROR_INVALID_PARAM;
-        }
-
-        /* A local struct to describe an ADV field. This definition comes from the Bluetooth Core Spec. (v4.2) Part C, Section 11. */
-        struct ADVField_t {
-            uint8_t  len;      /* Describes the length (in bytes) of the following type and bytes. */
-            uint8_t  type;     /* Should have the same representation of DataType_t (above). */
-            uint8_t  bytes[0]; /* A placeholder for variable length data. */
-        };
+        // find field
+        uint8_t* field = findField(advDataType);
 
-        /* Iterate over the adv fields looking for the first match. */
-        uint8_t byteIndex = 0;
-        while (byteIndex < _payloadLen) {
-            ADVField_t *currentADV = (ADVField_t *)&_payload[byteIndex];
-            if ((currentADV->len  == (len + 1)) && /* Incoming len only describes the payload, whereas ADV->len describes [type + payload]. */
-                (currentADV->type == advDataType)) {
-                memcpy(currentADV->bytes, payload, len);
-                return BLE_ERROR_NONE;
-            }
-
-            byteIndex += (currentADV->len + 1); /* Advance by len+1; '+1' is needed to span the len field itself. */
+        if (field) {
+            // Field type already exist, either add to field or replace
+            return updateFieldPayload(advDataType, payload, len, field);
+        } else {
+            // field doesn't exists, return an error
+            return BLE_ERROR_UNSPECIFIED;
         }
-
-        return BLE_ERROR_UNSPECIFIED;
     }
 
     /**
@@ -475,6 +389,85 @@
         return NULL;
     }
 
+    /**
+     * Given the a pointer to a field in the advertising payload it replaces
+     * the existing data in the field with the supplied data.
+     * Returns BLE_ERROR_NONE on success.
+     */
+    ble_error_t updateFieldPayload(DataType_t advDataType, const uint8_t *payload, uint8_t len, uint8_t* field)
+    {
+        ble_error_t result = BLE_ERROR_BUFFER_OVERFLOW;
+
+        switch(advDataType) {
+            // 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;
+            }
+            //  These fields will be overwritten with the new value
+            default: {
+                // 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);
+                    }
+                }
+
+                break;
+            }
+        }
+
+        return result;
+    }
+
     uint8_t  _payload[GAP_ADVERTISING_DATA_MAX_PAYLOAD];
     uint8_t  _payloadLen;
     uint16_t _appearance;