Remove libgato code from to-be-released code

It continues to live in the development branches

Change-Id: I13f140f78b26e4f1bf23d8e023834038812190c6
Reviewed-by: Alex Blasche <alexander.blasche@digia.com>
This commit is contained in:
Alex Blasche 2014-08-20 10:25:50 +02:00
parent 624f8a7a5c
commit 8a02c2ffd8
6 changed files with 0 additions and 1832 deletions

View File

@ -1,609 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Javier de San Pedro <dev.git@javispedro.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtCore/QDataStream>
#include <QtCore/QDebug>
#include "gatoattclient.h"
#include "helpers.h"
#define PROTOCOL_DEBUG 0
#define ATT_CID 4
#define ATT_PSM 31
#define ATT_DEFAULT_LE_MTU 23
#define ATT_MAX_LE_MTU 0x200
QT_BEGIN_NAMESPACE
enum AttOpcode {
AttOpNone = 0,
AttOpErrorResponse = 0x1,
AttOpExchangeMTURequest = 0x2,
AttOpExchangeMTUResponse = 0x3,
AttOpFindInformationRequest = 0x4,
AttOpFindInformationResponse = 0x5,
AttOpFindByTypeValueRequest = 0x6,
AttOpFindByTypeValueResponse = 0x7,
AttOpReadByTypeRequest = 0x8,
AttOpReadByTypeResponse = 0x9,
AttOpReadRequest = 0xA,
AttOpReadResponse = 0xB,
AttOpReadBlobRequest = 0xC,
AttOpReadBlobResponse = 0xD,
AttOpReadMultipleRequest = 0xE,
AttOpReadMultipleResponse = 0xF,
AttOpReadByGroupTypeRequest = 0x10,
AttOpReadByGroupTypeResponse = 0x11,
AttOpWriteRequest = 0x12,
AttOpWriteResponse = 0x13,
AttOpWriteCommand = 0x52,
AttOpPrepareWriteRequest = 0x16,
AttOpPrepareWriteResponse = 0x17,
AttOpExecuteWriteRequest = 0x18,
AttOpExecuteWriteResponse = 0x19,
AttOpHandleValueNotification = 0x1B,
AttOpHandleValueIndication = 0x1D,
AttOpHandleValueConfirmation = 0x1E,
AttOpSignedWriteCommand = 0xD2
};
static QByteArray remove_method_signature(const char *sig)
{
const char* bracketPosition = strchr(sig, '(');
if (!bracketPosition || !(sig[0] >= '0' && sig[0] <= '3')) {
qWarning("Invalid slot specification");
return QByteArray();
}
return QByteArray(sig + 1, bracketPosition - 1 - sig);
}
GatoAttClient::GatoAttClient(QObject *parent) :
QObject(parent), socket(new GatoSocket(this)), cur_mtu(ATT_DEFAULT_LE_MTU), next_id(1)
{
connect(socket, SIGNAL(connected()), SLOT(handleSocketConnected()));
connect(socket, SIGNAL(disconnected()), SLOT(handleSocketDisconnected()));
connect(socket, SIGNAL(readyRead()), SLOT(handleSocketReadyRead()));
}
GatoAttClient::~GatoAttClient()
{
}
GatoSocket::State GatoAttClient::state() const
{
return socket->state();
}
bool GatoAttClient::connectTo(const GatoAddress &addr)
{
return socket->connectTo(addr, ATT_CID);
}
void GatoAttClient::close()
{
socket->close();
}
int GatoAttClient::mtu() const
{
return cur_mtu;
}
uint GatoAttClient::request(int opcode, const QByteArray &data, QObject *receiver, const char *member)
{
Request req;
req.id = next_id++;
req.opcode = opcode;
req.pkt = data;
req.pkt.prepend(static_cast<char>(opcode));
req.receiver = receiver;
req.member = remove_method_signature(member);
pending_requests.enqueue(req);
if (pending_requests.size() == 1) {
// So we can just send this request instead of waiting for others to complete
sendARequest();
}
return req.id;
}
void GatoAttClient::cancelRequest(uint id)
{
QQueue<Request>::iterator it = pending_requests.begin();
while (it != pending_requests.end()) {
if (it->id == id) {
it = pending_requests.erase(it);
} else {
++it;
}
}
}
uint GatoAttClient::requestExchangeMTU(quint16 client_mtu, QObject *receiver, const char *member)
{
QByteArray data;
QDataStream s(&data, QIODevice::WriteOnly);
s.setByteOrder(QDataStream::LittleEndian);
s << client_mtu;
return request(AttOpExchangeMTURequest, data, receiver, member);
}
uint GatoAttClient::requestFindInformation(GatoHandle start, GatoHandle end, QObject *receiver, const char *member)
{
QByteArray data;
QDataStream s(&data, QIODevice::WriteOnly);
s.setByteOrder(QDataStream::LittleEndian);
s << start << end;
return request(AttOpFindInformationRequest, data, receiver, member);
}
uint GatoAttClient::requestFindByTypeValue(GatoHandle start, GatoHandle end, const GatoUUID &uuid, const QByteArray &value, QObject *receiver, const char *member)
{
QByteArray data;
QDataStream s(&data, QIODevice::WriteOnly);
s.setByteOrder(QDataStream::LittleEndian);
s << start << end;
bool uuid16_ok;
quint16 uuid16 = uuid.toUInt16(&uuid16_ok);
if (uuid16_ok) {
s << uuid16;
} else {
qWarning() << "FindByTypeValue does not support UUIDs other than UUID16";
return -1;
}
s << value;
return request(AttOpFindByTypeValueRequest, data, receiver, member);
}
uint GatoAttClient::requestReadByType(GatoHandle start, GatoHandle end, const GatoUUID &uuid, QObject *receiver, const char *member)
{
QByteArray data;
QDataStream s(&data, QIODevice::WriteOnly);
s.setByteOrder(QDataStream::LittleEndian);
s << start << end;
write_gatouuid(s, uuid, true, false);
return request(AttOpReadByTypeRequest, data, receiver, member);
}
uint GatoAttClient::requestRead(GatoHandle handle, QObject *receiver, const char *member)
{
QByteArray data;
QDataStream s(&data, QIODevice::WriteOnly);
s.setByteOrder(QDataStream::LittleEndian);
s << handle;
return request(AttOpReadRequest, data, receiver, member);
}
uint GatoAttClient::requestReadByGroupType(GatoHandle start, GatoHandle end, const GatoUUID &uuid, QObject *receiver, const char *member)
{
QByteArray data;
QDataStream s(&data, QIODevice::WriteOnly);
s.setByteOrder(QDataStream::LittleEndian);
s << start << end;
write_gatouuid(s, uuid, true, false);
return request(AttOpReadByGroupTypeRequest, data, receiver, member);
}
uint GatoAttClient::requestWrite(GatoHandle handle, const QByteArray &value, QObject *receiver, const char *member)
{
QByteArray data;
QDataStream s(&data, QIODevice::WriteOnly);
s.setByteOrder(QDataStream::LittleEndian);
s << handle;
s.writeRawData(value.constData(), value.length());
return request(AttOpWriteRequest, data, receiver, member);
}
void GatoAttClient::command(int opcode, const QByteArray &data)
{
QByteArray packet = data;
packet.prepend(static_cast<char>(opcode));
socket->send(packet);
#if PROTOCOL_DEBUG
qDebug() << "Wrote" << packet.size() << "bytes (command)" << packet.toHex();
#endif
}
void GatoAttClient::commandWrite(GatoHandle handle, const QByteArray &value)
{
QByteArray data;
QDataStream s(&data, QIODevice::WriteOnly);
s.setByteOrder(QDataStream::LittleEndian);
s << handle;
s.writeRawData(value.constData(), value.length());
command(AttOpWriteCommand, data);
}
void GatoAttClient::sendARequest()
{
if (pending_requests.isEmpty()) {
return;
}
Request &req = pending_requests.head();
socket->send(req.pkt);
#if PROTOCOL_DEBUG
qDebug() << "Wrote" << req.pkt.size() << "bytes (request)" << req.pkt.toHex();
#endif
}
bool GatoAttClient::handleEvent(const QByteArray &event)
{
const char *data = event.constData();
quint8 opcode = event[0];
GatoHandle handle;
switch (opcode) {
case AttOpHandleValueNotification:
handle = read_le<GatoHandle>(&data[1]);
emit attributeUpdated(handle, event.mid(3), false);
return true;
case AttOpHandleValueIndication:
handle = read_le<GatoHandle>(&data[1]);
// Send the confirmation back
command(AttOpHandleValueConfirmation, QByteArray());
emit attributeUpdated(handle, event.mid(3), true);
return true;
default:
return false;
}
}
bool GatoAttClient::handleResponse(const Request &req, const QByteArray &response)
{
// If we know the request, we can provide a decoded answer
switch (req.opcode) {
case AttOpExchangeMTURequest:
if (response[0] == AttOpExchangeMTUResponse) {
if (req.receiver) {
QMetaObject::invokeMethod(req.receiver, req.member.constData(),
Q_ARG(uint, req.id),
Q_ARG(quint16, read_le<quint16>(response.constData() + 1)));
}
return true;
} else if (response[0] == AttOpErrorResponse && response[1] == AttOpExchangeMTURequest) {
if (req.receiver) {
QMetaObject::invokeMethod(req.receiver, req.member.constData(),
Q_ARG(uint, req.id),
Q_ARG(quint16, 0));
}
return true;
} else {
return false;
}
break;
case AttOpFindInformationRequest:
if (response[0] == AttOpFindInformationResponse) {
if (req.receiver) {
QMetaObject::invokeMethod(req.receiver, req.member.constData(),
Q_ARG(uint, req.id),
Q_ARG(QList<GatoAttClient::InformationData>, parseInformationData(response.mid(1))));
}
return true;
} else if (response[0] == AttOpErrorResponse && response[1] == AttOpFindInformationRequest) {
if (req.receiver) {
QMetaObject::invokeMethod(req.receiver, req.member.constData(),
Q_ARG(uint, req.id),
Q_ARG(QList<GatoAttClient::InformationData>, QList<InformationData>()));
}
return true;
} else {
return false;
}
break;
case AttOpFindByTypeValueRequest:
if (response[0] == AttOpFindByTypeValueResponse) {
if (req.receiver) {
QMetaObject::invokeMethod(req.receiver, req.member.constData(),
Q_ARG(uint, req.id),
Q_ARG(QList<GatoAttClient::HandleInformation>, parseHandleInformation(response.mid(1))));
}
return true;
} else if (response[0] == AttOpErrorResponse && response[1] == AttOpFindByTypeValueRequest) {
if (req.receiver) {
QMetaObject::invokeMethod(req.receiver, req.member.constData(),
Q_ARG(uint, req.id),
Q_ARG(QList<GatoAttClient::HandleInformation>, QList<HandleInformation>()));
}
return true;
} else {
return false;
}
break;
case AttOpReadByTypeRequest:
if (response[0] == AttOpReadByTypeResponse) {
if (req.receiver) {
QMetaObject::invokeMethod(req.receiver, req.member.constData(),
Q_ARG(uint, req.id),
Q_ARG(QList<GatoAttClient::AttributeData>, parseAttributeData(response.mid(1))));
}
return true;
} else if (response[0] == AttOpErrorResponse && response[1] == AttOpReadByTypeRequest) {
if (req.receiver) {
QMetaObject::invokeMethod(req.receiver, req.member.constData(),
Q_ARG(uint, req.id),
Q_ARG(QList<GatoAttClient::AttributeData>, QList<AttributeData>()));
}
return true;
} else {
return false;
}
break;
case AttOpReadRequest:
if (response[0] == AttOpReadResponse) {
if (req.receiver) {
QMetaObject::invokeMethod(req.receiver, req.member.constData(),
Q_ARG(uint, req.id),
Q_ARG(QByteArray, response.mid(1)));
}
return true;
} else if (response[0] == AttOpErrorResponse && response[1] == AttOpReadRequest) {
if (req.receiver) {
QMetaObject::invokeMethod(req.receiver, req.member.constData(),
Q_ARG(uint, req.id),
Q_ARG(QByteArray, QByteArray()));
}
return true;
} else {
return false;
}
break;
case AttOpReadByGroupTypeRequest:
if (response[0] == AttOpReadByGroupTypeResponse) {
if (req.receiver) {
QMetaObject::invokeMethod(req.receiver, req.member.constData(),
Q_ARG(uint, req.id),
Q_ARG(QList<GatoAttClient::AttributeGroupData>, parseAttributeGroupData(response.mid(1))));
}
return true;
} else if (response[0] == AttOpErrorResponse && response[1] == AttOpReadByGroupTypeRequest) {
if (req.receiver) {
QMetaObject::invokeMethod(req.receiver, req.member.constData(),
Q_ARG(uint, req.id),
Q_ARG(QList<GatoAttClient::AttributeGroupData>, QList<AttributeGroupData>()));
}
return true;
} else {
return false;
}
break;
case AttOpWriteRequest:
if (response[0] == AttOpWriteResponse) {
if (req.receiver) {
QMetaObject::invokeMethod(req.receiver, req.member.constData(),
Q_ARG(uint, req.id),
Q_ARG(bool, true));
}
return true;
} else if (response[0] == AttOpErrorResponse && response[1] == AttOpWriteRequest) {
if (req.receiver) {
QMetaObject::invokeMethod(req.receiver, req.member.constData(),
Q_ARG(uint, req.id),
Q_ARG(bool, false));
}
return true;
} else {
return false;
}
break;
default: // Otherwise just send a QByteArray.
if (req.receiver) {
QMetaObject::invokeMethod(req.receiver, req.member.constData(),
Q_ARG(const QByteArray&, response));
}
return true;
}
}
QList<GatoAttClient::InformationData> GatoAttClient::parseInformationData(const QByteArray &data)
{
const int format = data[0];
QList<InformationData> list;
int item_len;
switch (format) {
case 1:
item_len = 2 + 2;
break;
case 2:
item_len = 2 + 16;
break;
default:
qWarning() << "Unknown InformationData format!";
return list;
}
int items = (data.size() - 1) / item_len;
list.reserve(items);
int pos = 1;
const char *s = data.constData();
for (int i = 0; i < items; i++) {
InformationData d;
QByteArray uuid;
d.handle = read_le<GatoHandle>(&s[pos]);
switch (format) {
case 1:
uuid = data.mid(pos + 2, 2);
break;
case 2:
uuid = data.mid(pos + 2, 16);
break;
}
d.uuid = bytearray_to_gatouuid(uuid);
list.append(d);
pos += item_len;
}
return list;
}
QList<GatoAttClient::HandleInformation> GatoAttClient::parseHandleInformation(const QByteArray &data)
{
const int item_len = 2;
const int items = data.size() / item_len;
QList<HandleInformation> list;
list.reserve(items);
int pos = 0;
const char *s = data.constData();
for (int i = 0; i < items; i++) {
HandleInformation d;
d.start = read_le<GatoHandle>(&s[pos]);
d.end = read_le<GatoHandle>(&s[pos + 2]);
list.append(d);
pos += item_len;
}
return list;
}
QList<GatoAttClient::AttributeData> GatoAttClient::parseAttributeData(const QByteArray &data)
{
const int item_len = data[0];
const int items = (data.size() - 1) / item_len;
QList<AttributeData> list;
list.reserve(items);
int pos = 1;
const char *s = data.constData();
for (int i = 0; i < items; i++) {
AttributeData d;
d.handle = read_le<GatoHandle>(&s[pos]);
d.value = data.mid(pos + 2, item_len - 2);
list.append(d);
pos += item_len;
}
return list;
}
QList<GatoAttClient::AttributeGroupData> GatoAttClient::parseAttributeGroupData(const QByteArray &data)
{
const int item_len = data[0];
const int items = (data.size() - 1) / item_len;
QList<AttributeGroupData> list;
list.reserve(items);
int pos = 1;
const char *s = data.constData();
for (int i = 0; i < items; i++) {
AttributeGroupData d;
d.start = read_le<GatoHandle>(&s[pos]);
d.end = read_le<GatoHandle>(&s[pos + 2]);
d.value = data.mid(pos + 4, item_len - 4);
list.append(d);
pos += item_len;
}
return list;
}
void GatoAttClient::handleSocketConnected()
{
requestExchangeMTU(ATT_MAX_LE_MTU, this, SLOT(handleServerMTU(quint16)));
emit connected();
}
void GatoAttClient::handleSocketDisconnected()
{
emit disconnected();
}
void GatoAttClient::handleSocketReadyRead()
{
QByteArray pkt = socket->receive();
if (!pkt.isEmpty()) {
#if PROTOCOL_DEBUG
qDebug() << "Received" << pkt.size() << "bytes" << pkt.toHex();
#endif
// Check if it is an event
if (handleEvent(pkt)) {
return;
}
// Otherwise, if we have a request waiting, check if this answers it
if (!pending_requests.isEmpty()) {
if (handleResponse(pending_requests.head(), pkt)) {
pending_requests.dequeue();
// Proceed to next request
if (!pending_requests.isEmpty()) {
sendARequest();
}
return;
}
}
qDebug() << "No idea what this packet ("
<< QString("0x%1").arg(uint(pkt.at(0)), 2, 16, QLatin1Char('0'))
<< ") is";
}
}
void GatoAttClient::handleServerMTU(uint req, quint16 server_mtu)
{
Q_UNUSED(req);
if (server_mtu) {
cur_mtu = server_mtu;
if (cur_mtu < ATT_DEFAULT_LE_MTU) {
cur_mtu = ATT_DEFAULT_LE_MTU;
}
}
}
QT_END_NAMESPACE

