Skip to content

Commit 093d067

Browse files
authored
[BluetoothControl] Configurable LE connection parameters (#853)
1 parent 6cfa111 commit 093d067

File tree

3 files changed

+106
-48
lines changed

3 files changed

+106
-48
lines changed

BluetoothControl/BluetoothControl.conf.in

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,10 @@ def UUIDs():
4848

4949
configuration.add("uuids", UUIDs())
5050

51+
le = JSON()
52+
le.add("mininterval", "@PLUGIN_BLUETOOTH_LE_CONNECTION_INTERVAL_MIN@")
53+
le.add("maxinterval", "@PLUGIN_BLUETOOTH_LE_CONNECTION_INTERVAL_MAX@")
54+
le.add("timeout", "@PLUGIN_BLUETOOTH_LE_CONNECTION_TIMEOUT@")
55+
le.add("latency", "@PLUGIN_BLUETOOTH_LE_CONNECTION_LATENCY@")
56+
57+
configuration.add("lowenergy", le)

BluetoothControl/BluetoothControl.h

Lines changed: 94 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,37 @@ class BluetoothControl : public PluginHost::IPlugin
4848
public:
4949
class Config : public Core::JSON::Container {
5050
public:
51+
class LowEnergyConfig : public Core::JSON::Container {
52+
public:
53+
// Default settings for LE connections
54+
LowEnergyConfig()
55+
: Core::JSON::Container()
56+
, MinInterval(8)
57+
, MaxInterval(8)
58+
, Timeout(3000 /* ms */)
59+
, Latency(0)
60+
{
61+
Add(_T("mininterval"), &MinInterval);
62+
Add(_T("maxinterval"), &MaxInterval);
63+
Add(_T("timeout"), &Timeout);
64+
Add(_T("latency"), &Latency);
65+
}
66+
~LowEnergyConfig()
67+
{
68+
}
69+
70+
LowEnergyConfig(const LowEnergyConfig&) = delete;
71+
LowEnergyConfig(LowEnergyConfig&&) = delete;
72+
LowEnergyConfig& operator=(const LowEnergyConfig&) = delete;
73+
LowEnergyConfig& operator=(LowEnergyConfig&&) = delete;
74+
75+
public:
76+
Core::JSON::DecUInt16 MinInterval;
77+
Core::JSON::DecUInt16 MaxInterval;
78+
Core::JSON::DecUInt16 Timeout;
79+
Core::JSON::DecUInt16 Latency;
80+
};
81+
5182
class UUIDConfig : public Core::JSON::Container {
5283
public:
5384
UUIDConfig& operator=(const UUIDConfig&) = delete;
@@ -106,6 +137,7 @@ class BluetoothControl : public PluginHost::IPlugin
106137
Add(_T("autopasskeyconfirm"), &AutoPasskeyConfirm);
107138
Add(_T("persistmac"), &PersistMAC);
108139
Add(_T("uuids"), &UUIDs);
140+
Add(_T("lowenergy"), &LowEnergy);
109141
}
110142
~Config() = default;
111143

@@ -119,6 +151,7 @@ class BluetoothControl : public PluginHost::IPlugin
119151
Core::JSON::Boolean AutoPasskeyConfirm;
120152
Core::JSON::Boolean PersistMAC;
121153
Core::JSON::ArrayType<UUIDConfig> UUIDs;
154+
LowEnergyConfig LowEnergy;
122155
}; // class Config
123156

124157
class Data : public Core::JSON::Container {
@@ -716,8 +749,13 @@ class BluetoothControl : public PluginHost::IPlugin
716749
}
717750
}
718751

