From 1c97f6df59c825b26ecb18975fd9f62e14fc46ce Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Tue, 10 Feb 2015 19:02:57 +0100 Subject: o wip. --- Bluetooth.cpp | 45 +++++++++++++++++++++++++++++++----- Bluetooth.h | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++---- ByteBuffer.cpp | 38 ++++++++++++++++++++++++++++++ ByteBuffer.h | 27 ++++++++++++++++++++++ CMakeLists.txt | 4 +++- LICENCE.txt | 21 +++++++++++++++++ LinuxBluetooth.cpp | 56 ++++++++++++++++++++++++++++++++++++-------- main.cpp | 18 +++++++++++---- 8 files changed, 250 insertions(+), 27 deletions(-) create mode 100644 ByteBuffer.cpp create mode 100644 ByteBuffer.h create mode 100644 LICENCE.txt diff --git a/Bluetooth.cpp b/Bluetooth.cpp index 22f67ec..43faf0a 100644 --- a/Bluetooth.cpp +++ b/Bluetooth.cpp @@ -14,12 +14,12 @@ namespace trygvis { std::ostringstream buf; buf - << setw(2) << hex << setfill('0') << (int)bytes[5] << ":" - << setw(2) << hex << setfill('0') << (int)bytes[4] << ":" - << setw(2) << hex << setfill('0') << (int)bytes[3] << ":" - << setw(2) << hex << setfill('0') << (int)bytes[2] << ":" - << setw(2) << hex << setfill('0') << (int)bytes[1] << ":" - << setw(2) << hex << setfill('0') << (int)bytes[0]; + << setw(2) << hex << setfill('0') << (int) bytes[5] << ":" + << setw(2) << hex << setfill('0') << (int) bytes[4] << ":" + << setw(2) << hex << setfill('0') << (int) bytes[3] << ":" + << setw(2) << hex << setfill('0') << (int) bytes[2] << ":" + << setw(2) << hex << setfill('0') << (int) bytes[1] << ":" + << setw(2) << hex << setfill('0') << (int) bytes[0]; return buf.str(); } @@ -46,6 +46,39 @@ namespace trygvis { return new Mac(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]); } + AttPdu::AttPdu(ByteBuffer &bytes) : bytes(bytes) { + } + + AttPdu::AttPdu(ByteBuffer &bytes, AttPduType type) : bytes(bytes) { + bytes.add8(type); + } + + AttPduType AttPdu::getType() { + return (AttPduType) bytes.get8(0); + } + + AttPdu AttPdu::parse(ByteBuffer & bytes) { + if(size == 0) { + throw BluetoothException("PDU is too small"); + } + + AttPdu pdu = AttPdu(bytes, size); + + AttPduType type = pdu.getType(); + + switch (type) { + case READ_BY_GROUP_TYPE_RES: + if (size < 4) { + throw BluetoothException("Bad READ_BY_GROUP_TYPE_RES packet, expected at least 4 octets, got " + size); + } + return pdu; + default: + throw BluetoothException("Uknown PDU type: " + type); + } + + return pdu; + } + // ----------------------------------------------------------------------- // Adapter // ----------------------------------------------------------------------- diff --git a/Bluetooth.h b/Bluetooth.h index 2e2f2ef..9496983 100644 --- a/Bluetooth.h +++ b/Bluetooth.h @@ -7,15 +7,24 @@ // For now #include #include +#include "ByteBuffer.h" #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 + namespace trygvis { using namespace std; class BluetoothAdapter; + class BluetoothDevice; class BluetoothException : public runtime_error { @@ -23,8 +32,13 @@ namespace trygvis { BluetoothException(const BluetoothAdapter *adapter, string const &what) : adapter(adapter), device(nullptr), runtime_error(what) { } - BluetoothException(const BluetoothAdapter *adapter, const BluetoothDevice *device, string const &what) : - adapter(adapter), device(device), runtime_error(what) { + + BluetoothException(const BluetoothDevice *device, string const &what) : + adapter(nullptr), device(device), runtime_error(what) { + } + + BluetoothException(string const &what) : + adapter(nullptr), device(nullptr), runtime_error(what) { } const BluetoothAdapter *adapter; @@ -44,9 +58,10 @@ namespace trygvis { string str() const; - bool operator==(Mac& other) const; + bool operator==(Mac &other) const; void copy(uint8_t &_0, uint8_t &_1, uint8_t &_2, uint8_t &_3, uint8_t &_4, uint8_t &_5) const; + static Mac *parseMac(string s); private: @@ -56,20 +71,63 @@ namespace trygvis { class BluetoothDevice { public: virtual Mac const &mac() = 0; + + virtual BluetoothAdapter &adapter() = 0; + virtual void connect() = 0; + virtual void disconnect() = 0; - virtual BluetoothAdapter& adapter() = 0; + + virtual void discoverServices() = 0; }; class BluetoothAdapter { public: - BluetoothAdapter() {}; + BluetoothAdapter() { + }; + virtual ~BluetoothAdapter(); virtual void stopScan() = 0; + virtual void runScan(void (callback)(BluetoothDevice &device)) = 0; }; + enum AttPduType { + ERROR = 0x00, + READ_BY_GROUP_TYPE_REQ = 0x10, + READ_BY_GROUP_TYPE_RES = 0x11 + }; + + class AttPdu { + public: + AttPdu(ByteBuffer &bytes); + + AttPdu(ByteBuffer &bytes, AttPduType type); + + AttPduType getType(); + + static AttPdu parse(ByteBuffer & bytes); + private: + ByteBuffer &bytes; + }; + + class AttributeDataList { + public: + AttributeDataList(ByteBuffer &bytes); + + private: + ByteBuffer &bytes; + }; + + class AttributeData { + public: + AttributeData(ByteBuffer &bytes); + + private: + ByteBuffer &bytes; + }; + // BluetoothAdapter &getDevice(int hciDevice); BluetoothAdapter *getDevice(int hciDevice); } diff --git a/ByteBuffer.cpp b/ByteBuffer.cpp new file mode 100644 index 0000000..9714c8c --- /dev/null +++ b/ByteBuffer.cpp @@ -0,0 +1,38 @@ +#include "ByteBuffer.h" +#include + +using namespace std; + +ByteBuffer::ByteBuffer(uint8_t *bytes, ssize_t size, ssize_t zero) : + bytes(bytes), size(size), ptr(zero), zero(zero) { +} + +ByteBuffer &ByteBuffer::add8(uint8_t value) { + canAccessNextBytes(1); + bytes[zero + ptr++] = value; + return *this; +} + +ByteBuffer &ByteBuffer::add16le(uint16_t value) { + canAccessNextBytes(2); + bytes[zero + ptr++] = (uint8_t) (value & 0xff); + bytes[zero + ptr++] = (uint8_t) ((value >> 8) & 0xff); + return *this; +} + +uint8_t ByteBuffer::get8(ssize_t index) { + canAccessIndex(index); + return bytes[zero + ptr]; +} + +void ByteBuffer::canAccessNextBytes(ssize_t newBytes) { + if (zero + ptr + newBytes >= size) { + throw exception(); + } +} + +void ByteBuffer::canAccessIndex(ssize_t index) { + if (zero + index >= size) { + throw exception(); + } +} diff --git a/ByteBuffer.h b/ByteBuffer.h new file mode 100644 index 0000000..a7e9f9f --- /dev/null +++ b/ByteBuffer.h @@ -0,0 +1,27 @@ +#ifndef BYTE_STREAM_WRAPPER_H +#define BYTE_STREAM_WRAPPER_H + +#include +#include + +class ByteBuffer { +public: + ByteBuffer(uint8_t *bytes, ssize_t size, ssize_t zero = 0); + + ByteBuffer &add8(uint8_t value); + + ByteBuffer &add16le(uint16_t value); + + uint8_t get8(ssize_t index); + +private: + void canAccessNextBytes(ssize_t count); + void canAccessIndex(ssize_t count); + + uint8_t *bytes; + ssize_t zero; + ssize_t size; + ssize_t ptr; +}; + +#endif diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a43b50..e23e674 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,9 @@ add_definitions(-DBOOST_ALL_DYN_LINK) find_package(Boost REQUIRED COMPONENTS system log thread) -set(SOURCE_FILES main.cpp Bluetooth.cpp LinuxBluetooth.cpp) +set(SOURCE_FILES "${SOURCE_FILES}" main.cpp Bluetooth.cpp LinuxBluetooth.cpp) +set(SOURCE_FILES "${SOURCE_FILES}" ByteBuffer.cpp) + add_executable(ble_toys ${SOURCE_FILES}) target_link_libraries(ble_toys bluetooth) diff --git a/LICENCE.txt b/LICENCE.txt new file mode 100644 index 0000000..81330eb --- /dev/null +++ b/LICENCE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015, Trygve Laugstøl + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/LinuxBluetooth.cpp b/LinuxBluetooth.cpp index ee93dee..14407c5 100644 --- a/LinuxBluetooth.cpp +++ b/LinuxBluetooth.cpp @@ -15,6 +15,8 @@ // Got to love magic constants. Taken from bluez.git/tools/btgatt-client.c #define ATT_CID 4 +#define MAX_MTU 256 + namespace trygvis { class LinuxBluetoothDevice; @@ -48,11 +50,13 @@ namespace trygvis { Mac const &mac() override; + LinuxBluetoothAdapter &adapter() override; + void connect() override; void disconnect() override; - virtual LinuxBluetoothAdapter &adapter(); + void discoverServices() override; private: LinuxBluetoothAdapter &_adapter; @@ -96,8 +100,8 @@ namespace trygvis { D << "connect: mac=" << _mac.str(); l2cap = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); - if(l2cap < 0) { - throw BluetoothException(&_adapter, this, "LinuxBluetoothDevice::connect(): socket(): " + errnoAsString()); + if (l2cap < 0) { + throw BluetoothException(this, "LinuxBluetoothDevice::connect(): socket(): " + errnoAsString()); } memset(&addr, 0, sizeof(addr)); @@ -108,7 +112,7 @@ namespace trygvis { if (bind(l2cap, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(l2cap); - throw BluetoothException(&_adapter, this, "LinuxBluetoothDevice::connect(): bind(): " + errnoAsString()); + throw BluetoothException(this, "LinuxBluetoothDevice::connect(): bind(): " + errnoAsString()); } struct bt_security btsec; @@ -116,7 +120,7 @@ namespace trygvis { btsec.level = BT_SECURITY_LOW; if (setsockopt(l2cap, SOL_BLUETOOTH, BT_SECURITY, &btsec, sizeof(btsec)) != 0) { close(l2cap); - throw BluetoothException(&_adapter, this, "LinuxBluetoothDevice::connect(): setsockopt(): " + errnoAsString()); + throw BluetoothException(this, "LinuxBluetoothDevice::connect(): setsockopt(): " + errnoAsString()); } memset(&addr, 0, sizeof(addr)); @@ -132,12 +136,44 @@ namespace trygvis { if (::connect(l2cap, (struct sockaddr *) &addr, sizeof(addr)) < 0) { close(l2cap); - throw BluetoothException(&_adapter, this, "LinuxBluetoothDevice::connect(): connect(): " + errnoAsString()); + throw BluetoothException(this, "LinuxBluetoothDevice::connect(): connect(): " + errnoAsString()); } } void LinuxBluetoothDevice::disconnect() { + DF << "mac=" << _mac.str(); + close(l2cap); + } + + void LinuxBluetoothDevice::discoverServices() { + DF; + + uint8_t buffer[MAX_MTU]; + ByteBuffer bytes = ByteBuffer(buffer, MAX_MTU); + + AttPdu pdu = AttPdu(bytes, AttPduType::READ_BY_GROUP_TYPE_REQ). + add16l(0x0001). + add16l(0x1234). + add16l(UUID_PRIMARY_SERVICE); + + D << "pdu.size()=" << pdu.size(); + ssize_t written = write(l2cap, bytes, pdu.size()); + + D << "written=" << written; + uint8_t *buffer = new uint8_t[MAX_MTU]; + + ssize_t r = read(l2cap, buffer, MAX_MTU); + + D << "read: " << r << " bytes"; + + pdu = AttPdu::parse(buffer, r); + + D << "type= " << pdu.getType(); + + if (pdu.getType() != AttPduType::READ_BY_GROUP_TYPE_REQ) { + + } } // ----------------------------------------------------------------------- @@ -146,7 +182,7 @@ namespace trygvis { LinuxBluetoothAdapter::LinuxBluetoothAdapter(int hciDeviceId) { - D << "LinuxBluetoothAdapter(), hciDeviceId " << hciDeviceId; + DF << "hciDeviceId=" << hciDeviceId; this->hciDeviceId = hciDeviceId; hciSocket = ::hci_open_dev(hciDeviceId); @@ -165,7 +201,7 @@ namespace trygvis { } LinuxBluetoothAdapter::~LinuxBluetoothAdapter() { - D << "~LinuxBluetoothAdapter()"; + DF; stopScan(); @@ -173,7 +209,7 @@ namespace trygvis { } void LinuxBluetoothAdapter::startScan() { - D << "startScan()"; + DF; struct hci_dev_info di; @@ -208,7 +244,7 @@ namespace trygvis { } void LinuxBluetoothAdapter::stopScan() { - D << "stopScan()"; + DF; if (!scanning) { return; diff --git a/main.cpp b/main.cpp index ab1a9b0..ad713e8 100644 --- a/main.cpp +++ b/main.cpp @@ -18,6 +18,9 @@ void scan_callback(BluetoothDevice &device) { cout << "found device: " << device.mac().str() << endl; device.connect(); + + device.discoverServices(); + device.disconnect(); } @@ -29,16 +32,21 @@ int main(int argc, char *argv[]) { targetMac = Mac::parseMac(argv[1]); + BluetoothAdapter *adapter = nullptr; + int e; try { - BluetoothAdapter *adapter = trygvis::getDevice(0); + adapter = trygvis::getDevice(0); adapter->runScan(scan_callback); - delete adapter; - - return EXIT_SUCCESS; + e = EXIT_SUCCESS; } catch (BluetoothException ex) { W << "Excpetion: " << ex.what(); - return EXIT_FAILURE; + e = EXIT_FAILURE; + } + + if (adapter != nullptr) { + delete adapter; } + return e; } -- cgit v1.2.3