View File

@ -1,135 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Javier de San Pedro <dev.git@javispedro.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef GATOATTCLIENT_H
#define GATOATTCLIENT_H
#include <QtCore/QObject>
#include <QtCore/QQueue>
#include "gatosocket.h"
#include "gatouuid.h"
QT_BEGIN_NAMESPACE
class GatoAttClient : public QObject
{
Q_OBJECT
public:
explicit GatoAttClient(QObject *parent = 0);
~GatoAttClient();
GatoSocket::State state() const;
bool connectTo(const GatoAddress& addr);
void close();
struct InformationData
{
GatoHandle handle;
GatoUUID uuid;
};
struct HandleInformation
{
GatoHandle start;
GatoHandle end;
};
struct AttributeData
{
GatoHandle handle;
QByteArray value;
};
struct AttributeGroupData
{
GatoHandle start;
GatoHandle end;
QByteArray value;
};
int mtu() const;
uint request(int opcode, const QByteArray &data, QObject *receiver, const char *member);
uint requestExchangeMTU(quint16 client_mtu, QObject *receiver, const char *member);
uint requestFindInformation(GatoHandle start, GatoHandle end, QObject *receiver, const char *member);
uint requestFindByTypeValue(GatoHandle start, GatoHandle end, const GatoUUID &uuid, const QByteArray& value, QObject *receiver, const char *member);
uint requestReadByType(GatoHandle start, GatoHandle end, const GatoUUID &uuid, QObject *receiver, const char *member);
uint requestRead(GatoHandle handle, QObject *receiver, const char *member);
uint requestReadByGroupType(GatoHandle start, GatoHandle end, const GatoUUID &uuid, QObject *receiver, const char *member);
uint requestWrite(GatoHandle handle, const QByteArray &value, QObject *receiver, const char *member);
void cancelRequest(uint id);
void command(int opcode, const QByteArray &data);
void commandWrite(GatoHandle handle, const QByteArray &value);
Q_SIGNALS:
void connected();
void disconnected();
void attributeUpdated(GatoHandle handle, const QByteArray &value, bool confirmed);
private:
struct Request
{
uint id;
quint8 opcode;
QByteArray pkt;
QObject *receiver;
QByteArray member;
};
void sendARequest();
bool handleEvent(const QByteArray &event);
bool handleResponse(const Request& req, const QByteArray &response);
QList<InformationData> parseInformationData(const QByteArray &data);
QList<HandleInformation> parseHandleInformation(const QByteArray &data);
QList<AttributeData> parseAttributeData(const QByteArray &data);
QList<AttributeGroupData> parseAttributeGroupData(const QByteArray &data);
private Q_SLOTS:
void handleSocketConnected();
void handleSocketDisconnected();
void handleSocketReadyRead();
void handleServerMTU(uint req, quint16 server_mtu);
private:
GatoSocket *socket;
quint16 cur_mtu;
uint next_id;
QQueue<Request> pending_requests;
};
QT_END_NAMESPACE
#endif // GATOATTCLIENT_H

