Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
dbt_server01.hpp
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2022 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#ifndef DBT_SERVER01_HPP_
26#define DBT_SERVER01_HPP_
27
28#include <random>
29
30#include "dbt_constants.hpp"
31
32#include "dbt_server_test.hpp"
33
34#include <jau/latch.hpp>
35
36class DBTServer01;
37typedef std::shared_ptr<DBTServer01> DBTServer01Ref;
38
39using namespace jau;
40
41/**
42 * This peripheral BTRole::Slave test participant works with DBTClient00.
43 */
44class DBTServer01 : public DBTServerTest {
45 private:
46 static const bool GATT_VERBOSE = false;
47 static const bool SHOW_UPDATE_EVENTS = false;
48
49 const std::string adapterShortName = "TDev1Srv";
50 std::string adapterName = "TestDev1_Srv";
52 BTMode btMode = BTMode::DUAL;
53 bool use_SC = true;
54 BTSecurityLevel adapterSecurityLevel = BTSecurityLevel::UNSET;
55
56 jau::latch running_threads = jau::latch(0);
57 jau::sc_atomic_int disconnectCount = 0;
58 jau::sc_atomic_int servedProtocolSessionsTotal = 0;
59 jau::sc_atomic_int servedProtocolSessionsSuccess = 0;
60 jau::sc_atomic_int servingProtocolSessionsLeft = 1;
61
62 bool do_disconnect_randomly = false;
63
64 DBGattServerRef dbGattServer = std::make_shared<DBGattServer>(
65 /* services: */
66 jau::make_darray( // DBGattService
67 std::make_shared<DBGattService> ( true /* primary */,
68 std::make_unique<const jau::uuid16_t>(GattServiceType::GENERIC_ACCESS) /* type_ */,
69 jau::make_darray ( // DBGattChar
70 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::DEVICE_NAME) /* value_type_ */,
72 jau::darray<DBGattDescRef>() /* intentionally empty */,
73 make_gvalue(adapterName.c_str(), 128) /* value */, true /* variable_length */ ),
74 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::APPEARANCE) /* value_type_ */,
76 jau::darray<DBGattDescRef>() /* intentionally empty */,
77 make_gvalue((uint16_t)0) /* value */ )
78 ) ),
79 std::make_shared<DBGattService> ( true /* primary */,
80 std::make_unique<const jau::uuid16_t>(GattServiceType::DEVICE_INFORMATION) /* type_ */,
81 jau::make_darray ( // DBGattChar
82 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::MANUFACTURER_NAME_STRING) /* value_type_ */,
84 jau::darray<DBGattDescRef>() /* intentionally empty */,
85 make_gvalue("Gothel Software") /* value */ ),
86 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::MODEL_NUMBER_STRING) /* value_type_ */,
88 jau::darray<DBGattDescRef>() /* intentionally empty */,
89 make_gvalue("3.2.0-pre") /* value */ ),
90 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::SERIAL_NUMBER_STRING) /* value_type_ */,
92 jau::darray<DBGattDescRef>() /* intentionally empty */,
93 make_gvalue("sn:0123456789") /* value */ ),
94 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::HARDWARE_REVISION_STRING) /* value_type_ */,
96 jau::darray<DBGattDescRef>() /* intentionally empty */,
97 make_gvalue("hw:0123456789") /* value */ ),
98 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::FIRMWARE_REVISION_STRING) /* value_type_ */,
100 jau::darray<DBGattDescRef>() /* intentionally empty */,
101 make_gvalue("fw:0123456789") /* value */ ),
102 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid16_t>(GattCharacteristicType::SOFTWARE_REVISION_STRING) /* value_type_ */,
104 jau::darray<DBGattDescRef>() /* intentionally empty */,
105 make_gvalue("sw:0123456789") /* value */ )
106 ) ),
107 std::make_shared<DBGattService> ( true /* primary */,
108 std::make_unique<const jau::uuid128_t>(DBTConstants::DataServiceUUID) /* type_ */,
109 jau::make_darray ( // DBGattChar
110 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid128_t>(DBTConstants::StaticDataUUID) /* value_type_ */,
112 jau::make_darray ( // DBGattDesc
113 std::make_shared<DBGattDesc>( BTGattDesc::TYPE_USER_DESC, make_gvalue("DATA_STATIC") )
114 ),
115 make_gvalue("Proprietary Static Data 0x00010203") /* value */ ),
116 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid128_t>(DBTConstants::CommandUUID) /* value_type_ */,
118 jau::make_darray ( // DBGattDesc
119 std::make_shared<DBGattDesc>( BTGattDesc::TYPE_USER_DESC, make_gvalue("COMMAND") )
120 ),
121 make_gvalue(128, 64) /* value */, true /* variable_length */ ),
122 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid128_t>(DBTConstants::ResponseUUID) /* value_type_ */,
124 jau::make_darray ( // DBGattDesc
125 std::make_shared<DBGattDesc>( BTGattDesc::TYPE_USER_DESC, make_gvalue("RESPONSE") ),
126 DBGattDesc::createClientCharConfig()
127 ),
128 make_gvalue((uint16_t)0) /* value */ ),
129 std::make_shared<DBGattChar>( std::make_unique<const jau::uuid128_t>(DBTConstants::PulseDataUUID) /* value_type_ */,
131 jau::make_darray ( // DBGattDesc
132 std::make_shared<DBGattDesc>( BTGattDesc::TYPE_USER_DESC, make_gvalue("DATA_PULSE") ),
133 DBGattDesc::createClientCharConfig()
134 ),
135 make_gvalue("Synthethic Sensor 01") /* value */ )
136 ) )
137 ) );
138
139
140 std::mutex mtx_sync;
141 BTDeviceRef connectedDevice;
142
143 void setDevice(const BTDeviceRef& cd) {
144 const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor
145 connectedDevice = cd;
146 }
147
148 BTDeviceRef getDevice() {
149 const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor
150 return connectedDevice;
151 }
152
153 bool matches(const BTDeviceRef& device) {
154 const BTDeviceRef d = getDevice();
155 return nullptr != d ? (*d) == *device : false;
156 }
157
159 public:
160 DBTServer01& parent;
161
162 MyAdapterStatusListener(DBTServer01& p) : parent(p) {}
163
164 void adapterSettingsChanged(BTAdapter &a, const AdapterSetting oldmask, const AdapterSetting newmask,
165 const AdapterSetting changedmask, const uint64_t timestamp) override {
166 const bool initialSetting = AdapterSetting::NONE == oldmask;
167 if( initialSetting ) {
168 fprintf_td(stderr, "****** Server SETTINGS_INITIAL: %s -> %s, changed %s\n", to_string(oldmask).c_str(),
169 to_string(newmask).c_str(), to_string(changedmask).c_str());
170 } else {
171 fprintf_td(stderr, "****** Server SETTINGS_CHANGED: %s -> %s, changed %s\n", to_string(oldmask).c_str(),
172 to_string(newmask).c_str(), to_string(changedmask).c_str());
173
174 const bool justPoweredOn = isAdapterSettingBitSet(changedmask, AdapterSetting::POWERED) &&
175 isAdapterSettingBitSet(newmask, AdapterSetting::POWERED);
176 if( justPoweredOn && *parent.serverAdapter == a ) {
177 parent.startAdvertising("powered_on");
178 }
179 }
180 fprintf_td(stderr, "Server Status BTAdapter:\n");
181 fprintf_td(stderr, "%s\n", a.toString().c_str());
182 (void)timestamp;
183 }
184
185 void discoveringChanged(BTAdapter &a, const ScanType currentMeta, const ScanType changedType, const bool changedEnabled, const DiscoveryPolicy policy, const uint64_t timestamp) override {
186 fprintf_td(stderr, "****** Server DISCOVERING: meta %s, changed[%s, enabled %d, policy %s]: %s\n",
187 to_string(currentMeta).c_str(), to_string(changedType).c_str(), changedEnabled, to_string(policy).c_str(), a.toString().c_str());
188 (void)timestamp;
189 }
190
191 bool deviceFound(const BTDeviceRef& device, const uint64_t timestamp) override {
192 (void)timestamp;
193
194 fprintf_td(stderr, "****** Server FOUND__-1: NOP %s\n", device->toString(true).c_str());
195 return false;
196 }
197
198 void deviceUpdated(const BTDeviceRef& device, const EIRDataType updateMask, const uint64_t timestamp) override {
199 if( SHOW_UPDATE_EVENTS ) {
200 fprintf_td(stderr, "****** Server UPDATED: %s of %s\n", to_string(updateMask).c_str(), device->toString(true).c_str());
201 }
202 (void)timestamp;
203 }
204
205 void deviceConnected(const BTDeviceRef& device, const bool discovered, const uint64_t timestamp) override {
206 fprintf_td(stderr, "****** Server CONNECTED (discovered %d): %s\n", discovered, device->toString(true).c_str());
207 const bool available = nullptr == parent.getDevice();
208 if( available ) {
209 parent.setDevice(device);
210 }
211 (void)discovered;
212 (void)timestamp;
213 }
214
215 void devicePairingState(const BTDeviceRef& device, const SMPPairingState state, const PairingMode mode, const uint64_t timestamp) override {
216 fprintf_td(stderr, "****** Server PAIRING STATE: state %s, mode %s, %s\n",
217 to_string(state).c_str(), to_string(mode).c_str(), device->toString().c_str());
218 (void)timestamp;
219 switch( state ) {
220 case SMPPairingState::NONE:
221 // next: deviceReady(..)
222 break;
223 case SMPPairingState::FAILED: {
224 // next: deviceReady() or deviceDisconnected(..)
225 } break;
226 case SMPPairingState::REQUESTED_BY_RESPONDER:
227 // next: FEATURE_EXCHANGE_STARTED
228 break;
229 case SMPPairingState::FEATURE_EXCHANGE_STARTED:
230 // next: FEATURE_EXCHANGE_COMPLETED
231 break;
232 case SMPPairingState::FEATURE_EXCHANGE_COMPLETED:
233 // next: PASSKEY_EXPECTED... or KEY_DISTRIBUTION
234 break;
235 case SMPPairingState::PASSKEY_EXPECTED: {
236 const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, "");
237 if( nullptr != sec && sec->getPairingPasskey() != BTSecurityRegistry::Entry::NO_PASSKEY ) {
238 std::thread dc(&BTDevice::setPairingPasskey, device, static_cast<uint32_t>( sec->getPairingPasskey() ));
239 dc.detach();
240 } else {
241 std::thread dc(&BTDevice::setPairingPasskey, device, 0);
242 // 3s disconnect: std::thread dc(&BTDevice::setPairingPasskeyNegative, device);
243 dc.detach();
244 }
245 // next: KEY_DISTRIBUTION or FAILED
246 } break;
247 case SMPPairingState::NUMERIC_COMPARE_EXPECTED: {
248 const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getStartOf(device->getAddressAndType().address, "");
249 if( nullptr != sec ) {
250 std::thread dc(&BTDevice::setPairingNumericComparison, device, sec->getPairingNumericComparison());
251 dc.detach();
252 } else {
253 std::thread dc(&BTDevice::setPairingNumericComparison, device, false);
254 dc.detach();
255 }
256 // next: KEY_DISTRIBUTION or FAILED
257 } break;
258 case SMPPairingState::OOB_EXPECTED:
259 // FIXME: ABORT
260 break;
261 case SMPPairingState::KEY_DISTRIBUTION:
262 // next: COMPLETED or FAILED
263 break;
264 case SMPPairingState::COMPLETED:
265 // next: deviceReady(..)
266 break;
267 default: // nop
268 break;
269 }
270 }
271
272 void deviceReady(const BTDeviceRef& device, const uint64_t timestamp) override {
273 (void)timestamp;
274 fprintf_td(stderr, "****** Server READY-1: NOP %s\n", device->toString(true).c_str());
275 }
276
277 void deviceDisconnected(const BTDeviceRef& device, const HCIStatusCode reason, const uint16_t handle, const uint64_t timestamp) override {
278 fprintf_td(stderr, "****** Server DISCONNECTED (count %zu): Reason 0x%X (%s), old handle %s: %s\n",
279 1+parent.disconnectCount.load(), static_cast<uint8_t>(reason), to_string(reason).c_str(),
280 to_hexstring(handle).c_str(), device->toString(true).c_str());
281
282 const bool match = parent.matches(device);
283 if( match ) {
284 parent.setDevice(nullptr);
285 }
286 parent.running_threads.count_up();
287 std::thread sd(&DBTServer01::processDisconnectedDevice, &parent, device); // @suppress("Invalid arguments")
288 sd.detach();
289 (void)timestamp;
290 }
291
292 std::string toString() const noexcept override {
293 return "Server MyAdapterStatusListener[this "+to_hexstring(this)+"]";
294 }
295
296 };
297
299 private:
300 DBTServer01& parent;
301 jau::service_runner pulse_service;
302
303 jau::sc_atomic_uint16 handlePulseDataNotify = 0;
304 jau::sc_atomic_uint16 handlePulseDataIndicate = 0;
305 jau::sc_atomic_uint16 handleResponseDataNotify = 0;
306 jau::sc_atomic_uint16 handleResponseDataIndicate = 0;
307
308 uint16_t usedMTU = BTGattHandler::number(BTGattHandler::Defaults::MIN_ATT_MTU);
309
310 void pulse_worker_init(jau::service_runner& sr) noexcept {
311 (void)sr;
312 const BTDeviceRef connectedDevice_ = parent.getDevice();
313 const std::string connectedDeviceStr = nullptr != connectedDevice_ ? connectedDevice_->toString() : "n/a";
314 fprintf_td(stderr, "****** Server GATT::PULSE Start %s\n", connectedDeviceStr.c_str());
315 }
316 void pulse_worker(jau::service_runner& sr) noexcept {
317 BTDeviceRef connectedDevice_ = parent.getDevice();
318 if( nullptr != connectedDevice_ && connectedDevice_->getConnected() ) {
319 if( 0 != handlePulseDataNotify || 0 != handlePulseDataIndicate ) {
320 std::string data( "Dynamic Data Example. Elapsed Milliseconds: "+jau::to_decstring(environment::getElapsedMillisecond(), ',', 9) );
321 jau::POctets v(data.size()+1, jau::lb_endian_t::little);
322 v.put_string_nc(0, data, v.size(), true /* includeEOS */);
323 if( 0 != handlePulseDataNotify ) {
324 if( GATT_VERBOSE ) {
325 fprintf_td(stderr, "****** Server GATT::sendNotification: PULSE to %s\n", connectedDevice_->toString().c_str());
326 }
327 connectedDevice_->sendNotification(handlePulseDataNotify, v);
328 }
329 if( 0 != handlePulseDataIndicate ) {
330 if( GATT_VERBOSE ) {
331 fprintf_td(stderr, "****** Server GATT::sendIndication: PULSE to %s\n", connectedDevice_->toString().c_str());
332 }
333 connectedDevice_->sendIndication(handlePulseDataIndicate, v);
334 }
335 }
336 }
337 if( !sr.shall_stop() ) {
338 jau::sleep_for( 100_ms );
339 }
340 }
341 void pulse_worker_end(jau::service_runner& sr) noexcept {
342 (void)sr;
343 const BTDeviceRef connectedDevice_ = parent.getDevice();
344 const std::string connectedDeviceStr = nullptr != connectedDevice_ ? connectedDevice_->toString() : "n/a";
345 fprintf_td(stderr, "****** Server GATT::PULSE End %s\n", connectedDeviceStr.c_str());
346 }
347
348 void sendResponse(jau::POctets data) { // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
349 BTDeviceRef connectedDevice_ = parent.getDevice();
350 if( nullptr != connectedDevice_ && connectedDevice_->getConnected() ) {
351 if( 0 != handleResponseDataNotify || 0 != handleResponseDataIndicate ) {
352 if( 0 != handleResponseDataNotify ) {
353 if( GATT_VERBOSE ) {
354 fprintf_td(stderr, "****** GATT::sendNotification: %s to %s\n",
355 data.toString().c_str(), connectedDevice_->toString().c_str());
356 }
357 connectedDevice_->sendNotification(handleResponseDataNotify, data);
358 }
359 if( 0 != handleResponseDataIndicate ) {
360 if( GATT_VERBOSE ) {
361 fprintf_td(stderr, "****** GATT::sendIndication: %s to %s\n",
362 data.toString().c_str(), connectedDevice_->toString().c_str());
363 }
364 connectedDevice_->sendIndication(handleResponseDataIndicate, data);
365 }
366 }
367 }
368 parent.running_threads.count_down();
369 }
370
371 void disconnectDeviceRandomly() {
372 // sleep range: 100 - 1500 ms
373 // sleep range: 100 - 1500 ms
374 static const int sleep_min = 100;
375 static const int sleep_max = 1500;
376 static std::random_device rng_hw;;
377 static std::uniform_int_distribution<int> rng_dist(sleep_min, sleep_max);
378 const int64_t sleep_dur = rng_dist(rng_hw);
379 jau::sleep_for( sleep_dur * 1_ms );
380 BTDeviceRef connectedDevice_ = parent.getDevice();
381 if( nullptr != connectedDevice_ ) {
382 fprintf_td(stderr, "****** Server i470 disconnectDevice(delayed %d ms): client %s\n", sleep_dur, connectedDevice_->toString().c_str());
383 connectedDevice_->disconnect();
384 } else {
385 fprintf_td(stderr, "****** Server i470 disconnectDevice(delayed %d ms): client null\n", sleep_dur);
386 }
387 parent.running_threads.count_down();
388 }
389
390 public:
391
393 : parent(p),
394 pulse_service("MyGATTServerListener::pulse", THREAD_SHUTDOWN_TIMEOUT_MS,
395 jau::bind_member(this, &MyGATTServerListener::pulse_worker),
396 jau::bind_member(this, &MyGATTServerListener::pulse_worker_init),
397 jau::bind_member(this, &MyGATTServerListener::pulse_worker_end))
398 {
399 pulse_service.start();
400 }
401
402 ~MyGATTServerListener() noexcept override {
403 pulse_service.stop();
404 }
405
406 void clear() {
407 const std::lock_guard<std::mutex> lock(parent.mtx_sync); // RAII-style acquire and relinquish via destructor
408
409 handlePulseDataNotify = 0;
410 handlePulseDataIndicate = 0;
411 handleResponseDataNotify = 0;
412 handleResponseDataIndicate = 0;
413
414 parent.dbGattServer->resetGattClientCharConfig(DBTConstants::DataServiceUUID, DBTConstants::PulseDataUUID);
415 parent.dbGattServer->resetGattClientCharConfig(DBTConstants::DataServiceUUID, DBTConstants::ResponseUUID);
416 }
417
418 void close() noexcept {
419 pulse_service.stop();
420 clear();
421 }
422
423 void connected(const BTDeviceRef& device, const uint16_t initialMTU) override {
424 const bool match = parent.matches(device);
425 fprintf_td(stderr, "****** Server GATT::connected(match %d): initMTU %d, %s\n",
426 match, (int)initialMTU, device->toString().c_str());
427 if( match ) {
428 const std::lock_guard<std::mutex> lock(parent.mtx_sync); // RAII-style acquire and relinquish via destructor
429 usedMTU = initialMTU;
430 }
431 }
432
433 void disconnected(const BTDeviceRef& device) override {
434 const bool match = parent.matches(device);
435 fprintf_td(stderr, "****** Server GATT::disconnected(match %d): %s\n", match, device->toString().c_str());
436 if( match ) {
437 clear();
438 }
439 }
440
441 void mtuChanged(const BTDeviceRef& device, const uint16_t mtu) override {
442 const bool match = parent.matches(device);
443 const uint16_t usedMTU_old = usedMTU;
444 if( match ) {
445 const std::lock_guard<std::mutex> lock(parent.mtx_sync); // RAII-style acquire and relinquish via destructor
446 usedMTU = mtu;
447 }
448 fprintf_td(stderr, "****** Server GATT::mtuChanged(match %d, served %zu, left %zu): %d -> %d, %s\n",
449 match, parent.servedProtocolSessionsTotal.load(), parent.servingProtocolSessionsLeft.load(),
450 match ? (int)usedMTU_old : 0, (int)mtu, device->toString().c_str());
451 if( parent.do_disconnect_randomly ) {
452 parent.running_threads.count_up();
453 std::thread disconnectThread(&MyGATTServerListener::disconnectDeviceRandomly, this);
454 disconnectThread.detach();
455 }
456 }
457
458 bool readCharValue(const BTDeviceRef& device, const DBGattServiceRef& s, const DBGattCharRef& c) override {
459 const bool match = parent.matches(device);
460 if( GATT_VERBOSE ) {
461 fprintf_td(stderr, "****** Server GATT::readCharValue(match %d): to %s, from\n %s\n %s\n",
462 match, device->toString().c_str(), s->toString().c_str(), c->toString().c_str());
463 }
464 return match;
465 }
466
467 bool readDescValue(const BTDeviceRef& device, const DBGattServiceRef& s, const DBGattCharRef& c, const DBGattDescRef& d) override {
468 const bool match = parent.matches(device);
469 if( GATT_VERBOSE ) {
470 fprintf_td(stderr, "****** Server GATT::readDescValue(match %d): to %s, from\n %s\n %s\n %s\n",
471 match, device->toString().c_str(), s->toString().c_str(), c->toString().c_str(), d->toString().c_str());
472 }
473 return match;
474 }
475
476 bool writeCharValue(const BTDeviceRef& device, const DBGattServiceRef& s, const DBGattCharRef& c, const jau::TROOctets & value, const uint16_t value_offset) override {
477 const bool match = parent.matches(device);
478 if( GATT_VERBOSE ) {
479 fprintf_td(stderr, "****** Server GATT::writeCharValue(match %d): %s '%s' @ %u from %s, to\n %s\n %s\n",
480 match, value.toString().c_str(), jau::dfa_utf8_decode( value.get_ptr(), value.size() ).c_str(),
481 value_offset,
482 device->toString().c_str(), s->toString().c_str(), c->toString().c_str());
483 }
484 return match;
485 }
486
487 void writeCharValueDone(const BTDeviceRef& device, const DBGattServiceRef& s, const DBGattCharRef& c) override {
488 const bool match = parent.matches(device);
489 const jau::TROOctets& value = c->getValue();
490 bool isFinalHandshake = false;
491 bool isFinalHandshakeSuccess = false;
492
493 if( match &&
494 c->getValueType()->equivalent( DBTConstants::CommandUUID ) &&
495 ( 0 != handleResponseDataNotify || 0 != handleResponseDataIndicate ) )
496 {
497 isFinalHandshakeSuccess = DBTConstants::SuccessHandshakeCommandData.size() == value.size() &&
498 0 == ::memcmp(DBTConstants::SuccessHandshakeCommandData.data(), value.get_ptr(), value.size());
499 isFinalHandshake = isFinalHandshakeSuccess ||
500 ( DBTConstants::FailHandshakeCommandData.size() == value.size() &&
501 0 == ::memcmp(DBTConstants::FailHandshakeCommandData.data(), value.get_ptr(), value.size()) );
502
503 if( isFinalHandshake ) {
504 if( isFinalHandshakeSuccess ) {
505 parent.servedProtocolSessionsSuccess++;
506 }
507 parent.servedProtocolSessionsTotal++;
508 if( parent.servingProtocolSessionsLeft > 0 ) {
509 parent.servingProtocolSessionsLeft--;
510 }
511 }
512 parent.running_threads.count_up();
513 jau::POctets value2(value);
514 std::thread senderThread(&MyGATTServerListener::sendResponse, this, value2);
515 senderThread.detach();
516 }
517 if( GATT_VERBOSE || isFinalHandshake ) {
518 fprintf_td(stderr, "****** Server GATT::writeCharValueDone(match %d, finalCmd %d, sessions [%d ok / %d total], left %d): From %s, to\n %s\n %s\n Char-Value: %s\n",
519 match, isFinalHandshake, parent.servedProtocolSessionsSuccess.load(), parent.servedProtocolSessionsTotal.load(), parent.servingProtocolSessionsLeft.load(),
520 device->toString().c_str(), s->toString().c_str(), c->toString().c_str(), value.toString().c_str());
521 }
522 }
523
524 bool writeDescValue(const BTDeviceRef& device, const DBGattServiceRef& s, const DBGattCharRef& c, const DBGattDescRef& d, const jau::TROOctets & value, const uint16_t value_offset) override {
525 const bool match = parent.matches(device);
526 if( GATT_VERBOSE ) {
527 fprintf_td(stderr, "****** Server GATT::writeDescValue(match %d): %s '%s' @ %u from %s\n %s\n %s\n %s\n",
528 match, value.toString().c_str(), jau::dfa_utf8_decode( value.get_ptr(), value.size() ).c_str(),
529 value_offset,
530 device->toString().c_str(), s->toString().c_str(), c->toString().c_str(), d->toString().c_str());
531 }
532 return match;
533 }
534 void writeDescValueDone(const BTDeviceRef& device, const DBGattServiceRef& s, const DBGattCharRef& c, const DBGattDescRef& d) override {
535 if( GATT_VERBOSE ) {
536 const bool match = parent.matches(device);
537 const jau::TROOctets& value = d->getValue();
538 fprintf_td(stderr, "****** Server GATT::writeDescValueDone(match %d): From %s\n %s\n %s\n %s\n Desc-Value: %s\n",
539 match, device->toString().c_str(), s->toString().c_str(), c->toString().c_str(), d->toString().c_str(), value.toString().c_str());
540 }
541 }
542
543 void clientCharConfigChanged(const BTDeviceRef& device, const DBGattServiceRef& s, const DBGattCharRef& c, const DBGattDescRef& d, const bool notificationEnabled, const bool indicationEnabled) override {
544 const bool match = parent.matches(device);
545 if( GATT_VERBOSE ) {
546 const jau::TROOctets& value = d->getValue();
547 fprintf_td(stderr, "****** GATT::clientCharConfigChanged(match %d): notify %d, indicate %d from %s\n %s\n %s\n %s\n Desc-Value: %s\n",
548 match, notificationEnabled, indicationEnabled,
549 device->toString().c_str(), s->toString().c_str(), c->toString().c_str(), d->toString().c_str(), value.toString().c_str());
550 }
551 if( match ) {
552 if( c->getValueType()->equivalent( DBTConstants::PulseDataUUID ) ) {
553 const std::lock_guard<std::mutex> lock(parent.mtx_sync); // RAII-style acquire and relinquish via destructor
554 handlePulseDataNotify = notificationEnabled ? c->getValueHandle() : 0;
555 handlePulseDataIndicate = indicationEnabled ? c->getValueHandle() : 0;
556 } else if( c->getValueType()->equivalent( DBTConstants::ResponseUUID ) ) {
557 const std::lock_guard<std::mutex> lock(parent.mtx_sync); // RAII-style acquire and relinquish via destructor
558 handleResponseDataNotify = notificationEnabled ? c->getValueHandle() : 0;
559 handleResponseDataIndicate = indicationEnabled ? c->getValueHandle() : 0;
560 }
561 }
562 }
563 };
564
565 std::shared_ptr<MyGATTServerListener> gattServerListener = std::make_shared<MyGATTServerListener>(*this);
566 std::shared_ptr<AdapterStatusListener> myAdapterStatusListener = std::make_shared<MyAdapterStatusListener>(*this);
567
568 BTAdapterRef serverAdapter = nullptr;
569
570 public:
571
572 DBTServer01(const std::string& adapterName_, const jau::EUI48& useAdapter_, const BTMode btMode_,
573 const bool use_SC_, const BTSecurityLevel adapterSecurityLevel_, const bool do_disconnect_randomly_=false)
574 {
575 this->adapterName = adapterName_;
576 this->useAdapter = useAdapter_;
577 this->btMode = btMode_;
578 this->use_SC = use_SC_;
579 this->adapterSecurityLevel = adapterSecurityLevel_;
580 this->do_disconnect_randomly = do_disconnect_randomly_;
581
582 dbGattServer->addListener( gattServerListener );
583 }
584
585 ~DBTServer01() override {
586 fprintf_td(stderr, "****** Server dtor: running_threads %zu\n", running_threads.value());
587 running_threads.wait_for( 10_s );
588 }
589
590 std::string getName() override { return adapterName; }
591
592 BTSecurityLevel getSecurityLevel() override { return adapterSecurityLevel; }
593
594 void setAdapter(BTAdapterRef serverAdapter_) override {
595 this->serverAdapter = serverAdapter_;
596 }
597
598 BTAdapterRef getAdapter() override { return serverAdapter; }
599
600 private:
601 static const uint16_t adv_interval_min=160; // x0.625 = 100ms
602 static const uint16_t adv_interval_max=480; // x0.625 = 300ms
603 static const AD_PDU_Type adv_type=AD_PDU_Type::ADV_IND;
604 static const uint8_t adv_chan_map=0x07;
605 static const uint8_t filter_policy=0x00;
606
607 public:
608
609 void close(const std::string& msg) override {
610 fprintf_td(stderr, "****** Server Close.0: %s\n", msg.c_str());
611 REQUIRE( true == serverAdapter->removeStatusListener( myAdapterStatusListener ) );
612 {
613 stopAdvertising(msg);
614 BTDeviceRef connectedDevice_ = getDevice();
615 if( nullptr != connectedDevice_ ) {
616 setDevice(nullptr);
617 connectedDevice_->disconnect();
618 }
619 }
620 gattServerListener->close();
621 fprintf_td(stderr, "****** Server close: running_threads %zu\n", running_threads.value());
622 running_threads.wait_for( 10_s );
623
624 // dbGattServer = nullptr; // keep alive
625 stopAdvertising(msg); // try once more in case of already started AdapterStatusListener
626 fprintf_td(stderr, "****** Server Close.X: %s\n", msg.c_str());
627 }
628
629 void setProtocolSessionsLeft(const int v) override {
630 servingProtocolSessionsLeft = v;
631 }
632 int getProtocolSessionsLeft() override {
633 return servingProtocolSessionsLeft;
634 }
636 return servedProtocolSessionsTotal;
637 }
639 return servedProtocolSessionsSuccess;
640 }
641 int getDisconnectCount() override {
642 return disconnectCount;
643 }
644
645 private:
646 HCIStatusCode stopAdvertising(const std::string& msg) {
647 HCIStatusCode status = serverAdapter->stopAdvertising();
648 fprintf_td(stderr, "****** Server Stop advertising (%s) result: %s: %s\n", msg.c_str(), to_string(status).c_str(), serverAdapter->toString().c_str());
649 return status;
650 }
651
652 public:
653
654 HCIStatusCode startAdvertising(const std::string& msg) override {
655 EInfoReport eir;
656 EIRDataType adv_mask = EIRDataType::FLAGS | EIRDataType::SERVICE_UUID;
657 EIRDataType scanrsp_mask = EIRDataType::NAME | EIRDataType::CONN_IVAL;
658
659 eir.addFlags(GAPFlags::LE_Gen_Disc);
660 eir.addFlags(GAPFlags::BREDR_UNSUP);
661
663 eir.setServicesComplete(false);
664
665 eir.setName(serverAdapter->getName());
666 eir.setConnInterval(8, 12); // 10ms - 15ms
667
670 if( nullptr != gattDevNameChar ) {
671 std::string aname = serverAdapter->getName();
672 gattDevNameChar->setValue(reinterpret_cast<uint8_t*>(aname.data()), aname.size(), 0);
673 }
674
675 fprintf_td(stderr, "****** Start advertising (%s): EIR %s\n", msg.c_str(), eir.toString().c_str());
676 fprintf_td(stderr, "****** Start advertising (%s): adv %s, scanrsp %s\n", msg.c_str(), to_string(adv_mask).c_str(), to_string(scanrsp_mask).c_str());
677
678 HCIStatusCode status = serverAdapter->startAdvertising(dbGattServer, eir, adv_mask, scanrsp_mask,
679 adv_interval_min, adv_interval_max,
680 adv_type, adv_chan_map, filter_policy);
681 fprintf_td(stderr, "****** Server Start advertising (%s) result: %s: %s\n", msg.c_str(), to_string(status).c_str(), serverAdapter->toString().c_str());
682 if( GATT_VERBOSE ) {
683 fprintf_td(stderr, "%s", dbGattServer->toFullString().c_str());
684 }
685 return status;
686 }
687
688 private:
689 void processDisconnectedDevice(BTDeviceRef device) { // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
690 fprintf_td(stderr, "****** Server Disconnected Device (count %zu, served %zu, left %zu): Start %s\n",
691 1+disconnectCount.load(), servedProtocolSessionsTotal.load(), servingProtocolSessionsLeft.load(), device->toString().c_str());
692
693 // already unpaired
694 stopAdvertising("device-disconnected");
695 device->remove();
696
697 disconnectCount++;
698
699 jau::sleep_for( 100_ms ); // wait a little (FIXME: Fast restart of advertising error)
700
701 if( servingProtocolSessionsLeft > 0 ) {
702 startAdvertising("device-disconnected");
703 }
704
705 fprintf_td(stderr, "****** Server Disonnected Device: End %s\n", device->toString().c_str());
706 running_threads.count_down();
707 }
708
709 public:
710
711 bool initAdapter(BTAdapterRef adapter) override {
712 if( useAdapter != EUI48::ALL_DEVICE && useAdapter != adapter->getAddressAndType().address ) {
713 fprintf_td(stderr, "initServerAdapter: Adapter not selected: %s\n", adapter->toString().c_str());
714 return false;
715 }
716 adapterName = adapterName + "-" + adapter->getAddressAndType().address.toString();
717 {
718 auto it = std::remove( adapterName.begin(), adapterName.end(), ':');
719 adapterName.erase(it, adapterName.end());
720 }
721
722 if( !adapter->isInitialized() ) {
723 // Initialize with defaults and power-on
724 const HCIStatusCode status = adapter->initialize( btMode, false );
725 if( HCIStatusCode::SUCCESS != status ) {
726 fprintf_td(stderr, "initServerAdapter: initialize failed: %s: %s\n",
727 to_string(status).c_str(), adapter->toString().c_str());
728 return false;
729 }
730 } else if( !adapter->setPowered( false ) ) {
731 fprintf_td(stderr, "initServerAdapter: setPower.1 off failed: %s\n", adapter->toString().c_str());
732 return false;
733 }
734 // adapter is powered-off
735 fprintf_td(stderr, "initServerAdapter.1: %s\n", adapter->toString().c_str());
736
737 {
738 HCIStatusCode status = adapter->setName(adapterName, adapterShortName);
739 if( HCIStatusCode::SUCCESS == status ) {
740 fprintf_td(stderr, "initServerAdapter: setLocalName OK: %s\n", adapter->toString().c_str());
741 } else {
742 fprintf_td(stderr, "initServerAdapter: setLocalName failed: %s\n", adapter->toString().c_str());
743 return false;
744 }
745
746 status = adapter->setSecureConnections( use_SC );
747 if( HCIStatusCode::SUCCESS == status ) {
748 fprintf_td(stderr, "initServerAdapter: setSecureConnections OK: %s\n", adapter->toString().c_str());
749 } else {
750 fprintf_td(stderr, "initServerAdapter: setSecureConnections failed: %s\n", adapter->toString().c_str());
751 return false;
752 }
753
754 const uint16_t conn_min_interval = 8; // 10ms
755 const uint16_t conn_max_interval = 40; // 50ms
756 const uint16_t conn_latency = 0;
757 const uint16_t supervision_timeout = 50; // 500ms
758 status = adapter->setDefaultConnParam(conn_min_interval, conn_max_interval, conn_latency, supervision_timeout);
759 if( HCIStatusCode::SUCCESS == status ) {
760 fprintf_td(stderr, "initServerAdapter: setDefaultConnParam OK: %s\n", adapter->toString().c_str());
761 } else if( HCIStatusCode::UNKNOWN_COMMAND == status ) {
762 fprintf_td(stderr, "initServerAdapter: setDefaultConnParam UNKNOWN_COMMAND (ignored): %s\n", adapter->toString().c_str());
763 } else {
764 fprintf_td(stderr, "initServerAdapter: setDefaultConnParam failed: %s, %s\n", to_string(status).c_str(), adapter->toString().c_str());
765 return false;
766 }
767
768 if( !adapter->setPowered( true ) ) {
769 fprintf_td(stderr, "initServerAdapter: setPower.2 on failed: %s\n", adapter->toString().c_str());
770 return false;
771 }
772 }
773 // adapter is powered-on
774 fprintf_td(stderr, "initServerAdapter.2: %s\n", adapter->toString().c_str());
775
776 {
777 const LE_Features le_feats = adapter->getLEFeatures();
778 fprintf_td(stderr, "initServerAdapter: LE_Features %s\n", to_string(le_feats).c_str());
779 }
780 if( adapter->getBTMajorVersion() > 4 ) {
781 LE_PHYs Tx { LE_PHYs::LE_2M }, Rx { LE_PHYs::LE_2M };
782 HCIStatusCode res = adapter->setDefaultLE_PHY(Tx, Rx);
783 fprintf_td(stderr, "initServerAdapter: Set Default LE PHY: status %s: Tx %s, Rx %s\n",
784 to_string(res).c_str(), to_string(Tx).c_str(), to_string(Rx).c_str());
785 }
786 adapter->setSMPKeyPath(DBTConstants::SERVER_KEY_PATH);
787
788 REQUIRE( true == adapter->addStatusListener( myAdapterStatusListener ) );
789
790 adapter->setServerConnSecurity(adapterSecurityLevel, SMPIOCapability::UNSET);
791
792 return true;
793 }
794};
795
796#endif // DBT_SERVER01_HPP_
static constexpr const char SERVER_KEY_PATH[]
static const jau::uuid128_t ResponseUUID
static const jau::uuid128_t StaticDataUUID
static const jau::uuid128_t CommandUUID
static const std::vector< uint8_t > FailHandshakeCommandData
Fail handshake command data, where client is signaling unsuccessful completion of test to server.
static const std::vector< uint8_t > SuccessHandshakeCommandData
Success handshake command data, where client is signaling successful completion of test to server.
static const jau::uuid128_t DataServiceUUID
static const jau::uuid128_t PulseDataUUID
This peripheral BTRole::Slave test participant works with DBTClient00.
void setAdapter(BTAdapterRef serverAdapter_) override
Set the server adapter for this endpoint.
int getProtocolSessionsDoneTotal() override
int getProtocolSessionsLeft() override
BTSecurityLevel getSecurityLevel() override
~DBTServer01() override
void setProtocolSessionsLeft(const int v) override
BTAdapterRef getAdapter() override
Return the adapter for this endpoint.
int getDisconnectCount() override
void close(const std::string &msg) override
DBTServer01(const std::string &adapterName_, const jau::EUI48 &useAdapter_, const BTMode btMode_, const bool use_SC_, const BTSecurityLevel adapterSecurityLevel_, const bool do_disconnect_randomly_=false)
int getProtocolSessionsDoneSuccess() override
bool initAdapter(BTAdapterRef adapter) override
Initialize the given adapter for this endpoint.
HCIStatusCode startAdvertising(const std::string &msg) override
std::string getName() override
Return name of this endpoint, which becomes the adapter's name.
BTAdapter status listener for remote BTDevice discovery events: Added, updated and removed; as well a...
Definition: BTAdapter.hpp:127
BTAdapter represents one local Bluetooth Controller.
Definition: BTAdapter.hpp:324
std::string toString() const noexcept override
Definition: BTAdapter.hpp:1331
Listener to remote master device's operations on the local GATT-Server.
Collection of 'Extended Advertising Data' (EAD), 'Advertising Data' (AD) or 'Extended Inquiry Respons...
Definition: BTTypes0.hpp:898
std::string toString(const bool includeServices=true) const noexcept
Definition: BTTypes0.cpp:854
void setServicesComplete(const bool v) noexcept
Definition: BTTypes0.hpp:999
void addFlags(GAPFlags f) noexcept
Definition: BTTypes0.hpp:992
bool addService(const std::shared_ptr< const jau::uuid_t > &uuid) noexcept
Definition: BTTypes0.cpp:834
void setConnInterval(const uint16_t min, const uint16_t max) noexcept
Set slave connection interval range.
Definition: BTTypes0.hpp:1014
Persistent endian aware octet data, i.e.
Definition: octets.hpp:560
std::string toString() const
Definition: octets.hpp:932
void put_string_nc(const nsize_t i, const std::string &v, const nsize_t max_len, const bool includeEOS) noexcept
Definition: octets.hpp:457
Transient read only and endian aware octet data, i.e.
Definition: octets.hpp:67
std::string toString() const noexcept
Definition: octets.hpp:288
constexpr nsize_t size() const noexcept
Returns the used memory size for read and write operations, may be zero.
Definition: octets.hpp:162
constexpr uint8_t const * get_ptr() const noexcept
Definition: octets.hpp:272
Inspired by std::latch of C++20.
Definition: latch.hpp:50
size_t value() const noexcept
Return the current atomic internal counter.
Definition: latch.hpp:94
bool wait_for(const fraction_i64 &timeout_duration) const noexcept
Blocks the calling thread until the internal counter reaches 0 or the given timeout duration has expi...
Definition: latch.hpp:210
void count_down(const size_t n=1) noexcept
Atomically decrements the internal counter by n and notifies all blocked wait() threads if zero is re...
Definition: latch.hpp:108
void count_up(const size_t n=1) noexcept
Atomically increments the internal counter by n, i.e.
Definition: latch.hpp:136
Service runner, a reusable dedicated thread performing custom user services.
bool stop() noexcept
Stops this service, if running.
bool shall_stop() const noexcept
Returns true if service shall stop.
void start() noexcept
Starts this service, if not running already.
DBGattServerRef dbGattServer(new DBGattServer(jau::make_darray(std::make_shared< DBGattService >(true, std::make_unique< const jau::uuid16_t >(GattServiceType::GENERIC_ACCESS), jau::make_darray(std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::DEVICE_NAME), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue("Jausoft_Dev", 128), true), std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::APPEARANCE), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue((uint16_t) 0)))), std::make_shared< DBGattService >(true, std::make_unique< const jau::uuid16_t >(GattServiceType::DEVICE_INFORMATION), jau::make_darray(std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::MANUFACTURER_NAME_STRING), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue("Gothel Software")), std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::MODEL_NUMBER_STRING), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue("2.4.0-pre")), std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::SERIAL_NUMBER_STRING), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue("sn:0123456789")), std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::HARDWARE_REVISION_STRING), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue("hw:0123456789")), std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::FIRMWARE_REVISION_STRING), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue("fw:0123456789")), std::make_shared< DBGattChar >(std::make_unique< const jau::uuid16_t >(GattCharacteristicType::SOFTWARE_REVISION_STRING), BTGattChar::PropertyBitVal::Read, jau::darray< DBGattDescRef >(), make_gvalue("sw:0123456789")))))))
std::shared_ptr< DBTServer01 > DBTServer01Ref
uint32_t dfa_utf8_decode(uint32_t &state, uint32_t &codep, const uint32_t byte_value)
@ little
Identifier for little endian, equivalent to endian::little.
std::string to_string(const alphabet &v) noexcept
Definition: base_codec.hpp:97
constexpr const jau::fraction_i64 THREAD_SHUTDOWN_TIMEOUT_MS
Maximum time in fractions of seconds to wait for a thread shutdown.
Definition: DBTConst.hpp:73
SMPPairingState
SMP Pairing Process state definition.
Definition: SMPTypes.hpp:107
Entry * getStartOf(const EUI48 &addr, const std::string &name) noexcept
Returns a matching Entry,.
BTMode
Bluetooth adapter operating mode.
Definition: BTTypes0.hpp:112
void clear() noexcept
Clears internal list.
LE_Features
HCI Supported Commands.
Definition: BTTypes0.hpp:162
DiscoveryPolicy
Discovery policy defines the BTAdapter discovery mode after connecting a remote BTDevice:
Definition: BTAdapter.hpp:82
std::shared_ptr< BTDevice > BTDeviceRef
Definition: BTDevice.hpp:1347
std::shared_ptr< BTAdapter > BTAdapterRef
Definition: BTAdapter.hpp:1354
LE_PHYs
LE Transport PHY bit values.
Definition: BTTypes0.hpp:231
ScanType
Meta ScanType as derived from BTMode, with defined value mask consisting of BDAddressType bits.
Definition: BTTypes0.hpp:350
AdapterSetting
Adapter Setting Bits.
Definition: BTTypes1.hpp:144
constexpr bool isAdapterSettingBitSet(const AdapterSetting mask, const AdapterSetting bit) noexcept
Definition: BTTypes1.hpp:185
BTSecurityLevel
Bluetooth Security Level.
Definition: BTTypes0.hpp:267
AD_PDU_Type
LE Advertising (AD) Protocol Data Unit (PDU) Types.
Definition: BTTypes0.hpp:397
PairingMode
Bluetooth secure pairing mode.
Definition: BTTypes0.hpp:317
HCIStatusCode
BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes.
Definition: HCITypes.hpp:138
EIRDataType
Bit mask of 'Extended Inquiry Response' (EIR) data fields, indicating a set of related data.
Definition: BTTypes0.hpp:838
std::shared_ptr< DBGattDesc > DBGattDescRef
jau::POctets make_gvalue(const char *name)
Convenience jau::POctets ctor function to create DBGattChar or DBGattDesc values.
std::shared_ptr< DBGattService > DBGattServiceRef
std::shared_ptr< DBGattServer > DBGattServerRef
std::shared_ptr< DBGattChar > DBGattCharRef
constexpr darray< First > make_darray(First &&arg1, Next &&... argsN)
Construct a darray<T> instance, initialized by move semantics from the variadic (template pack) argum...
Definition: darray.hpp:1373
bool remove(const std::string &path, const traverse_options topts=traverse_options::none) noexcept
Remove the given path.
Definition: file_util.cpp:1173
jau::function< R(A...)> bind_member(C1 *base, R(C0::*mfunc)(A...)) noexcept
Bind given class instance and non-void member function to an anonymous function using func_member_tar...
constexpr uint32_t number(const iostate rhs) noexcept
Definition: byte_stream.hpp:72
std::string to_hexstring(value_type const &v) noexcept
Produce a lower-case hexadecimal string representation of the given pointer.
std::string to_decstring(const value_type &v, const char separator=',', const nsize_t width=0) noexcept
Produce a decimal string representation of an integral integer value.
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
int fprintf_td(const uint64_t elapsed_ms, FILE *stream, const char *format,...) noexcept
Convenient fprintf() invocation, prepending the given elapsed_ms timestamp.
Definition: debug.cpp:270
bool sleep_for(const fraction_timespec &relative_time, const bool monotonic=true, const bool ignore_irq=true) noexcept
sleep_for causes the current thread to block until a specific amount of time has passed.
constexpr int getPairingPasskey() const noexcept
constexpr bool getPairingNumericComparison() const noexcept
A packed 48 bit EUI-48 identifier, formerly known as MAC-48 or simply network device MAC address (Med...
Definition: eui48.hpp:324
static const EUI48 ALL_DEVICE
EUI48 MAC address matching all device, i.e.
Definition: eui48.hpp:324
CXX_ALWAYS_INLINE _Tp load() const noexcept
@ GENERIC_ACCESS
This service contains generic information about the device.
@ DEVICE_INFORMATION
This service exposes manufacturer and/or vendor information about a device.
@ Notify
@ Indicate
@ WriteNoAck
@ Read
@ WriteWithAck
@ SOFTWARE_REVISION_STRING
@ SERIAL_NUMBER_STRING
@ MODEL_NUMBER_STRING
@ DEVICE_NAME
@ APPEARANCE
@ MANUFACTURER_NAME_STRING
@ FIRMWARE_REVISION_STRING
@ HARDWARE_REVISION_STRING