aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Bluetooth.cpp45
-rw-r--r--Bluetooth.h68
-rw-r--r--ByteBuffer.cpp38
-rw-r--r--ByteBuffer.h27
-rw-r--r--CMakeLists.txt4
-rw-r--r--LICENCE.txt21
-rw-r--r--LinuxBluetooth.cpp56
-rw-r--r--main.cpp18
8 files changed, 250 insertions, 27 deletions
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 <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)
+#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 <stdexcept>
+
+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 <cstdint>
+#include <cstdlib>
+
+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 <trygvis@inamo.no>
+
+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;
}