View File

@ -1,866 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Javier de San Pedro <dev.git@javispedro.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtCore/QDebug>
#include <assert.h>
#include <bluetooth/bluetooth.h>
#include "gatoperipheral_p.h"
#include "gatoaddress.h"
#include "gatouuid.h"
#include "helpers.h"
QT_BEGIN_NAMESPACE
enum EIRDataFields {
EIRFlags = 0x01,
EIRIncompleteUUID16List = 0x02,
EIRCompleteUUID16List = 0x03,
EIRIncompleteUUID32List = 0x04,
EIRCompleteUUID32List = 0x05,
EIRIncompleteUUID128List = 0x06,
EIRCompleteUUID128List = 0x07,
EIRIncompleteLocalName = 0x08,
EIRCompleteLocalName = 0x09,
EIRTxPowerLevel = 0x0A,
EIRDeviceClass = 0x0D,
EIRSecurityManagerTKValue = 0x10,
EIRSecurityManagerOutOfBandFlags = 0x11,
EIRSolicitedUUID128List = 0x15
};
GatoPeripheral::GatoPeripheral(const GatoAddress &addr, QObject *parent) :
QObject(parent), d_ptr(new GatoPeripheralPrivate(this))
{
Q_D(GatoPeripheral);
d->addr = addr;
d->att = new GatoAttClient(this);
connect(d->att, SIGNAL(connected()), d, SLOT(handleAttConnected()));
connect(d->att, SIGNAL(disconnected()), d, SLOT(handleAttDisconnected()));
connect(d->att, SIGNAL(attributeUpdated(GatoHandle,QByteArray,bool)), d, SLOT(handleAttAttributeUpdated(GatoHandle,QByteArray,bool)));
}
GatoPeripheral::~GatoPeripheral()
{
if (state() != StateDisconnected) {
disconnect();
}
delete d_ptr;
}
GatoPeripheral::State GatoPeripheral::state() const
{
Q_D(const GatoPeripheral);
return static_cast<State>(d->att->state());
}
GatoAddress GatoPeripheral::address() const
{
Q_D(const GatoPeripheral);
return d->addr;
}
QString GatoPeripheral::name() const
{
Q_D(const GatoPeripheral);
return d->name;
}
QList<GatoService> GatoPeripheral::services() const
{
Q_D(const GatoPeripheral);
return d->services.values();
}
void GatoPeripheral::parseEIR(quint8 data[], int len)
{
Q_D(GatoPeripheral);
int pos = 0;
while (pos < len) {
int item_len = data[pos];
pos++;
if (item_len == 0) break;
int type = data[pos];
assert(pos + item_len <= len);
switch (type) {
case EIRFlags:
d->parseEIRFlags(&data[pos + 1], item_len - 1);
break;
case EIRIncompleteUUID16List:
d->parseEIRUUIDs(16/8, false, &data[pos + 1], item_len - 1);
break;
case EIRCompleteUUID16List:
d->parseEIRUUIDs(16/8, true, &data[pos + 1], item_len - 1);
break;
case EIRIncompleteUUID32List:
d->parseEIRUUIDs(32/8, false, &data[pos + 1], item_len - 1);
break;
case EIRCompleteUUID32List:
d->parseEIRUUIDs(32/8, true, &data[pos + 1], item_len - 1);
break;
case EIRIncompleteUUID128List:
d->parseEIRUUIDs(128/8, false, &data[pos + 1], item_len - 1);
break;
case EIRCompleteUUID128List:
d->parseEIRUUIDs(128/8, true, &data[pos + 1], item_len - 1);
break;
case EIRIncompleteLocalName:
d->parseName(false, &data[pos + 1], item_len - 1);
break;
case EIRCompleteLocalName:
d->parseName(true, &data[pos + 1], item_len - 1);
break;
case EIRTxPowerLevel:
case EIRSolicitedUUID128List:
qDebug() << "Unhandled EIR data type" << type;
break;
default:
qWarning() << "Unknown EIR data type" << type;
break;
}
pos += item_len;
}
assert(pos == len);
}
bool GatoPeripheral::advertisesService(const GatoUUID &uuid) const
{
Q_D(const GatoPeripheral);
return d->service_uuids.contains(uuid);
}
void GatoPeripheral::connectPeripheral()
{
Q_D(GatoPeripheral);
if (d->att->state() != GatoSocket::StateDisconnected) {
qDebug() << "Already connecting";
return;
}
d->att->connectTo(d->addr);
}
void GatoPeripheral::disconnectPeripheral()
{
Q_D(GatoPeripheral);
d->att->close();
}
void GatoPeripheral::discoverServices()
{
Q_D(GatoPeripheral);
if (!d->complete_services && state() == StateConnected) {
d->clearServices();
d->att->requestReadByGroupType(0x0001, 0xFFFF, GatoUUID::GattPrimaryService,
d, SLOT(handlePrimary(QList<GatoAttClient::AttributeGroupData>)));
} else {
qWarning() << "Not connected";
}
}
void GatoPeripheral::discoverServices(const QList<GatoUUID> &serviceUUIDs)
{
Q_D(GatoPeripheral);
if (serviceUUIDs.isEmpty()) return;
if (state() == StateConnected) {
foreach (const GatoUUID& uuid, serviceUUIDs) {
QByteArray value = gatouuid_to_bytearray(uuid, true, false);
uint req = d->att->requestFindByTypeValue(0x0001, 0xFFFF, GatoUUID::GattPrimaryService, value,
d, SLOT(handlePrimaryForService(uint,QList<GatoAttClient::HandleInformation>)));
d->pending_primary_reqs.insert(req, uuid);
}
} else {
qWarning() << "Not connected";
}
}
void GatoPeripheral::discoverCharacteristics(const GatoService &service)
{
Q_D(GatoPeripheral);
if (!d->services.contains(service.startHandle())) {
qWarning() << "Unknown service for this peripheral";
return;
}
GatoService &our_service = d->services[service.startHandle()];
if (our_service.startHandle() != service.startHandle() ||
our_service.endHandle() != service.endHandle() ||
our_service.uuid() != service.uuid()) {
qWarning() << "Unknown service for this peripheral";
return;
}
if (state() == StateConnected) {
GatoHandle start = our_service.startHandle();
GatoHandle end = our_service.endHandle();
d->clearServiceCharacteristics(&our_service);
uint req = d->att->requestReadByType(start, end, GatoUUID::GattCharacteristic,
d, SLOT(handleCharacteristic(QList<GatoAttClient::AttributeData>)));
d->pending_characteristic_reqs.insert(req, start);
} else {
qWarning() << "Not connected";
}
}
void GatoPeripheral::discoverCharacteristics(const GatoService &service, const QList<GatoUUID> &characteristicUUIDs)
{
// TODO There seems to be no way to ask for the peripheral to filter by uuid
Q_UNUSED(characteristicUUIDs);
discoverCharacteristics(service);
}
void GatoPeripheral::discoverDescriptors(const GatoCharacteristic &characteristic)
{
Q_D(GatoPeripheral);
GatoHandle char_handle = characteristic.startHandle();
GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
if (!service_handle) {
qWarning() << "Unknown characteristic for this peripheral";
return;
}
GatoService &our_service = d->services[service_handle];
Q_ASSERT(our_service.containsCharacteristic(char_handle));
GatoCharacteristic our_char = our_service.getCharacteristic(char_handle);
Q_ASSERT(our_char.startHandle() == char_handle);
if (state() == StateConnected) {
d->clearCharacteristicDescriptors(&our_char);
our_service.addCharacteristic(our_char); // Update service with empty descriptors list
uint req = d->att->requestFindInformation(our_char.startHandle() + 1, our_char.endHandle(),
d, SLOT(handleDescriptors(uint,QList<GatoAttClient::InformationData>)));
d->pending_descriptor_reqs.insert(req, char_handle);
} else {
qWarning() << "Not connected";
}
}
void GatoPeripheral::readValue(const GatoCharacteristic &characteristic)
{
Q_D(GatoPeripheral);
GatoHandle char_handle = characteristic.startHandle();
GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
if (!service_handle) {
qWarning() << "Unknown characteristic for this peripheral";
return;
}
GatoService &our_service = d->services[service_handle];
Q_ASSERT(our_service.containsCharacteristic(char_handle));
if (state() == StateConnected) {
uint req = d->att->requestRead(characteristic.valueHandle(),
d, SLOT(handleCharacteristicRead(uint,QByteArray)));
d->pending_characteristic_read_reqs.insert(req, char_handle);
} else {
qWarning() << "Not connected";
}
}
void GatoPeripheral::readValue(const GatoDescriptor &descriptor)
{
Q_D(GatoPeripheral);
GatoHandle desc_handle = descriptor.handle();
GatoHandle char_handle = d->descriptor_to_characteristic.value(desc_handle);
if (!char_handle) {
qWarning() << "Unknown descriptor for this peripheral";
return;
}
GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
Q_ASSERT(service_handle);
GatoService &our_service = d->services[service_handle];
Q_ASSERT(our_service.containsCharacteristic(char_handle));
if (state() == StateConnected) {
uint req = d->att->requestRead(descriptor.handle(),
d, SLOT(handleDescriptorRead(uint,QByteArray)));
d->pending_descriptor_read_reqs.insert(req, char_handle);
} else {
qWarning() << "Not connected";
}
}
void GatoPeripheral::writeValue(const GatoCharacteristic &characteristic, const QByteArray &data, WriteType type)
{
Q_D(GatoPeripheral);
GatoHandle char_handle = characteristic.startHandle();
GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
if (!service_handle) {
qWarning() << "Unknown characteristic for this peripheral";
return;
}
GatoService &our_service = d->services[service_handle];
Q_ASSERT(our_service.containsCharacteristic(char_handle));
if (state() == StateConnected) {
switch (type) {
case WriteWithResponse:
d->att->requestWrite(characteristic.valueHandle(), data,
d, SLOT(handleCharacteristicWrite(uint,bool)));
break;
case WriteWithoutResponse:
d->att->commandWrite(characteristic.valueHandle(), data);
break;
}
} else {
qWarning() << "Not connected";
}
}
void GatoPeripheral::writeValue(const GatoDescriptor &descriptor, const QByteArray &data)
{
Q_D(GatoPeripheral);
GatoHandle desc_handle = descriptor.handle();
GatoHandle char_handle = d->descriptor_to_characteristic.value(desc_handle);
if (!char_handle) {
qWarning() << "Unknown descriptor for this peripheral";
return;
}
GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
Q_ASSERT(service_handle);
GatoService &our_service = d->services[service_handle];
Q_ASSERT(our_service.containsCharacteristic(char_handle));
if (state() == StateConnected) {
d->att->requestWrite(descriptor.handle(), data,
d, SLOT(handleDescriptorWrite(uint,bool)));
} else {
qWarning() << "Not connected";
}
}
void GatoPeripheral::setNotification(const GatoCharacteristic &characteristic, bool enabled)
{
Q_D(GatoPeripheral);
GatoHandle char_handle = characteristic.startHandle();
GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
if (!service_handle) {
qWarning() << "Unknown characteristic for this peripheral";
return;
}
GatoService &our_service = d->services[service_handle];
Q_ASSERT(our_service.containsCharacteristic(char_handle));
GatoCharacteristic our_char = our_service.getCharacteristic(char_handle);
if (!(our_char.properties() & GatoCharacteristic::PropertyNotify)) {
qWarning() << "Characteristic does not support notifications";
return;
}
if (state() != StateConnected) {
qWarning() << "Not connected";
return;
}
const GatoUUID uuid(GatoUUID::GattClientCharacteristicConfiguration);
if (our_char.containsDescriptor(uuid)) {
GatoDescriptor desc = our_char.getDescriptor(uuid);
d->pending_set_notify.remove(char_handle);
writeValue(characteristic, d->genClientCharConfiguration(true, false));
} else {
d->pending_set_notify[char_handle] = enabled;
discoverDescriptors(our_char); // May need to find appropiate descriptor
}
}
GatoPeripheralPrivate::GatoPeripheralPrivate(GatoPeripheral *parent)
: QObject(parent), q_ptr(parent),
complete_name(false), complete_services(false)
{
}
GatoPeripheralPrivate::~GatoPeripheralPrivate()
{
delete att;
}
void GatoPeripheralPrivate::parseEIRFlags(quint8 data[], int len)
{
Q_UNUSED(data);
Q_UNUSED(len);
// Nothing to do for now.
}
void GatoPeripheralPrivate::parseEIRUUIDs(int size, bool complete, quint8 data[], int len)
{
Q_UNUSED(complete);
if (size != 16/8 && size != 32/8 && size != 128/8) {
qWarning() << "Unhandled UUID size: " << size;
return;
}
for (int pos = 0; pos < len; pos += size) {
char *ptr = reinterpret_cast<char*>(&data[pos]);
QByteArray ba = QByteArray::fromRawData(ptr, size/8);
service_uuids.insert(bytearray_to_gatouuid(ba));
}
}
void GatoPeripheralPrivate::parseName(bool complete, quint8 data[], int len)
{
Q_Q(GatoPeripheral);
if (complete || !complete_name) {
name = QString::fromUtf8(reinterpret_cast<char*>(data), len);
complete_name = complete;
emit q->nameChanged();
}
}
GatoCharacteristic GatoPeripheralPrivate::parseCharacteristicValue(const QByteArray &ba)
{
GatoCharacteristic characteristic;
const char *data = ba.constData();
quint8 properties = data[0];
characteristic.setProperties(GatoCharacteristic::Properties(properties));
GatoHandle handle = read_le<quint16>(&data[1]);
characteristic.setValueHandle(handle);
GatoUUID uuid = bytearray_to_gatouuid(ba.mid(3));
characteristic.setUuid(uuid);
return characteristic;
}
QByteArray GatoPeripheralPrivate::genClientCharConfiguration(bool notification, bool indication)
{
QByteArray ba;
ba.resize(sizeof(quint16));
quint16 val = 0;
if (notification)
val |= 0x1;
if (indication)
val |= 0x2;
write_le<quint16>(val, ba.data());
return ba;
}
void GatoPeripheralPrivate::clearServices()
{
characteristic_to_service.clear();
value_to_characteristic.clear();
descriptor_to_characteristic.clear();
services.clear();
}
void GatoPeripheralPrivate::clearServiceCharacteristics(GatoService *service)
{
QList<GatoCharacteristic> chars = service->characteristics();
QList<GatoCharacteristic>::iterator it;
for (it = chars.begin(); it != chars.end(); ++it) {
clearCharacteristicDescriptors(&*it);
characteristic_to_service.remove(it->startHandle());
value_to_characteristic.remove(it->valueHandle());
}
service->clearCharacteristics();
}
void GatoPeripheralPrivate::clearCharacteristicDescriptors(GatoCharacteristic *characteristic)
{
QList<GatoDescriptor> descs = characteristic->descriptors();
foreach (const GatoDescriptor& d, descs) {
descriptor_to_characteristic.remove(d.handle());
}
characteristic->clearDescriptors();
}
void GatoPeripheralPrivate::finishSetNotifyOperations(const GatoCharacteristic &characteristic)
{
Q_Q(GatoPeripheral);
GatoHandle handle = characteristic.startHandle();
if (pending_set_notify.contains(handle)) {
const GatoUUID uuid(GatoUUID::GattClientCharacteristicConfiguration);
bool notify = pending_set_notify.value(handle);
foreach (const GatoDescriptor &descriptor, characteristic.descriptors()) {
if (descriptor.uuid() == uuid) {
q->writeValue(descriptor, genClientCharConfiguration(notify, false));
}
}
pending_set_notify.remove(handle);
}
}
void GatoPeripheralPrivate::handleAttConnected()
{
Q_Q(GatoPeripheral);
emit q->connected();
}
void GatoPeripheralPrivate::handleAttDisconnected()
{
Q_Q(GatoPeripheral);
// Forget about all pending requests
pending_primary_reqs.clear();
pending_characteristic_reqs.clear();
pending_characteristic_read_reqs.clear();
pending_descriptor_reqs.clear();
pending_descriptor_read_reqs.clear();
emit q->disconnected();
}
void GatoPeripheralPrivate::handleAttAttributeUpdated(GatoHandle handle, const QByteArray &value, bool confirmed)
{
Q_Q(GatoPeripheral);
Q_UNUSED(confirmed);
// Let's see if this is a handle we know about.
if (value_to_characteristic.contains(handle)) {
// Ok, it's a characteristic value.
GatoHandle char_handle = value_to_characteristic.value(handle);
GatoHandle service_handle = characteristic_to_service.value(char_handle);
if (!service_handle) {
qWarning() << "Got a notification for a characteristic I don't know about";
return;
}
GatoService &service = services[service_handle];
GatoCharacteristic characteristic = service.getCharacteristic(char_handle);
emit q->valueUpdated(characteristic, value);
}
}
void GatoPeripheralPrivate::handlePrimary(uint req, const QList<GatoAttClient::AttributeGroupData> &list)
{
Q_Q(GatoPeripheral);
Q_UNUSED(req);
if (list.isEmpty()) {
complete_services = true;
emit q->servicesDiscovered();
} else {
GatoHandle last_handle = 0;
foreach (const GatoAttClient::AttributeGroupData &data, list) {
GatoUUID uuid = bytearray_to_gatouuid(data.value);
GatoService service;
service.setUuid(uuid);
service.setStartHandle(data.start);
service.setEndHandle(data.end);
services.insert(data.start, service);
service_uuids.insert(uuid);
last_handle = data.end;
}
// Fetch following attributes
att->requestReadByGroupType(last_handle + 1, 0xFFFF, GatoUUID::GattPrimaryService,
this, SLOT(handlePrimary(uint,QList<GatoAttClient::AttributeGroupData>)));
}
}
void GatoPeripheralPrivate::handlePrimaryForService(uint req, const QList<GatoAttClient::HandleInformation> &list)
{
Q_Q(GatoPeripheral);
GatoUUID uuid = pending_primary_reqs.value(req, GatoUUID());
if (uuid.isNull()) {
qDebug() << "Got primary for service response for a request I did not make";
return;
}
pending_primary_reqs.remove(req);
if (list.isEmpty()) {
if (pending_primary_reqs.isEmpty()) {
emit q->servicesDiscovered();
}
} else {
GatoHandle last_handle = 0;
foreach (const GatoAttClient::HandleInformation &data, list) {
GatoService service;
service.setUuid(uuid);
service.setStartHandle(data.start);
service.setEndHandle(data.end);
services.insert(data.start, service);
service_uuids.insert(uuid);
last_handle = data.end;
}
// Fetch following attributes
QByteArray value = gatouuid_to_bytearray(uuid, true, false);
uint req = att->requestFindByTypeValue(last_handle + 1, 0xFFFF, GatoUUID::GattPrimaryService, value,
this, SLOT(handlePrimaryForService(uint,QList<GatoAttClient::HandleInformation>)));
pending_primary_reqs.insert(req, uuid);
}
}
void GatoPeripheralPrivate::handleCharacteristic(uint req, const QList<GatoAttClient::AttributeData> &list)
{
Q_Q(GatoPeripheral);
GatoHandle service_start = pending_characteristic_reqs.value(req, 0);
if (!service_start) {
qDebug() << "Got characteristics for a request I did not make";
return;
}
pending_characteristic_reqs.remove(req);
Q_ASSERT(services.contains(service_start));
GatoService &service = services[service_start];
Q_ASSERT(service.startHandle() == service_start);
if (list.isEmpty()) {
emit q->characteristicsDiscovered(service);
} else {
GatoHandle last_handle = 0;
// If we are continuing a characteristic list, this means the
// last service we discovered in the previous iteration was not
// the last one, so we have to reduce its endHandle!
QList<GatoCharacteristic> cur_chars = service.characteristics();
if (!cur_chars.isEmpty()) {
GatoCharacteristic &last = cur_chars.back();
last.setEndHandle(list.front().handle - 1);
service.addCharacteristic(last);
}
for (int i = 0; i < list.size(); i++) {
const GatoAttClient::AttributeData &data = list.at(i);
GatoCharacteristic characteristic = parseCharacteristicValue(data.value);
characteristic.setStartHandle(data.handle);
if (i + 1 < list.size()) {
characteristic.setEndHandle(list.at(i + 1).handle - 1);
} else {
characteristic.setEndHandle(service.endHandle());
}
service.addCharacteristic(characteristic);
characteristic_to_service.insert(data.handle, service_start);
value_to_characteristic.insert(characteristic.valueHandle(), data.handle);
last_handle = data.handle;
}
if (last_handle >= service.endHandle()) {
// Already finished, no need to send another request
emit q->characteristicsDiscovered(service);
return;
}
// Fetch following attributes
uint req = att->requestReadByType(last_handle + 1, service.endHandle(), GatoUUID::GattCharacteristic,
this, SLOT(handleCharacteristic(uint,QList<GatoAttClient::AttributeData>)));
pending_characteristic_reqs.insert(req, service.startHandle());
}
}
void GatoPeripheralPrivate::handleDescriptors(uint req, const QList<GatoAttClient::InformationData> &list)
{
Q_Q(GatoPeripheral);
GatoHandle char_handle = pending_descriptor_reqs.value(req);
if (!char_handle) {
qDebug() << "Got descriptor for a request I did not make";
return;
}
pending_descriptor_reqs.remove(req);
GatoHandle service_handle = characteristic_to_service.value(char_handle);
if (!service_handle) {
qWarning() << "Unknown characteristic during descriptor discovery: " << char_handle;
return;
}
Q_ASSERT(services.contains(service_handle));
GatoService &service = services[service_handle];
Q_ASSERT(service.startHandle() == service_handle);
Q_ASSERT(service.containsCharacteristic(char_handle));
GatoCharacteristic characteristic = service.getCharacteristic(char_handle);
if (list.isEmpty()) {
finishSetNotifyOperations(characteristic);
emit q->descriptorsDiscovered(characteristic);
} else {
GatoHandle last_handle = 0;
foreach (const GatoAttClient::InformationData &data, list) {
// Skip the value attribute itself.
if (data.handle == characteristic.valueHandle()) continue;
GatoDescriptor descriptor;
descriptor.setHandle(data.handle);
descriptor.setUuid(data.uuid);
characteristic.addDescriptor(descriptor);
service.addCharacteristic(characteristic);
descriptor_to_characteristic.insert(data.handle, char_handle);
last_handle = data.handle;
}
service.addCharacteristic(characteristic);
if (last_handle >= characteristic.endHandle()) {
// Already finished, no need to send another request
finishSetNotifyOperations(characteristic);
emit q->descriptorsDiscovered(characteristic);
return;
}
// Fetch following attributes
uint req = att->requestFindInformation(last_handle + 1, characteristic.endHandle(),
this, SLOT(handleDescriptors(uint,QList<GatoAttClient::InformationData>)));
pending_descriptor_reqs.insert(req, char_handle);
}
}
void GatoPeripheralPrivate::handleCharacteristicRead(uint req, const QByteArray &value)
{
Q_Q(GatoPeripheral);
GatoHandle char_handle = pending_characteristic_read_reqs.value(req);
if (!char_handle) {
qDebug() << "Got characteristics for a request I did not make";
return;
}
pending_characteristic_read_reqs.remove(req);
GatoHandle service_handle = characteristic_to_service.value(char_handle);
if (!service_handle) {
qWarning() << "Unknown characteristic during read: " << char_handle;
return;
}
Q_ASSERT(services.contains(service_handle));
GatoService &service = services[service_handle];
Q_ASSERT(service.startHandle() == service_handle);
Q_ASSERT(service.containsCharacteristic(char_handle));
GatoCharacteristic characteristic = service.getCharacteristic(char_handle);
emit q->valueUpdated(characteristic, value);
}
void GatoPeripheralPrivate::handleDescriptorRead(uint req, const QByteArray &value)
{
Q_Q(GatoPeripheral);
GatoHandle desc_handle = pending_descriptor_read_reqs.value(req);
if (!desc_handle) {
qDebug() << "Got characteristics for a request I did not make";
return;
}
pending_descriptor_read_reqs.remove(req);
GatoHandle char_handle = descriptor_to_characteristic.value(desc_handle);
if (!char_handle) {
qWarning() << "Unknown characteristic during read: " << char_handle;
return;
}
GatoHandle service_handle = characteristic_to_service.value(char_handle);
if (!service_handle) {
qWarning() << "Unknown characteristic during read: " << char_handle;
return;
}
Q_ASSERT(services.contains(service_handle));
GatoService &service = services[service_handle];
Q_ASSERT(service.startHandle() == service_handle);
Q_ASSERT(service.containsCharacteristic(char_handle));
GatoCharacteristic characteristic = service.getCharacteristic(char_handle);
Q_ASSERT(characteristic.containsDescriptor(desc_handle));
GatoDescriptor descriptor = characteristic.getDescriptor(desc_handle);
emit q->descriptorValueUpdated(descriptor, value);
}
void GatoPeripheralPrivate::handleCharacteristicWrite(uint req, bool ok)
{
Q_UNUSED(req);
if (!ok) {
qWarning() << "Failed to write some characteristic";
}
}
void GatoPeripheralPrivate::handleDescriptorWrite(uint req, bool ok)
{
Q_UNUSED(req);
if (!ok) {
qWarning() << "Failed to write some characteristic";
}
}
QT_END_NAMESPACE