719-
TRACE(ControlFlow, (_T("%s background scan..."), (start? "Restarting": "Stopping")));
720-
HCISocket::Discovery(start);
752+
TRACE(ControlFlow, (_T("%s background scan..."), (start? _T("Restarting") : _T("Stopping"))));
753+
754+
uint32_t result = HCISocket::Discovery(start);
755+
756+
if ((result != Core::ERROR_NONE) && (result != Core::ERROR_ILLEGAL_STATE)) {
757+
TRACE(Trace::Error, (_T("BLE discovery %s failed"), _T("start"), _T("stop")));
758+
}
721759
}
722760
#endif // !defined(USE_KERNEL_CONNECTION_CONTROL)
723761
}
@@ -1515,9 +1553,6 @@ class BluetoothControl : public PluginHost::IPlugin
15151553
, _capabilities(~0)
15161554
, _authentication(~0)
15171555
, _oobData(~0)
1518-
, _interval(0)
1519-
, _latency(0)
1520-
, _timeout(0)
15211556
#if defined(USE_KERNEL_CONNECTION_CONTROL)
15221557
, _autoConnectionSubmitted(false)
15231558
#endif
@@ -1645,19 +1680,22 @@ class BluetoothControl : public PluginHost::IPlugin
16451680
case HCI_NO_CONNECTION:
16461681
result = Core::ERROR_ALREADY_RELEASED;
16471682
TRACE(ControlFlow, (_T("Device not connected")));
1648-
BackgroundScan(true);
16491683
break;
16501684
default:
16511685
result = Core::ERROR_ASYNC_FAILED;
16521686
TRACE(ControlFlow, (_T("Disconnect command failed [%d]"), disconnect.Result()));
16531687
break;
16541688
}
1655-
ClearState(DISCONNECTING);
16561689
}
1657-
} else {
1658-
TRACE(ControlFlow, (_T("Failed to disconnect [%d]"), result));
1659-
ClearState(DISCONNECTING);
16601690
}
1691+
1692+
BackgroundScan(true);
1693+
1694+
if (result != Core::ERROR_NONE) {
1695+
TRACE(ControlFlow, (_T("Unable to disconnect [%d]"), result));
1696+
}
1697+
1698+
ClearState(DISCONNECTING);
16611699
} else {
16621700
TRACE(Trace::Information, (_T("Device is currently busy")));
16631701
}
@@ -1669,6 +1707,7 @@ class BluetoothControl : public PluginHost::IPlugin
16691707
uint32_t result = Core::ERROR_INPROGRESS;
16701708

16711709
if (SetState(PAIRING) == Core::ERROR_NONE) {
1710+
BackgroundScan(false);
16721711
result = _parent->Connector().Pair(Address(), AddressType(), static_cast<Bluetooth::ManagementSocket::capabilities>(capabilities));
16731712
if (result == Core::ERROR_INPROGRESS) {
16741713
UpdateListener();
@@ -1677,6 +1716,7 @@ class BluetoothControl : public PluginHost::IPlugin
16771716
_abortPairingJob.Schedule([this](){
16781717
TRACE(Trace::Information, (_T("Timeout! Aborting pairing!"), RemoteId().c_str()));
16791718
AbortPairing();
1719+
BackgroundScan(true);
16801720
}, (1000L * timeout));
16811721
result = Core::ERROR_NONE;
16821722
} else {
@@ -1685,7 +1725,10 @@ class BluetoothControl : public PluginHost::IPlugin
16851725
} else if (result != Core::ERROR_NONE) {
16861726
TRACE(Trace::Error, (_T("Failed to pair device %s [%d]"), RemoteId().c_str(), result));
16871727
}
1728+
}
16881729

1730+
if (result != Core::ERROR_REQUEST_SUBMITTED) {
1731+
BackgroundScan(true);
16891732
ClearState(PAIRING);
16901733
}
16911734
} else {
@@ -1705,12 +1748,14 @@ class BluetoothControl : public PluginHost::IPlugin
17051748
} else if (result != Core::ERROR_NONE) {
17061749
TRACE(Trace::Error, (_T("Failed to abort pairing [%d]"), result));
17071750
}
1751+
1752+
if (result != Core::ERROR_INPROGRESS) {
1753+
ClearState(PAIRING);
1754+
}
17081755
} else {
17091756
TRACE(Trace::Information, (_T("Not currently pairing to this device")));
17101757
}
17111758

1712-
ClearState(PAIRING);
1713-
17141759
return (result);
17151760
}
17161761
uint32_t Unpair() override
@@ -1730,7 +1775,6 @@ class BluetoothControl : public PluginHost::IPlugin
17301775
}
17311776

