qt-ble: set up infrastructure for better preferred service choice

We used to just pick the first non-standard service we found (with a
special case for the Heinrichs Weikamp dive computers that have an
actual registered standard service).

We then waited for that service to finish discovery, and started using
it.

This changes the logic to wait for _all_ services to finish discovery,
and then after that we pick the one we like best.  Right now the rule
for picking a preferred service is the same one we had before, but the
difference is that we now have the full discovery data, so we *could* do
something better.

Plus this makes our debug messages a lot more legible, when we don't
have the mix of overlapping service discovery with the actual IO we do
to the preferred service.

NOTE! This doesn't much matter for most of the dive computers that we
currently support BLE for.  They don't tend to have a lot of odd
services.

But at least both the Mares BlueLink and the Garmin Descent both have
multiple services and it's not obvious which one to use, and this will
make it not only easier to debug those, it will make it easier to pick
the right preferred service descriptor to use.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Linus Torvalds 2018-09-23 12:07:01 -07:00 committed by Dirk Hohndel
parent 594f13eeaf
commit 30fb7bf35c
2 changed files with 82 additions and 30 deletions

View file

@ -100,20 +100,6 @@ void BLEObject::writeCompleted(const QLowEnergyDescriptor&, const QByteArray&)
void BLEObject::addService(const QBluetoothUuid &newService)
{
qDebug() << "Found service" << newService;
bool isStandardUuid = false;
newService.toUInt16(&isStandardUuid);
if (IS_HW(device)) {
/* The HW BT/BLE piece or hardware uses, what we
* call here, "a Standard UUID. It is standard because the Telit/Stollmann
* manufacturer applied for an own UUID for its product, and this was granted
* by the Bluetooth SIG.
*/
if (newService != QUuid("{0000fefb-0000-1000-8000-00805f9b34fb}"))
return; // skip all services except the right one
} else if (isStandardUuid) {
qDebug () << " .. ignoring standard service";
return;
}
auto service = controller->createServiceObject(newService, this);
qDebug() << " .. created service object" << service;
@ -201,6 +187,80 @@ dc_status_t BLEObject::read(void *data, size_t size, size_t *actual)
return DC_STATUS_SUCCESS;
}
//
// select_preferred_service() gets called after all services
// have been discovered, and the discovery process has been
// started (by addService(), which calls service->discoverDetails())
//
// The role of this function is to wait for all service
// discovery to finish, and pick the preferred service.
//
// NOTE! Picking the preferred service is divecomputer-specific.
// Right now we special-case the HW known service number, but for
// others we just pick the first one that isn't a standard service.
//
// That's wrong, but works for the simple case.
//
dc_status_t BLEObject::select_preferred_service(void)
{
QLowEnergyService *s;
// Wait for each service to finish discovering
foreach (s, services) {
WAITFOR(s->state() != QLowEnergyService::DiscoveringServices, BLE_TIMEOUT);
}
// Print out the services for debugging
foreach (s, services) {
qDebug() << "Found service" << s->serviceUuid() << s->serviceName();
QLowEnergyCharacteristic c;
foreach (c, s->characteristics()) {
qDebug() << " c:" << c.uuid();
QLowEnergyDescriptor d;
foreach (d, c.descriptors())
qDebug() << " d:" << d.uuid();
}
}
// Pick the preferred one
foreach (s, services) {
if (s->state() != QLowEnergyService::ServiceDiscovered)
continue;
bool isStandardUuid = false;
QBluetoothUuid uuid = s->serviceUuid();
uuid.toUInt16(&isStandardUuid);
if (IS_HW(device)) {
/* The HW BT/BLE piece or hardware uses, what we
* call here, "a Standard UUID. It is standard because the Telit/Stollmann
* manufacturer applied for an own UUID for its product, and this was granted
* by the Bluetooth SIG.
*/
if (uuid != QUuid("{0000fefb-0000-1000-8000-00805f9b34fb}"))
continue; // skip all services except the right one
} else if (isStandardUuid) {
qDebug () << " .. ignoring standard service" << uuid;
continue;
}
preferred = s;
qDebug() << "Using service" << s->serviceUuid() << "as preferred service";
break;
}
if (!preferred) {
qDebug() << "failed to find suitable service";
report_error("Failed to find suitable BLE GATT service");
return DC_STATUS_IO;
}
return DC_STATUS_SUCCESS;
}
dc_status_t BLEObject::setHwCredit(unsigned int c)
{
/* The Terminal I/O client transmits initial UART credits to the server (see 6.5).
@ -341,24 +401,16 @@ dc_status_t qt_ble_open(void **io, dc_context_t *, const char *devaddr, dc_user_
WAITFOR(controller->state() != QLowEnergyController::DiscoveringState, BLE_TIMEOUT);
qDebug() << " .. done discovering services";
if (ble->preferredService() == nullptr) {
dc_status_t error = ble->select_preferred_service();
if (error != DC_STATUS_SUCCESS) {
qDebug() << "failed to find suitable service on" << devaddr;
report_error("Failed to find suitable service on '%s'", devaddr);
delete ble;
return DC_STATUS_IO;
return error;
}
qDebug() << " .. discovering details";
WAITFOR(ble->preferredService()->state() != QLowEnergyService::DiscoveringServices, BLE_TIMEOUT);
if (ble->preferredService()->state() != QLowEnergyService::ServiceDiscovered) {
qDebug() << "failed to find suitable service on" << devaddr;
report_error("Failed to find suitable service on '%s'", devaddr);
delete ble;
return DC_STATUS_IO;
}
qDebug() << " .. enabling notifications";
/* Enable notifications */

View file

@ -23,9 +23,8 @@ public:
dc_status_t write(const void* data, size_t size, size_t *actual);
dc_status_t read(void* data, size_t size, size_t *actual);
//TODO: need better mode of selecting the desired service than below
inline QLowEnergyService *preferredService()
{ return services.isEmpty() ? nullptr : services[0]; }
inline QLowEnergyService *preferredService() { return preferred; }
dc_status_t select_preferred_service(void);
public slots:
void addService(const QBluetoothUuid &newService);
@ -39,6 +38,7 @@ private:
QVector<QLowEnergyService *> services;
QLowEnergyController *controller = nullptr;
QLowEnergyService *preferred = nullptr;
QList<QByteArray> receivedPackets;
bool isCharacteristicWritten;
dc_user_device_t *device;