View File

@ -1,111 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Javier de San Pedro <dev.git@javispedro.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef GATOPERIPHERAL_H
#define GATOPERIPHERAL_H
#include <QtCore/QObject>
#include "libgato_global.h"
#include "gatouuid.h"
#include "gatoaddress.h"
QT_BEGIN_NAMESPACE
class GatoService;
class GatoCharacteristic;
class GatoDescriptor;
class GatoPeripheralPrivate;
class LIBGATO_EXPORT GatoPeripheral : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(GatoPeripheral)
Q_ENUMS(State)
Q_ENUMS(WriteType)
Q_PROPERTY(GatoAddress address READ address)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
public:
GatoPeripheral(const GatoAddress& addr, QObject *parent = 0);
~GatoPeripheral();
enum State {
StateDisconnected,
StateConnecting,
StateConnected
};
enum WriteType {
WriteWithResponse = 0,
WriteWithoutResponse
};
State state() const;
GatoAddress address() const;
QString name() const;
QList<GatoService> services() const;
void parseEIR(quint8 data[], int len);
bool advertisesService(const GatoUUID &uuid) const;
public Q_SLOTS:
void connectPeripheral();
void disconnectPeripheral();
void discoverServices();
void discoverServices(const QList<GatoUUID>& serviceUUIDs);
void discoverCharacteristics(const GatoService &service);
void discoverCharacteristics(const GatoService &service, const QList<GatoUUID>& characteristicUUIDs);
void discoverDescriptors(const GatoCharacteristic &characteristic);
void readValue(const GatoCharacteristic &characteristic);
void readValue(const GatoDescriptor &descriptor);
void writeValue(const GatoCharacteristic &characteristic, const QByteArray &data, WriteType type = WriteWithResponse);
void writeValue(const GatoDescriptor &descriptor, const QByteArray &data);
void setNotification(const GatoCharacteristic &characteristic, bool enabled);
Q_SIGNALS:
void connected();
void disconnected();
void nameChanged();
void servicesDiscovered();
void characteristicsDiscovered(const GatoService &service);
void descriptorsDiscovered(const GatoCharacteristic &characteristic);
void valueUpdated(const GatoCharacteristic &characteristic, const QByteArray &value);
void descriptorValueUpdated(const GatoDescriptor &descriptor, const QByteArray &value);
private:
GatoPeripheralPrivate *const d_ptr;
};
QT_END_NAMESPACE
#endif // GATOPERIPHERAL_H

