diff options
-rw-r--r-- | Bluetooth.cpp | 36 | ||||
-rw-r--r-- | Bluetooth.h | 39 | ||||
-rw-r--r-- | BluetoothImpl.h | 77 | ||||
-rw-r--r-- | ByteBuffer.cpp | 31 | ||||
-rw-r--r-- | ByteBuffer.h | 20 | ||||
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | LinuxBluetooth.cpp | 101 | ||||
-rw-r--r-- | test/ByteBufferTest.cpp | 40 |
8 files changed, 268 insertions, 78 deletions
diff --git a/Bluetooth.cpp b/Bluetooth.cpp index ebdd527..a8b95a1 100644 --- a/Bluetooth.cpp +++ b/Bluetooth.cpp @@ -65,7 +65,7 @@ AttPdu::AttPdu(ByteBuffer &bytes) : bytes(bytes) { } AttPdu::AttPdu(ByteBuffer &bytes, AttPduType type) : bytes(bytes) { - bytes.add8(type); + bytes.write8(type); } AttPduType AttPdu::getType() { @@ -73,11 +73,17 @@ AttPduType AttPdu::getType() { } void AttPdu::makeReadByGroupType(ByteBuffer &bytes, uint16_t startHandle, uint16_t endHandle, uint16_t uuid) { - bytes.setCursor(0); - bytes.add8(AttPduType::READ_BY_GROUP_TYPE_REQ); - bytes.add16le(startHandle); - bytes.add16le(endHandle); - bytes.add16le(uuid); + bytes.write8(AttPduType::READ_BY_GROUP_TYPE_REQ); + bytes.write16le(startHandle); + bytes.write16le(endHandle); + bytes.write16le(uuid); +} + +void AttPdu::makeReadByType(ByteBuffer &bytes, uint16_t startHandle, uint16_t endHandle, uint16_t uuid) { + bytes.write8(AttPduType::READ_BY_TYPE_REQ); + bytes.write16le(startHandle); + bytes.write16le(endHandle); + bytes.write16le(uuid); } void AttPdu::checkType(ByteBuffer &bytes, AttPduType type) { @@ -86,7 +92,7 @@ void AttPdu::checkType(ByteBuffer &bytes, AttPduType type) { } bytes.setCursor(0); - AttPduType t = (AttPduType) bytes.get8(); + AttPduType t = (AttPduType) bytes.read8(); if (t != type) { throw BluetoothException("Unexpected type: " + to_string(t)); @@ -102,7 +108,7 @@ vector<AttributeData> AttPdu::parseReadByGroupType(ByteBuffer &bytes) { throw BluetoothException("Bad READ_BY_GROUP_TYPE_RES packet, expected at least 4 octets, got " + to_string(bytes.getSize())); } - uint8_t length = bytes.get8(); + uint8_t length = bytes.read8(); D << "length=" << (int) length; size_t count = (bytes.getSize() - 2) / length; @@ -124,8 +130,8 @@ vector<AttributeData> AttPdu::parseReadByGroupType(ByteBuffer &bytes) { // ----------------------------------------------------------------------- AttributeData AttributeData::fromByteBuffer(ByteBuffer &bytes) { - uint16_t handle = bytes.get16le(); - uint16_t groupEndHandle = bytes.get16le(); + uint16_t handle = bytes.read16le(); + uint16_t groupEndHandle = bytes.read16le(); return AttributeData(handle, groupEndHandle, bytes.view()); } @@ -138,6 +144,16 @@ AttributeData::~AttributeData() { } // ----------------------------------------------------------------------- +// Device +// ----------------------------------------------------------------------- + +BluetoothDevice::BluetoothDevice() { +} + +BluetoothDevice::~BluetoothDevice() { +} + +// ----------------------------------------------------------------------- // Adapter // ----------------------------------------------------------------------- diff --git a/Bluetooth.h b/Bluetooth.h index 1b5d5f0..157ae0b 100644 --- a/Bluetooth.h +++ b/Bluetooth.h @@ -3,20 +3,23 @@ #include <string> #include <stdexcept> - -// For now -#include "log.h" +#include <boost/uuid/uuid.hpp> #include "ByteBuffer.h" namespace trygvis { namespace bluetooth { using namespace std; +//using namespace boost; class BluetoothAdapter; class BluetoothDevice; +class BluetoothGattService; + +class BluetoothGattCharacteristic; + class BluetoothException : public runtime_error { public: BluetoothException(const BluetoothAdapter *adapter, string const &what) : @@ -62,8 +65,28 @@ private: uint8_t bytes[6]; }; +class BluetoothGattCharacteristic { +public: + virtual BluetoothGattService &getService() const = 0; + + virtual const boost::uuids::uuid getUuid() const = 0; +}; + +class BluetoothGattService { +public: + virtual BluetoothDevice &getDevice() const = 0; + + virtual boost::uuids::uuid getUuid() const = 0; + + virtual vector<BluetoothGattCharacteristic *>::const_iterator getCharacteristics() const = 0; +}; + class BluetoothDevice { public: + BluetoothDevice(); + + virtual ~BluetoothDevice(); + virtual Mac const &mac() = 0; virtual BluetoothAdapter &adapter() = 0; @@ -73,10 +96,14 @@ public: virtual void disconnect() = 0; virtual void discoverServices() = 0; + + virtual vector<BluetoothGattService *>::const_iterator getServices() const = 0; }; class BluetoothAdapter { public: + virtual void startScan() = 0; + virtual void stopScan() = 0; virtual void runScan(void (callback)(BluetoothDevice &device)) = 0; @@ -91,8 +118,10 @@ protected: enum AttPduType { ERROR = 0x00, + READ_BY_TYPE_REQ = 0x08, + READ_BY_TYPE_RES = 0x09, READ_BY_GROUP_TYPE_REQ = 0x10, - READ_BY_GROUP_TYPE_RES = 0x11 + READ_BY_GROUP_TYPE_RES = 0x11, }; class AttributeData; @@ -109,6 +138,8 @@ public: static void makeReadByGroupType(ByteBuffer &bytes, uint16_t startHandle, uint16_t endHandle, uint16_t uuid); + static void makeReadByType(ByteBuffer &bytes, uint16_t startHandle, uint16_t endHandle, uint16_t uuid); + private: static void checkType(ByteBuffer &bytes, AttPduType type); diff --git a/BluetoothImpl.h b/BluetoothImpl.h index ba44853..3d3ce29 100644 --- a/BluetoothImpl.h +++ b/BluetoothImpl.h @@ -1,9 +1,86 @@ #ifndef BLUETOOTH_IMPL_H #define BLUETOOTH_IMPL_H +#include "Bluetooth.h" +#include <boost/uuid/uuid_io.hpp> + +#define BLUETOOTH_UUID_INITIALIZER \ + { \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, \ + 0x10, 0x00, \ + 0x80, 0x00, \ + 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb \ + }; + namespace trygvis { namespace bluetooth { +typedef boost::uuids::uuid uuid_t; + +class DefaultBluetoothGattCharacteristic : public BluetoothGattCharacteristic { +public: + DefaultBluetoothGattCharacteristic(BluetoothGattService& service, uuid_t uuid) : service(service), uuid(uuid) { + } + + virtual ~DefaultBluetoothGattCharacteristic() { + } + + const uuid_t getUuid() const { + return uuid; + }; + + BluetoothGattService& getService() const { + return service; + }; + +protected: + BluetoothGattService& service; + uuid_t uuid; +}; + +class DefaultBluetoothGattService : public BluetoothGattService { +public: + DefaultBluetoothGattService(BluetoothDevice& device, const uuid_t uuid) : + device(device), uuid(uuid) { + } + + virtual ~DefaultBluetoothGattService() { + } + + BluetoothDevice& getDevice() const { + return device; + } + + uuid_t getUuid() const { + return uuid; + } + + vector<BluetoothGattCharacteristic* >::const_iterator getCharacteristics() const { + return characteristics.begin(); + } + +protected: + BluetoothDevice &device; + const uuid_t uuid; + const vector<BluetoothGattCharacteristic*> characteristics; +}; + +class DefaultBluetoothDevice : public BluetoothDevice { +public: + virtual vector<BluetoothGattService*>::const_iterator getServices() const { + return services.begin(); + }; + +protected: + virtual ~DefaultBluetoothDevice() { + for (auto s: services) { + delete s; + } + } + vector<BluetoothGattService*> services; +}; + BluetoothAdapter &getAdapterImpl(int hciDevice); void shutdownImpl(); diff --git a/ByteBuffer.cpp b/ByteBuffer.cpp index d60ad6f..820c638 100644 --- a/ByteBuffer.cpp +++ b/ByteBuffer.cpp @@ -5,46 +5,46 @@ using namespace std; -ByteBuffer::ByteBuffer(const uint8_t *bytes, size_t capacity) : - bytes(bytes), capacity(capacity), zero(&bytes[0]), end(&bytes[capacity]) { - ptr = (uint8_t *) &bytes[0]; +ByteBuffer::ByteBuffer(const std::shared_ptr<uint8_t> bytes, size_t capacity) : + bytes(bytes), capacity(capacity), zero(bytes.get()), end(&bytes.get()[capacity]) { + ptr = (uint8_t *) zero; } -ByteBuffer::ByteBuffer(const uint8_t *bytes, size_t capacity, size_t zero, size_t size) : - bytes(bytes), capacity(capacity), zero(&bytes[0]), end(&bytes[size]) { +ByteBuffer::ByteBuffer(const std::shared_ptr<uint8_t> bytes, size_t capacity, size_t zero, size_t size) : + bytes(bytes), capacity(capacity), zero(&bytes.get()[zero]), end(&bytes.get()[size]) { assert(zero <= size); assert(size <= capacity); - ptr = (uint8_t *) &bytes[0]; + ptr = (uint8_t *) zero; } -ByteBuffer::ByteBuffer(const uint8_t *bytes, size_t capacity, const uint8_t *zero, const uint8_t *end) : +ByteBuffer::ByteBuffer(const std::shared_ptr<uint8_t> bytes, size_t capacity, const uint8_t *zero, const uint8_t *end) : bytes(bytes), capacity(capacity), zero(zero), end(end), ptr((uint8_t *) zero) { } -ByteBuffer &ByteBuffer::add8(uint8_t value) { +ByteBuffer &ByteBuffer::write8(uint8_t value) { checkAndUpdateEnd(1); (*ptr++) = value; return *this; } -ByteBuffer &ByteBuffer::add16le(uint16_t value) { +ByteBuffer &ByteBuffer::write16le(uint16_t value) { checkAndUpdateEnd(2); (*ptr++) = (uint8_t) (value & 0xff); (*ptr++) = (uint8_t) ((value >> 8) & 0xff); return *this; } -uint8_t ByteBuffer::get8(size_t index) { +uint8_t ByteBuffer::get8(size_t index) const { assertCanAccessRelative(index); - return *ptr++; + return ptr[index]; } -uint8_t ByteBuffer::get8() { +uint8_t ByteBuffer::read8() { assertCanAccessRelative(0); return *ptr++; } -uint16_t ByteBuffer::get16le() { +uint16_t ByteBuffer::read16le() { assertCanAccessRelative(0); uint16_t value; value = *ptr++; @@ -52,11 +52,10 @@ uint16_t ByteBuffer::get16le() { return value; } -void ByteBuffer::copy(uint8_t *bytes, size_t length) { - assertCanAccessRelative(length); +void ByteBuffer::copy(uint8_t *bytes, size_t length) const { + assertCanAccessRelative(length - 1); memcpy(bytes, ptr, length); - ptr += length; } ByteBuffer ByteBuffer::view() const { diff --git a/ByteBuffer.h b/ByteBuffer.h index a572097..b3e06cf 100644 --- a/ByteBuffer.h +++ b/ByteBuffer.h @@ -20,12 +20,12 @@ public: /** * Wrapping constructor, the size will be equal to the capacity. */ - ByteBuffer(const uint8_t *bytes, size_t capacity); + ByteBuffer(const std::shared_ptr<uint8_t> bytes, size_t capacity); /** * Wrapping constructor. */ - ByteBuffer(const uint8_t *bytes, size_t capacity, size_t zero, size_t size); + ByteBuffer(const std::shared_ptr<uint8_t> bytes, size_t capacity, size_t zero, size_t size); inline size_t getSize() const { // DF << "end=" << (uint64_t)end << ", zero=" << (uint64_t)zero << ", size=" << (end - zero); @@ -50,17 +50,17 @@ public: ptr += length; } - ByteBuffer &add8(uint8_t value); + ByteBuffer &write8(uint8_t value); - ByteBuffer &add16le(uint16_t value); + ByteBuffer &write16le(uint16_t value); - uint8_t get8(size_t index); + uint8_t get8(size_t index) const; - uint8_t get8(); + uint8_t read8(); - uint16_t get16le(); + uint16_t read16le(); - void copy(uint8_t *bytes, size_t length); + void copy(uint8_t *bytes, size_t length) const; /** * Creates a view from cursor to size. @@ -73,7 +73,7 @@ public: std::string toString() const; private: - ByteBuffer(const uint8_t *bytes, size_t capacity, const uint8_t *zero, const uint8_t *end); + 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; @@ -83,7 +83,7 @@ private: void assertCanAccessIndex(uint8_t *p) const; - const uint8_t *bytes; + const std::shared_ptr<uint8_t> bytes; const size_t capacity; const uint8_t *zero; const uint8_t *end; diff --git a/CMakeLists.txt b/CMakeLists.txt index e09c024..bd0d7a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 2.8.4) project(ble_toys C CXX) find_package(PkgConfig) +# Use Clang by default: http://stackoverflow.com/a/7031553/245614 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") set(SOURCE_FILES "${SOURCE_FILES}" diff --git a/LinuxBluetooth.cpp b/LinuxBluetooth.cpp index b886dde..7be7c9a 100644 --- a/LinuxBluetooth.cpp +++ b/LinuxBluetooth.cpp @@ -1,4 +1,4 @@ -#include "Bluetooth.h" +#include "BluetoothImpl.h" #include <string.h> #include <bluetooth/bluetooth.h> @@ -35,9 +35,9 @@ public: BluetoothDevice &getDevice(Mac &mac) override; private: - void startScan(); + void startScan() override; - void stopScan(); + void stopScan() override; int hciDeviceId; int hciSocket; @@ -47,7 +47,7 @@ private: map<Mac, LinuxBluetoothDevice *> devices; }; -class LinuxBluetoothDevice : public BluetoothDevice { +class LinuxBluetoothDevice : public DefaultBluetoothDevice { public: LinuxBluetoothDevice(LinuxBluetoothAdapter &adapter, Mac mac); @@ -62,6 +62,8 @@ public: void discoverServices() override; private: + vector <AttributeData> discoverServices(uint16_t startHandle); + LinuxBluetoothAdapter &_adapter; Mac _mac; int l2cap; @@ -86,7 +88,7 @@ Mac parseMac(bdaddr_t &a) { // ----------------------------------------------------------------------- LinuxBluetoothDevice::LinuxBluetoothDevice(LinuxBluetoothAdapter &adapter, Mac mac) : - _adapter(adapter), _mac(mac) { + DefaultBluetoothDevice(), _adapter(adapter), _mac(mac) { } Mac const &LinuxBluetoothDevice::mac() { @@ -149,25 +151,87 @@ void LinuxBluetoothDevice::disconnect() { } void LinuxBluetoothDevice::discoverServices() { + uint16_t startHandle = 0x0001; + + do { + vector<AttributeData> values = discoverServices(startHandle); + + // Shouldn't happen, but you never know. + if (values.size() == 0) { + break; + } + + for (auto &data: values) { + D << "handle: 0x" << hex << setw(4) << setfill('0') << data.handle << + ", groupEndHandle: 0x" << hex << setw(4) << setfill('0') << data.groupEndHandle << + ", value: " << data.value.toString(); + + boost::uuids::uuid u; + + size_t s = data.value.getSize(); + + if (s == 2) { + uint8_t bs[16] = BLUETOOTH_UUID_INITIALIZER; + bs[2] = data.value.get8(1); + bs[3] = data.value.get8(0); + memcpy(&u, bs, 16); + } else if (s == 16) { + uint8_t bs[16]; + bs[15] = data.value.get8(0); + bs[14] = data.value.get8(1); + bs[13] = data.value.get8(2); + bs[12] = data.value.get8(3); + bs[11] = data.value.get8(4); + bs[10] = data.value.get8(5); + bs[9] = data.value.get8(6); + bs[8] = data.value.get8(7); + bs[7] = data.value.get8(8); + bs[6] = data.value.get8(9); + bs[5] = data.value.get8(10); + bs[4] = data.value.get8(11); + bs[3] = data.value.get8(12); + bs[2] = data.value.get8(13); + bs[1] = data.value.get8(14); + bs[0] = data.value.get8(15); + memcpy(&u, bs, 16); + } + else { + throw BluetoothException(this, "Unexpected size: " + to_string(data.value.getSize())); + } + + services.push_back(new DefaultBluetoothGattService(*this, u)); + } + + auto last = values.back(); + + startHandle = last.groupEndHandle; + } while (startHandle != 0xffff); + + for(auto s : services) { + D << "service: " << to_string(s->getUuid()); + } +} + +vector<AttributeData> LinuxBluetoothDevice::discoverServices(uint16_t startHandle) { DF; - uint8_t buffer[MAX_MTU]; + shared_ptr<uint8_t> buffer(new uint8_t[MAX_MTU]); ByteBuffer out = ByteBuffer(buffer, MAX_MTU); - AttPdu::makeReadByGroupType(out, 0x0001, 0xffff, UUID_PRIMARY_SERVICE); + AttPdu::makeReadByGroupType(out, startHandle, 0xffff, UUID_PRIMARY_SERVICE); D << "pdu size=" << out.getCursor(); - ssize_t written = write(l2cap, buffer, out.getCursor()); + ssize_t written = write(l2cap, buffer.get(), out.getCursor()); D << "written=" << written; - ssize_t r = read(l2cap, buffer, MAX_MTU); + ssize_t r = read(l2cap, buffer.get(), MAX_MTU); if (r == -1) { throw BluetoothException(this, "read(): " + errnoAsString()); } - ByteBuffer in = ByteBuffer(buffer, (size_t) r); + auto in = ByteBuffer(buffer, (size_t) r); D << "read: " << r << " bytes: " << in.toString(); @@ -175,11 +239,7 @@ void LinuxBluetoothDevice::discoverServices() { D << "READ_BY_GROUP_TYPE response has " + to_string(values.size()) + " values"; - for (auto &data: values) { - D << "handle: 0x" << setw(4) << setfill('0') << hex << data.handle << - ", groupEndHandle: 0x" << hex << setw(4) << setfill('0') << data.groupEndHandle << - ", value: " << data.value.toString(); - } + return values; } // ----------------------------------------------------------------------- @@ -241,7 +301,7 @@ void LinuxBluetoothAdapter::startScan() { int up = hci_test_bit(HCI_UP, &di.flags); if (!up) { - throw BluetoothException(this, "HCI adapter is not up: " + hciDeviceId); + throw BluetoothException(this, "HCI adapter is not up: " + to_string(hciDeviceId)); } if (hci_le_set_scan_parameters(hciSocket, 0x01, htobs(0x0010), htobs(0x0010), 0x00, 0, 1000) < 0) { @@ -326,7 +386,9 @@ void LinuxBluetoothAdapter::runScan(void (*callback)(BluetoothDevice &device)) { } } -}}} +} +} +} // ----------------------------------------------------------------------- // Implementation of platform-specific method. @@ -338,7 +400,7 @@ using namespace trygvis::bluetooth::linux; map<int, LinuxBluetoothAdapter *> adapters; -LinuxBluetoothAdapter &getAdapterImpl(int hciDevice) { +BluetoothAdapter &getAdapterImpl(int hciDevice) { map<int, LinuxBluetoothAdapter *>::iterator it = adapters.find(hciDevice); if (it == adapters.end()) { @@ -356,4 +418,5 @@ void shutdownImpl() { } } -}} +} +} diff --git a/test/ByteBufferTest.cpp b/test/ByteBufferTest.cpp index 2a9bb8c..7ed75e1 100644 --- a/test/ByteBufferTest.cpp +++ b/test/ByteBufferTest.cpp @@ -13,22 +13,24 @@ using namespace std; +template<typename T> +struct do_nothing { + void operator()(T *) { + } +}; + class Bytes { public: Bytes(size_t size) : capacity(size) { - _bytes = new uint8_t[size + 0x100]; - - bytes = (uint8_t *) (((uint64_t) &_bytes[0x100]) & 0xffffffffffffff00); - + _bytes = shared_ptr<uint8_t>(new uint8_t[size]); + uint8_t *tmp = (uint8_t *) (((uint64_t) &_bytes.get()[0x100]) & 0xffffffffffffff00); + bytes = shared_ptr<uint8_t>(tmp, ::do_nothing<uint8_t>()); for (int i = 0; i < size; i++) { - bytes[i] = (uint8_t) i; + bytes.get()[i] = (uint8_t) i; } } - ~Bytes() { - delete _bytes; - } - uint8_t *bytes; - uint8_t *_bytes; + shared_ptr<uint8_t> bytes; + shared_ptr<uint8_t> _bytes; size_t capacity; }; @@ -39,7 +41,7 @@ BOOST_AUTO_TEST_CASE(empty_buffer) { checkBuffer(buffer, 0, 0, 0); try { - buffer.get8(); + buffer.read8(); BOOST_FAIL("Expected exception"); } catch (ByteBufferException e) { } @@ -50,11 +52,11 @@ BOOST_AUTO_TEST_CASE(basic) { ByteBuffer buffer(b.bytes, 1000, 0, 1000); checkBuffer(buffer, 1000, 1000, 0); - BOOST_CHECK_EQUAL(buffer.get8(), 0); + BOOST_CHECK_EQUAL(buffer.read8(), 0); checkBuffer(buffer, 1000, 1000, 1); for (int i = 1; i < b.capacity; i++) { - BOOST_CHECK_EQUAL(buffer.get8(), b.bytes[i]); + BOOST_CHECK_EQUAL(buffer.read8(), b.bytes.get()[i]); } } @@ -63,13 +65,13 @@ BOOST_AUTO_TEST_CASE(setCursor) { ByteBuffer buffer(b.bytes, 1000, 0, 10); checkBuffer(buffer, 10, 1000, 0); - BOOST_CHECK_EQUAL(buffer.get8(), 0); + BOOST_CHECK_EQUAL(buffer.read8(), 0); checkBuffer(buffer, 10, 1000, 1); buffer.setCursor(0); checkBuffer(buffer, 10, 1000, 0); - BOOST_CHECK_EQUAL(buffer.get8(), 0); + BOOST_CHECK_EQUAL(buffer.read8(), 0); checkBuffer(buffer, 10, 1000, 1); buffer.setCursor(9); @@ -80,15 +82,15 @@ BOOST_AUTO_TEST_CASE(view) { Bytes b(1000); ByteBuffer buffer(b.bytes, b.capacity, 0, 10); - BOOST_CHECK_EQUAL(buffer.get8(), 0); + BOOST_CHECK_EQUAL(buffer.read8(), 0); ByteBuffer view1 = buffer.view(); checkBuffer(view1, 9, 9, 0); - BOOST_CHECK_EQUAL(view1.get8(), 1); - BOOST_CHECK_EQUAL(view1.get8(), 2); + BOOST_CHECK_EQUAL(view1.read8(), 1); + BOOST_CHECK_EQUAL(view1.read8(), 2); ByteBuffer view2 = view1.view(); checkBuffer(view2, 7, 7, 0); - BOOST_CHECK_EQUAL(view1.get8(), 3); + BOOST_CHECK_EQUAL(view1.read8(), 3); } |