diff options
author | Trygve Laugstøl <trygvis@inamo.no> | 2018-09-05 14:14:42 +0200 |
---|---|---|
committer | Trygve Laugstøl <trygvis@inamo.no> | 2018-09-05 14:14:42 +0200 |
commit | 91e54cf9150b37036447d423857d2bd18e4bf02b (patch) | |
tree | 7738db9a61cb6385f0abc6359b5b89aaa1ff104d /include/ble | |
parent | 25d82b0c52120c81cfed5bc1f245408f08203b7b (diff) | |
download | ble-toys-91e54cf9150b37036447d423857d2bd18e4bf02b.tar.gz ble-toys-91e54cf9150b37036447d423857d2bd18e4bf02b.tar.bz2 ble-toys-91e54cf9150b37036447d423857d2bd18e4bf02b.tar.xz ble-toys-91e54cf9150b37036447d423857d2bd18e4bf02b.zip |
Major overhaul of BLE code:
o Starting to remove shared_ptr. The code shouldn't be shared between threads, any thread safety will have to be built on the outside.
o Better service discovery, don't fail when there are multiple requests that have to be done.
o AttributeData was buggy, now it is just less than ideal.
o Much better ByteBuffer. Now it is a simple view + cursor.
Diffstat (limited to 'include/ble')
-rw-r--r-- | include/ble/Bluetooth.h | 174 | ||||
-rw-r--r-- | include/ble/ByteBuffer.h | 69 | ||||
-rw-r--r-- | include/ble/att.h | 186 | ||||
-rw-r--r-- | include/ble/misc.h | 84 |
4 files changed, 317 insertions, 196 deletions
diff --git a/include/ble/Bluetooth.h b/include/ble/Bluetooth.h index abddc94..477bd29 100644 --- a/include/ble/Bluetooth.h +++ b/include/ble/Bluetooth.h @@ -11,7 +11,8 @@ #include <functional> #include <cstring> -#include "ByteBuffer.h" +#include "ble/ByteBuffer.h" +#include "ble/att.h" namespace trygvis { namespace bluetooth { @@ -20,74 +21,6 @@ using namespace std; template<typename T> using o = std::experimental::optional<T>; -struct Uuid { - uint8_t value[16]; - - explicit Uuid(uint8_t value[16]) noexcept : value() { - memcpy(this->value, value, 16); - } - - Uuid(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5, uint8_t b6, uint8_t b7, - uint8_t b8, uint8_t b9, uint8_t b10, uint8_t b11, uint8_t b12, uint8_t b13, uint8_t b14, uint8_t b15) noexcept : value{ - b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15} {} - - bool operator==(const Uuid &other) { - return std::memcmp(value, other.value, 16) == 0; - } - - bool operator==(const Uuid &other) const { - return std::memcmp(value, other.value, 16) == 0; - } - - friend std::ostream &operator<<(std::ostream &s, Uuid const &uuid) { - auto &v = uuid.value; - s << std::hex << - v[0] << v[1] << ":" << - v[2] << v[3] << ":" << - v[4] << v[5] << ":" << - v[6] << v[7] << ":" << - v[8] << v[9] << ":" << - v[10] << v[11] << ":" << - v[12] << v[13] << ":" << - v[14] << v[15] << ":" << - std::endl; - return s; - } - - static Uuid fromShort(uint8_t b2, uint8_t b3) { - return {0x00, 0x00, b2, b3, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}; - } -}; - -struct ShortUuid { -private: -public: - explicit ShortUuid(uint16_t value) : value(value) {} - - Uuid toLong() - { - auto b2 = static_cast<uint8_t>(value >> 8); - auto b3 = static_cast<uint8_t>(value & 0xff); - return Uuid::fromShort(b2, b3); - } - - uint16_t value; -}; - - -namespace uuids { - -const ShortUuid HealthTermometerService{0x1809}; -const ShortUuid DeviceInformationService{0x180a}; -const ShortUuid BatteryService{0x180f}; - -const ShortUuid PRIMARY_SERVICE{0x2800}; -const ShortUuid SECONDARY_SERVICE{0x2801}; -const ShortUuid CHARACTERISTIC{0x2803}; - -const ShortUuid TemperatureMeasurement{0x2A1C}; -} - class BluetoothAdapter; class BluetoothDevice; @@ -102,18 +35,6 @@ typedef shared_ptr<BluetoothGatt> BluetoothGattPtr; typedef shared_ptr<BluetoothGattCharacteristic> BluetoothGattCharacteristicPtr; typedef shared_ptr<BluetoothGattService> BluetoothGattServicePtr; -class BluetoothException : public runtime_error { -public: - BluetoothException(const BluetoothAdapter *adapter, string const &what) : runtime_error(what), adapter(adapter), device(nullptr) {} - - BluetoothException(const BluetoothDevice *device, string const &what) : runtime_error(what), adapter(nullptr), device(device) {} - - explicit BluetoothException(string const &what) : runtime_error(what), adapter(nullptr), device(nullptr) {} - - const BluetoothAdapter *adapter; - const BluetoothDevice *device; -}; - class Mac { public: explicit Mac(uint8_t _5, uint8_t _4, uint8_t _3, uint8_t _2, uint8_t _1, uint8_t _0) : bytes() { @@ -269,94 +190,7 @@ private: map<string, shared_ptr<BluetoothAdapter>> adapters; }; -/** - * BLUETOOTH SPECIFICATION Version 4.0 [Vol 3] - Attribute Protocol (ATT) - 3.4.8 Attribute Opcode Summary - * Table 3.37 - */ -enum AttPduType { - ERROR = 0x01, - EXCHANGE_MTU_REQ = 0x02, - EXCHANGE_MTU_RES = 0x03, - FIND_INFORMATION_REQ = 0x04, - FIND_INFORMATION_RES = 0x05, - FIND_BY_TYPE_VALUE_REQ = 0x06, - FIND_BY_TYPE_VALUE_RES = 0x07, - READ_BY_TYPE_REQ = 0x08, - READ_BY_TYPE_RES = 0x09, - READ_REQ = 0x0a, - READ_RES = 0x0b, - READ_BLOB_REQ = 0x0c, - READ_BLOB_RES = 0x0d, - READ_MULTIPLE_REQ = 0x0e, - READ_MULTIPLE_RES = 0x0f, - READ_BY_GROUP_TYPE_REQ = 0x10, - READ_BY_GROUP_TYPE_RES = 0x11, - WRITE_REQ = 0x12, - WRITE_RES = 0x13, - - WRITE_CMD = 0x52, - PREPARE_WRITE_REQ = 0x16, - PREPARE_WRITE_RES = 0x17, - EXECUTE_WRITE_REQ = 0x18, - EXECUTE_WRITE_RES = 0x19, - HANDLE_VALUE_NOTIFICATION = 0x1b, - HANDLE_VALUE_INDICATION = 0x1d, - HANDLE_VALUE_CONFIRMATION = 0x1e, - SIGNED_WRITE_COMMAND = 0xd2, -}; - -class AttributeData; - -class AttPdu { -public: - explicit AttPdu(ByteBuffer &bytes); - - AttPdu(ByteBuffer &bytes, AttPduType type); - - AttPduType getType(); - - static vector<AttributeData> parseReadByGroupType(ByteBuffer &bytes); - - static vector<AttributeData> parseReadByType(ByteBuffer &bytes); - - static uint16_t parseExchangeMtuReq(ByteBuffer &bytes); - - static void parseRead(ByteBuffer &bytes); - - static void parseWrite(ByteBuffer &bytes); - - static void makeReadByGroupType(ByteBuffer &bytes, uint16_t startHandle, uint16_t endHandle, ShortUuid uuid); - - static void makeReadByType(ByteBuffer &bytes, uint16_t startHandle, uint16_t endHandle, ShortUuid uuid); - - static void makeRead(ByteBuffer &bytes, uint16_t handle); - - static void makeWrite(ByteBuffer &req, uint16_t handle, const ByteBuffer &bytes); - -private: -// static void checkType(ByteBuffer &bytes, AttPduType type); - - static vector<AttributeData> parse(ByteBuffer &bytes, AttPduType type); - - ByteBuffer &bytes; -}; - -class AttributeData { -public: - ~AttributeData(); - - static AttributeData fromByteBuffer(ByteBuffer &value); - - const uint16_t handle; - ByteBuffer value; - -private: - AttributeData(uint16_t handle, ByteBuffer value); -}; - -Uuid makeUuid(const Uuid& base, uint8_t a, uint8_t b); - -} -} +} // namespace bluetooth +} // namespace trygvis #endif diff --git a/include/ble/ByteBuffer.h b/include/ble/ByteBuffer.h index 1032187..5e904c2 100644 --- a/include/ble/ByteBuffer.h +++ b/include/ble/ByteBuffer.h @@ -33,43 +33,53 @@ static const double precision = 10000000; class ByteBufferException : public std::runtime_error { public: - ByteBufferException(std::string const &what) : std::runtime_error(what) { - } + explicit ByteBufferException(std::string const &what) : std::runtime_error(what) {} }; class ByteBuffer { public: static ByteBuffer alloc(std::size_t capacity); - ByteBuffer(const std::shared_ptr<uint8_t> bytes, size_t capacity); - - ByteBuffer(const std::shared_ptr<uint8_t> bytes, size_t capacity, size_t size); + template<size_t N> + explicit ByteBuffer(uint8_t (&bytes)[N]) : zero(bytes), end_(&bytes[N]), cursor(bytes) {} - ByteBuffer(const std::shared_ptr<uint8_t> bytes, size_t capacity, size_t size, size_t zero); + ByteBuffer(uint8_t* bytes, size_t capacity); inline size_t getSize() const { - return end - zero; - } - - inline size_t getCapacity() const { - return capacity; + return end_ - zero; } inline size_t getCursor() const { - return ptr - zero; + return cursor - zero; } inline size_t getBytesLeft() const { - return end - ptr; + return end_ - cursor; } inline ByteBuffer &setCursor(size_t newCursor) { - ptr = (uint8_t *) &zero[newCursor]; + cursor = (uint8_t *) &zero[newCursor]; return *this; } inline void skip(size_t length) { - ptr += length; + cursor += length; + } + + inline const uint8_t *cbegin() { + return zero; + } + + inline const uint8_t *cend() { + return end_; + } + + inline uint8_t *begin() { + return const_cast<uint8_t *>(zero); + } + + inline uint8_t *end() { + return const_cast<uint8_t *>(end_); } ByteBuffer &write8(uint8_t value); @@ -87,8 +97,16 @@ public: ByteBuffer &writeFLOAT(double d); + /** + * Reads a uint8_t from the start of the buffer. + */ uint8_t get8(size_t index) const; + /** + * Reads a uint8_t relative to the pointer. + */ + uint8_t peek8(size_t relative_index) const; + uint8_t read8(); uint16_t read16le(); @@ -102,32 +120,31 @@ public: void copy(uint8_t *bytes, size_t length) const; + void reset(); + /** * Creates a view from cursor to size. */ ByteBuffer view() const; - // TODO: should return const ByteBuffer view(size_t length) const; + ByteBuffer viewAndSkip(size_t length) { + auto v = view(length); + skip(length); + return v; + } + std::string toString() const; private: - ByteBuffer(const std::shared_ptr<uint8_t> bytes, size_t capacity, const uint8_t *zero, const uint8_t *end); - - ByteBuffer view(uint8_t *ptr, const uint8_t *end) const; - - void checkAndUpdateEnd(size_t count); - void assertCanAccessRelative(size_t diff) const; void assertCanAccessIndex(uint8_t *p) const; - const std::shared_ptr<uint8_t> bytes; - const size_t capacity; const uint8_t *zero; - const uint8_t *end; - uint8_t *ptr; + const uint8_t *end_; + uint8_t *cursor; }; #endif diff --git a/include/ble/att.h b/include/ble/att.h new file mode 100644 index 0000000..3c7914f --- /dev/null +++ b/include/ble/att.h @@ -0,0 +1,186 @@ +#pragma once + +#include <experimental/optional> +#include <variant> +#include <vector> + +#include "ble/ByteBuffer.h" +#include "ble/misc.h" + +namespace trygvis { +namespace bluetooth { + +template<typename T> +using o = std::experimental::optional<T>; + +/** + * BLUETOOTH SPECIFICATION Version 4.0 [Vol 3] - Attribute Protocol (ATT) - 3.4.8 Attribute Opcode Summary + * Table 3.37 + */ +enum AttPduType { + ERROR = 0x01, + EXCHANGE_MTU_REQ = 0x02, + EXCHANGE_MTU_RES = 0x03, + FIND_INFORMATION_REQ = 0x04, + FIND_INFORMATION_RES = 0x05, + FIND_BY_TYPE_VALUE_REQ = 0x06, + FIND_BY_TYPE_VALUE_RES = 0x07, + READ_BY_TYPE_REQ = 0x08, + READ_BY_TYPE_RES = 0x09, + READ_REQ = 0x0a, + READ_RES = 0x0b, + READ_BLOB_REQ = 0x0c, + READ_BLOB_RES = 0x0d, + READ_MULTIPLE_REQ = 0x0e, + READ_MULTIPLE_RES = 0x0f, + READ_BY_GROUP_TYPE_REQ = 0x10, + READ_BY_GROUP_TYPE_RES = 0x11, + WRITE_REQ = 0x12, + WRITE_RES = 0x13, + + WRITE_CMD = 0x52, + PREPARE_WRITE_REQ = 0x16, + PREPARE_WRITE_RES = 0x17, + EXECUTE_WRITE_REQ = 0x18, + EXECUTE_WRITE_RES = 0x19, + HANDLE_VALUE_NOTIFICATION = 0x1b, + HANDLE_VALUE_INDICATION = 0x1d, + HANDLE_VALUE_CONFIRMATION = 0x1e, + SIGNED_WRITE_COMMAND = 0xd2, +}; + +std::string to_string(AttPduType t); + +class AttributeData; + +/** + * Application Error 0x80 – 0xFF Application error code defined by a higher layer specification. + * + * BLUETOOTH SPECIFICATION Version 4.0 [Vol 3] - 3.4.1.1 Error Response - Table 3.3 + */ +enum ErrorCode : uint8_t { + INVALID_HANDLE = 0x01, ///< The attribute handle given was not valid on this server. + READ_NOT_PERMITTED = 0x02,///< The attribute cannot be read. + WRITE_NOT_PERMITTED = 0x03,///< The attribute cannot be written. + INVALID_PDU = 0x04, ///<The attribute PDU was invalid. + INSUFFICIENT_AUTHENTICATION = 0x05, ///<The attribute requires authentication before it can be read or written. + REQUEST_NOT_SUPPORTED = 0x06, ///<Attribute server does not support the request received from the client. + INVALID_OFFSET = 0x07, ///<Offset specified was past the end of the attribute. + INSUFFICIENT_AUTHORIZATION = 0x08, ///<The attribute requires authorization before it can be read or written. + PREPARE_QUEUE_FULL = 0x09, ///<Too many prepare writes have been queued. + ATTRIBUTE_NOT_FOUND = 0x0a, ///<No attribute found within the given attribute handle range. + ATTRIBUTE_NOT_LONG = 0x0b, ///<The attribute cannot be read or written using the Read Blob Request + INSUFFICIENT_ENCRYPTION_KEY_SIZE = 0x0c, ///<The Encryption Key Size used for encrypting this link is insufficient. + INVALID_ATTRIBUTE_VALUE_LENGTH = 0x0d, ///<The attribute value length is invalid for the operation + UNLIKELY_ERROR = 0x0e, ///<The attribute request that was requested has encountered an error that was unlikely, and therefore could not be completed as requested. + INSUFFICIENT_ENCRYPTION = 0x0f, ///<The attribute requires encryption before it can be read or written. + UNSUPPORTED_GROUP_TYPE = 0x10, ///<The attribute type is not a supported grouping attribute as defined by a higher layer specification. + INSUFFICIENT_RESOURCES = 0x11, ///<Insufficient Resources to complete the request Reserved 0x012 – 0x7F Reserved for future use. +}; + +struct ErrorRes { + static constexpr auto att_pdu_type = AttPduType::EXCHANGE_MTU_REQ; + + /** + * The request that generated this error response. + */ + uint8_t requestOpcode; + + /** + * The attribute handle that generated this error response. + */ + uint16_t attributeHandle; + + /** + * The reason why the request has generated an error response. + */ + uint8_t errorCode; + + static ErrorRes parse(ByteBuffer& buffer) { + return {buffer.read8(), buffer.read16le(), buffer.read8()}; + } +}; + +struct ExchangeMtuReq { + static constexpr auto att_pdu_type = AttPduType::EXCHANGE_MTU_REQ; + + uint16_t clientRxMtu; +}; + +struct ExchangeMtuRes { + static constexpr auto att_pdu_type = AttPduType::EXCHANGE_MTU_RES; + + uint16_t serverRxMtu; +}; + +struct ReadByGroupTypeRes { + static constexpr auto att_pdu_type = AttPduType::READ_BY_GROUP_TYPE_RES; + + std::vector<AttributeData> attributes; +}; + +struct ReadByTypeRes { + static constexpr auto att_pdu_type = AttPduType::READ_BY_TYPE_RES; + + std::vector<AttributeData> attributes; +}; + +using AttVariant = std::variant<std::monostate, + ErrorRes, + ExchangeMtuReq, ExchangeMtuRes, + ReadByGroupTypeRes, ReadByTypeRes>; + +o<AttPduType> attPduType(const AttVariant& v); + +class AttPdu { +public: + explicit AttPdu(ByteBuffer &bytes); + + AttPdu(ByteBuffer &bytes, AttPduType type); + + AttPduType getType(); + + static AttVariant parse(ByteBuffer &bytes); + + static ExchangeMtuReq parseExchangeMtuReq(ByteBuffer &bytes); + static ExchangeMtuRes parseExchangeMtuRes(ByteBuffer &bytes); + + static void parseRead(ByteBuffer &bytes); + + static void parseWrite(ByteBuffer &bytes); + + static void makeExchangeMtuRes(ByteBuffer &bytes, uint16_t serverRxMtu); + + static void makeReadByGroupType(ByteBuffer &bytes, uint16_t startHandle, uint16_t endHandle, ShortUuid uuid); + + static void makeReadByType(ByteBuffer &bytes, uint16_t startHandle, uint16_t endHandle, ShortUuid uuid); + + static void makeRead(ByteBuffer &bytes, uint16_t handle); + + static void makeWrite(ByteBuffer &req, uint16_t handle, const ByteBuffer &bytes); + +private: +// static void checkType(ByteBuffer &bytes, AttPduType type); + + static std::vector<AttributeData> parseAttributeData(ByteBuffer &bytes); + + ByteBuffer &bytes; +}; + +class AttributeData { +public: + ~AttributeData(); + + static AttributeData fromByteBuffer(ByteBuffer &value); + + const uint16_t handle; + ByteBuffer value; + +private: + AttributeData(uint16_t handle, ByteBuffer value, std::shared_ptr<uint8_t[]> raw); + + std::shared_ptr<uint8_t[]> raw; +}; + +} // namespace bluetooth +} // namespace trygvis diff --git a/include/ble/misc.h b/include/ble/misc.h new file mode 100644 index 0000000..089c3a1 --- /dev/null +++ b/include/ble/misc.h @@ -0,0 +1,84 @@ +#pragma once + +#include <cstring> +#include <cctype> +#include <stdexcept> + +namespace trygvis { +namespace bluetooth { + +class BluetoothAdapter; +class BluetoothDevice; + +class BluetoothException : public std::runtime_error { +public: + BluetoothException(const BluetoothAdapter *adapter, std::string const &what) : runtime_error(what), adapter(adapter), device(nullptr) {} + + BluetoothException(const BluetoothDevice *device, std::string const &what) : runtime_error(what), adapter(nullptr), device(device) {} + + explicit BluetoothException(std::string const &what) : runtime_error(what), adapter(nullptr), device(nullptr) {} + + const BluetoothAdapter *adapter; + const BluetoothDevice *device; +}; + +struct Uuid { + uint8_t value[16]; + + explicit Uuid(uint8_t value[16]) noexcept : value() { + std::memcpy(this->value, value, 16); + } + + Uuid(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5, uint8_t b6, uint8_t b7, + uint8_t b8, uint8_t b9, uint8_t b10, uint8_t b11, uint8_t b12, uint8_t b13, uint8_t b14, uint8_t b15) noexcept : value{ + b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15} {} + + bool operator==(const Uuid &other) { + return std::memcmp(value, other.value, 16) == 0; + } + + bool operator==(const Uuid &other) const { + return std::memcmp(value, other.value, 16) == 0; + } + + friend std::ostream &operator<<(std::ostream &s, Uuid const &uuid); + + std::string str() const; + + static Uuid fromShort(uint8_t b2, uint8_t b3) { + return {0x00, 0x00, b2, b3, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb}; + } +}; + +struct ShortUuid { +private: +public: + explicit ShortUuid(uint16_t value) : value(value) {} + + Uuid toLong() + { + auto b2 = static_cast<uint8_t>(value >> 8); + auto b3 = static_cast<uint8_t>(value & 0xff); + return Uuid::fromShort(b2, b3); + } + + uint16_t value; +}; + +Uuid makeUuid(const Uuid& base, uint8_t a, uint8_t b); + +namespace uuids { + +const ShortUuid HealthTermometerService{0x1809}; +const ShortUuid DeviceInformationService{0x180a}; +const ShortUuid BatteryService{0x180f}; + +const ShortUuid PRIMARY_SERVICE{0x2800}; +const ShortUuid SECONDARY_SERVICE{0x2801}; +const ShortUuid CHARACTERISTIC{0x2803}; + +const ShortUuid TemperatureMeasurement{0x2A1C}; +} + +} // namespace bluetooth +} // namespace trygvis |