From 47ada94baa424e56d2ded256fddc91e6aa4d3090 Mon Sep 17 00:00:00 2001 From: "Javier S. Pedro" Date: Tue, 3 Dec 2013 03:05:47 +0100 Subject: initial import --- gatocentralmanager.cpp | 222 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 gatocentralmanager.cpp (limited to 'gatocentralmanager.cpp') diff --git a/gatocentralmanager.cpp b/gatocentralmanager.cpp new file mode 100644 index 0000000..3073d34 --- /dev/null +++ b/gatocentralmanager.cpp @@ -0,0 +1,222 @@ +/* + * libgato - A GATT/ATT library for use with Bluez + * + * Copyright (C) 2013 Javier S. Pedro + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include +#include +#include + +#include +#include +#include + +#include "gatocentralmanager_p.h" +#include "gatoperipheral.h" + +GatoCentralManager::GatoCentralManager(QObject *parent) : + QObject(parent), d_ptr(new GatoCentralManagerPrivate) +{ + Q_D(GatoCentralManager); + d->q_ptr = this; + + // Hardcode the "default" bluetooth adapter for now. + d->dev_id = hci_get_route(NULL); + d->timeout = 1000; + d->hci = -1; + d->notifier = 0; +} + +GatoCentralManager::~GatoCentralManager() +{ + Q_D(GatoCentralManager); + if (d->scanning()) stopScan(); + delete d_ptr; +} + +void GatoCentralManager::scanForPeripherals(PeripheralScanOptions options) +{ + scanForPeripheralsWithServices(QList(), options); +} + +void GatoCentralManager::scanForPeripheralsWithServices(const QList &uuids, PeripheralScanOptions options) +{ + Q_D(GatoCentralManager); + + if (d->scanning()) stopScan(); + + if (!d->openDevice()) return; + d->filter_uuids = uuids; + + quint8 filter_dup = options & PeripheralScanOptionAllowDuplicates ? 0 : 1; + quint8 scan_type = options & PeripheralScanOptionActive ? 1 : 0; + int rc; + + hci_le_set_scan_enable(d->hci, 0, 0, d->timeout); + + rc = hci_le_set_scan_parameters(d->hci, scan_type, + htobs(0x0010), htobs(0x0010), + 0 /* Public address */, + 0 /* No filter ? */, + d->timeout); + if (rc < 0) { + qErrnoWarning("LE Set scan parameters failed"); + d->closeDevice(); + return; + } + + rc = hci_le_set_scan_enable(d->hci, 1, filter_dup, d->timeout); + + if (rc < 0) { + qErrnoWarning("LE Set scan enable failed"); + d->closeDevice(); + return; + } + + qDebug() << "LE Scan in progress"; + + d->notifier = new QSocketNotifier(d->hci, QSocketNotifier::Read); + connect(d->notifier, SIGNAL(activated(int)), this, SLOT(_q_readNotify())); + + socklen_t olen = sizeof(d->hci_of); + if (getsockopt(d->hci, SOL_HCI, HCI_FILTER, &d->hci_of, &olen) < 0) { + qErrnoWarning("Could not get existing HCI socket options"); + return; + } + + hci_filter_clear(&d->hci_nf); + hci_filter_set_ptype(HCI_EVENT_PKT, &d->hci_nf); + hci_filter_set_event(EVT_LE_META_EVENT, &d->hci_nf); + + if (setsockopt(d->hci, SOL_HCI, HCI_FILTER, &d->hci_nf, sizeof(d->hci_nf)) < 0) { + qErrnoWarning("Could not set HCI socket options"); + return; + } + + // SocketNotifier will call _q_readNotify() when ready +} + +void GatoCentralManager::stopScan() +{ + Q_D(GatoCentralManager); + if (d->scanning()) { + qDebug() << "Stopping LE scan"; + delete d->notifier; + setsockopt(d->hci, SOL_HCI, HCI_FILTER, &d->hci_of, sizeof(d->hci_of)); + hci_le_set_scan_enable(d->hci, 0, 0, d->timeout); + d->closeDevice(); + } else { + qDebug() << "No scan to stop"; + } + d->notifier = 0; + d->filter_uuids.clear(); + hci_filter_clear(&d->hci_nf); + hci_filter_clear(&d->hci_of); +} + +void GatoCentralManager::_q_readNotify() +{ + Q_D(GatoCentralManager); + unsigned char buf[HCI_MAX_EVENT_SIZE]; + + // Read a full event + int len; + if ((len = read(d->hci, buf, sizeof(buf))) < 0) { + if (errno != EAGAIN && errno != EINTR) { + qErrnoWarning("Could not read HCI events"); + } + return; // Will be notified later, probably. + } + + int pos = HCI_EVENT_HDR_SIZE + 1; + assert(pos < len); + evt_le_meta_event *meta = reinterpret_cast(&buf[pos]); + + + if (meta->subevent == EVT_LE_ADVERTISING_REPORT) { + pos++; // Skip subevent field + int num_reports = buf[pos]; + pos++; // Skip num_reports field + + assert(pos < len); + + while (num_reports > 0) { + le_advertising_info *info = reinterpret_cast(&buf[pos]); + assert(pos + LE_ADVERTISING_INFO_SIZE < len); + assert(pos + LE_ADVERTISING_INFO_SIZE + info->length < len); + int8_t *rssi = reinterpret_cast(&buf[pos + LE_ADVERTISING_INFO_SIZE + info->length]); + + d->handleAdvertising(info, *rssi); + + pos += LE_ADVERTISING_INFO_SIZE + info->length + 1; + num_reports--; + } + } +} + +bool GatoCentralManagerPrivate::scanning() +{ + // If the HCI device is open for any reason, it means we're scanning. + return hci != -1; +} + +bool GatoCentralManagerPrivate::openDevice() +{ + hci = hci_open_dev(dev_id); + + if (hci == -1) { + qErrnoWarning("Could not open device"); + return false; + } + + return true; +} + +void GatoCentralManagerPrivate::closeDevice() +{ + hci_close_dev(hci); + hci = -1; +} + +void GatoCentralManagerPrivate::handleAdvertising(le_advertising_info *info, int rssi) +{ + Q_Q(GatoCentralManager); + + qDebug() << "Advertising event type" << info->evt_type + << "address type" << info->bdaddr_type + << "data length" << info->length + << "rssi" << rssi; + + GatoAddress addr(info->bdaddr.b); + GatoPeripheral *peripheral; + QHash::iterator it = peripherals.find(addr); + if (it == peripherals.end()) { + peripheral = new GatoPeripheral(addr, q); + peripherals.insert(addr, peripheral); + } else { + peripheral = *it; + } + + if (info->length > 0) { + peripheral->parseEIR(info->data, info->length); + } + + emit q->discoveredPeripheral(peripheral, rssi); +} -- cgit v1.2.3