Finish firmware private transport stack

This commit is contained in:
2026-04-11 13:53:34 +08:00
parent 30f1af1a8c
commit a8e0cfe1b3
12 changed files with 480 additions and 72 deletions

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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