View File

@ -1,108 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Javier de San Pedro <dev.git@javispedro.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef GATOPERIPHERAL_P_H
#define GATOPERIPHERAL_P_H
#include "gatoperipheral.h"
#include "gatoservice.h"
#include "gatocharacteristic.h"
#include "gatodescriptor.h"
#include "gatoattclient.h"
QT_BEGIN_NAMESPACE
class GatoPeripheralPrivate : public QObject
{
Q_OBJECT
Q_DECLARE_PUBLIC(GatoPeripheral)
public:
GatoPeripheralPrivate(GatoPeripheral *parent);
~GatoPeripheralPrivate();
GatoPeripheral *q_ptr;
GatoAddress addr;
QString name;
QSet<GatoUUID> service_uuids;
QMap<GatoHandle, GatoService> services;
bool complete_name : 1;
bool complete_services : 1;
/** Maps attribute handles to service handles. */
QMap<GatoHandle, GatoHandle> characteristic_to_service;
QMap<GatoHandle, GatoHandle> value_to_characteristic;
QMap<GatoHandle, GatoHandle> descriptor_to_characteristic;
GatoAttClient *att;
QMap<uint, GatoUUID> pending_primary_reqs;
QMap<uint, GatoHandle> pending_characteristic_reqs;
QMap<uint, GatoHandle> pending_characteristic_read_reqs;
QMap<uint, GatoHandle> pending_descriptor_reqs;
QMap<uint, GatoHandle> pending_descriptor_read_reqs;
QMap<GatoHandle, bool> pending_set_notify;
void parseEIRFlags(quint8 data[], int len);
void parseEIRUUIDs(int size, bool complete, quint8 data[], int len);
void parseName(bool complete, quint8 data[], int len);
static GatoCharacteristic parseCharacteristicValue(const QByteArray &ba);
static QByteArray genClientCharConfiguration(bool notification, bool indication);
void clearServices();
void clearServiceCharacteristics(GatoService *service);
void clearCharacteristicDescriptors(GatoCharacteristic *characteristic);
void finishSetNotifyOperations(const GatoCharacteristic &characteristic);
public slots:
void handleAttConnected();
void handleAttDisconnected();
void handleAttAttributeUpdated(GatoHandle handle, const QByteArray &value, bool confirmed);
void handlePrimary(uint req, const QList<GatoAttClient::AttributeGroupData>& list);
void handlePrimaryForService(uint req, const QList<GatoAttClient::HandleInformation>& list);
void handleCharacteristic(uint req, const QList<GatoAttClient::AttributeData> &list);
void handleDescriptors(uint req, const QList<GatoAttClient::InformationData> &list);
void handleCharacteristicRead(uint req, const QByteArray &value);
void handleDescriptorRead(uint req, const QByteArray &value);
void handleCharacteristicWrite(uint req, bool ok);
void handleDescriptorWrite(uint req, bool ok);
};
QT_END_NAMESPACE
#endif // GATOPERIPHERAL_P_H

View File

@ -17,6 +17,3 @@
"qtxmlpatterns" => "", "qtxmlpatterns" => "",
"qtandroidextras" => "", "qtandroidextras" => "",
); );
my @gato_headers = ("gatoattclient.h", "gatoperipheral.h");
@ignore_for_master_contents = ( @gato_headers );