diff --git a/features/FEATURE_BLE/ble/Gap.h b/features/FEATURE_BLE/ble/Gap.h index 965fe82f86..84a8bc9295 100644 --- a/features/FEATURE_BLE/ble/Gap.h +++ b/features/FEATURE_BLE/ble/Gap.h @@ -20,6 +20,7 @@ #include "BLETypes.h" #include "BLEProtocol.h" #include "GapAdvertisingData.h" +#include "ble/gap/AdvertisingData.h" #include "GapAdvertisingParams.h" #include "GapScanningParams.h" #include "GapEvents.h" diff --git a/features/FEATURE_BLE/ble/GapAdvertisingData.h b/features/FEATURE_BLE/ble/GapAdvertisingData.h index 562a072946..2131234818 100644 --- a/features/FEATURE_BLE/ble/GapAdvertisingData.h +++ b/features/FEATURE_BLE/ble/GapAdvertisingData.h @@ -14,16 +14,13 @@ * limitations under the License. */ -#ifndef MBED_GAP_ADVERTISING_DATA_H__ -#define MBED_GAP_ADVERTISING_DATA_H__ +#ifndef MBED_GAP_ADVERTISING_DATA__LEGACY_H__ +#define MBED_GAP_ADVERTISING_DATA__LEGACY_H__ #include #include -#include #include "blecommon.h" -#include "platform/Span.h" -#include "NonCopyable.h" /** * @addtogroup ble @@ -42,7 +39,7 @@ * in an advertisement payload. * * After construction, the advertising payload contained in the instance of - * AdvertisingData is empty. Adding new states and named fields can be + * GapAdvertisingData is empty. Adding new states and named fields can be * achieved by invoking the function addData(), and updating existing state * involves calling the function updateData(). * @@ -61,17 +58,17 @@ * static const uint8_t device_name[] = "HRM"; * * // construct an empty advertising payload - * AdvertisingData advertising_data; + * GapAdvertisingData advertising_data; * * // set the flags of the advertising device * advertising_data.addFlags( - * AdvertisingData::LE_GENERAL_DISCOVERABLE | - * AdvertisingData::BREDR_NOT_SUPPORTED + * GapAdvertisingData::LE_GENERAL_DISCOVERABLE | + * GapAdvertisingData::BREDR_NOT_SUPPORTED * ); * * // set the advertised name of the device * advertising_data.addData( - * AdvertisingData::COMPLETE_LOCAL_NAME, + * GapAdvertisingData::COMPLETE_LOCAL_NAME, * device_name, * sizeof(device_name) * ); @@ -96,7 +93,7 @@ * * @par * For convenience, all appropriate AD types are encapsulated in - * AdvertisingData::DataType. + * GapAdvertisingData::DataType. * * @par * Before the AD Types and their payload (if any) can be inserted into @@ -112,8 +109,12 @@ * some basic checks on the payload length and tries to avoid common * errors such as adding an exclusive AD field twice in the advertising * or scan response payload. + * + * @deprecated Use AdvertisingData instead. + * This version provides the buffer backing for the advertising data + * but it's only big enough for legacy advertising. */ -class AdvertisingData +class GapAdvertisingData { public: /*! @@ -129,7 +130,7 @@ public: */ enum DataType_t { /** - * Flags, refer to AdvertisingData::Flags_t. + * Flags, refer to GapAdvertisingData::Flags_t. */ FLAGS = 0x01, @@ -199,7 +200,7 @@ public: SERVICE_DATA = 0x16, /** - * Appearance, refer to AdvertisingData::Appearance_t. + * Appearance, refer to GapAdvertisingData::Appearance_t. */ APPEARANCE = 0x19, @@ -216,7 +217,7 @@ public: }; /** - * Alias for AdvertisingData::DataType_t. + * Alias for GapAdvertisingData::DataType_t. * * @deprecated Future releases will drop this type alias. */ @@ -262,7 +263,7 @@ public: }; /** - * Alias for AdvertisingData::Flags_t. + * Alias for GapAdvertisingData::Flags_t. * * @deprecated Future releases will drop this type alias. */ @@ -528,77 +529,19 @@ public: }; /** - * Alias for AdvertisingData::Appearance_t. + * Alias for GapAdvertisingData::Appearance_t. * * @deprecated Future releases will drop this type alias. */ typedef enum Appearance_t Appearance; - /** Advertising data needs a user provided buffer to store the data. - * - * @param buffer Buffer used to store the data. - * @note Use Gap::getMaxAdvertisingDataLength() to find out how much can be accepted. - */ - AdvertisingData(mbed::Span buffer) : - _buffer(buffer), - _payloadLen(0) { - } - - /** Advertising data needs a user provided buffer to store the data. - * - * @param buffer Pointer to buffer to be used for storing advertising data. - * @param buffer_size Size of the buffer. - * @note Use Gap::getMaxAdvertisingDataLength() to find out how much can be accepted. - */ - AdvertisingData(uint8_t* buffer, size_t buffer_size) : - _buffer(buffer, buffer_size), - _payloadLen(0) { - } - - /** Return maximum size of the data that can be stored. - * - * @return Size of the buffer used to store the data. - */ - size_t getBufferSize() const { - return _buffer.size(); - } - /** - * Get the subspan of the buffer containing valid data. - * - * @return A Span containing the payload. + * Construct a GapAdvertising instance with an empty payload. */ - void getData(mbed::Span &data) { - data = mbed::make_Span(_buffer.data(), _payloadLen); - } - - /** - * Get the pointer to the advertising payload bytes. - * - * @return A pointer to the payload. - */ - const uint8_t *getPayload() const - { - return _buffer.data(); - } - - /** - * Get the pointer to the advertising payload bytes. - * - * @return A pointer to the payload. - */ - uint8_t *getPayload() { - return _buffer.data(); - } - - /** - * Get the payload length. - * - * @return The payload length in bytes. - */ - uint8_t getPayloadLen(void) const - { - return _payloadLen; + GapAdvertisingData(void) : + _payload(), + _payloadLen(0), + _appearance(GENERIC_TAG) { } /** @@ -671,11 +614,12 @@ public: * advertising buffer to overflow. * * @note This call is equivalent to calling addData() with - * AdvertisingData::APPEARANCE as the field type. + * GapAdvertisingData::APPEARANCE as the field type. */ ble_error_t addAppearance(Appearance appearance = GENERIC_TAG) { - return addData(AdvertisingData::APPEARANCE, (uint8_t *)&appearance, 2); + _appearance = appearance; + return addData(GapAdvertisingData::APPEARANCE, (uint8_t *)&appearance, 2); } /** @@ -689,11 +633,11 @@ public: * advertising buffer to overflow. * * @note This call is equivalent to calling addData() with - * AdvertisingData::FLAGS as the field type. + * GapAdvertisingData::FLAGS as the field type. */ ble_error_t addFlags(uint8_t flags = LE_GENERAL_DISCOVERABLE) { - return addData(AdvertisingData::FLAGS, &flags, 1); + return addData(GapAdvertisingData::FLAGS, &flags, 1); } /** @@ -706,12 +650,12 @@ public: * advertising buffer to overflow. * * @note This call is equivalent to calling addData() with - * AdvertisingData::TX_POWER_LEVEL as the field type. + * GapAdvertisingData::TX_POWER_LEVEL as the field type. */ ble_error_t addTxPower(int8_t txPower) { /* To Do: Basic error checking to make sure txPower is in range. */ - return addData(AdvertisingData::TX_POWER_LEVEL, (uint8_t *)&txPower, 1); + return addData(GapAdvertisingData::TX_POWER_LEVEL, (uint8_t *)&txPower, 1); } /** @@ -721,10 +665,30 @@ public: */ void clear(void) { - memset(_buffer.data(), 0, GAP_ADVERTISING_DATA_MAX_PAYLOAD); + memset(&_payload, 0, GAP_ADVERTISING_DATA_MAX_PAYLOAD); _payloadLen = 0; } + /** + * Get the pointer to the advertising payload bytes. + * + * @return A pointer to the payload. + */ + const uint8_t *getPayload(void) const + { + return _payload; + } + + /** + * Get the payload length. + * + * @return The payload length in bytes. + */ + uint8_t getPayloadLen(void) const + { + return _payloadLen; + } + /** * Get the appearance set. * @@ -734,12 +698,7 @@ public: */ uint16_t getAppearance(void) const { - uint16_t appearance = GENERIC_TAG; - const uint8_t *field = findField(AdvertisingData::APPEARANCE); - if (field) { - memcpy((uint8_t*)&appearance, field, 2); - } - return appearance; + return (uint16_t)_appearance; } /** @@ -755,14 +714,14 @@ public: { /* Scan through advertisement data */ for (uint8_t idx = 0; idx < _payloadLen; ) { - uint8_t fieldType = _buffer[idx + 1]; + uint8_t fieldType = _payload[idx + 1]; if (fieldType == type) { - return (_buffer.data() + idx); + return &_payload[idx]; } /* Advance to next field */ - idx += _buffer[idx] + 1; + idx += _payload[idx] + 1; } /* Field not found */ @@ -790,15 +749,15 @@ private: } /* Field length. */ - memset(_buffer.data() + _payloadLen, len + 1, 1); + memset(&_payload[_payloadLen], len + 1, 1); _payloadLen++; /* Field ID. */ - memset(_buffer.data() + _payloadLen, (uint8_t)advDataType, 1); + memset(&_payload[_payloadLen], (uint8_t)advDataType, 1); _payloadLen++; /* Payload. */ - memcpy(_buffer.data() + _payloadLen, payload, len); + memcpy(&_payload[_payloadLen], payload, len); _payloadLen += len; return BLE_ERROR_NONE; @@ -816,7 +775,7 @@ private: uint8_t* findField(DataType_t type) { return const_cast( - static_cast(this)->findField(type) + static_cast(this)->findField(type) ); } @@ -857,13 +816,13 @@ private: case COMPLETE_LIST_128BIT_SERVICE_IDS: case LIST_128BIT_SOLICITATION_IDS: { /* Check if data fits */ - if ((_payloadLen + len) <= _buffer.size()) { + 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 = _buffer.data() +_payloadLen; + uint8_t* end = &_payload[_payloadLen]; while (&field[1] < end) { end[len] = *end; @@ -928,7 +887,7 @@ private: if ((_payloadLen - dataLength + len) <= GAP_ADVERTISING_DATA_MAX_PAYLOAD) { /* Remove old field */ - while ((field + dataLength + 2) < _buffer.data() + _payloadLen) { + while ((field + dataLength + 2) < &_payload[_payloadLen]) { *field = field[dataLength + 2]; field++; } @@ -944,42 +903,21 @@ private: return result; } -protected: - AdvertisingData(const AdvertisingData& other) : - _buffer(other._buffer), - _payloadLen(other._payloadLen) { - } - -protected: - /** The memory backing the the data provided by the user. */ - mbed::Span _buffer; - - /** Length of the data added to the advertising buffer. */ - uint8_t _payloadLen; -}; - -/** @deprecated Use AdvertisingData instead. - * This version provides the buffer backing for the advertising data - * but it's only big enough for legacy advertising. - */ -class GapAdvertisingData : public AdvertisingData -{ -public: - GapAdvertisingData() : - AdvertisingData(_payload, GAP_ADVERTISING_DATA_MAX_PAYLOAD) { } - - GapAdvertisingData(const GapAdvertisingData& other) : - AdvertisingData(_payload, GAP_ADVERTISING_DATA_MAX_PAYLOAD) - { - memcpy(_payload, other.getPayload(), GAP_ADVERTISING_DATA_MAX_PAYLOAD); - _payloadLen = other.getPayloadLen(); - } - -private: - /** Advertising data buffer. */ + /** + * Advertising data buffer. + */ uint8_t _payload[GAP_ADVERTISING_DATA_MAX_PAYLOAD]; -}; + /** + * Length of the data added to the advertising buffer. + */ + uint8_t _payloadLen; + + /** + * Appearance value. + */ + uint16_t _appearance; +}; /** * @} @@ -987,4 +925,4 @@ private: */ -#endif /* ifndef MBED_GAP_ADVERTISING_DATA_H__ */ +#endif /* ifndef MBED_GAP_ADVERTISING_DATA__LEGACY_H__ */ diff --git a/features/FEATURE_BLE/ble/gap/AdvertisingData.h b/features/FEATURE_BLE/ble/gap/AdvertisingData.h new file mode 100644 index 0000000000..c669cb6cc6 --- /dev/null +++ b/features/FEATURE_BLE/ble/gap/AdvertisingData.h @@ -0,0 +1,967 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_GAP_ADVERTISING_DATA_H__ +#define MBED_GAP_ADVERTISING_DATA_H__ + +#include +#include +#include + +#include "blecommon.h" +#include "platform/Span.h" +#include "NonCopyable.h" + +/** + * @addtogroup ble + * @{ + * @addtogroup gap + * @{ + */ + +#define GAP_ADVERTISING_DATA_MAX_PAYLOAD (31) + +/** + * GAP advertising data builder. + * + * Advertising data are used by broadcaster or peripheral to advertise state + * about the device. This class offers the function to add and update states present + * in an advertisement payload. + * + * After construction, the advertising payload contained in the instance of + * AdvertisingData is empty. Adding new states and named fields can be + * achieved by invoking the function addData(), and updating existing state + * involves calling the function updateData(). + * + * Fields present in the payload can be retrieved by a call to the function + * findField. + * + * This class includes shorthand for the most common fields: + * - FLAGS: addFlags(). + * - APPEARANCE: addAppearance(). + * - TX_POWER_LEVEL: addTxPower(). + * + * @code + * + * Gap ⪆ + * + * static const uint8_t device_name[] = "HRM"; + * + * // construct an empty advertising payload + * AdvertisingData advertising_data; + * + * // set the flags of the advertising device + * advertising_data.addFlags( + * AdvertisingData::LE_GENERAL_DISCOVERABLE | + * AdvertisingData::BREDR_NOT_SUPPORTED + * ); + * + * // set the advertised name of the device + * advertising_data.addData( + * AdvertisingData::COMPLETE_LOCAL_NAME, + * device_name, + * sizeof(device_name) + * ); + * + * // update the advertising data of the gap payload + * gap.setAdvertisingPayload(advertising_data); + * + * @endcode + * + * @note See Bluetooth Specification 4.0 (Vol. 3), Part C, Sections 11 and 18 + * for further information on advertising and scan response data. + * + * @par Advertising and Scan Response Payloads + * Advertising data and scan response data are organized around a set of + * data types called 'AD types' in Bluetooth 4.0 (see the Bluetooth Core + * Specification v4.0, Vol. 3, Part C, Sections 11 and 18). + * + * @par + * Each AD type has its own standardized assigned number, as + * the Bluetooth SIG defines: + * https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile. + * + * @par + * For convenience, all appropriate AD types are encapsulated in + * AdvertisingData::DataType. + * + * @par + * Before the AD Types and their payload (if any) can be inserted into + * the advertising or scan response frames, they need to be formatted as + * follows: + * + * @li @c Record length (1 byte). + * @li @c AD Type (1 byte). + * @li @c AD payload (optional; only present if record length > 1). + * + * @par + * This class takes care of properly formatting the payload, performs + * some basic checks on the payload length and tries to avoid common + * errors such as adding an exclusive AD field twice in the advertising + * or scan response payload. + */ +class AdvertisingData +{ +public: + /*! + * List of standard Advertising Data types. + * + * These AD types are used to describe the capabilities of the peripheral + * and are inserted inside the advertising or scan response payloads. + * + * @par Source + * + * @li @c Bluetooth Core Specification 4.0 (Vol. 3), Part C, Section 11, 18. + * @li @c https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile. + */ + enum DataType_t { + /** + * Flags, refer to AdvertisingData::Flags_t. + */ + FLAGS = 0x01, + + /** + * Incomplete list of 16-bit Service IDs. + */ + INCOMPLETE_LIST_16BIT_SERVICE_IDS = 0x02, + + /** + * Complete list of 16-bit Service IDs. + */ + COMPLETE_LIST_16BIT_SERVICE_IDS = 0x03, + + /** + * Incomplete list of 32-bit Service IDs (not relevant for Bluetooth 4.0). + */ + INCOMPLETE_LIST_32BIT_SERVICE_IDS = 0x04, + + /** + * Complete list of 32-bit Service IDs (not relevant for Bluetooth 4.0). + */ + COMPLETE_LIST_32BIT_SERVICE_IDS = 0x05, + + /** + * Incomplete list of 128-bit Service IDs. + */ + INCOMPLETE_LIST_128BIT_SERVICE_IDS = 0x06, + + /** + * Complete list of 128-bit Service IDs. + */ + COMPLETE_LIST_128BIT_SERVICE_IDS = 0x07, + + /** + * Shortened Local Name. + */ + SHORTENED_LOCAL_NAME = 0x08, + + /** + * Complete Local Name. + */ + COMPLETE_LOCAL_NAME = 0x09, + + /** + * TX Power Level (in dBm). + */ + TX_POWER_LEVEL = 0x0A, + + /** + * Device ID. + */ + DEVICE_ID = 0x10, + + /** + * Slave Connection Interval Range. + */ + SLAVE_CONNECTION_INTERVAL_RANGE = 0x12, + + /** + * List of 128-bit service UUIDs the device is looking for. + */ + LIST_128BIT_SOLICITATION_IDS = 0x15, + + /** + * Service Data. + */ + SERVICE_DATA = 0x16, + + /** + * Appearance, refer to AdvertisingData::Appearance_t. + */ + APPEARANCE = 0x19, + + /** + * Advertising Interval. + */ + ADVERTISING_INTERVAL = 0x1A, + + /** + * Manufacturer Specific Data. + */ + MANUFACTURER_SPECIFIC_DATA = 0xFF + + }; + + /** + * Alias for AdvertisingData::DataType_t. + * + * @deprecated Future releases will drop this type alias. + */ + typedef enum DataType_t DataType; + + /** + * Enumeration of allowed flags for DataType_t::FLAGS. + * + * @note DataType_t::FLAGS may contain several flags that the bitwise + * and operator (ex.LE_GENERAL_DISCOVERABLE & BREDR_NOT_SUPPORTED) assembled. + * + * @par Source + * + * @li @c Bluetooth Core Specification 4.0 (Vol. 3), Part C, Section 18.1. + */ + enum Flags_t { + /** + * Peripheral device is discoverable for a limited period of time. + */ + LE_LIMITED_DISCOVERABLE = 0x01, + + /** + * Peripheral device is discoverable at any moment. + */ + LE_GENERAL_DISCOVERABLE = 0x02, + + /** + * Peripheral device is LE only and does not support Bluetooth Enhanced + * DataRate. + */ + BREDR_NOT_SUPPORTED = 0x04, + + /** + * Not relevant - dual mode only. + */ + SIMULTANEOUS_LE_BREDR_C = 0x08, + + /** + * Not relevant - dual mode only. + */ + SIMULTANEOUS_LE_BREDR_H = 0x10 + + }; + + /** + * Alias for AdvertisingData::Flags_t. + * + * @deprecated Future releases will drop this type alias. + */ + typedef enum Flags_t Flags; + + /** + * Enumeration of values for the DataType_t::APPEARANCE. + * + * These values describe the physical shape or appearance of the device. + * + * @par Source + * + * @li @c Bluetooth Core Specification Supplement, Part A, Section 1.12. + * @li @c Bluetooth Core Specification 4.0 (Vol. 3), Part C, Section 12.2. + * @li @c https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml. + */ + enum Appearance_t { + /** + * Unknown or unspecified appearance type. + */ + UNKNOWN = 0, + + /** + * Generic Phone. + */ + GENERIC_PHONE = 64, + + /** + * Generic Computer. + */ + GENERIC_COMPUTER = 128, + + /** + * Generic Watch. + */ + GENERIC_WATCH = 192, + + /** + * Sports Watch. + */ + WATCH_SPORTS_WATCH = 193, + + /** + * Generic Clock. + */ + GENERIC_CLOCK = 256, + + /** + * Generic Display. + */ + GENERIC_DISPLAY = 320, + + /** + * Generic Remote Control. + */ + GENERIC_REMOTE_CONTROL = 384, + + /** + * Generic Eye Glasses. + */ + GENERIC_EYE_GLASSES = 448, + + /** + * Generic Tag. + */ + GENERIC_TAG = 512, + + /** + * Generic Keyring. + */ + GENERIC_KEYRING = 576, + + /** + * Generic Media Player. + */ + GENERIC_MEDIA_PLAYER = 640, + + /** + * Generic Bar Code Scanner. + */ + GENERIC_BARCODE_SCANNER = 704, + + /** + * Generic Thermometer. + */ + GENERIC_THERMOMETER = 768, + + /** + * Ear Thermometer. + */ + THERMOMETER_EAR = 769, + + /** + * Generic Heart Rate Sensor. + */ + GENERIC_HEART_RATE_SENSOR = 832, + + /** + * Belt Heart Rate Sensor. + */ + HEART_RATE_SENSOR_HEART_RATE_BELT = 833, + + /** + * Generic Blood Pressure. + */ + GENERIC_BLOOD_PRESSURE = 896, + + /** + * Arm Blood Pressure. + */ + BLOOD_PRESSURE_ARM = 897, + + /** + * Wrist Blood Pressure. + */ + BLOOD_PRESSURE_WRIST = 898, + + /** + * Human Interface Device (HID). + */ + HUMAN_INTERFACE_DEVICE_HID = 960, + + /** + * Keyboard. + */ + KEYBOARD = 961, + + /** + * Mouse. + */ + MOUSE = 962, + + /** + * Joystick. + */ + JOYSTICK = 963, + + /** + * Gamepad. + */ + GAMEPAD = 964, + + /** + * Digitizer Tablet. + */ + DIGITIZER_TABLET = 965, + + /** + * Card Reader. + */ + CARD_READER = 966, + + /** + * Digital Pen. + */ + DIGITAL_PEN = 967, + + /** + * Bar Code Scanner. + */ + BARCODE_SCANNER = 968, + + /** + * Generic Glucose Meter. + */ + GENERIC_GLUCOSE_METER = 1024, + + /** + * Generic Running/Walking Sensor. + */ + GENERIC_RUNNING_WALKING_SENSOR = 1088, + + /** + * In Shoe Running/Walking Sensor. + */ + RUNNING_WALKING_SENSOR_IN_SHOE = 1089, + + /** + * On Shoe Running/Walking Sensor. + */ + RUNNING_WALKING_SENSOR_ON_SHOE = 1090, + + /** + * On Hip Running/Walking Sensor. + */ + RUNNING_WALKING_SENSOR_ON_HIP = 1091, + + /** + * Generic Cycling. + */ + GENERIC_CYCLING = 1152, + + /** + * Cycling Computer. + */ + CYCLING_CYCLING_COMPUTER = 1153, + + /** + * Cycling Speed Sensor. + */ + CYCLING_SPEED_SENSOR = 1154, + + /** + * Cycling Cadence Sensor. + */ + CYCLING_CADENCE_SENSOR = 1155, + + /** + * Cycling Power Sensor. + */ + CYCLING_POWER_SENSOR = 1156, + + /** + * Cycling Speed and Cadence Sensor. + */ + CYCLING_SPEED_AND_CADENCE_SENSOR = 1157, + + /** + * Generic Pulse Oximeter. + */ + PULSE_OXIMETER_GENERIC = 3136, + + /** + * Fingertip Pulse Oximeter. + */ + PULSE_OXIMETER_FINGERTIP = 3137, + + /** + * Wrist Worn Pulse Oximeter. + */ + PULSE_OXIMETER_WRIST_WORN = 3138, + + /** + * Generic Weight Scale. + */ + GENERIC_WEIGHT_SCALE = 3200, + + /** + * Generic Outdoor. + */ + OUTDOOR_GENERIC = 5184, + + /** + * Outdoor Location Display Device. + */ + OUTDOOR_LOCATION_DISPLAY_DEVICE = 5185, + + /** + * Outdoor Location and Navigation Display Device. + */ + OUTDOOR_LOCATION_AND_NAVIGATION_DISPLAY_DEVICE = 5186, + + /** + * Outdoor Location Pod. + */ + OUTDOOR_LOCATION_POD = 5187, + + /** + * Outdoor Location and Navigation Pod. + */ + OUTDOOR_LOCATION_AND_NAVIGATION_POD = 5188 + + }; + + /** + * Alias for AdvertisingData::Appearance_t. + * + * @deprecated Future releases will drop this type alias. + */ + typedef enum Appearance_t Appearance; + + /** Advertising data needs a user provided buffer to store the data. + * + * @param buffer Buffer used to store the data. + * @note Use Gap::getMaxAdvertisingDataLength() to find out how much can be accepted. + */ + AdvertisingData(mbed::Span buffer) : + _buffer(buffer), + _payloadLen(0) { + } + + /** Advertising data needs a user provided buffer to store the data. + * + * @param buffer Pointer to buffer to be used for storing advertising data. + * @param buffer_size Size of the buffer. + * @note Use Gap::getMaxAdvertisingDataLength() to find out how much can be accepted. + */ + AdvertisingData(uint8_t* buffer, size_t buffer_size) : + _buffer(buffer, buffer_size), + _payloadLen(0) { + } + + /** Return maximum size of the data that can be stored. + * + * @return Size of the buffer used to store the data. + */ + size_t getBufferSize() const { + return _buffer.size(); + } + + /** + * Get the subspan of the buffer containing valid data. + * + * @return A Span containing the payload. + */ + void getData(mbed::Span &data) { + data = mbed::make_Span(_buffer.data(), _payloadLen); + } + + /** + * Get the pointer to the advertising payload bytes. + * + * @return A pointer to the payload. + */ + const uint8_t *getPayload() const + { + return _buffer.data(); + } + + /** + * Get the pointer to the advertising payload bytes. + * + * @return A pointer to the payload. + */ + uint8_t *getPayload() { + return _buffer.data(); + } + + /** + * Get the payload length. + * + * @return The payload length in bytes. + */ + uint8_t getPayloadLen(void) const + { + return _payloadLen; + } + + /** + * Adds a new field into the payload. + * + * If the supplied advertising data type is already present in the + * advertising payload, then the value is updated. + * + * @param[in] advDataType The type of the field to add. + * @param[in] payload Pointer to the value of the field to add. + * @param[in] len Size in bytes of the value to add. + * + * @return BLE_ERROR_NONE on success. + * @return BLE_ERROR_BUFFER_OVERFLOW if the new value causes the advertising + * buffer to overflow. + * + * @note When the specified data 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 present in 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 exists, either add to field or replace */ + return addField(advDataType, payload, len, field); + } else { + /* Field doesn't exist, insert new */ + return appendField(advDataType, payload, len); + } + } + + /** + * Update a specific field in the advertising payload. + * + * @param[in] advDataType The type of the field to update. + * @param[in] payload Pointer to the updated value of the field. + * @param[in] len Size of the new value in bytes. + * + * @return BLE_ERROR_NONE returned on success. + * @return BLE_ERROR_UNSPECIFIED if the specified field is not found, + * @return BLE_ERROR_BUFFER_OVERFLOW if the new value causes the + * advertising buffer to overflow. + */ + ble_error_t updateData(DataType_t advDataType, const uint8_t *payload, uint8_t len) + { + /* Find field */ + uint8_t* field = findField(advDataType); + + if (field) { + /* Field type already exists, replace field contents */ + return updateField(advDataType, payload, len, field); + } else { + /* field doesn't exist, return an error */ + return BLE_ERROR_UNSPECIFIED; + } + } + + /** + * Add device appearance in the advertising payload. + * + * @param[in] appearance The appearance to advertise. + * + * @return BLE_ERROR_NONE on success. + * @return BLE_ERROR_BUFFER_OVERFLOW if the specified data would cause the + * advertising buffer to overflow. + * + * @note This call is equivalent to calling addData() with + * AdvertisingData::APPEARANCE as the field type. + */ + ble_error_t addAppearance(Appearance appearance = GENERIC_TAG) + { + return addData(AdvertisingData::APPEARANCE, (uint8_t *)&appearance, 2); + } + + /** + * Add BLE flags in the advertising payload. + * + * @param[in] flags Bitfield describing the capability of the device. See + * allowed flags in Flags_t. + * + * @return BLE_ERROR_NONE on success. + * @return BLE_ERROR_BUFFER_OVERFLOW if the specified data would cause the + * advertising buffer to overflow. + * + * @note This call is equivalent to calling addData() with + * AdvertisingData::FLAGS as the field type. + */ + ble_error_t addFlags(uint8_t flags = LE_GENERAL_DISCOVERABLE) + { + return addData(AdvertisingData::FLAGS, &flags, 1); + } + + /** + * Add the advertising TX in the advertising payload. + * + * @param[in] txPower Transmission power level in dB. + * + * @return BLE_ERROR_NONE on success. + * @return BLE_ERROR_BUFFER_OVERFLOW if the specified data would cause the + * advertising buffer to overflow. + * + * @note This call is equivalent to calling addData() with + * AdvertisingData::TX_POWER_LEVEL as the field type. + */ + ble_error_t addTxPower(int8_t txPower) + { + /* To Do: Basic error checking to make sure txPower is in range. */ + return addData(AdvertisingData::TX_POWER_LEVEL, (uint8_t *)&txPower, 1); + } + + /** + * Clears the advertising data payload. + * + * @post getPayloadLen() returns 0. + */ + void clear(void) + { + memset(_buffer.data(), 0, GAP_ADVERTISING_DATA_MAX_PAYLOAD); + _payloadLen = 0; + } + + /** + * Get the appearance set. + * + * If no value has been set, this function returns GENERIC_TAG. + * + * @return The appearance value set for this device. + */ + uint16_t getAppearance(void) const + { + uint16_t appearance = GENERIC_TAG; + const uint8_t *field = findField(AdvertisingData::APPEARANCE); + if (field) { + memcpy((uint8_t*)&appearance, field, 2); + } + return appearance; + } + + /** + * Search advertisement data for a specific field. + * + * @param[in] type The type of the field to find. + * + * @return A pointer to the first element in the field if found. The first + * element being the length of the field followed by the value of the field. + * @return NULL if the field is not present in the payload. + */ + const uint8_t* findField(DataType_t type) const + { + /* Scan through advertisement data */ + for (uint8_t idx = 0; idx < _payloadLen; ) { + uint8_t fieldType = _buffer[idx + 1]; + + if (fieldType == type) { + return (_buffer.data() + idx); + } + + /* Advance to next field */ + idx += _buffer[idx] + 1; + } + + /* Field not found */ + return NULL; + } + +private: + /** + * Append advertising data based on the specified type. + * + * @param[in] advDataType Type of the new data. + * @param[in] payload Pointer to the data to be appended to the advertising + * payload. + * @param[in] len Length of the data pointed to by @p payload. + * + * @return BLE_ERROR_NONE on success. + * @return BLE_ERROR_BUFFER_OVERFLOW if the specified data would cause the + * advertising buffer to overflow. + */ + 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(_buffer.data() + _payloadLen, len + 1, 1); + _payloadLen++; + + /* Field ID. */ + memset(_buffer.data() + _payloadLen, (uint8_t)advDataType, 1); + _payloadLen++; + + /* Payload. */ + memcpy(_buffer.data() + _payloadLen, payload, len); + _payloadLen += len; + + return BLE_ERROR_NONE; + } + + /** + * Search advertisement data for a specific field. + * + * @param[in] type The type of the field to find. + * + * @return A pointer to the first element in the field if found. The first + * element being the length of the field followed by the value of the field. + * @return NULL if the field is not present in the payload. + */ + uint8_t* findField(DataType_t type) + { + return const_cast( + static_cast(this)->findField(type) + ); + } + + /** + * Update in place the value of a field in the advertising payload. + * + * @param[in] advDataType Type of the new data. + * @param[in] payload Pointer to the data to be added to the advertising + * payload. + * @param[in] len Length of the data pointed to by @p payload. + * @param[in] field Pointer to the field of type @p advDataType in the + * advertising buffer. + * + * @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. + * + * @return BLE_ERROR_NONE on success. + */ + ble_error_t addField( + 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 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) <= _buffer.size()) { + /* + * Make room for new field by moving the remainder of the + * advertisement payload "to the right" starting after the + * TYPE field. + */ + uint8_t* end = _buffer.data() +_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 are overwritten with the new value */ + default: { + result = updateField(advDataType, payload, len, field); + + break; + } + } + + return result; + } + + /** + * Update in place the value of a field in the advertising payload. + * + * @param[in] advDataType Type of the new data. + * @param[in] payload Pointer to the data to be added to the advertising + * payload. + * @param[in] len Length of the data pointed to by @p payload. + * @param[in] field Pointer to the field of type @p advDataType in the + * advertising buffer. + * + * @return BLE_ERROR_NONE on success. + */ + ble_error_t updateField( + DataType_t advDataType, + const uint8_t *payload, + uint8_t len, + uint8_t* field + ) { + ble_error_t result = BLE_ERROR_BUFFER_OVERFLOW; + 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]; + } + + result = BLE_ERROR_NONE; + } else { + /* Check if data fits */ + if ((_payloadLen - dataLength + len) <= GAP_ADVERTISING_DATA_MAX_PAYLOAD) { + + /* Remove old field */ + while ((field + dataLength + 2) < _buffer.data() + _payloadLen) { + *field = field[dataLength + 2]; + field++; + } + + /* Reduce length */ + _payloadLen -= dataLength + 2; + + /* Add new field */ + result = appendField(advDataType, payload, len); + } + } + + return result; + } + +protected: + AdvertisingData(const AdvertisingData& other) : + _buffer(other._buffer), + _payloadLen(other._payloadLen) { + } + +protected: + /** The memory backing the the data provided by the user. */ + mbed::Span _buffer; + + /** Length of the data added to the advertising buffer. */ + uint8_t _payloadLen; +}; + +/** + * @} + * @} + */ + + +#endif /* ifndef MBED_GAP_ADVERTISING_DATA_H__ */