17321777
ClearState(UNPAIRING);
1733-
UpdateListener();
17341778
} else {
17351779
TRACE(Trace::Information, (_T("Device is currently busy")));
17361780
}
@@ -1746,7 +1790,7 @@ class BluetoothControl : public PluginHost::IPlugin
17461790
}
17471791
void ConfirmPasskey(const bool confirm) override
17481792
{
1749-
TRACE(Trace::Information, (_T("Passkey confirmation reply: %s"), confirm? "YES" : "NO"));
1793+
TRACE(Trace::Information, (_T("Passkey confirmation reply: %s"), confirm? _T("YES") : _T("NO")));
17501794
_userReplyJob.Submit([this, confirm]() {
17511795
_parent->Connector().PasskeyConfirmReply(Address(), AddressType(), confirm);
17521796
});
@@ -1904,7 +1948,7 @@ class BluetoothControl : public PluginHost::IPlugin
19041948
TRACE(Trace::Information, (_T("Pairing with device %s; confirm passkey %06d? (YES/NO)"), RemoteId().c_str(), passkey));
19051949

19061950
// Request passkey confirmation from client or, if auto confirm is enabled, reply already.
1907-
if (_parent->AutoConfirmPasskey() == true) {
1951+
if (_parent->Configuration().AutoPasskeyConfirm.Value() == true) {
19081952
TRACE(Trace::Information, (_T("Auto-confirm enabled, accepting the passkey!")));
19091953
ConfirmPasskey(true);
19101954
} else {
@@ -1965,14 +2009,14 @@ class BluetoothControl : public PluginHost::IPlugin
19652009

19662010
_state.Unlock();
19672011

1968-
_autoConnectJob.Submit([this]() {
1969-
// Each time a device connects evalute whether background scan is needed.
1970-
BackgroundScan(true);
1971-
});
1972-
19732012
if (updated == true) {
19742013
UpdateListener();
19752014
DeviceStateChanged(JBluetoothControl::devicestate::CONNECTED);
2015+
2016+
_autoConnectJob.Submit([this]() {
2017+
// Each time a device connects evalute whether background scan is needed.
2018+
BackgroundScan(true);
2019+
});
19762020
}
19772021
}
19782022
void Disconnection(const uint8_t reason)
@@ -2225,9 +2269,6 @@ class BluetoothControl : public PluginHost::IPlugin
22252269
uint8_t _capabilities;
22262270
uint8_t _authentication;
22272271
uint8_t _oobData;
2228-
uint16_t _interval;
2229-
uint16_t _latency;
2230-
uint16_t _timeout;
22312272
#if defined(USE_KERNEL_CONNECTION_CONTROL)
22322273
bool _autoConnectionSubmitted;
22332274
#endif
@@ -2303,14 +2344,20 @@ class BluetoothControl : public PluginHost::IPlugin
23032344
Connection(handle);
23042345
} else {
23052346
TRACE(ControlFlow, (_T("Connect command failed [%d]"), connect.Result()));
2347+
result = Core::ERROR_ASYNC_FAILED;
23062348
}
23072349
} else {
23082350
if (result == Core::ERROR_TIMEDOUT) {
2309-
TRACE(Trace::Information, (_T("Waiting for connection... [%d]"), result));
2351+
TRACE(Trace::Information, (_T("Waiting for connection... ")));
23102352
result = Core::ERROR_REQUEST_SUBMITTED;
23112353
}
2312-
else {
2313-
TRACE(Trace::Error, (_T("Failed to connect [%d]"), result));
2354+
}
2355+
2356+
if (result != Core::ERROR_NONE) {
2357+
BackgroundScan(true);
2358+
2359+
if (result != Core::ERROR_REQUEST_SUBMITTED) {
2360+
TRACE(Trace::Error, (_T("Unable to connect [%d]"), result));
23142361
}
23152362
}
23162363
} else {
@@ -2535,6 +2582,8 @@ class BluetoothControl : public PluginHost::IPlugin
25352582
if (IsPaired() == true) {
25362583
BackgroundScan(false);
25372584

2585+
const BluetoothControl::Config& config = _parent->Configuration();
2586+
25382587
Bluetooth::HCISocket::Command::ConnectLE connect;
25392588
connect.Clear();
25402589
connect->interval = htobs(4*0x0010);
@@ -2543,20 +2592,20 @@ class BluetoothControl : public PluginHost::IPlugin
25432592
connect->peer_bdaddr_type = LE_PUBLIC_ADDRESS;
25442593
connect->peer_bdaddr = *(Locator().Data());
25452594
connect->own_bdaddr_type = LE_PUBLIC_ADDRESS;
2546-
connect->min_interval = htobs(0x000F);
2547-
connect->max_interval = htobs(0x000F);
2548-
connect->latency = htobs(0x0000);
2549-
connect->supervision_timeout = htobs(8000/10); // in centiseconds
2550-
connect->min_ce_length = htobs(0x0001);
2551-
connect->max_ce_length = htobs(0x0001);
2595+
connect->min_interval = htobs(config.LowEnergy.MinInterval.Value());
2596+
connect->max_interval = htobs(config.LowEnergy.MaxInterval.Value());
2597+
connect->latency = htobs(config.LowEnergy.Latency.Value());
2598+
connect->supervision_timeout = htobs(config.LowEnergy.Timeout.Value() / 10); // in centiseconds
2599+
connect->min_ce_length = htobs(0);
2600+
connect->max_ce_length = htobs(0);
25522601

25532602
result = Connector().Exchange(MAX_ACTION_TIMEOUT, connect, connect);
25542603
if (result == Core::ERROR_NONE) {
25552604
if (connect.Result() == 0) {
25562605
Connection(btohs(connect.Response().handle));
25572606
} else {
25582607
TRACE(ControlFlow, (_T("ConnectLE command failed [%d]"), connect.Result()));
2559-
AutoConnect(false);
2608+
result = Core::ERROR_ASYNC_FAILED;
25602609
}
25612610
}
25622611
else {
@@ -2574,13 +2623,16 @@ class BluetoothControl : public PluginHost::IPlugin
25742623
TRACE(Trace::Information, (_T("Canceled connection attempt")));
25752624
}
25762625

2577-
TRACE(Trace::Information, (_T("Waiting for connection... [%d]"), result));
2578-
2626+
TRACE(Trace::Information, (_T("Waiting for connection...")));
25792627
result = Core::ERROR_REQUEST_SUBMITTED;
2580-
BackgroundScan(true);
2581-
} else {
2582-
TRACE(Trace::Error, (_T("Failed to connect [%d]"), result));
2583-
AutoConnect(false);
2628+
}
2629+
}
2630+
2631+
if (result != Core::ERROR_NONE) {
2632+
BackgroundScan(true); // also when waiting for BLE connection
2633+
2634+
if (result != Core::ERROR_REQUEST_SUBMITTED) {
2635+
TRACE(Trace::Error, (_T("Unable to connect [%d]"), result));
25842636
}
25852637
}
25862638
} else {
@@ -3250,7 +3302,7 @@ class BluetoothControl : public PluginHost::IPlugin
32503302
details.id = _parent.InterfaceID();
32513303
details.address = info.Address().ToString();
32523304
details.interface = ("hci" + Core::ToString(_parent.InterfaceID()));
3253-
details.type = _parent.AdapterType();
3305+
details.type = _parent.Configuration().Type.Value();
32543306
details.version = info.Version();
32553307

32563308
if (info.Manufacturer() != 0) {
@@ -3687,19 +3739,13 @@ POP_WARNING()
36873739
uint32_t SaveDevice(const DeviceImpl* device) const;
36883740

36893741
private:
3690-
bool AutoConfirmPasskey() const
3691-
{
3692-
return (_config.AutoPasskeyConfirm.Value());
3742+
const Config& Configuration() const {
3743+
return _config;
36933744
}
36943745
JSONRPCImplementation& JSONRPC()
36953746
{
36963747
return (_jsonrpcImplementation);
36973748
}
3698-
JBluetoothControl::adaptertype AdapterType() const
3699-
{
3700-
return (_config.Type.Value());
3701-
}
3702-
37033749

37043750
private:
37053751
Core::ProxyType<Web::Response> GetMethod(Core::TextSegmentIterator& index);

BluetoothControl/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ set(PLUGIN_BLUETOOTH_CONTINUOUS_BACKGROUND_SCAN false CACHE STRING "Denotes if b
4040
set(PLUGIN_BLUETOOTH_PERSISTMAC false CACHE STRING "Denotes Bluetooth address shall be made persistent")
4141
set(PLUGIN_BLUETOOTH_SERVICE_A2DP_AUDIO_SOURCE "" CACHE STRING "Callsign of the A2DP Audio Source service (if any)")
4242
set(PLUGIN_BLUETOOTH_SERVICE_A2DP_AUDIO_SINK "" CACHE STRING "Callsign of the A2DP Audio Sink service (if any)")
43+
set(PLUGIN_BLUETOOTH_LE_CONNECTION_INTERVAL_MIN 8 CACHE STRING "LE min connection interval")
44+
set(PLUGIN_BLUETOOTH_LE_CONNECTION_INTERVAL_MAX 8 CACHE STRING "LE max connection interval")
45+
set(PLUGIN_BLUETOOTH_LE_CONNECTION_TIMEOUT 300 CACHE STRING "LE connection timeout")
46+
set(PLUGIN_BLUETOOTH_LE_CONNECTION_LATENCY 0 CACHE STRING "LE connection latency")
47+
4348

4449
if(BUILD_REFERENCE)
4550
add_definitions(-DBUILD_REFERENCE=${BUILD_REFERENCE})

0 commit comments

Comments
 (0)