aboutsummaryrefslogtreecommitdiff
path: root/LinuxBluetooth.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'LinuxBluetooth.cpp')
-rw-r--r--LinuxBluetooth.cpp512
1 files changed, 0 insertions, 512 deletions
diff --git a/LinuxBluetooth.cpp b/LinuxBluetooth.cpp
deleted file mode 100644
index 4b7f0e0..0000000
--- a/LinuxBluetooth.cpp
+++ /dev/null
@@ -1,512 +0,0 @@
-#include "BluetoothImpl.h"
-
-#include <string.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/l2cap.h>
-#include <map>
-#include <sstream>
-#include <iomanip>
-#include <numeric>
-
-// Got to love magic constants. Taken from bluez.git/tools/btgatt-client.c
-#define ATT_CID 4
-
-#define MAX_MTU 256
-
-namespace trygvis {
-namespace bluetooth {
-namespace linux {
-
-typedef boost::uuids::uuid uuid_t;
-
-using namespace uuids;
-
-class LinuxBluetoothDevice;
-
-class LinuxBluetoothAdapter;
-
-class LinuxBluetoothManager;
-
-class LinuxBluetoothAdapter : public BluetoothAdapter {
-public:
- LinuxBluetoothAdapter(int hciDeviceId);
-
- ~LinuxBluetoothAdapter();
-
- void runScan(void (*callback)(BluetoothDevice &device)) override;
-
- BluetoothDevice &getDevice(Mac &mac) override;
-
-private:
- void startScan() override;
-
- void stopScan() override;
-
- int hciDeviceId;
- int hciSocket;
- struct hci_filter hciFilter;
- bool scanning;
-
- map<Mac, LinuxBluetoothDevice *> devices;
-};
-
-class LinuxBluetoothDevice : public DefaultBluetoothDevice {
-public:
- LinuxBluetoothDevice(LinuxBluetoothAdapter &adapter, Mac mac);
-
- Mac const &mac() override;
-
- LinuxBluetoothAdapter &adapter() override;
-
- void connect() override;
-
- void disconnect() override;
-
- void discoverServices() override;
-
-private:
- vector<AttributeData> discoverServices(uint16_t startHandle);
-
- vector<AttributeData> discoverCharacteristics(uint16_t startHandle, uint16_t endHandle);
-
- ByteBuffer writeAndRead(ByteBuffer &out, shared_ptr<uint8_t> buffer, size_t size);
-
- uuid_t readUuid(const ByteBuffer &bytes) const;
-
- LinuxBluetoothAdapter &_adapter;
- Mac _mac;
- int l2cap;
-};
-
-// Utilities
-
-string errnoAsString() {
- return string(strerror(errno));
-};
-
-// -----------------------------------------------------------------------
-// Mac
-// -----------------------------------------------------------------------
-
-Mac parseMac(bdaddr_t &a) {
- return Mac(a.b[0], a.b[1], a.b[2], a.b[3], a.b[4], a.b[5]);
-}
-
-// -----------------------------------------------------------------------
-// Device
-// -----------------------------------------------------------------------
-
-LinuxBluetoothDevice::LinuxBluetoothDevice(LinuxBluetoothAdapter &adapter, Mac mac) :
- DefaultBluetoothDevice(), _adapter(adapter), _mac(mac) {
-}
-
-Mac const &LinuxBluetoothDevice::mac() {
- return _mac;
-}
-
-LinuxBluetoothAdapter &LinuxBluetoothDevice::adapter() {
- return _adapter;
-}
-
-void LinuxBluetoothDevice::connect() {
- struct sockaddr_l2 addr;
-
- D << "connect: mac=" << _mac.str();
-
- l2cap = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
- if (l2cap < 0) {
- throw BluetoothException(this, "LinuxBluetoothDevice::connect(): socket(): " + errnoAsString());
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.l2_family = AF_BLUETOOTH;
- addr.l2_bdaddr = {{0, 0, 0, 0, 0, 0}};
- addr.l2_cid = htobs(ATT_CID);
- addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
-
- if (bind(l2cap, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- close(l2cap);
- throw BluetoothException(this, "LinuxBluetoothDevice::connect(): bind(): " + errnoAsString());
- }
-
- struct bt_security btsec;
- memset(&btsec, 0, sizeof(btsec));
- btsec.level = BT_SECURITY_LOW;
- if (setsockopt(l2cap, SOL_BLUETOOTH, BT_SECURITY, &btsec, sizeof(btsec)) != 0) {
- close(l2cap);
- throw BluetoothException(this, "LinuxBluetoothDevice::connect(): setsockopt(): " + errnoAsString());
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.l2_family = AF_BLUETOOTH;
- addr.l2_cid = htobs(ATT_CID);
- addr.l2_bdaddr_type = BDADDR_LE_RANDOM;
- _mac.copy(addr.l2_bdaddr.b[5],
- addr.l2_bdaddr.b[4],
- addr.l2_bdaddr.b[3],
- addr.l2_bdaddr.b[2],
- addr.l2_bdaddr.b[1],
- addr.l2_bdaddr.b[0]);
-
- if (::connect(l2cap, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
- close(l2cap);
- throw BluetoothException(this, "LinuxBluetoothDevice::connect(): connect(): " + errnoAsString());
- }
-}
-
-void LinuxBluetoothDevice::disconnect() {
- DF << "mac = " << _mac.str();
- close(l2cap);
-}
-
-uuid_t LinuxBluetoothDevice::readUuid(const ByteBuffer &bytes) const {
- size_t bytesLeft = bytes.getBytesLeft();
-
- uuid_t u;
-
- if (bytesLeft == 2) {
- uint8_t bs[16] = BLUETOOTH_UUID_INITIALIZER;
- bs[2] = bytes.get8(1);
- bs[3] = bytes.get8(0);
- memcpy(&u, bs, 16);
- } else if (bytesLeft == 16) {
- uint8_t bs[16];
- bs[15] = bytes.get8(0);
- bs[14] = bytes.get8(1);
- bs[13] = bytes.get8(2);
- bs[12] = bytes.get8(3);
- bs[11] = bytes.get8(4);
- bs[10] = bytes.get8(5);
- bs[9] = bytes.get8(6);
- bs[8] = bytes.get8(7);
- bs[7] = bytes.get8(8);
- bs[6] = bytes.get8(9);
- bs[5] = bytes.get8(10);
- bs[4] = bytes.get8(11);
- bs[3] = bytes.get8(12);
- bs[2] = bytes.get8(13);
- bs[1] = bytes.get8(14);
- bs[0] = bytes.get8(15);
- memcpy(&u, bs, 16);
- } else {
- throw BluetoothException(this, "Unexpected bytes left: " + to_string(bytesLeft));
- }
-
- return u;
-}
-
-void LinuxBluetoothDevice::discoverServices() {
- uint16_t startHandle = 0x0001;
-
- removeServices();
-
- do {
- vector<AttributeData> values = discoverServices(startHandle);
-
- // Shouldn't happen, but you never know.
- if (values.size() == 0) {
- break;
- }
-
- uint16_t endGroupHandle;
-
- for (auto &data: values) {
- endGroupHandle = data.value.read16le();
-
-// D << "handle: 0x" << hex << setw(4) << setfill('0') << data.handle <<
-// ", endGroupHandle: 0x" << hex << setw(4) << setfill('0') << endGroupHandle <<
-// ", value: " << data.value.toString();
-
- uuid_t u = readUuid(data.value);
-
- addService(new DefaultBluetoothGattService(*this, u, data.handle, endGroupHandle));
- }
-
- auto last = values.back();
-
- startHandle = endGroupHandle;
- } while (startHandle != 0xffff);
-
-// for (auto &s : services) {
-// D << "service: " << to_string(s->getUuid()) << ", handle: " << s->getHandle() << ", end group handle: " << s->getEndGroupHandle();
-// D << "s= " << (uint64_t) s;
-// }
-
- auto it = services.begin(),
- end = services.end();
-
- if (it == end) {
- return;
- }
-
- startHandle = 0x0001;
- vector<AttributeData> chars;
-
- auto s = *it;
-
- do {
- vector<AttributeData> values = discoverCharacteristics(startHandle, 0xffff);
-
- if (values.size() == 0) {
- break;
- }
-
- uint16_t lastHandle = 0xffff;
-
- for (auto &c : values) {
- while (c.handle > s->getEndGroupHandle() && it != end) {
- s = *it++;
-// D << "service: " << s->getHandle();
- }
-
- lastHandle = c.handle;
-
- uint8_t properties = c.value.read8();
- uint16_t valueHandle = c.value.read16le();
- uuid_t uuid = readUuid(c.value);
-
-// D << "characteristic: handle: " << setw(2) << setfill('0') << hex << (int) c.handle <<
-// ", properties: " << setw(2) << setfill('0') << hex << (int) properties <<
-// ", valueHandle: 0x" << setw(4) << setfill('0') << hex << (int) valueHandle <<
-// ", uuid: " << uuid;
-
- s->addCharacteristic(new DefaultBluetoothGattCharacteristic(*s, c.handle, uuid, properties, valueHandle));
- }
-
- auto last = values.back();
-
- startHandle = lastHandle + (uint8_t) 2;
- } while (startHandle != 0xffff);
-}
-
-ByteBuffer LinuxBluetoothDevice::writeAndRead(ByteBuffer &out, shared_ptr<uint8_t> buffer, size_t size) {
- D << "pdu size=" << out.getCursor();
- ssize_t written = write(l2cap, buffer.get(), out.getCursor());
-
- D << "written=" << written;
-
- ssize_t r = read(l2cap, buffer.get(), size);
-
- if (r == -1) {
- throw BluetoothException(this, "read(): " + errnoAsString());
- }
-
- auto in = ByteBuffer(buffer, (size_t) r);
-
- D << "read: " << r << " bytes: " << in.toString();
-
- return in;
-}
-
-vector<AttributeData> LinuxBluetoothDevice::discoverServices(uint16_t startHandle) {
- DF;
-
- shared_ptr<uint8_t> buffer(new uint8_t[MAX_MTU]);
- ByteBuffer out = ByteBuffer(buffer, MAX_MTU);
-
- AttPdu::makeReadByGroupType(out, startHandle, 0xffff, uuids::PRIMARY_SERVICE);
-
- ByteBuffer in = writeAndRead(out, buffer, MAX_MTU);
-
- vector<AttributeData> values = AttPdu::parseReadByGroupType(in);
-
- D << "READ_BY_GROUP_TYPE response has " + to_string(values.size()) + " values";
-
- return values;
-}
-
-vector<AttributeData> LinuxBluetoothDevice::discoverCharacteristics(uint16_t startHandle, uint16_t endHandle) {
- DF;
-
- shared_ptr<uint8_t> buffer(new uint8_t[MAX_MTU]);
- ByteBuffer out = ByteBuffer(buffer, MAX_MTU);
-
- AttPdu::makeReadByType(out, startHandle, endHandle, uuids::CHARACTERISTIC);
-
- ByteBuffer in = writeAndRead(out, buffer, MAX_MTU);
-
- vector<AttributeData> values = AttPdu::parseReadByType(in);
-
- D << "READ_BY_TYPE response has " + to_string(values.size()) + " values";
-
- return values;
-}
-
-// -----------------------------------------------------------------------
-// Adapter
-// -----------------------------------------------------------------------
-
-LinuxBluetoothAdapter::LinuxBluetoothAdapter(int hciDeviceId) :
- scanning(false) {
- DF << "hciDeviceId=" << hciDeviceId;
-
- this->hciDeviceId = hciDeviceId;
- hciSocket = ::hci_open_dev(hciDeviceId);
-
- D << "HCI socket: " << hciSocket;
-
- if (hciSocket == -1) {
- throw BluetoothException(this, "Could not open HCI device " + to_string(hciDeviceId));
- }
-
- hci_filter_clear(&hciFilter);
- hci_filter_set_ptype(HCI_EVENT_PKT, &hciFilter);
- hci_filter_set_event(EVT_LE_META_EVENT, &hciFilter);
- hci_filter_set_event(EVT_LE_ADVERTISING_REPORT, &hciFilter);
- setsockopt(hciSocket, SOL_HCI, HCI_FILTER, &hciFilter, sizeof(hciFilter));
-}
-
-LinuxBluetoothAdapter::~LinuxBluetoothAdapter() {
- DF;
-
- stopScan();
-
- close(hciSocket);
-
- for (auto &pair : devices) {
- delete pair.second;
- }
-}
-
-void LinuxBluetoothAdapter::startScan() {
- DF;
-
- struct hci_dev_info di;
-
- if (hci_devinfo(hciDeviceId, &di) < 0) {
- throw BluetoothException(this, "HCI adapter is not up: " + to_string(hciDeviceId));
- }
-
- D << "hciDeviceId.dev_id=" << di.dev_id;
- D << "hciDeviceId.bdaddr=" << parseMac(di.bdaddr).str();
- D << "hciDeviceId.flags=" << setw(8) << setfill('0') << hex << di.flags;
- D << "hciDeviceId.flags RUNNING = " << hci_test_bit(HCI_RUNNING, &di.flags);
- D << "hciDeviceId.flags UP = " << hci_test_bit(HCI_UP, &di.flags);
- D << "hciDeviceId.flags PSCAN = " << hci_test_bit(HCI_PSCAN, &di.flags);
- D << "hciDeviceId.flags ISCAN = " << hci_test_bit(HCI_ISCAN, &di.flags);
- D << "hciDeviceId.name=" << di.name;
-
- int up = hci_test_bit(HCI_UP, &di.flags);
-
- if (!up) {
- 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) {
- throw BluetoothException(this, "hci_le_set_scan_parameters: " + errnoAsString());
- }
-
- if (hci_le_set_scan_enable(hciSocket, 1, 0, 1000)) {
- throw BluetoothException(this, "Could not start scanning: other" + errnoAsString());
- }
-
- scanning = true;
-}
-
-void LinuxBluetoothAdapter::stopScan() {
- DF;
-
- if (!scanning) {
- return;
- }
-
- scanning = false;
-
- if (hci_le_set_scan_enable(hciSocket, 0, 0, 1000) < 0) {
- W << "stopScan: hci_le_set_scan_enable: " << errnoAsString();
- }
-}
-
-BluetoothDevice &LinuxBluetoothAdapter::getDevice(Mac &mac) {
- map<Mac, LinuxBluetoothDevice *>::iterator it = devices.find(mac);
-
- if (it == devices.end()) {
- LinuxBluetoothDevice *device = new LinuxBluetoothDevice(*this, mac);
- devices[mac] = device;
- return *device;
- }
-
- return *it->second;
-}
-
-void LinuxBluetoothAdapter::runScan(void (*callback)(BluetoothDevice &device)) {
- fd_set rfds;
- FD_ZERO(&rfds);
- FD_SET(hciSocket, &rfds);
-
- startScan();
-
- while (scanning) {
- // Linux can change tv, so it has to be reinitialized
- struct timeval tv;
- tv.tv_sec = 1;
- tv.tv_usec = 0;
-
- int selected = select(hciSocket + 1, &rfds, NULL, NULL, &tv);
-
- if (selected == -1) {
- throw BluetoothException(this, "select() failed");
- }
-
- if (selected == 0) {
- D << "timeout";
- // Timeout, just continue
- continue;
- }
-
- unsigned char hciEventBuf[HCI_MAX_EVENT_SIZE];
-
- ssize_t len = read(hciSocket, hciEventBuf, sizeof(hciEventBuf));
- evt_le_meta_event *metaEvent = (evt_le_meta_event *) (hciEventBuf + (1 + HCI_EVENT_HDR_SIZE));
- len -= (1 + HCI_EVENT_HDR_SIZE);
-
- D << "metaEvent->subevent = " << std::hex << (int) metaEvent->subevent;
-
- if (metaEvent->subevent == EVT_LE_ADVERTISING_REPORT) {
- le_advertising_info *advertisingInfo = (le_advertising_info *) (metaEvent->data + 1);
-
- Mac mac = parseMac(advertisingInfo->bdaddr);
-
- BluetoothDevice &device = getDevice(mac);
-
- callback(device);
- }
- }
-}
-
-}
-}
-}
-
-// -----------------------------------------------------------------------
-// Implementation of platform-specific method.
-// -----------------------------------------------------------------------
-
-namespace trygvis {
-namespace bluetooth {
-using namespace trygvis::bluetooth::linux;
-
-map<int, LinuxBluetoothAdapter *> adapters;
-
-BluetoothAdapter &getAdapterImpl(int hciDevice) {
- map<int, LinuxBluetoothAdapter *>::iterator it = adapters.find(hciDevice);
-
- if (it == adapters.end()) {
- LinuxBluetoothAdapter *adapter = new LinuxBluetoothAdapter(hciDevice);
- adapters[hciDevice] = adapter;
- return *adapter;
- }
-
- return *it->second;
-}
-
-void shutdownImpl() {
- for (auto &pair: adapters) {
- delete pair.second;
- }
-}
-
-}
-}