Finish firmware private transport stack
This commit is contained in:
@@ -1,10 +1,210 @@
|
||||
#include "DRI/GATT/Dri_Gatt.h"
|
||||
|
||||
#include <QtBluetooth/QBluetoothAddress>
|
||||
#include <QtBluetooth/QBluetoothDeviceDiscoveryAgent>
|
||||
#include <QtBluetooth/QBluetoothDeviceInfo>
|
||||
#include <QtBluetooth/QBluetoothUuid>
|
||||
#include <QtBluetooth/QLowEnergyCharacteristic>
|
||||
#include <QtBluetooth/QLowEnergyController>
|
||||
#include <QtBluetooth/QLowEnergyDescriptor>
|
||||
#include <QtBluetooth/QLowEnergyService>
|
||||
#include <QtCore/QEventLoop>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
struct Dri_Gatt_Struct_Context
|
||||
{
|
||||
QLowEnergyController* Controller = nullptr;
|
||||
QLowEnergyService* Service = nullptr;
|
||||
QLowEnergyCharacteristic RxCharacteristic;
|
||||
QLowEnergyCharacteristic TxCharacteristic;
|
||||
QList<QByteArray> ReadQueue;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const QBluetoothUuid kServiceUuid(
|
||||
QStringLiteral("0b7f6000-38d2-4f62-8f6f-36c4fd73a110"));
|
||||
const QBluetoothUuid kRxUuid(
|
||||
QStringLiteral("0b7f6001-38d2-4f62-8f6f-36c4fd73a110"));
|
||||
const QBluetoothUuid kTxUuid(
|
||||
QStringLiteral("0b7f6002-38d2-4f62-8f6f-36c4fd73a110"));
|
||||
|
||||
bool Dri_Gatt_Func_WaitForControllerConnected(QLowEnergyController* p_Controller)
|
||||
{
|
||||
QEventLoop EventLoop;
|
||||
QTimer TimeoutTimer;
|
||||
bool IsConnected = false;
|
||||
|
||||
TimeoutTimer.setSingleShot(true);
|
||||
|
||||
QObject::connect(
|
||||
p_Controller,
|
||||
&QLowEnergyController::connected,
|
||||
&EventLoop,
|
||||
[&EventLoop, &IsConnected]()
|
||||
{
|
||||
IsConnected = true;
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
p_Controller,
|
||||
static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(
|
||||
&QLowEnergyController::error),
|
||||
&EventLoop,
|
||||
[&EventLoop]()
|
||||
{
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
&TimeoutTimer,
|
||||
&QTimer::timeout,
|
||||
&EventLoop,
|
||||
&QEventLoop::quit);
|
||||
|
||||
p_Controller->connectToDevice();
|
||||
TimeoutTimer.start(5000);
|
||||
EventLoop.exec();
|
||||
return IsConnected;
|
||||
}
|
||||
|
||||
bool Dri_Gatt_Func_WaitForServiceDiscovery(QLowEnergyController* p_Controller)
|
||||
{
|
||||
QEventLoop EventLoop;
|
||||
QTimer TimeoutTimer;
|
||||
bool IsFinished = false;
|
||||
|
||||
TimeoutTimer.setSingleShot(true);
|
||||
|
||||
QObject::connect(
|
||||
p_Controller,
|
||||
&QLowEnergyController::discoveryFinished,
|
||||
&EventLoop,
|
||||
[&EventLoop, &IsFinished]()
|
||||
{
|
||||
IsFinished = true;
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
p_Controller,
|
||||
static_cast<void (QLowEnergyController::*)(QLowEnergyController::Error)>(
|
||||
&QLowEnergyController::error),
|
||||
&EventLoop,
|
||||
[&EventLoop]()
|
||||
{
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
&TimeoutTimer,
|
||||
&QTimer::timeout,
|
||||
&EventLoop,
|
||||
&QEventLoop::quit);
|
||||
|
||||
p_Controller->discoverServices();
|
||||
TimeoutTimer.start(5000);
|
||||
EventLoop.exec();
|
||||
return IsFinished;
|
||||
}
|
||||
|
||||
bool Dri_Gatt_Func_WaitForServiceDetails(QLowEnergyService* p_Service)
|
||||
{
|
||||
QEventLoop EventLoop;
|
||||
QTimer TimeoutTimer;
|
||||
bool IsDiscovered = false;
|
||||
|
||||
TimeoutTimer.setSingleShot(true);
|
||||
|
||||
QObject::connect(
|
||||
p_Service,
|
||||
&QLowEnergyService::stateChanged,
|
||||
&EventLoop,
|
||||
[&EventLoop, &IsDiscovered](QLowEnergyService::ServiceState State)
|
||||
{
|
||||
if (State == QLowEnergyService::ServiceDiscovered)
|
||||
{
|
||||
IsDiscovered = true;
|
||||
EventLoop.quit();
|
||||
}
|
||||
});
|
||||
QObject::connect(
|
||||
p_Service,
|
||||
static_cast<void (QLowEnergyService::*)(QLowEnergyService::ServiceError)>(
|
||||
&QLowEnergyService::error),
|
||||
&EventLoop,
|
||||
[&EventLoop]()
|
||||
{
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
&TimeoutTimer,
|
||||
&QTimer::timeout,
|
||||
&EventLoop,
|
||||
&QEventLoop::quit);
|
||||
|
||||
p_Service->discoverDetails();
|
||||
TimeoutTimer.start(5000);
|
||||
EventLoop.exec();
|
||||
return IsDiscovered;
|
||||
}
|
||||
|
||||
bool Dri_Gatt_Func_EnableNotify(
|
||||
Dri_Gatt_Struct_Context* p_Context,
|
||||
const QLowEnergyCharacteristic& Characteristic)
|
||||
{
|
||||
if (!Characteristic.isValid() || (p_Context == nullptr) || (p_Context->Service == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const QLowEnergyDescriptor Cccd =
|
||||
Characteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
|
||||
if (!Cccd.isValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QEventLoop EventLoop;
|
||||
QTimer TimeoutTimer;
|
||||
bool IsWritten = false;
|
||||
|
||||
TimeoutTimer.setSingleShot(true);
|
||||
|
||||
QObject::connect(
|
||||
p_Context->Service,
|
||||
&QLowEnergyService::descriptorWritten,
|
||||
&EventLoop,
|
||||
[&EventLoop, &IsWritten, Cccd](const QLowEnergyDescriptor& Descriptor, const QByteArray&)
|
||||
{
|
||||
if (Descriptor == Cccd)
|
||||
{
|
||||
IsWritten = true;
|
||||
EventLoop.quit();
|
||||
}
|
||||
});
|
||||
QObject::connect(
|
||||
p_Context->Service,
|
||||
static_cast<void (QLowEnergyService::*)(QLowEnergyService::ServiceError)>(
|
||||
&QLowEnergyService::error),
|
||||
&EventLoop,
|
||||
[&EventLoop]()
|
||||
{
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
&TimeoutTimer,
|
||||
&QTimer::timeout,
|
||||
&EventLoop,
|
||||
&QEventLoop::quit);
|
||||
|
||||
p_Context->Service->writeDescriptor(Cccd, QByteArray::fromHex("0100"));
|
||||
TimeoutTimer.start(3000);
|
||||
EventLoop.exec();
|
||||
return IsWritten;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QVector<Dri_Gatt_Struct_PortInfo> Dri_Gatt_Enum()
|
||||
{
|
||||
QVector<Dri_Gatt_Struct_PortInfo> PortList;
|
||||
@@ -79,6 +279,17 @@ void Dri_Gatt_Close(Dri_Gatt_Struct_Port* p_Port)
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_Port->p_Context != nullptr)
|
||||
{
|
||||
if (p_Port->p_Context->Controller != nullptr)
|
||||
{
|
||||
p_Port->p_Context->Controller->disconnectFromDevice();
|
||||
delete p_Port->p_Context->Controller;
|
||||
}
|
||||
|
||||
delete p_Port->p_Context;
|
||||
}
|
||||
|
||||
*p_Port = Dri_Gatt_Struct_Port();
|
||||
}
|
||||
|
||||
@@ -93,38 +304,183 @@ bool Dri_Gatt_Open(
|
||||
}
|
||||
|
||||
Dri_Gatt_Close(p_Port);
|
||||
|
||||
Dri_Gatt_Struct_Context* const p_Context = new Dri_Gatt_Struct_Context();
|
||||
p_Context->Controller = new QLowEnergyController(QBluetoothAddress(DeviceAddress.trimmed()));
|
||||
|
||||
if (!Dri_Gatt_Func_WaitForControllerConnected(p_Context->Controller))
|
||||
{
|
||||
Dri_Gatt_Close(p_Port);
|
||||
delete p_Context->Controller;
|
||||
delete p_Context;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Dri_Gatt_Func_WaitForServiceDiscovery(p_Context->Controller) ||
|
||||
!p_Context->Controller->services().contains(kServiceUuid))
|
||||
{
|
||||
Dri_Gatt_Close(p_Port);
|
||||
delete p_Context->Controller;
|
||||
delete p_Context;
|
||||
return false;
|
||||
}
|
||||
|
||||
p_Context->Service = p_Context->Controller->createServiceObject(kServiceUuid, p_Context->Controller);
|
||||
if (p_Context->Service == nullptr)
|
||||
{
|
||||
Dri_Gatt_Close(p_Port);
|
||||
delete p_Context->Controller;
|
||||
delete p_Context;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Dri_Gatt_Func_WaitForServiceDetails(p_Context->Service))
|
||||
{
|
||||
Dri_Gatt_Close(p_Port);
|
||||
delete p_Context->Controller;
|
||||
delete p_Context;
|
||||
return false;
|
||||
}
|
||||
|
||||
p_Context->RxCharacteristic = p_Context->Service->characteristic(kRxUuid);
|
||||
p_Context->TxCharacteristic = p_Context->Service->characteristic(kTxUuid);
|
||||
|
||||
if (!p_Context->RxCharacteristic.isValid() || !p_Context->TxCharacteristic.isValid())
|
||||
{
|
||||
Dri_Gatt_Close(p_Port);
|
||||
delete p_Context->Controller;
|
||||
delete p_Context;
|
||||
return false;
|
||||
}
|
||||
|
||||
QObject::connect(
|
||||
p_Context->Service,
|
||||
&QLowEnergyService::characteristicChanged,
|
||||
[p_Context](const QLowEnergyCharacteristic& Characteristic, const QByteArray& Value)
|
||||
{
|
||||
if (Characteristic == p_Context->TxCharacteristic)
|
||||
{
|
||||
p_Context->ReadQueue.append(Value);
|
||||
}
|
||||
});
|
||||
|
||||
if (!Dri_Gatt_Func_EnableNotify(p_Context, p_Context->TxCharacteristic))
|
||||
{
|
||||
Dri_Gatt_Close(p_Port);
|
||||
delete p_Context->Controller;
|
||||
delete p_Context;
|
||||
return false;
|
||||
}
|
||||
|
||||
p_Port->p_Context = p_Context;
|
||||
p_Port->IsOpened = true;
|
||||
p_Port->DeviceAddress = DeviceAddress.trimmed();
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Dri_Gatt_ReadBytes(
|
||||
Dri_Gatt_Struct_Port* p_Port,
|
||||
QByteArray* p_ByteArray,
|
||||
int)
|
||||
int TimeoutMs)
|
||||
{
|
||||
if (p_ByteArray != nullptr)
|
||||
{
|
||||
p_ByteArray->clear();
|
||||
}
|
||||
|
||||
if ((p_Port == nullptr) || (p_ByteArray == nullptr) || !p_Port->IsOpened)
|
||||
if ((p_Port == nullptr) || (p_ByteArray == nullptr) ||
|
||||
!p_Port->IsOpened || (p_Port->p_Context == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (p_Port->p_Context->ReadQueue.isEmpty() && (TimeoutMs > 0))
|
||||
{
|
||||
QEventLoop EventLoop;
|
||||
QTimer TimeoutTimer;
|
||||
|
||||
TimeoutTimer.setSingleShot(true);
|
||||
QObject::connect(
|
||||
p_Port->p_Context->Service,
|
||||
&QLowEnergyService::characteristicChanged,
|
||||
&EventLoop,
|
||||
[&EventLoop](const QLowEnergyCharacteristic&, const QByteArray&)
|
||||
{
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
&TimeoutTimer,
|
||||
&QTimer::timeout,
|
||||
&EventLoop,
|
||||
&QEventLoop::quit);
|
||||
|
||||
TimeoutTimer.start(TimeoutMs);
|
||||
EventLoop.exec();
|
||||
}
|
||||
|
||||
if (p_Port->p_Context->ReadQueue.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*p_ByteArray = p_Port->p_Context->ReadQueue.takeFirst();
|
||||
return !p_ByteArray->isEmpty();
|
||||
}
|
||||
|
||||
bool Dri_Gatt_WriteBytes(
|
||||
Dri_Gatt_Struct_Port* p_Port,
|
||||
const QByteArray& ByteArray,
|
||||
int)
|
||||
int TimeoutMs)
|
||||
{
|
||||
if ((p_Port == nullptr) || !p_Port->IsOpened)
|
||||
if ((p_Port == nullptr) || !p_Port->IsOpened || (p_Port->p_Context == nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return !ByteArray.isEmpty();
|
||||
if (ByteArray.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QEventLoop EventLoop;
|
||||
QTimer TimeoutTimer;
|
||||
bool IsWritten = false;
|
||||
|
||||
TimeoutTimer.setSingleShot(true);
|
||||
|
||||
QObject::connect(
|
||||
p_Port->p_Context->Service,
|
||||
&QLowEnergyService::characteristicWritten,
|
||||
&EventLoop,
|
||||
[&EventLoop, &IsWritten, p_Port](const QLowEnergyCharacteristic& Characteristic, const QByteArray&)
|
||||
{
|
||||
if (Characteristic == p_Port->p_Context->RxCharacteristic)
|
||||
{
|
||||
IsWritten = true;
|
||||
EventLoop.quit();
|
||||
}
|
||||
});
|
||||
QObject::connect(
|
||||
p_Port->p_Context->Service,
|
||||
static_cast<void (QLowEnergyService::*)(QLowEnergyService::ServiceError)>(
|
||||
&QLowEnergyService::error),
|
||||
&EventLoop,
|
||||
[&EventLoop]()
|
||||
{
|
||||
EventLoop.quit();
|
||||
});
|
||||
QObject::connect(
|
||||
&TimeoutTimer,
|
||||
&QTimer::timeout,
|
||||
&EventLoop,
|
||||
&QEventLoop::quit);
|
||||
|
||||
p_Port->p_Context->Service->writeCharacteristic(
|
||||
p_Port->p_Context->RxCharacteristic,
|
||||
ByteArray,
|
||||
QLowEnergyService::WriteWithResponse);
|
||||
TimeoutTimer.start(TimeoutMs > 0 ? TimeoutMs : 3000);
|
||||
EventLoop.exec();
|
||||
|
||||
return IsWritten;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVector>
|
||||
|
||||
struct Dri_Gatt_Struct_Context;
|
||||
|
||||
struct Dri_Gatt_Struct_PortInfo
|
||||
{
|
||||
QString DeviceName;
|
||||
@@ -17,7 +19,9 @@ struct Dri_Gatt_Struct_OpenConfig
|
||||
struct Dri_Gatt_Struct_Port
|
||||
{
|
||||
bool IsOpened = false;
|
||||
QString DeviceName;
|
||||
QString DeviceAddress;
|
||||
Dri_Gatt_Struct_Context* p_Context = nullptr;
|
||||
};
|
||||
|
||||
QVector<Dri_Gatt_Struct_PortInfo> Dri_Gatt_Enum();
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
CdcFrame.payload max_size:64
|
||||
Bitmap.usage_bitmap max_size:29
|
||||
keyboard.cdc.CdcFrame.payload max_size:64
|
||||
keyboard.cdc.Bitmap.usage_bitmap max_size:29
|
||||
|
||||
Reference in New Issue
Block a user