aboutsummaryrefslogtreecommitdiff
path: root/include/ble
diff options
context:
space:
mode:
authorTrygve Laugstøl <trygvis@inamo.no>2018-09-05 14:14:42 +0200
committerTrygve Laugstøl <trygvis@inamo.no>2018-09-05 14:14:42 +0200
commit91e54cf9150b37036447d423857d2bd18e4bf02b (patch)
tree7738db9a61cb6385f0abc6359b5b89aaa1ff104d /include/ble
parent25d82b0c52120c81cfed5bc1f245408f08203b7b (diff)
downloadble-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.h174
-rw-r--r--include/ble/ByteBuffer.h69
-rw-r--r--include/ble/att.h186
-rw-r--r--include/ble/misc.h84
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