diff options
-rw-r--r-- | Bluetooth.cpp | 22 | ||||
-rw-r--r-- | Bluetooth.h | 21 | ||||
-rw-r--r-- | ByteBuffer.cpp | 84 | ||||
-rw-r--r-- | ByteBuffer.h | 52 | ||||
-rw-r--r-- | CMakeLists.txt | 5 | ||||
-rw-r--r-- | LinuxBluetooth.cpp | 30 | ||||
-rw-r--r-- | log.h | 17 | ||||
-rw-r--r-- | test/ByteBufferTest.cpp | 63 |
8 files changed, 193 insertions, 101 deletions
diff --git a/Bluetooth.cpp b/Bluetooth.cpp index dd9e48e..f589d1f 100644 --- a/Bluetooth.cpp +++ b/Bluetooth.cpp @@ -1,7 +1,8 @@ +#include "Bluetooth.h" + #include <sstream> #include <iomanip> #include <string.h> -#include "Bluetooth.h" namespace trygvis { using namespace std; @@ -86,26 +87,31 @@ namespace trygvis { AttPduType t = (AttPduType) bytes.get8(); if (t != type) { - throw BluetoothException("Unexpected type: " + t); + throw BluetoothException("Unexpected type: " + to_string(t)); } } vector<AttributeData> AttPdu::parseReadByGroupType(ByteBuffer &bytes) { + DF; + checkType(bytes, READ_BY_GROUP_TYPE_RES); if (bytes.getSize() < 4) { - throw BluetoothException("Bad READ_BY_GROUP_TYPE_RES packet, expected at least 4 octets, got " + bytes.getSize()); + throw BluetoothException("Bad READ_BY_GROUP_TYPE_RES packet, expected at least 4 octets, got " + to_string(bytes.getSize())); } uint8_t length = bytes.get8(); - DF << "length=" << (int) length; + D << "length=" << (int) length; size_t count = (bytes.getSize() - 2) / length; - DF << "count=" << count; + D << "count=" << count; vector<AttributeData> values; for (int i = 0; i < count; i++) { - values.push_back(AttributeData::fromByteBuffer(bytes, length)); + auto data = bytes.view(length); + D << "data, size=" << data.getSize() << ", bytes=" << data.toString(); + bytes.skip(length); + values.push_back(AttributeData::fromByteBuffer(data)); } return values; @@ -115,11 +121,11 @@ namespace trygvis { // AttributeData // ----------------------------------------------------------------------- - AttributeData AttributeData::fromByteBuffer(ByteBuffer &bytes, uint8_t length) { + AttributeData AttributeData::fromByteBuffer(ByteBuffer &bytes) { uint16_t handle = bytes.get16le(); uint16_t groupEndHandle = bytes.get16le(); - return AttributeData(handle, groupEndHandle, bytes.view(length)); + return AttributeData(handle, groupEndHandle, bytes.view()); } AttributeData::AttributeData(uint16_t handle, uint16_t groupEndHandle, ByteBuffer value) : diff --git a/Bluetooth.h b/Bluetooth.h index 15ef171..7bc5bc3 100644 --- a/Bluetooth.h +++ b/Bluetooth.h @@ -5,20 +5,9 @@ #include <stdexcept> // For now -#include <boost/log/core.hpp> -#include <boost/log/trivial.hpp> -#include "ByteBuffer.h" - -#define D BOOST_LOG_TRIVIAL(debug) -#define I BOOST_LOG_TRIVIAL(info) -#define W BOOST_LOG_TRIVIAL(warning) +#include "log.h" -#define DF BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ": " -#define IF BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": " -#define WF BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": " - -#define UUID_PRIMARY_SERVICE 0x2800 -#define UUID_SECONDARY_SERVICE 0x2801 +#include "ByteBuffer.h" namespace trygvis { using namespace std; @@ -91,9 +80,11 @@ namespace trygvis { virtual ~BluetoothAdapter(); + virtual void runScan(void (callback)(BluetoothDevice &device)) = 0; + virtual void stopScan() = 0; - virtual void runScan(void (callback)(BluetoothDevice &device)) = 0; + virtual void startScan() = 0; virtual BluetoothDevice &getDevice(Mac& mac) = 0; }; @@ -128,7 +119,7 @@ namespace trygvis { public: ~AttributeData(); - static AttributeData fromByteBuffer(ByteBuffer &value, uint8_t length); + static AttributeData fromByteBuffer(ByteBuffer &value); const uint16_t handle; const uint16_t groupEndHandle; diff --git a/ByteBuffer.cpp b/ByteBuffer.cpp index f27deeb..c5f7905 100644 --- a/ByteBuffer.cpp +++ b/ByteBuffer.cpp @@ -1,5 +1,4 @@ #include "ByteBuffer.h" -#include <stdint-gcc.h> #include <string.h> #include <sstream> #include <iomanip> @@ -7,75 +6,96 @@ using namespace std; -ByteBuffer::ByteBuffer(uint8_t *bytes, size_t capacity, size_t size, size_t zero) : - bytes(bytes), capacity(capacity), size(size), cursor(zero), zero(zero) { +ByteBuffer::ByteBuffer(uint8_t *bytes, size_t capacity) : + bytes(bytes), capacity(capacity), zero(&bytes[0]), end(&bytes[capacity]) { + ptr = &bytes[0]; +} + +ByteBuffer::ByteBuffer(uint8_t *bytes, size_t capacity, size_t zero, size_t size) : + bytes(bytes), capacity(capacity), zero(&bytes[0]), end(&bytes[size]) { assert(zero <= size); + assert(size <= capacity); + ptr = &bytes[0]; +} + +ByteBuffer::ByteBuffer(const uint8_t *bytes, size_t capacity, uint8_t *zero, uint8_t *end) : + bytes(bytes), capacity(capacity), zero(zero), end(end), ptr(zero) { } ByteBuffer &ByteBuffer::add8(uint8_t value) { - checkAndUpdateSize(1); - bytes[zero + cursor++] = value; + checkAndUpdateEnd(1); + (*ptr++) = value; return *this; } ByteBuffer &ByteBuffer::add16le(uint16_t value) { - checkAndUpdateSize(2); - bytes[zero + cursor++] = (uint8_t) (value & 0xff); - bytes[zero + cursor++] = (uint8_t) ((value >> 8) & 0xff); + checkAndUpdateEnd(2); + (*ptr++) = (uint8_t) (value & 0xff); + (*ptr++) = (uint8_t) ((value >> 8) & 0xff); return *this; } uint8_t ByteBuffer::get8(size_t index) { - canAccessIndex(index); - return bytes[zero + cursor]; + assertCanAccessRelative(index); + return *ptr++; } uint8_t ByteBuffer::get8() { - canAccessIndex(cursor); - return bytes[zero + cursor++]; + assertCanAccessRelative(1); + return *ptr++; } uint16_t ByteBuffer::get16le() { - canAccessIndex(cursor + 1); + assertCanAccessRelative(1); uint16_t value; - value = bytes[zero + cursor++]; - value |= ((uint16_t) bytes[zero + cursor++]) << 8; + value = *ptr++; + value |= ((uint16_t) *ptr++) << 8; return value; } void ByteBuffer::copy(uint8_t *bytes, size_t length) { - canAccessIndex(cursor + length); + assertCanAccessRelative(length); - memcpy(bytes, &this->bytes[zero + cursor], length); - cursor += length; + memcpy(bytes, ptr, length); + ptr += length; } -ByteBuffer ByteBuffer::view(size_t length) { - canAccessIndex(cursor + length); - size_t s = zero + cursor + length; - return ByteBuffer(bytes, s, s, cursor); +ByteBuffer ByteBuffer::view() const { + DF << "cursor=" << getCursor() << ", size=" << getSize(); + return view(end - ptr); } -void ByteBuffer::checkAndUpdateSize(size_t newBytes) { - size_t newSize = zero + cursor + newBytes; - if (newSize >= capacity) { - throw ByteBufferException(string("Out of bounds! zero=") + to_string(zero) + ", cursor=" + to_string(cursor) + ", size=" + to_string(size) + ", capacity=" + to_string(capacity) + ", newSize=" + to_string(newSize)); +ByteBuffer ByteBuffer::view(size_t length) const { + assertCanAccessRelative(length); + return ByteBuffer(bytes, length, ptr, ptr + length); +} + +void ByteBuffer::checkAndUpdateEnd(size_t newBytes) { + uint8_t *newPtr = ptr + newBytes; + if (newPtr >= end) { + throw ByteBufferException(string("New size is too large! cursor=") + to_string(getCursor()) + ", size=" + to_string(getSize()) + ", capacity=" + to_string(capacity)); + } + + if (newPtr > end) { + end = newPtr; } +} - size = max(newSize, size); +void ByteBuffer::assertCanAccessRelative(size_t diff) const { + assertCanAccessIndex(ptr + diff - 1); } -void ByteBuffer::canAccessIndex(size_t index) { - if (zero + index >= size) { - throw ByteBufferException(string("Out of bounds! zero=") + to_string(zero) + ", index=" + to_string(index) + ", size=" + to_string(size)); +void ByteBuffer::assertCanAccessIndex(uint8_t *p) const { + if (p >= end || p < zero) { + throw ByteBufferException(string("Out of bounds! size=") + to_string(getSize())); } } std::string ByteBuffer::toString() const { stringstream s; - for(size_t i = zero; i < size; i++) { - s << hex << setfill('0') << setw(2) << (int) bytes[i] << " "; + for (uint8_t *i = (uint8_t *) zero; i < end; i++) { + s << hex << setfill('0') << setw(2) << (int) *i << " "; } return string(s.str()); diff --git a/ByteBuffer.h b/ByteBuffer.h index 828ee4b..ee54a0a 100644 --- a/ByteBuffer.h +++ b/ByteBuffer.h @@ -6,6 +6,9 @@ #include <string> #include <stdexcept> +// For now +#include "log.h" + class ByteBufferException : public std::runtime_error { public: ByteBufferException(std::string const &what) : std::runtime_error(what) { @@ -14,22 +17,31 @@ public: class ByteBuffer { public: - ByteBuffer(uint8_t *bytes, size_t capacity, size_t size, size_t zero = 0); + ByteBuffer(uint8_t *bytes, size_t capacity); + + ByteBuffer(uint8_t *bytes, size_t capacity, size_t zero, size_t size); - inline size_t getSize() { - return size; + inline size_t getSize() const { + DF << "end=" << (uint64_t)end << ", zero=" << (uint64_t)zero; + return end - zero; } - inline size_t getCapacity() { + inline size_t getCapacity() const { return capacity; } - inline size_t getCursor() { - return cursor; + inline size_t getCursor() const { + return ptr - zero; } inline void setCursor(size_t newCursor) { - cursor = newCursor; + assertCanAccessRelative(newCursor); + ptr = ptr + newCursor; + } + + inline void skip(size_t length) { + checkAndUpdateEnd(length); + ptr += length; } ByteBuffer &add8(uint8_t value); @@ -44,20 +56,30 @@ public: void copy(uint8_t *bytes, size_t length); - ByteBuffer view(size_t length); + /** + * Creates a view from cursor to size. + */ + ByteBuffer view() const; + + // TODO: should return const + ByteBuffer view(size_t length) const; std::string toString() const; private: - void checkAndUpdateSize(size_t count); + ByteBuffer(const uint8_t *bytes, size_t capacity, uint8_t *zero, uint8_t *end); + + void checkAndUpdateEnd(size_t count); + + void assertCanAccessRelative(size_t diff) const; - void canAccessIndex(size_t count); + void assertCanAccessIndex(uint8_t *p) const; - uint8_t *bytes; - size_t zero; - size_t size; - size_t capacity; - size_t cursor; + const uint8_t *bytes; + const size_t capacity; + const uint8_t *zero; + const uint8_t *end; + uint8_t *ptr; }; #endif diff --git a/CMakeLists.txt b/CMakeLists.txt index 1220f20..0f2cbf2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ target_link_libraries(ble_toys ${Boost_LIBRARIES}) target_link_libraries(ble_toys ble) enable_testing() -find_package(Boost COMPONENTS unit_test_framework REQUIRED) +find_package(Boost COMPONENTS log unit_test_framework REQUIRED) file(GLOB TEST_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} test/*.cpp) foreach(testSrc ${TEST_SRCS}) @@ -39,8 +39,9 @@ foreach(testSrc ${TEST_SRCS}) include_directories(${PROJECT_SOURCE_DIR}) #link to Boost libraries AND your targets and dependencies - target_link_libraries(${testName} ${Boost_LIBRARIES}) target_link_libraries(${testName} ble) + target_link_libraries(${testName} pthread) + target_link_libraries(${testName} ${Boost_LIBRARIES}) #I like to move testing binaries into a testBin directory set_target_properties(${testName} PROPERTIES diff --git a/LinuxBluetooth.cpp b/LinuxBluetooth.cpp index c30a2b7..1399161 100644 --- a/LinuxBluetooth.cpp +++ b/LinuxBluetooth.cpp @@ -1,3 +1,5 @@ +#include "Bluetooth.h" + #include <string.h> #include <stropts.h> #include <sys/select.h> @@ -10,8 +12,6 @@ #include <sstream> #include <iomanip> -#include "Bluetooth.h" - // Got to love magic constants. Taken from bluez.git/tools/btgatt-client.c #define ATT_CID 4 @@ -31,14 +31,15 @@ namespace trygvis { ~LinuxBluetoothAdapter(); - void runScan(void (*callback)(BluetoothDevice &device)); + void runScan(void (*callback)(BluetoothDevice &device)) override; BluetoothDevice &getDevice(Mac &mac) override; - private: - void startScan(); + void startScan() override; - void stopScan(); + void stopScan() override; + + private: int hciDeviceId; int hciSocket; @@ -151,7 +152,7 @@ namespace trygvis { DF; uint8_t buffer[MAX_MTU]; - ByteBuffer out = ByteBuffer(buffer, MAX_MTU, 0); + ByteBuffer out = ByteBuffer(buffer, MAX_MTU); AttPdu::makeReadByGroupType(out, 0x0001, 0xffff, UUID_PRIMARY_SERVICE); @@ -161,9 +162,14 @@ namespace trygvis { D << "written=" << written; ssize_t r = read(l2cap, buffer, MAX_MTU); - ByteBuffer in = ByteBuffer(buffer, MAX_MTU, r); - D << "read: " << r << " bytes"; + if (r == -1) { + throw BluetoothException(this, "read(): " + errnoAsString()); + } + + ByteBuffer in = ByteBuffer(buffer, r); + + D << "read: " << r << " bytes: " << in.toString(); vector<AttributeData> values = AttPdu::parseReadByGroupType(in); @@ -188,7 +194,7 @@ namespace trygvis { D << "HCI socket: " << hciSocket; if (hciSocket == -1) { - throw BluetoothException(this, "Could not open HCI device " + hciDeviceId); + throw BluetoothException(this, "Could not open HCI device " + to_string(hciDeviceId)); } hci_filter_clear(&hciFilter); @@ -212,7 +218,7 @@ namespace trygvis { struct hci_dev_info di; if (hci_devinfo(hciDeviceId, &di) < 0) { - throw BluetoothException(this, "hci_devinfo: " + hciDeviceId); + throw BluetoothException(this, "hci_devinfo: " + to_string(hciDeviceId)); } D << "hciDeviceId.dev_id=" << di.dev_id; @@ -227,7 +233,7 @@ namespace trygvis { 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) { @@ -0,0 +1,17 @@ +#ifndef LOG_H +#define LOG_H + +#include <boost/log/core.hpp> +#include <boost/log/trivial.hpp> +#define D BOOST_LOG_TRIVIAL(debug) +#define I BOOST_LOG_TRIVIAL(info) +#define W BOOST_LOG_TRIVIAL(warning) + +#define DF BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ": " +#define IF BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": " +#define WF BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": " + +#define UUID_PRIMARY_SERVICE 0x2800 +#define UUID_SECONDARY_SERVICE 0x2801 + +#endif diff --git a/test/ByteBufferTest.cpp b/test/ByteBufferTest.cpp index f98a770..d6e45f7 100644 --- a/test/ByteBufferTest.cpp +++ b/test/ByteBufferTest.cpp @@ -6,21 +6,36 @@ #define BOOST_TEST_MODULE "ByteBuffer" #include <boost/test/unit_test.hpp> +#include "Bluetooth.h" -void checkBuffer(ByteBuffer& buffer, size_t size, size_t capacity, size_t cursor) { - BOOST_CHECK(buffer.getSize() == size); - BOOST_CHECK(buffer.getCapacity() == capacity); - BOOST_CHECK(buffer.getCursor() == cursor); -} +#define checkBuffer(buffer, size, capacity, cursor) \ + D << "size=" << buffer.getSize() << ", capacity=" << buffer.getCapacity() << ", cursor=" << buffer.getCursor(); \ + BOOST_REQUIRE_EQUAL(buffer.getSize(), size); \ + BOOST_REQUIRE_EQUAL(buffer.getCapacity(), capacity); \ + BOOST_REQUIRE_EQUAL(buffer.getCursor(), cursor) -BOOST_AUTO_TEST_CASE(empty_buffer) { - uint8_t bytes[1000]; +class Bytes { +public: + Bytes(size_t size) : capacity(size) { + _bytes = new uint8_t[size + 0x100]; - for (int i = 0; i < sizeof(bytes); i++) { - bytes[i] = (uint8_t) i; + bytes = (uint8_t *) (((uint64_t) &_bytes[0x100]) & 0xffffffffffffff00); + + for (int i = 0; i < size; i++) { + bytes[i] = (uint8_t) i; + } + } + ~Bytes() { + delete _bytes; } + uint8_t *bytes; + uint8_t *_bytes; + size_t capacity; +}; - ByteBuffer buffer(bytes, sizeof(bytes), 0, 0); +BOOST_AUTO_TEST_CASE(empty_buffer) { + Bytes b(1000); + ByteBuffer buffer(b.bytes, b.capacity, 0, 0); checkBuffer(buffer, 0, 1000, 0); @@ -32,15 +47,29 @@ BOOST_AUTO_TEST_CASE(empty_buffer) { } BOOST_AUTO_TEST_CASE(basic) { - uint8_t bytes[1000]; - - for (int i = 0; i < sizeof(bytes); i++) { - bytes[i] = (uint8_t) i; - } - - ByteBuffer buffer(bytes, sizeof(bytes), 10, 0); + Bytes b(1000); + ByteBuffer buffer(b.bytes, 1000, 0, 10); checkBuffer(buffer, 10, 1000, 0); buffer.get8(); checkBuffer(buffer, 10, 1000, 1); } + +BOOST_AUTO_TEST_CASE(view) { + Bytes b(1000); + ByteBuffer buffer(b.bytes, b.capacity, 0, 10); + + BOOST_REQUIRE_EQUAL(buffer.get8(), 0); +// checkBuffer(buffer, 10, 1000, 1); + + ByteBuffer view1 = buffer.view(); + checkBuffer(view1, 9, 9, 0); + + BOOST_REQUIRE_EQUAL(view1.get8(), 1); + BOOST_REQUIRE_EQUAL(view1.get8(), 2); + + ByteBuffer view2 = view1.view(); + checkBuffer(view2, 7, 7, 0); + + BOOST_REQUIRE_EQUAL(view1.get8(), 3); +} |